Начни здесь

scout: радар экосистемы навыков

Практический курс по @dzhechkov/scout для того, кто поддерживает свой харнес AI-агента и устал вручную листать GitHub. Вместе с Леной ты запустишь радар по одиннадцати источникам, разберёшь формулу релевантности по частям, научишься читать отчёт, включишь глубокий анализ и выберешь путь интеграции для найденного навыка, а в конце поймёшь, почему ноль в отчёте — ещё не факт о мире.

Знакомься: Лена

Лена поддерживает харнес AI-агента для небольшой команды. Раз в неделю она тратила вечер на то, чтобы вручную искать новые наборы навыков и MCP-серверы — и каждый раз что-то пропускала. Теперь она осваивает scout, и на каждой секции идёт на шаг впереди тебя: сначала ошибается сама, потом показывает, как правильно.

Содержание курса

1. Зачем нужен scout

Что такое scout одной фразой — и почему это не «ещё один поиск по GitHub».

Ключевая мысль: разведка экосистемы навыков

Лена поддерживает харнес AI-агента для своей команды. Харнес — это вся обвязка вокруг модели: навыки, команды, хуки, память. И у неё была еженедельная боль: каждую пятницу она открывала GitHub, вбивала «claude skills», листала три страницы, потом шла на npm, потом в реестр MCP-серверов — и всё равно через неделю узнавала от коллеги о наборе навыков, который «все уже поставили».

scout — это разведка экосистемы навыков: одна команда, которая опрашивает одиннадцать источников, оценивает каждую находку по одной формуле и говорит, что с ней делать. Не «вот список ссылок», а «вот три репозитория, которые стоит интегрировать, восемь — понаблюдать, остальное — пропустить».

Что это меняет на практике:

  • вместо ручного обхода сайтов — dz scout, один вызов;
  • вместо «кажется, где-то видела» — память: scout помнит, что уже показывал, и отмечает по-настоящему новое;
  • вместо интуиции — число от 0 до 100 и рекомендация с понятной причиной.

Пакет живёт на npm: страница @dzhechkov/scout на npmjs.com, исходники — в публичном репозитории DZ Harness Hub на GitHub. Командой dz scout он доступен из @dzhechkov/harness-cli.

Компромисс: ты получаешь широкий охват и единую шкалу, но платишь тем, что оценка — эвристика. Она сортирует, а не решает: последнее слово всё равно за тобой.

💬 Не обязательно помнить команды. Внутри Claude Code или Codex достаточно сказать помощнику «посмотри, что нового появилось в экосистеме навыков» — и он сам выполнит dz scout. Команды в этом курсе нужны, чтобы ты понимал, что происходит под капотом.

2. Одиннадцать источников

Кто именно отвечает на запрос радара — от GitHub до arXiv — и почему их не пять.

Ключевая мысль: одиннадцать источников

Первое, что спросила Лена: «а почему одиннадцать? Мне хватило бы GitHub». Ответ — в истории пакета: он начинался с одного GitHub, и именно тогда Лена пропускала MCP-серверы, которые на GitHub помечены как угодно, только не темой mcp-server.

Сейчас scout опрашивает одиннадцать источников, и они делятся на четыре семьи:

  1. Код и пакеты — GitHub (репозитории по темам agent-skills, claude-code-skills, mcp-server; нужен токен) и реестр npm (пакеты с ключевыми словами mcp-server, claude-code, agent-skills; без авторизации).
  2. Реестры MCP-серверов — официальный реестр modelcontextprotocol.io, метареестр Glama.ai (по README — 29 900+ серверов) и маркетплейс Smithery.ai (7 300+ серверов).
  3. Пульс сообщества — Hacker News через поиск Algolia и OSSInsight Trending: сто репозиториев с самым быстрым ростом звёзд за неделю.
  4. Наука — Semantic Scholar (не чаще одного запроса в секунду) и arXiv (пауза три секунды между запросами): статьи про использование инструментов агентами.

Плюс два курируемых репозитория, которые сканируются целиком как инвентарь навыков: affaan-m/ECC и DreamLab-AI/agentbox (больше ста навыков, но без лицензии — поэтому scout по умолчанию ставит ему рекомендацию «наблюдать»).

Важная деталь механики: девять быстрых источников опрашиваются параллельно, а два академических — последовательно после них, потому что у них обязательные паузы между запросами. Результаты склеиваются и дедуплицируются по полному имени репозитория.

Компромисс: широкий охват стоит времени — академические источники с их паузами удлиняют прогон; зато ни один класс находок не остаётся слепым пятном.

3. Установка и первый запуск радара

npm i -g, токен GitHub и первый dz scout — от пустого терминала до первого отчёта.

Ключевая мысль: первый запуск радара

Хватит вводных — Лена уже открыла терминал, открывай и ты. Первый запуск радара занимает три шага:

  1. Поставь командную оболочку харнеса: npm i -g @dzhechkov/harness-cli — команда dz scout приезжает вместе с ней, отдельно ставить @dzhechkov/scout не нужно.
  2. Положи токен GitHub в окружение: export GITHUB_TOKEN=.... Без него запросы к GitHub идут с лимитом 60 в час вместо 5 000 — и три источника, которые ходят в GitHub (сам GitHub, ECC и AgentBox), быстро в него упираются. Команда предупредит об отсутствующем токене одной строкой: dz scout: set GITHUB_TOKEN env var for higher rate limits.
  3. Запусти радар: dz scout.

Первая строка вывода — баннер Scanning ... sources, дальше — сводка по источникам вида Sources: github: 50, npm: 42, hackernews: 30, ..., строка памяти Memory: ... total tracked, ... new this scan и сам отчёт. Радар берёт до 50 лучших находок.

Полезные вариации первого запуска:

  • dz scout --since 2026-05-01 — только репозитории, обновлённые после даты (фильтр действует на GitHub-поиск и Hacker News);
  • dz scout --topics mcp-server,ai-agent — свои темы вместо шести тем по умолчанию (agent-skills, claude-code-skills, agentskills-io, mcp-server, ai-harness, claude-code-plugin); темы касаются только GitHub.

Грабли Лены — не повторяй: она запустила dz scout с давно отозванным токеном и получила отчёт, в строке Sources: которого имени github не было вовсе. Отчёт выглядел нормально — просто в нём не было главного. Почему это опасно и как scout теперь об этом предупреждает — в последней секции курса.

Компромисс: первый запуск прост — три команды, — но он ходит в сеть по одиннадцати адресам, и результат зависит от того, кто из них ответил. Полный прогон с академическими источниками занимает заметно больше времени, чем «мгновенно».

4. Радарный режим: шесть шагов

Что происходит между баннером «Scanning … sources» и таблицей отчёта — по порядку.

Ключевая мысль: шесть шагов радарного режима

Лена запустила радар и смотрит на бегущие строки. Между первой строкой и таблицей отчёта scout проходит шесть шагов радарного режима — и порядок здесь не случаен: каждый шаг готовит данные для следующего.

  1. Сканирует одиннадцать источников — быстрые параллельно, академические следом — и склеивает результаты без дублей.
  2. Определяет формат каждой находки: SKILL.md, plugin.json, каталог .claude/skills/, манифест MCP-сервера.
  3. Считает релевантность по одной формуле: формат 40 %, звёзды 30 %, свежесть 20 %, новизна 10 %.
  4. Сверяет со встроенным инвентарём — списком наших пакетов и известных навыков — и находит то, чего у нас нет.
  5. Выдаёт рекомендацию по каждой находке: интегрировать, наблюдать или пропустить.
  6. Показывает происхождение — какой именно источник нашёл каждую строку.

Зачем шаг 6, если отчёт уже готов? Лена поняла это на второй неделе: когда какая-то строка вызывает сомнение, первый вопрос — «откуда она?». Находка из OSSInsight Trending и находка из arXiv — это разные типы свидетельств, и читать их надо по-разному.

Обрати внимание на границу: шаги 1–3 работают с тем, что пришло из сети, шаги 4–6 — с тем, что scout знает о ТЕБЕ. Именно поэтому один и тот же репозиторий у разных команд получает разную новизну: инвентарь у всех свой.

Компромисс: такая конвейерная схема прозрачна и легко объяснима, но каждая эвристика в ней (формат по темам, новизна по совпадению имён) даёт ложные срабатывания — ниже мы разберём каждую и увидим, где именно.

5. Как scout узнаёт формат навыка

Шесть форматов, их веса и неожиданный факт: слово skill.md в описании тоже считается.

Ключевая мысль: детектор форматов навыков

Лена увидела в отчёте колонку Formats со значением agentskills-io и спросила: «а откуда scout это знает, он же не скачивал репозиторий?» Хороший вопрос — в радарном режиме scout действительно НЕ читает файлы. Детектор форматов навыков смотрит только на темы репозитория и текст его описания.

Вот таблица из исходников (src/analyzer.ts), она же — самая весомая часть формулы оценки:

  • agentskills-io — вес 1.0: тема agentskills-io или agent-skills, либо слово skill.md в описании;
  • claude-plugin — 0.9: тема claude-code-plugin, либо plugin.json / .claude-plugin в описании;
  • claude-skills — 0.85: тема claude-code-skills или claude-code, либо .claude/skills в описании;
  • codex-skills — 0.7: тема codex или .agents/skills в описании;
  • mcp-server — 0.7: тема mcp-server или mcp, либо «mcp server» в описании;
  • generic-agent — 0.3: если ничего из списка не сработало, но есть тема ai-agent или слово «agent» в описании.

У репозитория может быть несколько форматов сразу; в формулу идёт лучший вес, умноженный на 40. То есть репозиторий с темой agent-skills получает сорок баллов из ста только за правильную тему — ещё до того, как посчитали звёзды.

А теперь неожиданность, которую Лена нашла сама: репозиторий с описанием «my todo app, skill.md coming soon» получит формат agentskills-io и все сорок баллов. Детектор честный, но наивный — он верит словам. Это и есть причина, почему рекомендация «интегрировать» — повод посмотреть, а не команда к действию.

Компромисс: детектор по темам и описанию — мгновенный и не ходит в сеть за каждым репозиторием; за это он платит ложными срабатываниями на словах в описании. Глубокий режим (--deep) исправляет именно это — он уже читает файлы.

6. Формула релевантности: 40 / 30 / 20 / 10

Из чего складывается число 0–100 и где проходят пороги «интегрировать / наблюдать / пропустить».

Ключевая мысль: формула релевантности

Прежде чем читать дальше, реши за Лену сам. Два репозитория: первый — тема agent-skills, 40 звёзд, коммит вчера; второй — тема mcp, 5 000 звёзд, последний коммит полтора года назад. Какой получит больше баллов? Запомни ответ — сейчас проверим.

Формула релевантности складывает четыре слагаемых, и сумма всегда в пределах 0–100:

  1. Формат — до 40. Лучший вес формата × 40 (прошлая секция).
  2. Звёзды — до 30. Не линейно, а по логарифму: min(1, log2(звёзды + 1) / 10) × 30. Насыщение наступает примерно на 1 023 звёздах — дальше рост звёзд ничего не добавляет. Сорок звёзд дают около 16 баллов, пять тысяч — все 30.
  3. Свежесть — до 20. Ступеньками по дням с последнего коммита: моложе 30 дней — 1.0; моложе 90 — 0.7; моложе года — 0.3; старше — 0.1. Умножается на 20.
  4. Новизна — до 10. Доля навыков-подсказок из тем репозитория, которых нет в нашем инвентаре, × 10.

Теперь пороги рекомендации:

  • интегрировать — оценка ≥ 70;
  • наблюдать — оценка от 40 до 69 и не меньше 50 звёзд;
  • пропустить — всё остальное.

Сверь свой прогноз. Первый репозиторий: 40 (формат) + ~16 (звёзды) + 20 (свежесть) + новизна — уже под 80, «интегрировать». Второй: 28 (mcp, 0.7 × 40) + 30 (звёзды) + 2 (старше года) — около 60, и при пяти тысячах звёзд это «наблюдать». Лена ставила на звёзды — и промахнулась: формат и свежесть вместе весят 60, звёзды — только 30.

Компромисс: логарифм по звёздам защищает от гигантов-ветеранов, а ступеньки свежести — от заброшенных проектов; но веса зашиты в код, и если для тебя звёзды важнее свежести, перенастроить их из командной строки нельзя.

7. Как читать отчёт разведки

Сводка по источникам, три таблицы по цветам и колонка «Novel skills» — что где искать.

Ключевая мысль: отчёт разведки

Лена получила свой первый отчёт и растерялась: две сотни строк, эмодзи, таблицы. Давай прочитаем отчёт разведки сверху вниз, как его печатает scout, — и ты увидишь, что смотреть нужно всего в четыре места.

Место 1 — строка источников. Sources: github: 50, npm: 42, hackernews: 30, mcp-registry: 20, .... Это сколько находок дал КАЖДЫЙ источник до дедупликации. Важная деталь из исходника команды (harness-cli/src/cli.ts): источник с нулём в эту строку не попадает вовсе — его имя просто исчезает. Первая проверка Лены теперь всегда здесь: пересчитать имена. Нет github — читать остальное рано (почему — в последней секции).

Место 2 — шапка. Total found: 216 | Scanned: 216 | New: 14 — сколько всего нашлось, сколько попало в отчёт после склейки дублей и сколько из них scout видит впервые (в первом прогоне здесь будет число всех находок).

Место 3 — три таблицы по цветам. Это те самые пороги из прошлой секции:

  • 🟢 Integrate — оценка ≥ 70. Сюда Лена смотрит первым делом, и обычно здесь три–пять строк;
  • 🟡 Monitor — 40–69 и не меньше 50 звёзд. Кандидаты на следующую неделю;
  • ⚪ Skip — всё остальное. Пролистать, но не удалять: через --diff видно, кто отсюда вырос.

В каждой таблице пять колонок: репозиторий (ссылкой), звёзды, форматы, оценка и Novel skills — навыки, извлечённые из тем репозитория, которых нет в нашем инвентаре. Если в колонке прочерк — новизны по темам не найдено, и десять баллов новизны репозиторий не получил.

Место 4 — разделы --deep. «🔬 Deep Analysis» и «📊 Harness Gap Analysis» появляются только в глубоком режиме. Если их нет — ты запускал радар, а не аналитика.

Компромисс: отчёт — это markdown, его удобно читать в терминале и вставлять в заметки, но это не структурированные данные: для автоматической обработки бери программный API, а не парси текст.

8. Глубокий анализ и путь интеграции

Что делает --deep: скачивает SKILL.md, ищет ближайший наш навык и выбирает один из четырёх путей интеграции.

Ключевая мысль: путь интеграции

Радар сказал Лене «интегрировать» — и тут же встал следующий вопрос: интегрировать КАК? Скопировать целиком? Добавить кусок в наш похожий навык? Этот вопрос радар не решает: он не читал файлы. Его решает глубокий анализ — dz scout --deep.

Что он делает, шаг за шагом:

1. Берёт репозитории с оценкой ≥ 50 (заметь: ниже порога «интегрировать» — чтобы посмотреть и на кандидатов из «наблюдать»), только с GitHub и не больше десяти лучших.
2. Скачивает дерево репозитория, находит файлы SKILL.md — не больше пяти на репозиторий — и читает их frontmatter: имя и описание.
3. Ищет ближайший наш навык по совпадению ключевых слов описания: нужно как минимум два общих слова, иначе совпадения нет.
4. Выбирает путь интеграции — один из четырёх, строго по порядку проверок:
- skip — навык с таким именем уже есть в нашем инвентаре;
- merge — ближайший наш навык найден: добавить к нему уникальное, а не дублировать;
- canonicalize — ближайшего нет и у репозитория ≥ 100 звёзд: сильный сигнал, делать новый пак @dzhechkov/skills-*;
- new-preset — ближайшего нет, звёзд меньше ста: добавить в пресет или завести пак, когда сигнал подтвердится.
5. Пишет дельту — что найденный навык добавляет к нашему ближайшему.

Порядок проверок важен: Лена сначала решила, что canonicalize — это «лучшая» рекомендация. Нет: она означает «похожего у нас нет», а это не всегда хорошо — иногда нет, потому что и не нужно. Самый ценный путь на практике — merge: чужая идея усиливает уже работающий навык.

Сейчас твоя очередь принимать решения — в сценарии ниже ты и есть аналитик, а не scout.

Компромисс: глубокий режим читает реальные файлы и потому точнее радара, но каждый репозиторий — это несколько запросов к GitHub, а инвентарь описаний для сравнения — статичный снимок внутри пакета; чем дальше твой харнес ушёл от него, тем чаще «ближайший» будет промахиваться.

9. Анализ пробелов: чего не хватает твоему харнесу

Раздел Gap Analysis: тренды экосистемы, которых у тебя нет, и академические темы из arXiv.

Ключевая мысль: анализ пробелов

До сих пор scout отвечал на вопрос «что нового появилось?». Лена задала другой, куда более неудобный: «а чего у меня НЕТ, о чём я даже не думала?» На него отвечает анализ пробелов — раздел «📊 Harness Gap Analysis», который появляется в отчёте только в глубоком режиме.

Как он строится:

  1. У каждого репозитория, прошедшего глубокий анализ, берутся его темы — например ai-agents, mcp, deploy-automation. Каждая тема — это категория; scout считает, сколько репозиториев в неё попало.
  2. К категориям из кода добавляются академические темы: в описаниях статей arXiv, Semantic Scholar и историй Hacker News ищутся слова вроде «tool use», «multi-agent», «planning», «grounding», «alignment». Каждое попадание — плюс один к частоте темы.
  3. Для известных категорий scout подставляет короткое «What it is» — что это вообще такое, — чтобы Лене не пришлось гуглить runtime-governance.
  4. Рекомендация по каждой строке — например «Create @dzhechkov/skills-ai-agents» при большой частоте или «Monitor — academic research trend» для темы из статей.

Пример из README: ai-agents — 15 repos — Create @dzhechkov/skills-ai-agents; tool use — 5 papers — Monitor — academic research trend.

Вот тут начинается открытый вопрос, на который scout не ответит за тебя. Частота 15 репозиториев — это спрос экосистемы или мода недели? Пять статей про «tool use» — это пробел в твоём харнесе или тема, которая в нём не нужна вовсе? Лена завела правило: строка анализа пробелов — это гипотеза для бэклога, а не задача. Она записывает её и смотрит, повторится ли на следующей неделе. Повторилась дважды — тогда берётся.

Компромисс: анализ пробелов — единственное место, где scout говорит о том, чего ты не искал; но он считает частоту тем, а частота — это популярность, не ценность для твоей команды. Различить их можешь только ты.

10. Память: scout помнит, что уже видел

Каталог .dz/scout/, firstSeen/lastSeen, лимиты хранения и почему твои решения никогда не стираются.

Ключевая мысль: постоянная память сканирований

На второй неделе Лена заметила в шапке отчёта новое число: рядом с Scanned: 216 появилось «новых: 14». Откуда scout знает, что новых именно четырнадцать? Из постоянной памяти сканирований — каталога .dz/scout/ в проекте.

Три файла в нём:

  • scan-history.json — по записи на каждый виденный репозиторий: полное имя, firstSeen и lastSeen (даты первого и последнего появления), последняя оценка, рекомендация, источник и — если ты его записал — твоё решение userDecision;
  • last-report.json — последний отчёт целиком, для чтения без сети;
  • config.json — необязательные лимиты хранения.

Как память обновляется: после каждого прогона scout вливает результаты в историю. Знакомый репозиторий получает новые lastSeen и оценку; незнакомый заводится с firstSeen = сейчас и считается новым. Именно это число ты видишь в шапке.

Лимиты, чтобы история не разрасталась вечно (значения по умолчанию, меняются в config.json):

  • retentionDays: 90 — запись, которую не видели дольше 90 дней, удаляется;
  • maxEntries: 500 — при переполнении удаляются самые старые по lastSeen.

И одно правило, которое Лена оценила больше всего: запись с твоим решением не удаляется никогда — ни по возрасту, ни по переполнению. Если ты пометил репозиторий как «пропустить», scout не забудет этого через три месяца и не предложит его снова как новинку. Память здесь — не кэш, а твой журнал решений.

Остановись на секунду и подумай: сколько раз за последний месяц ты заново оценивал то, что уже оценивал? Именно этот повторный труд память убирает.

Компромисс: память живёт в каталоге проекта, а не в общем хранилище — на другой машине scout начнёт с чистого листа; зато она обычный JSON, и перенести её — значит скопировать три файла.

11. Флаги --diff и --report: что изменилось, без сети

Три раздела дифа — новые, исчезнувшие, изменившие оценку — и отчёт из кэша без единого запроса.

Ключевая мысль: флаги --diff и --report

В поезде без интернета Лена захотела перечитать пятничный отчёт — и обнаружила, что для этого не нужна сеть. Два флага работают поверх памяти из прошлой секции: флаги --diff и --report.

dz scout --report — показать последний сохранённый отчёт из .dz/scout/last-report.json. Ни одного сетевого запроса. Если отчёта ещё нет — scout так и скажет, а не покажет пустую таблицу.

dz scout --diff — сравнить текущий скан с историей и показать раздел «🔄 Changes Since Last Scan» из трёх частей:

  • 🆕 New — репозитории, которых не было в истории (до двадцати строк со ссылкой, оценкой и рекомендацией);
  • ❌ Gone — были в истории, а в этом скане не появились (до десяти);
  • 📈 Score Changed — оценка изменилась на 5 баллов и больше, со стрелкой вверх или вниз.

Небольшая тонкость из исходника команды: начиная со второго прогона раздел изменений печатается и без флага — команда показывает его всякий раз, когда память не пуста; --diff лишь гарантирует его. Так что не удивляйся, если увидел его, не прося.

Порог в пять баллов — сознательный: свежесть считается ступеньками по дням, и без порога каждую неделю «менялись» бы десятки строк только потому, что репозиторий стал на день старше.

А теперь сюрприз, который поймал Лену. Раздел Gone не означает «репозиторий удалили». Он означает «в ЭТОМ скане его не нашли» — а не нашли его, возможно, потому что источник, который его давал, в этот раз не ответил. Дюжина строк в Gone сразу — это сигнал проверить строку Sources:, а не хоронить проекты. Об этом — последняя секция.

Если ничего не изменилось, scout честно печатает «No changes since last scan» — пустой раздел он не рисует.

Компромисс: --diff превращает еженедельный прогон из «прочитай двести строк» в «прочитай пятнадцать», но он сравнивает с историей, а история подчиняется лимитам хранения — репозиторий, стёртый по возрасту, всплывёт как «новый».

12. Программный API: scout внутри твоего кода

scanAllSources, generateReport, deepAnalyze и ScoutMemory — те же шаги радара, но из TypeScript.

Ключевая мысль: программный API

Лене надоело копировать отчёт в заметки руками, и она захотела складывать находки прямо в бэклог команды. Для этого есть программный API — пакет @dzhechkov/scout экспортирует те же шаги, что проходит dz scout, по одной функции на шаг.

Минимальный сценарий — три вызова:

import { scanAllSources, generateReport, deepAnalyze } from '@dzhechkov/scout';

const { results, totalBySource, statusBySource } = await scanAllSources({ token: process.env.GITHUB_TOKEN });
const report = generateReport({ repos: results, totalFound: results.length, newSinceLastScan: 0, scannedAt: new Date().toISOString(), topics: [] });
const deep = await deepAnalyze(results, { token: process.env.GITHUB_TOKEN });
```

Что здесь что:

  1. scanAllSources — радар целиком: одиннадцать источников, дедупликация, сортировка по оценке. Возвращает results (профили с тегом источника), totalBySource (строка Sources: в виде объекта) и statusBySource — здоровье каждого источника, о нём в последней секции.
  2. generateReport — превращает результаты в объект отчёта с полями summary (сколько интегрировать / наблюдать / пропустить) и markdown — тот самый текст, который печатает команда.
  3. deepAnalyze — глубокий анализ; принимает minScore (по умолчанию 50) и возвращает разборы по репозиториям, список пробелов и markdown.

Отдельные источники тоже экспортируются — scanNpm, scanHN, scanMcpRegistry, scanGlama и остальные — если тебе нужен один канал, а не радар. А класс ScoutMemory даёт то, чего нет в командной строке: метод recordDecision(fullName, 'integrate' | 'monitor' | 'skip') записывает твоё решение в память — ту самую запись, которая никогда не удаляется.

Лена написала двадцать строк: скан → report.summary.integrate → по каждой строке «интегрировать» создать карточку в бэклоге → recordDecision, чтобы не создать её дважды. Пятничный вечер стал пятиминутным.

Компромисс: программный API даёт структуру вместо markdown и доступ к решениям, но это ESM-пакет для Node 20+, и его сигнатуры — часть версии: обновляя пакет, перечитывай README, а не полагайся на память.

13. Ноль — это не находка: здоровье источника

История одного отозванного токена: почему «github: 0» молчал, и как scout теперь отличает пустой ответ от отсутствия ответа.

Ключевая мысль: здоровье источника

Помнишь грабли Лены из третьей секции — отчёт без GitHub, который выглядел нормально? Эта секция про то, почему так вышло, и почему сегодня так уже не выйдет. Она опирается не на README, а на комментарий в исходнике src/sources/index.ts, где записан измеренный случай.

Что было измерено (2026-08-22). С отозванным GITHUB_TOKEN сканер GitHub упал с ошибкой 401 Bad credentials. Но одиннадцать источников опрашиваются через Promise.allSettled — конструкцию, которая по определению не роняет весь прогон из-за одного отказа. Отказ был проглочен, и в строке Sources: напечаталось github: 0. Главный источник разведки молчал, и это было неотличимо от «на GitHub ничего нового нет». Хуже того: команда предупреждала только об ОТСУТСТВИИ токена — плохой токен был тише, чем никакого.

Что изменилось. У каждого источника появилось здоровье источника — поле SourceHealth со значениями ok или failed, плюс первая строка причины отказа. Правило одно: ноль со статусом ok — это измеренный ноль, факт о мире; ноль со статусом failed — это отсутствие измерения, и печататься одинаково они не должны. В программном API это statusBySource, который возвращает scanAllSources.

Честная оговорка про командную строку. На момент написания курса сама команда dz scout (harness-cli/src/cli.ts) этот статус НЕ печатает: она берёт из scanAllSources только результаты и счётчики и убирает из строки Sources: источники с нулём. Поэтому в терминале отказ выглядит как отсутствие имени источника в строке — а причину отказа увидишь только из своего кода через statusBySource. Знать это важнее, чем верить README.

Общий урок, который Лена унесла из этой истории дальше scout: пустой результат и отсутствующий результат — разные вещи, и инструмент обязан их различать. Если твой отчёт, тест или мониторинг выводит «всё чисто» из тишины, он сломается молча на первом же новом способе отказа.

Поэтому у Лены теперь ритуал: в терминале — пересчитать имена в строке Sources:; в своём скрипте — прочитать statusBySource до того, как трогать результаты. Ноль у GitHub при статусе ok — редкость, но бывает; ноль при failed — чинить токен, а не читать отчёт.

Компромисс: статус здоровья добавляет в вывод ещё одну строку, которую надо читать; зато он убирает целый класс тихих ошибок — «отчёт выглядит нормально, а главного в нём нет».

Частые вопросы

Нужно ли ставить @dzhechkov/scout отдельно?

Нет. Команда dz scout приезжает вместе с @dzhechkov/harness-cli (npm i -g @dzhechkov/harness-cli). Отдельно пакет нужен только для программного API в своём коде.

Что будет без GITHUB_TOKEN?

GitHub, ECC и AgentBox ходят в GitHub без авторизации с лимитом 60 запросов в час вместо 5 000 — команда предупредит об отсутствующем токене. С отозванным (а не отсутствующим) токеном GitHub отвечает 401: в программном API это статус failed с причиной, а в терминале имя github просто исчезает из строки Sources.

Почему репозиторий с тысячами звёзд попал в «наблюдать», а не в «интегрировать»?

Звёзды весят максимум 30 баллов и насыщаются около 1 023. Формат (до 40) и свежесть (до 20) весят вместе больше; старый репозиторий с форматом mcp получает около 60 — это «наблюдать».

Чем отличаются dz scout и dz scout --deep?

Радар (dz scout) смотрит на темы и описания и не скачивает файлы. Глубокий режим (--deep) для репозиториев с оценкой от 50 скачивает SKILL.md, ищет ближайший наш навык, выбирает путь интеграции и строит анализ пробелов.

Что значит canonicalize в разделе Deep Analysis?

Ближайшего нашего навыка не нашлось и у репозитория не меньше 100 звёзд: сильный сигнал завести новый пак @dzhechkov/skills-*. Это не «лучшая» рекомендация, а «похожего у нас нет» — иногда потому, что и не нужно.

Где хранится память scout и можно ли её перенести?

В каталоге проекта .dz/scout/: scan-history.json, last-report.json и config.json. Это обычный JSON — перенос на другую машину равен копированию трёх файлов.

Почему в разделе Gone после --diff сразу много репозиториев?

Gone означает «не найден в этом скане», а не «удалён». Массовое исчезновение почти всегда значит, что источник не ответил — пересчитай имена в строке Sources (нулевые источники из неё исчезают), а в своём коде прочитай statusBySource.

Можно ли поменять веса формулы 40 / 30 / 20 / 10?

Из командной строки — нет, веса зашиты в src/analyzer.ts. Порог глубокого анализа (по умолчанию 50) задаётся параметром minScore в программном API deepAnalyze.

Бесплатный интерактивный курс · aicoding.space

Лицензии шрифтов

Onest

Copyright 2021 The Onest Project Authors (https://github.com/googlefonts/onest)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, 
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

JetBrainsMono

Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, 
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.