@rt-tools/agent-kit 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -0
- package/assets/checks/check-doc-paths.mjs +200 -30
- package/assets/checks/check-specs.mjs +42 -7
- package/assets/checks/rt-kit-checks.config.mjs +12 -0
- package/assets/commands/agent-kit-digest.md +83 -0
- package/assets/commands/next-session.md +122 -0
- package/assets/commands/skill-curator.md +33 -1
- package/assets/defaults/gate-map.sh +23 -1
- package/assets/defaults/project.sh +37 -1
- package/assets/docs/GLOSSARY.md +77 -0
- package/assets/hooks/docs-guard.sh +18 -1
- package/assets/hooks/git-guard-delivery.sh +30 -3
- package/assets/hooks/git-guard-main.sh +8 -0
- package/assets/hooks/git-guard-push-tests.sh +8 -1
- package/assets/hooks/grill-gate.sh +38 -16
- package/assets/hooks/lint-after-edit.sh +10 -3
- package/assets/hooks/observe.sh +90 -0
- package/assets/hooks/postmortem-guard.sh +92 -0
- package/assets/hooks/profile-check.sh +43 -0
- package/assets/hooks/qa-dataid-guard.sh +9 -2
- package/assets/hooks/reuse-first-guard.sh +9 -2
- package/assets/hooks/skill-gate.sh +15 -0
- package/assets/hooks/skill-loaded.sh +7 -0
- package/assets/hooks/task-context-load.sh +16 -2
- package/assets/hooks/task-flow-guard.sh +9 -2
- package/assets/hooks/window-fill-guard.sh +157 -0
- package/assets/laws/code-structure.md +10 -0
- package/assets/laws/delivery.md +8 -1
- package/assets/laws/project-documentation.md +9 -0
- package/assets/laws/verifiability.md +9 -0
- package/assets/laws/work-conduct.md +47 -0
- package/assets/patterns/git-workflow-commit.azure.md +16 -12
- package/assets/patterns/git-workflow-commit.github.md +16 -12
- package/assets/patterns/git-workflow-commit.gitlab.md +16 -12
- package/assets/patterns/spec-driven-domain.md +19 -0
- package/assets/patterns/task-flow-close.md +4 -4
- package/assets/patterns/task-flow-handoff.md +115 -0
- package/assets/patterns/task-flow-resume.md +14 -2
- package/assets/patterns/task-flow-start.md +40 -2
- package/assets/rules/doc-style.md +39 -1
- package/assets/rules/git-workflow.azure.md +39 -0
- package/assets/rules/git-workflow.github.md +38 -0
- package/assets/rules/git-workflow.gitlab.md +38 -0
- package/assets/rules/spec-driven.md +14 -0
- package/assets/rules/task-flow.md +70 -3
- package/assets/skills/agent-kit.md +32 -0
- package/assets/templates/postmortem.md +32 -0
- package/assets/templates/proposal.md +39 -0
- package/bin/agent-kit.d.ts.map +1 -1
- package/bin/agent-kit.js +50 -1
- package/bin/agent-kit.js.map +1 -1
- package/lib/catalog.d.ts +33 -0
- package/lib/catalog.d.ts.map +1 -1
- package/lib/catalog.js +55 -1
- package/lib/catalog.js.map +1 -1
- package/lib/commands.d.ts +41 -0
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +315 -3
- package/lib/commands.js.map +1 -1
- package/lib/config.d.ts +11 -1
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +6 -0
- package/lib/config.js.map +1 -1
- package/lib/hooks-map.d.ts +15 -3
- package/lib/hooks-map.d.ts.map +1 -1
- package/lib/hooks-map.js +47 -11
- package/lib/hooks-map.js.map +1 -1
- package/lib/observations.d.ts +72 -0
- package/lib/observations.d.ts.map +1 -0
- package/lib/observations.js +126 -0
- package/lib/observations.js.map +1 -0
- package/lib/proposals.d.ts +48 -0
- package/lib/proposals.d.ts.map +1 -0
- package/lib/proposals.js +111 -0
- package/lib/proposals.js.map +1 -0
- package/lib/submit.d.ts +24 -0
- package/lib/submit.d.ts.map +1 -0
- package/lib/submit.js +26 -0
- package/lib/submit.js.map +1 -0
- package/lib/sync.d.ts +9 -1
- package/lib/sync.d.ts.map +1 -1
- package/lib/sync.js +4 -6
- package/lib/sync.js.map +1 -1
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.5.2.tgz +0 -0
- package/rt-tools-agent-kit-0.5.0.tgz +0 -0
|
@@ -11,6 +11,8 @@ description: Правило под «Закон о ведении работы»
|
|
|
11
11
|
про ход работы; здесь — чем это названо в этом дереве, где лежит и что из закона у нас не
|
|
12
12
|
проверяется.
|
|
13
13
|
|
|
14
|
+
**Требует:** `hooks/task-flow-guard.sh`, `hooks/task-context-load.sh`, `hooks/grill-gate.sh`, `hooks/window-fill-guard.sh`
|
|
15
|
+
|
|
14
16
|
## Как это называется здесь
|
|
15
17
|
|
|
16
18
|
| В законе | Здесь |
|
|
@@ -31,6 +33,34 @@ description: Правило под «Закон о ведении работы»
|
|
|
31
33
|
переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
|
|
32
34
|
же дереве, которое держит код иначе.
|
|
33
35
|
|
|
36
|
+
## Шаги работы
|
|
37
|
+
|
|
38
|
+
Счёт сплошной: номер шага один на весь путь и не начинается заново в каждом паттерне. Шаг
|
|
39
|
+
называют номером вслух, и два разных первых шага мешают разговору; а пропущенный шаг виден
|
|
40
|
+
только по списку, из которого он выпал.
|
|
41
|
+
|
|
42
|
+
| Шаг | Что происходит | Ведёт паттерн |
|
|
43
|
+
| --- | -------------------------------------------------------- | ------------------- |
|
|
44
|
+
| 1 | Разведка по дереву — до первого вопроса владельцу | `task-flow-start` |
|
|
45
|
+
| 2 | Разбор просьбы с владельцем, шесть обязательных вопросов | `task-flow-start` |
|
|
46
|
+
| 3 | Конвейер ролей после разбора | `task-flow-start` |
|
|
47
|
+
| 4 | Задача, ветка, папка задачи | `task-flow-start` |
|
|
48
|
+
| 5 | Шапка замысла и сам замысел | `task-flow-start` |
|
|
49
|
+
| 6 | Возвращение к работе новым заходом | `task-flow-resume` |
|
|
50
|
+
| 7 | Этап замысла делается и отмечается в ходе работы | `task-flow-resume` |
|
|
51
|
+
| 8 | Заход закрывается передачей | `task-flow-handoff` |
|
|
52
|
+
| 9 | Договорённость вливается в спек домена | `task-flow-close` |
|
|
53
|
+
| 10 | Тексты домена приводятся к сделанному | `task-flow-close` |
|
|
54
|
+
| 11 | Папка задачи разбирается | `task-flow-close` |
|
|
55
|
+
| 12 | Сверка очереди работ | `task-flow-close` |
|
|
56
|
+
|
|
57
|
+
Шаги 6–8 повторяются, пока этапы замысла не кончатся: первый заход входит в работу шагом 1 и
|
|
58
|
+
доходит до 7, дальше каждый следующий начинается шагом 6. Шаги 9–12 идут один раз, когда работа
|
|
59
|
+
кончена.
|
|
60
|
+
|
|
61
|
+
Список показывается владельцу в начале работы, и на нём же отмечается, где стоим: иначе после
|
|
62
|
+
шести вопросов не видно ни того, что будет дальше, ни сколько всего впереди.
|
|
63
|
+
|
|
34
64
|
## Как закон применяется здесь
|
|
35
65
|
|
|
36
66
|
- **Правка кода приложения отбивается, пока на диске нет замысла.** Гард требует папку задачи
|
|
@@ -48,11 +78,31 @@ description: Правило под «Закон о ведении работы»
|
|
|
48
78
|
законов или правил, поиск по ним. Отбивает гард разговора — на завершении хода, а не на
|
|
49
79
|
инструменте вопроса: спрашивают чаще прозой, чем меню. Найденное ложится в раздел «Что уже
|
|
50
80
|
сказано в правилах» разбора.
|
|
81
|
+
- **Действия, которые исполнитель не делает без слова владельца, перечислены в компаньоне
|
|
82
|
+
правила.** Список у каждого дерева свой — пакет знает только требование, чтобы список был
|
|
83
|
+
назван. Не названный, он выводится из общих слов, и «делай, что нужно по плану» становится
|
|
84
|
+
разрешением на пуш и правку общих документов заодно с коммитом.
|
|
85
|
+
- **Ход, в котором исполнитель признал промах, не заканчивается, пока записи о происшествии
|
|
86
|
+
нет.** Отбивает гард происшествия — на завершении хода: к моменту признания промах уже
|
|
87
|
+
случился, и ловить раньше нечего. Признание ловится набором образцов, а не пониманием смысла;
|
|
88
|
+
промах, признанный словами вне набора, гард пропускает, и это его известная граница, а не
|
|
89
|
+
обещание.
|
|
51
90
|
- **Состояние незаконченной работы приходит в контекст на запуске сессии.** Замысел и ход
|
|
52
91
|
работы отдаются целиком, разбор просьбы — путём. Ветка вида `<КЛЮЧ>-*` без папки даёт
|
|
53
92
|
предупреждение с готовой командой, но сессию не рвёт.
|
|
54
93
|
- **Сделанное отмечается только в ходе работы.** «Где стоим» перезаписывается каждым заходом,
|
|
55
94
|
а не дописывается: это первое, что читает следующий заход.
|
|
95
|
+
- **Заполнение окна захода стережёт гард, а не память исполнителя.** На первом пороге он
|
|
96
|
+
напоминает выбирать точку остановки, на втором отбивает всё, кроме записи хода работы,
|
|
97
|
+
передачи и команд поставки. Размер окна и оба порога дерево задаёт само; не задавшее размера
|
|
98
|
+
стража не получает.
|
|
99
|
+
- **Заход закрывается передачей, которая лежит вне дерева.** Состояние работы коммитится ходом
|
|
100
|
+
работы, а передача его пересказывает для вставки в новый заход: рабочее дерево, ветка,
|
|
101
|
+
сделанное, следующий шаг и особенности захода. В историю она не едет — иначе рядом с ходом
|
|
102
|
+
работы заводится вторая запись об одном и том же.
|
|
103
|
+
- **Слово для нового понятия ищется в словаре дерева.** Общую часть словаря везёт пакет,
|
|
104
|
+
предметную дописывает дерево надстройкой; словарь уезжает в контекст на запуске сессии
|
|
105
|
+
целиком, поэтому «не читал» основанием не бывает.
|
|
56
106
|
- **Папка задачи заводится черновиком и получает номер командой.** До конца разбора
|
|
57
107
|
неизвестно, сколько задач из него выйдет, поэтому номер не может быть первым;
|
|
58
108
|
`npm run task:new` переименовывает черновик и проставляет шапку замысла.
|
|
@@ -96,6 +146,12 @@ description: Правило под «Закон о ведении работы»
|
|
|
96
146
|
обязательных вопросов, ни о том, ответил ли на них владелец. Образец разбора перечисляет их
|
|
97
147
|
таблицей, но пустая таблица проходит так же, как заполненная.
|
|
98
148
|
|
|
149
|
+
Гард разговора судит завершение хода, а не отправку вопроса. К моменту отказа вопрос уже у
|
|
150
|
+
владельца, и владелец видит его вместе с отбитым ходом: требование срабатывает, но исполняется
|
|
151
|
+
задним числом. Прозаический вопрос инструментом не является, и раньше поймать его нечем.
|
|
152
|
+
Отсюда порядок: правило работы читается первым движением захода, до первой реплики владельцу,
|
|
153
|
+
а не по отказу гарда; гард отмечает пропуск, но не отменяет его.
|
|
154
|
+
|
|
99
155
|
Неизменность замысла не стережёт ничто: `plan.md` правится тем же инструментом, что и
|
|
100
156
|
остальные тексты, и правка по ходу отличима от первоначальной записи только по истории.
|
|
101
157
|
Держится это тем же, чем и порядок разбора.
|
|
@@ -117,6 +173,7 @@ description: Правило под «Закон о ведении работы»
|
|
|
117
173
|
- `task-flow-start` — разведка, разбор, договорённость, замысел, задача и ветка.
|
|
118
174
|
- `task-flow-resume` — возвращение к незаконченной работе новым заходом.
|
|
119
175
|
- `task-flow-close` — вливание договорённости, разбор папки, переезд в архив.
|
|
176
|
+
- `task-flow-handoff` — закрытие захода по заполнению окна: точка остановки и передача.
|
|
120
177
|
|
|
121
178
|
## Ловушки
|
|
122
179
|
|
|
@@ -130,9 +187,13 @@ description: Правило под «Закон о ведении работы»
|
|
|
130
187
|
договорённость обязана его пережить: её сценарии получают номера в общей нумерации домена,
|
|
131
188
|
и на них ссылаются заголовки тестов. Обратное тоже верно — ход работы не кладётся в
|
|
132
189
|
`proposed/`: спек, в котором завелись шаги, снова становится планом и умирает после мержа.
|
|
133
|
-
-
|
|
134
|
-
постановка вопроса не подтверждена,
|
|
135
|
-
|
|
190
|
+
- **У меню нет строки «вопрос не тот».** Меню годится для выбора значения из закрытого набора;
|
|
191
|
+
пока постановка вопроса не подтверждена, отвергнуть её владельцу нечем — он выбирает из
|
|
192
|
+
вариантов, выведенных из неверной посылки. Настройки владельца, требующие меню, требование
|
|
193
|
+
не снимают: тогда к каждому вопросу добавляется свободный вариант, и он же — единственное
|
|
194
|
+
место, где вопрос отвергается целиком. Три вопроса ушли одним меню, у одного постановка была
|
|
195
|
+
ложной, и сказать «вопрос не тот» было нечем. Выбор слова, имени и термина узким вопросом не
|
|
196
|
+
является никогда.
|
|
136
197
|
- **Субагент вопросов владельцу не задаёт.** Ни роли, ни конвейер до него не достучатся —
|
|
137
198
|
они возвращают текст главному агенту. Поэтому разбор ведёт главный агент, а роли стоят по
|
|
138
199
|
обе стороны от него.
|
|
@@ -145,6 +206,12 @@ description: Правило под «Закон о ведении работы»
|
|
|
145
206
|
которые пережили десяток задач, и разведка по коду их не находит: снятое решение следа в
|
|
146
207
|
дереве не оставляет. Домен, заведённый генератором и снесённый через полчаса, стоял в линии
|
|
147
208
|
прямым запретом — но линию открыли уже после того, как он был заведён во второй раз.
|
|
209
|
+
- **Работа, которая разбирает папку задачи, своей папки не заводит.** Иначе она снимает одну и
|
|
210
|
+
оставляет вторую, сверка очереди краснеет на неё, и следующая работа повторяет то же — круг
|
|
211
|
+
не кончается. Так и вышло: разбор папки закрытой работы завёл себе папку, и на неё тут же
|
|
212
|
+
пришлось заводить третью задачу. Замысел на диске требует гард, и требует он его только под
|
|
213
|
+
правку кода приложения; разбор папок туда не доходит, а понимание такой работы целиком
|
|
214
|
+
помещается в тело отчёта.
|
|
148
215
|
- **Слово для нового понятия берётся из `docs/GLOSSARY.md` или заводится там же.** Третий файл
|
|
149
216
|
папки задачи называется `progress.md`, а не `journal.md`, ровно поэтому: журнал в этом
|
|
150
217
|
дереве один, и он другой.
|
|
@@ -44,8 +44,35 @@ description: Переносимый слой правил агента — за
|
|
|
44
44
|
npx agent-kit doctor # что разложено, что отстало, что лежит от отказанного
|
|
45
45
|
npx agent-kit sync # разложить
|
|
46
46
|
npx agent-kit sync --check # ничего не писать, отказать при расхождении
|
|
47
|
+
npx agent-kit stats # чем пользовались, чем ни разу, обо что спотыкались
|
|
48
|
+
npx agent-kit propose # отправить предложения, адресованные пакету
|
|
47
49
|
```
|
|
48
50
|
|
|
51
|
+
## Обратная связь наверх
|
|
52
|
+
|
|
53
|
+
Слой правил правится не по памяти, а по тому, как им пользовались. Держится это тремя вещами.
|
|
54
|
+
|
|
55
|
+
**Наблюдения** пишут сами гарды — в `.claude/rt-kit/observations/`, файлом на день. В строке
|
|
56
|
+
имя ресурса пакета, род события, род правки, версия и признак сессии; путей дерева, имён его
|
|
57
|
+
доменов и его собственного имени там нет. Выключаются ключом `"observe": false` в конфиге.
|
|
58
|
+
|
|
59
|
+
**Сводка** — `agent-kit stats`. Самая ценная её строка не «чем пользовались», а **что разложено
|
|
60
|
+
и не загружено ни разу**: правило, которого никто не открыл, ничем себя не выдаёт.
|
|
61
|
+
|
|
62
|
+
**Предложения** приносит разбор закрытой задачи — командой `/skill-curator`. Каждому он ставит
|
|
63
|
+
адрес: «пакет», «компаньон» или «дерево». Выгружаются они файлом в `.claude/rt-kit/proposals/`
|
|
64
|
+
(форма — шаблон `proposal.md`), а `agent-kit propose` отправляет наружу те, что адресованы
|
|
65
|
+
пакету, вместе со сводкой. Текст с адресом этого дерева отбивает отправку целиком.
|
|
66
|
+
|
|
67
|
+
Разбираются они в репозитории самого пакета — командой `/agent-kit-digest`, там же, где лежат
|
|
68
|
+
правимые ресурсы и видно всех потребителей сразу.
|
|
69
|
+
|
|
70
|
+
Предложение работу не выправляет. Оно лежит текстом, читается глазами и в контекст сам собой
|
|
71
|
+
не приходит: замечание было прочитано, процитировано владельцу и нарушено в том же ходе —
|
|
72
|
+
цитата не становится правилом от того, что её произнесли. Закрытым предложение считается,
|
|
73
|
+
только войдя в ресурс пакета; до этого на него не ссылаются как на действующее требование и не
|
|
74
|
+
считают дырку закрытой.
|
|
75
|
+
|
|
49
76
|
## Установка туда, где уже всё своё
|
|
50
77
|
|
|
51
78
|
1. `init`, затем `skip` на всё, что дерево держит само. Пустая раскладка — законное начало.
|
|
@@ -83,3 +110,8 @@ npx agent-kit sync --check # ничего не писать, отказать
|
|
|
83
110
|
- **Утверждение правила переезжает вместе с кодом.** Вынесенное в надстройку перестаёт
|
|
84
111
|
находиться по прежнему символу, и привязка в спутнике правила врёт молча — сверка спеков
|
|
85
112
|
ловит это, но только если её позвать.
|
|
113
|
+
- **Признак по подстроке пути судит и то, что лежит вне дерева.** Гарды отдают в профиль
|
|
114
|
+
абсолютный путь целиком, и образец вида `*/projects/*` совпадает с домашним каталогом агента
|
|
115
|
+
ровно так же, как с кодом дерева: запись в файл вне репозитория была отбита гардом хода
|
|
116
|
+
работы с требованием замысла, к ней не относящегося. Путь в профиле сначала приводится к
|
|
117
|
+
корню дерева, и всё, что вне корня, признака не получает.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# <чем был промах, а не на какой задаче случился>
|
|
2
|
+
|
|
3
|
+
<дата>. <Одна строка: где шла работа и до чего она дошла.>
|
|
4
|
+
|
|
5
|
+
## Что произошло
|
|
6
|
+
|
|
7
|
+
<Что исполнитель сделал и чем это было неверно. Без оценок: разбирается механизм, а не
|
|
8
|
+
намерение.>
|
|
9
|
+
|
|
10
|
+
## Механизм промаха
|
|
11
|
+
|
|
12
|
+
1. <что принято за данность>
|
|
13
|
+
2. <откуда взято>
|
|
14
|
+
3. <чем подтверждено — и почему подтверждение оказалось ложным>
|
|
15
|
+
4. <что сделано дальше>
|
|
16
|
+
|
|
17
|
+
## Что было доступно до промаха
|
|
18
|
+
|
|
19
|
+
- <файл, команда или уже прочитанное, где лежал ответ>
|
|
20
|
+
|
|
21
|
+
Отсюда видно, промах это или нехватка данных.
|
|
22
|
+
|
|
23
|
+
## Чем ловилось
|
|
24
|
+
|
|
25
|
+
- **Слоем правил —** <что отбило, что промолчало>.
|
|
26
|
+
- **Гардом —** <сработал, не сработал, сработал поздно>.
|
|
27
|
+
- **Владельцем —** <заметил сразу, заметил на приёмке, не заметил>.
|
|
28
|
+
|
|
29
|
+
## Что ушло в слой правил
|
|
30
|
+
|
|
31
|
+
<Предложение с адресом либо правка закона, правила, паттерна — с именами. Запись без этого
|
|
32
|
+
раздела закрытой не считается: разбор, из которого ничего не вышло, — жалоба.>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Предложения по слою правил
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
Кладёт этот файл главный агент — шагом команды `/skill-curator`, из ответа роли разбора. Роль
|
|
5
|
+
файлов не пишет: правила действуют на все будущие сессии, и менять их молча нельзя.
|
|
6
|
+
|
|
7
|
+
Читает файл `agent-kit propose`. Форма заголовка — не украшение: по ней команда отбирает то,
|
|
8
|
+
что уезжает в репозиторий пакета.
|
|
9
|
+
|
|
10
|
+
## <адрес> · <ресурс>
|
|
11
|
+
|
|
12
|
+
Адрес один из трёх, и ставит его роль:
|
|
13
|
+
|
|
14
|
+
пакет — правка ресурса @rt-tools/agent-kit; уезжает наружу
|
|
15
|
+
компаньон — implementation.md рядом с правилом: имена этого дерева
|
|
16
|
+
дерево — надстройка этого дерева; наружу не уезжает никогда
|
|
17
|
+
|
|
18
|
+
Ресурс называется идентификатором пакета — `rules/styling-bem.md`, `hooks/skill-gate.sh`, — а у
|
|
19
|
+
адресов «компаньон» и «дерево» путём в дереве.
|
|
20
|
+
|
|
21
|
+
В тексте предложения не бывает ни путей этого дерева, ни имён его доменов, ни его собственного
|
|
22
|
+
имени: файл уезжает в чужой репозиторий целиком. Найденный адрес дерева отбивает отправку с
|
|
23
|
+
номером строки — это проверка, а не напоминание.
|
|
24
|
+
-->
|
|
25
|
+
|
|
26
|
+
## пакет · rules/<правило>.md
|
|
27
|
+
|
|
28
|
+
- **место:** раздел «<заголовок>», в конец
|
|
29
|
+
- **повод:** что в этой задаче пошло не так без этого правила
|
|
30
|
+
|
|
31
|
+
> Готовый текст правки — ровно то, что вставить, в стиле соседних правил: по-русски,
|
|
32
|
+
> утверждением, без воды.
|
|
33
|
+
|
|
34
|
+
## дерево · .claude/rt-kit/gate-map.sh
|
|
35
|
+
|
|
36
|
+
- **место:** ветка `edit`, рядом с соседним родом файлов
|
|
37
|
+
- **повод:** свой род файлов, которого у других деревьев нет
|
|
38
|
+
|
|
39
|
+
> Готовый текст правки.
|
package/bin/agent-kit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-kit.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/bin/agent-kit.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"agent-kit.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/bin/agent-kit.ts"],"names":[],"mappings":";AAcA,OAAO,EAAqC,iBAAiB,EAA8B,MAAM,oBAAoB,CAAC;AA6LtH,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAiE9E"}
|
package/bin/agent-kit.js
CHANGED
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
* спеками — кроме одного, чего команде знать не по чину: откуда взялся выбор законов. У строки
|
|
6
6
|
* запуска это флаг, у терминала — вопрос, у прогона без терминала нет ни того ни другого.
|
|
7
7
|
*/
|
|
8
|
+
import { execFileSync } from 'node:child_process';
|
|
8
9
|
import { readFileSync } from 'node:fs';
|
|
9
10
|
import { dirname, join, resolve } from 'node:path';
|
|
10
11
|
import { fileURLToPath } from 'node:url';
|
|
11
12
|
import process from 'node:process';
|
|
12
13
|
import { readCatalog, resolveSelection } from '../lib/catalog.js';
|
|
13
|
-
import { adopt, doctor, init, list, sync } from '../lib/commands.js';
|
|
14
|
+
import { adopt, doctor, init, list, propose, stats, sync } from '../lib/commands.js';
|
|
15
|
+
import { DEFAULT_DAYS } from '../lib/observations.js';
|
|
16
|
+
import { ghIssue, repositoryOf } from '../lib/submit.js';
|
|
14
17
|
import { staleBuild } from '../lib/freshness.js';
|
|
15
18
|
import { packageRootFrom } from '../lib/package-root.js';
|
|
16
19
|
import { readAxes } from '../lib/variants.js';
|
|
@@ -23,6 +26,11 @@ const USAGE = [
|
|
|
23
26
|
' sync разложить ресурсы пакета в дерево проекта',
|
|
24
27
|
' sync --check ничего не писать, отказать при расхождении — для гейта пуша',
|
|
25
28
|
' doctor рассказать о состоянии раскладки, ничего не меняя',
|
|
29
|
+
' stats свести наблюдения: чем пользовались, чем ни разу, обо что спотыкались',
|
|
30
|
+
' stats --days N за сколько дней; без довода — за три',
|
|
31
|
+
' stats --json то же машиночитаемо — этим сводку прикладывают к предложению',
|
|
32
|
+
' propose отправить предложения с адресом «пакет» в очередь работ пакета',
|
|
33
|
+
' propose --dry-run показать, что уехало бы, и ничего не отправлять',
|
|
26
34
|
' adopt [файлы] отдать пакету файлы, лежащие на его путях не от него',
|
|
27
35
|
'',
|
|
28
36
|
' --root <путь> корень проекта; по умолчанию текущий каталог',
|
|
@@ -53,8 +61,23 @@ function environmentOf(root) {
|
|
|
53
61
|
// Сверка со своими исходниками возможна только отсюда: здесь пакет знает, где лежит сам.
|
|
54
62
|
// У потребителя исходников рядом нет, и сверка молчит.
|
|
55
63
|
stale: staleBuild(pkg, manifest.name),
|
|
64
|
+
// Куда уезжают предложения. Читается из манифеста: зашитый в код адрес назвал бы чужое
|
|
65
|
+
// дерево в текстах пакета — и врал бы у всякого, кто пакет форкнул.
|
|
66
|
+
repository: repositoryOf(manifest.repository?.url ?? ''),
|
|
56
67
|
};
|
|
57
68
|
}
|
|
69
|
+
/** Чем это дерево себя выдаёт снаружи. Нет удалённого репозитория — нечем, и это не отказ. */
|
|
70
|
+
function remoteOf(root) {
|
|
71
|
+
try {
|
|
72
|
+
return execFileSync('git', ['-C', root, 'remote', 'get-url', 'origin'], {
|
|
73
|
+
encoding: 'utf8',
|
|
74
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
75
|
+
}).trim();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return '';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
58
81
|
function optionOf(argv, name, fallback) {
|
|
59
82
|
const index = argv.indexOf(name);
|
|
60
83
|
return index >= 0 && argv[index + 1] ? argv[index + 1] : fallback;
|
|
@@ -168,6 +191,32 @@ export async function main(argv) {
|
|
|
168
191
|
return list(env);
|
|
169
192
|
case 'sync':
|
|
170
193
|
return sync(env, argv.includes('--check'));
|
|
194
|
+
case 'stats': {
|
|
195
|
+
const spoken = Number(optionOf(argv, '--days', ''));
|
|
196
|
+
return stats(env, {
|
|
197
|
+
days: Number.isFinite(spoken) && spoken > 0 ? Math.floor(spoken) : DEFAULT_DAYS,
|
|
198
|
+
// Сегодняшний день берётся здесь: у команды своих часов нет, иначе сводку за
|
|
199
|
+
// отрезок не проверить спекой — вчерашняя фикстура завтра станет позавчерашней.
|
|
200
|
+
today: new Date().toISOString().slice(0, 10),
|
|
201
|
+
json: argv.includes('--json'),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
case 'propose': {
|
|
205
|
+
// Сводка едет вместе с предложением: без цифр оно читается как мнение. Берётся тем
|
|
206
|
+
// же отрезком, что и сводка по умолчанию, — предложение пишут по свежей задаче.
|
|
207
|
+
const summary = stats(env, {
|
|
208
|
+
days: DEFAULT_DAYS,
|
|
209
|
+
today: new Date().toISOString().slice(0, 10),
|
|
210
|
+
json: false,
|
|
211
|
+
});
|
|
212
|
+
return propose(env, {
|
|
213
|
+
dryRun: argv.includes('--dry-run'),
|
|
214
|
+
submit: ghIssue,
|
|
215
|
+
repository: env.repository ?? '',
|
|
216
|
+
remote: remoteOf(env.root),
|
|
217
|
+
summary: summary.lines,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
171
220
|
case 'doctor':
|
|
172
221
|
return doctor(env);
|
|
173
222
|
case 'adopt':
|
package/bin/agent-kit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-kit.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/bin/agent-kit.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * Точка входа. Разбирает аргументы, добывает выбор проекта, зовёт команду и печатает то, что\n * она вернула. Своей работы у неё нет: всё, что решает, живёт в `commands.ts` и проверяется\n * спеками — кроме одного, чего команде знать не по чину: откуда взялся выбор законов. У строки\n * запуска это флаг, у терминала — вопрос, у прогона без терминала нет ни того ни другого.\n */\nimport { readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport process from 'node:process';\n\nimport { IEntryOfCatalog, readCatalog, resolveSelection } from '../lib/catalog.js';\nimport { adopt, doctor, IEnvironment, init, IOutcomeOfCommand, list, sync } from '../lib/commands.js';\nimport { staleBuild } from '../lib/freshness.js';\nimport { packageRootFrom } from '../lib/package-root.js';\nimport { IChoice } from '../lib/picker.js';\nimport { IAxis, IOptionOfAxis, readAxes } from '../lib/variants.js';\nimport { ask, askOne, canAsk } from './prompt.js';\n\nconst USAGE: readonly string[] = [\n 'agent-kit <команда>',\n '',\n ' init завести .claude/rt-kit.json и каталог надстроек',\n ' list что везёт пакет и что из этого взято здесь',\n ' sync разложить ресурсы пакета в дерево проекта',\n ' sync --check ничего не писать, отказать при расхождении — для гейта пуша',\n ' doctor рассказать о состоянии раскладки, ничего не меняя',\n ' adopt [файлы] отдать пакету файлы, лежащие на его путях не от него',\n '',\n ' --root <путь> корень проекта; по умолчанию текущий каталог',\n '',\n 'Выбор законов при `init`:',\n '',\n ' --all взять все законы, ни о чём не спрашивая',\n ' --laws a,b,c взять только названные; имена — из `agent-kit list`',\n ' без обоих спросить галочками; без терминала — отказ',\n '',\n 'Вид ресурсов при `init` — там, где один закон исполняется разными командами:',\n '',\n ' --<ось> <вид> например `--host gitlab`; оси и виды — из `agent-kit list`',\n ' без флага спросить; без терминала — отказ, кроме `--all`',\n];\n\n/**\n * Единственное место, где пакет узнаёт собственное расположение. `import.meta` живёт только\n * здесь: библиотечные модули получают каталог ресурсов входным значением, и трансформ тестов,\n * разбирающий модули как CommonJS, на них уже не спотыкается.\n */\nfunction environmentOf(root: string): IEnvironment {\n const pkg: string = packageRootFrom(dirname(fileURLToPath(import.meta.url)));\n const manifest: { name: string; version: string } = JSON.parse(readFileSync(join(pkg, 'package.json'), 'utf8')) as {\n name: string;\n version: string;\n };\n\n return {\n root,\n version: manifest.version,\n assetsDir: join(pkg, 'assets'),\n // Сверка со своими исходниками возможна только отсюда: здесь пакет знает, где лежит сам.\n // У потребителя исходников рядом нет, и сверка молчит.\n stale: staleBuild(pkg, manifest.name),\n };\n}\n\nfunction optionOf(argv: readonly string[], name: string, fallback: string): string {\n const index: number = argv.indexOf(name);\n\n return index >= 0 && argv[index + 1] ? argv[index + 1] : fallback;\n}\n\nconst isOutcome: (value: unknown) => value is IOutcomeOfCommand = (value: unknown): value is IOutcomeOfCommand =>\n typeof value === 'object' && value !== null && 'code' in value;\n\n/**\n * Выбор законов для `init`: список идентификаторов, `null` на брошенном выборе или готовый\n * отказ. Пустой список означает «взять всё», поэтому брошенный выбор отличается от него не\n * значением, а `null`: подставить «всё» вместо неотвеченного вопроса значило бы разложить\n * пятнадцать законов тому, кто как раз просил спросить.\n */\nasync function selectionFor(argv: readonly string[], assetsDir: string): Promise<readonly string[] | null | IOutcomeOfCommand> {\n if (argv.includes('--all')) {\n return [];\n }\n\n const catalog: readonly IEntryOfCatalog[] = readCatalog(assetsDir);\n const spoken: string = optionOf(argv, '--laws', '');\n\n if (spoken) {\n const named: string[] = spoken\n .split(',')\n .map((name: string): string => name.trim())\n .filter(Boolean);\n const { ids, unknown } = resolveSelection(named, 'laws', catalog);\n\n return unknown.length\n ? { code: 1, lines: [`таких законов пакет не везёт: ${unknown.join(', ')}`, 'что везёт — `agent-kit list`'] }\n : ids;\n }\n\n if (!canAsk()) {\n return {\n code: 1,\n lines: [\n 'спросить некого: запуск без терминала',\n 'назови законы флагом `--laws a,b,c` или возьми все флагом `--all`',\n 'что везёт пакет — `agent-kit list`',\n ],\n };\n }\n\n const choices: readonly IChoice[] = catalog\n .filter((entry: IEntryOfCatalog): boolean => entry.kind === 'laws')\n .map((entry: IEntryOfCatalog): IChoice => ({ id: entry.id, name: entry.name, title: entry.title }));\n\n return ask(choices, 'Какие законы разложить в этом проекте?');\n}\n\n/**\n * Виды для `init` по осям, объявленным пакетом: что взято из флага, что спрошено.\n *\n * `--all` берёт первый объявленный вид оси и **говорит об этом**: в CI спрашивать некого, а\n * отказ там означал бы, что пакет нельзя разложить без человека. Первый вид — умолчание, а не\n * догадка: порядок в объявлении оси и есть порядок предпочтения.\n */\nasync function variantsFor(argv: readonly string[], assetsDir: string): Promise<Record<string, string> | null | IOutcomeOfCommand> {\n const axes: readonly IAxis[] = readAxes(assetsDir);\n const chosen: Record<string, string> = {};\n\n for (const axis of axes) {\n const spoken: string = optionOf(argv, `--${axis.name}`, '');\n const known: (value: string) => boolean = (value: string): boolean =>\n axis.options.some((option: IOptionOfAxis): boolean => option.value === value);\n\n if (spoken) {\n if (!known(spoken)) {\n return {\n code: 1,\n lines: [\n `такого вида по оси «${axis.name}» пакет не везёт: ${spoken}`,\n `есть: ${axis.options.map((option: IOptionOfAxis): string => option.value).join(', ')}`,\n ],\n };\n }\n chosen[axis.name] = spoken;\n continue;\n }\n\n if (argv.includes('--all')) {\n chosen[axis.name] = axis.options[0]?.value ?? '';\n continue;\n }\n\n if (!canAsk()) {\n return {\n code: 1,\n lines: [\n 'спросить некого: запуск без терминала',\n `назови вид флагом \\`--${axis.name} <вид>\\`: ${axis.options.map((option: IOptionOfAxis): string => option.value).join(', ')}`,\n ],\n };\n }\n\n const answer: string | null = await askOne(\n axis.options.map((option: IOptionOfAxis): IChoice => ({ id: option.value, name: option.value, title: option.title })),\n axis.question\n );\n if (answer === null) {\n return null;\n }\n chosen[axis.name] = answer;\n }\n\n return chosen;\n}\n\nexport async function main(argv: readonly string[]): Promise<IOutcomeOfCommand> {\n const command: string = argv[0] ?? '';\n const env: IEnvironment = environmentOf(resolve(optionOf(argv, '--root', process.cwd())));\n\n switch (command) {\n case 'init': {\n const selection: readonly string[] | null | IOutcomeOfCommand = await selectionFor(argv, env.assetsDir);\n if (isOutcome(selection)) {\n return selection;\n }\n if (selection === null) {\n return { code: 1, lines: ['выбор брошен — ничего не заведено'] };\n }\n\n const variants: Record<string, string> | null | IOutcomeOfCommand = await variantsFor(argv, env.assetsDir);\n if (isOutcome(variants)) {\n return variants;\n }\n\n return variants === null\n ? { code: 1, lines: ['выбор брошен — ничего не заведено'] }\n : init(env.root, selection, variants, env.assetsDir);\n }\n case 'list':\n return list(env);\n case 'sync':\n return sync(env, argv.includes('--check'));\n case 'doctor':\n return doctor(env);\n case 'adopt':\n return adopt(\n env,\n argv.slice(1).filter((value: string): boolean => !value.startsWith('--') && value !== optionOf(argv, '--root', ''))\n );\n default:\n return { code: command ? 1 : 0, lines: command ? [`неизвестная команда «${command}»`, '', ...USAGE] : USAGE };\n }\n}\n\nconst outcome: IOutcomeOfCommand = await main(process.argv.slice(2));\nfor (const line of outcome.lines) {\n // eslint-disable-next-line no-console\n console.log(line);\n}\nprocess.exit(outcome.code);\n"]}
|
|
1
|
+
{"version":3,"file":"agent-kit.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/bin/agent-kit.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * Точка входа. Разбирает аргументы, добывает выбор проекта, зовёт команду и печатает то, что\n * она вернула. Своей работы у неё нет: всё, что решает, живёт в `commands.ts` и проверяется\n * спеками — кроме одного, чего команде знать не по чину: откуда взялся выбор законов. У строки\n * запуска это флаг, у терминала — вопрос, у прогона без терминала нет ни того ни другого.\n */\nimport { execFileSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport process from 'node:process';\n\nimport { IEntryOfCatalog, readCatalog, resolveSelection } from '../lib/catalog.js';\nimport { adopt, doctor, IEnvironment, init, IOutcomeOfCommand, list, propose, stats, sync } from '../lib/commands.js';\nimport { DEFAULT_DAYS } from '../lib/observations.js';\nimport { ghIssue, repositoryOf } from '../lib/submit.js';\nimport { staleBuild } from '../lib/freshness.js';\nimport { packageRootFrom } from '../lib/package-root.js';\nimport { IChoice } from '../lib/picker.js';\nimport { IAxis, IOptionOfAxis, readAxes } from '../lib/variants.js';\nimport { ask, askOne, canAsk } from './prompt.js';\n\nconst USAGE: readonly string[] = [\n 'agent-kit <команда>',\n '',\n ' init завести .claude/rt-kit.json и каталог надстроек',\n ' list что везёт пакет и что из этого взято здесь',\n ' sync разложить ресурсы пакета в дерево проекта',\n ' sync --check ничего не писать, отказать при расхождении — для гейта пуша',\n ' doctor рассказать о состоянии раскладки, ничего не меняя',\n ' stats свести наблюдения: чем пользовались, чем ни разу, обо что спотыкались',\n ' stats --days N за сколько дней; без довода — за три',\n ' stats --json то же машиночитаемо — этим сводку прикладывают к предложению',\n ' propose отправить предложения с адресом «пакет» в очередь работ пакета',\n ' propose --dry-run показать, что уехало бы, и ничего не отправлять',\n ' adopt [файлы] отдать пакету файлы, лежащие на его путях не от него',\n '',\n ' --root <путь> корень проекта; по умолчанию текущий каталог',\n '',\n 'Выбор законов при `init`:',\n '',\n ' --all взять все законы, ни о чём не спрашивая',\n ' --laws a,b,c взять только названные; имена — из `agent-kit list`',\n ' без обоих спросить галочками; без терминала — отказ',\n '',\n 'Вид ресурсов при `init` — там, где один закон исполняется разными командами:',\n '',\n ' --<ось> <вид> например `--host gitlab`; оси и виды — из `agent-kit list`',\n ' без флага спросить; без терминала — отказ, кроме `--all`',\n];\n\n/**\n * Единственное место, где пакет узнаёт собственное расположение. `import.meta` живёт только\n * здесь: библиотечные модули получают каталог ресурсов входным значением, и трансформ тестов,\n * разбирающий модули как CommonJS, на них уже не спотыкается.\n */\nfunction environmentOf(root: string): IEnvironment {\n const pkg: string = packageRootFrom(dirname(fileURLToPath(import.meta.url)));\n const manifest: { name: string; version: string; repository?: { url?: string } } = JSON.parse(\n readFileSync(join(pkg, 'package.json'), 'utf8')\n ) as {\n name: string;\n version: string;\n repository?: { url?: string };\n };\n\n return {\n root,\n version: manifest.version,\n assetsDir: join(pkg, 'assets'),\n // Сверка со своими исходниками возможна только отсюда: здесь пакет знает, где лежит сам.\n // У потребителя исходников рядом нет, и сверка молчит.\n stale: staleBuild(pkg, manifest.name),\n // Куда уезжают предложения. Читается из манифеста: зашитый в код адрес назвал бы чужое\n // дерево в текстах пакета — и врал бы у всякого, кто пакет форкнул.\n repository: repositoryOf(manifest.repository?.url ?? ''),\n };\n}\n\n/** Чем это дерево себя выдаёт снаружи. Нет удалённого репозитория — нечем, и это не отказ. */\nfunction remoteOf(root: string): string {\n try {\n return execFileSync('git', ['-C', root, 'remote', 'get-url', 'origin'], {\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n } catch {\n return '';\n }\n}\n\nfunction optionOf(argv: readonly string[], name: string, fallback: string): string {\n const index: number = argv.indexOf(name);\n\n return index >= 0 && argv[index + 1] ? argv[index + 1] : fallback;\n}\n\nconst isOutcome: (value: unknown) => value is IOutcomeOfCommand = (value: unknown): value is IOutcomeOfCommand =>\n typeof value === 'object' && value !== null && 'code' in value;\n\n/**\n * Выбор законов для `init`: список идентификаторов, `null` на брошенном выборе или готовый\n * отказ. Пустой список означает «взять всё», поэтому брошенный выбор отличается от него не\n * значением, а `null`: подставить «всё» вместо неотвеченного вопроса значило бы разложить\n * пятнадцать законов тому, кто как раз просил спросить.\n */\nasync function selectionFor(argv: readonly string[], assetsDir: string): Promise<readonly string[] | null | IOutcomeOfCommand> {\n if (argv.includes('--all')) {\n return [];\n }\n\n const catalog: readonly IEntryOfCatalog[] = readCatalog(assetsDir);\n const spoken: string = optionOf(argv, '--laws', '');\n\n if (spoken) {\n const named: string[] = spoken\n .split(',')\n .map((name: string): string => name.trim())\n .filter(Boolean);\n const { ids, unknown } = resolveSelection(named, 'laws', catalog);\n\n return unknown.length\n ? { code: 1, lines: [`таких законов пакет не везёт: ${unknown.join(', ')}`, 'что везёт — `agent-kit list`'] }\n : ids;\n }\n\n if (!canAsk()) {\n return {\n code: 1,\n lines: [\n 'спросить некого: запуск без терминала',\n 'назови законы флагом `--laws a,b,c` или возьми все флагом `--all`',\n 'что везёт пакет — `agent-kit list`',\n ],\n };\n }\n\n const choices: readonly IChoice[] = catalog\n .filter((entry: IEntryOfCatalog): boolean => entry.kind === 'laws')\n .map((entry: IEntryOfCatalog): IChoice => ({ id: entry.id, name: entry.name, title: entry.title }));\n\n return ask(choices, 'Какие законы разложить в этом проекте?');\n}\n\n/**\n * Виды для `init` по осям, объявленным пакетом: что взято из флага, что спрошено.\n *\n * `--all` берёт первый объявленный вид оси и **говорит об этом**: в CI спрашивать некого, а\n * отказ там означал бы, что пакет нельзя разложить без человека. Первый вид — умолчание, а не\n * догадка: порядок в объявлении оси и есть порядок предпочтения.\n */\nasync function variantsFor(argv: readonly string[], assetsDir: string): Promise<Record<string, string> | null | IOutcomeOfCommand> {\n const axes: readonly IAxis[] = readAxes(assetsDir);\n const chosen: Record<string, string> = {};\n\n for (const axis of axes) {\n const spoken: string = optionOf(argv, `--${axis.name}`, '');\n const known: (value: string) => boolean = (value: string): boolean =>\n axis.options.some((option: IOptionOfAxis): boolean => option.value === value);\n\n if (spoken) {\n if (!known(spoken)) {\n return {\n code: 1,\n lines: [\n `такого вида по оси «${axis.name}» пакет не везёт: ${spoken}`,\n `есть: ${axis.options.map((option: IOptionOfAxis): string => option.value).join(', ')}`,\n ],\n };\n }\n chosen[axis.name] = spoken;\n continue;\n }\n\n if (argv.includes('--all')) {\n chosen[axis.name] = axis.options[0]?.value ?? '';\n continue;\n }\n\n if (!canAsk()) {\n return {\n code: 1,\n lines: [\n 'спросить некого: запуск без терминала',\n `назови вид флагом \\`--${axis.name} <вид>\\`: ${axis.options.map((option: IOptionOfAxis): string => option.value).join(', ')}`,\n ],\n };\n }\n\n const answer: string | null = await askOne(\n axis.options.map((option: IOptionOfAxis): IChoice => ({ id: option.value, name: option.value, title: option.title })),\n axis.question\n );\n if (answer === null) {\n return null;\n }\n chosen[axis.name] = answer;\n }\n\n return chosen;\n}\n\nexport async function main(argv: readonly string[]): Promise<IOutcomeOfCommand> {\n const command: string = argv[0] ?? '';\n const env: IEnvironment = environmentOf(resolve(optionOf(argv, '--root', process.cwd())));\n\n switch (command) {\n case 'init': {\n const selection: readonly string[] | null | IOutcomeOfCommand = await selectionFor(argv, env.assetsDir);\n if (isOutcome(selection)) {\n return selection;\n }\n if (selection === null) {\n return { code: 1, lines: ['выбор брошен — ничего не заведено'] };\n }\n\n const variants: Record<string, string> | null | IOutcomeOfCommand = await variantsFor(argv, env.assetsDir);\n if (isOutcome(variants)) {\n return variants;\n }\n\n return variants === null\n ? { code: 1, lines: ['выбор брошен — ничего не заведено'] }\n : init(env.root, selection, variants, env.assetsDir);\n }\n case 'list':\n return list(env);\n case 'sync':\n return sync(env, argv.includes('--check'));\n case 'stats': {\n const spoken: number = Number(optionOf(argv, '--days', ''));\n\n return stats(env, {\n days: Number.isFinite(spoken) && spoken > 0 ? Math.floor(spoken) : DEFAULT_DAYS,\n // Сегодняшний день берётся здесь: у команды своих часов нет, иначе сводку за\n // отрезок не проверить спекой — вчерашняя фикстура завтра станет позавчерашней.\n today: new Date().toISOString().slice(0, 10),\n json: argv.includes('--json'),\n });\n }\n case 'propose': {\n // Сводка едет вместе с предложением: без цифр оно читается как мнение. Берётся тем\n // же отрезком, что и сводка по умолчанию, — предложение пишут по свежей задаче.\n const summary: IOutcomeOfCommand = stats(env, {\n days: DEFAULT_DAYS,\n today: new Date().toISOString().slice(0, 10),\n json: false,\n });\n\n return propose(env, {\n dryRun: argv.includes('--dry-run'),\n submit: ghIssue,\n repository: env.repository ?? '',\n remote: remoteOf(env.root),\n summary: summary.lines,\n });\n }\n case 'doctor':\n return doctor(env);\n case 'adopt':\n return adopt(\n env,\n argv.slice(1).filter((value: string): boolean => !value.startsWith('--') && value !== optionOf(argv, '--root', ''))\n );\n default:\n return { code: command ? 1 : 0, lines: command ? [`неизвестная команда «${command}»`, '', ...USAGE] : USAGE };\n }\n}\n\nconst outcome: IOutcomeOfCommand = await main(process.argv.slice(2));\nfor (const line of outcome.lines) {\n // eslint-disable-next-line no-console\n console.log(line);\n}\nprocess.exit(outcome.code);\n"]}
|
package/lib/catalog.d.ts
CHANGED
|
@@ -20,7 +20,22 @@ export interface IEntryOfCatalog {
|
|
|
20
20
|
* «правка отбита». Признак берётся с файла в пакете и переносится на разложенный.
|
|
21
21
|
*/
|
|
22
22
|
readonly executable: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Ресурсы, без которых этот неисполним, — их идентификаторы.
|
|
25
|
+
*
|
|
26
|
+
* Читается из шапки самого ресурса, а не из отдельного списка при пакете: список разошёлся
|
|
27
|
+
* бы с ресурсами молча, ровно тем же молчанием, ради которого требование и заведено. Паттерн
|
|
28
|
+
* возвращения к работе начинается словами «хук запуска сессии отдал замысел и ход работы» —
|
|
29
|
+
* без хука он неисполним, и до этой строки узнать об этом было нечем.
|
|
30
|
+
*/
|
|
31
|
+
readonly requires: readonly string[];
|
|
23
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Что ресурс о себе объявил. Имена читаются идентификаторами — `hooks/observe.sh`: короткая
|
|
35
|
+
* форма совпала бы у закона и правила с одним именем, а требование, указавшее не туда, хуже
|
|
36
|
+
* ненайденного.
|
|
37
|
+
*/
|
|
38
|
+
export declare function requiresOf(text: string): readonly string[];
|
|
24
39
|
/** Заголовок нужен человеку, который выбирает: имена законов ему ни о чём не говорят. */
|
|
25
40
|
export declare function titleOf(text: string, fallback: string): string;
|
|
26
41
|
/**
|
|
@@ -76,6 +91,24 @@ export interface IGapOfVariant {
|
|
|
76
91
|
* существующие в `skip` — дерево тем самым говорит, что обходится без этого ресурса.
|
|
77
92
|
*/
|
|
78
93
|
export declare function variantGaps(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IGapOfVariant[];
|
|
94
|
+
/** Выбранный ресурс, чьё требование в дерево не поехало. */
|
|
95
|
+
export interface IBrokenLink {
|
|
96
|
+
/** Кто требует — идентификатор выбранного ресурса. */
|
|
97
|
+
readonly id: string;
|
|
98
|
+
/** Чего не хватает — идентификатор требования. */
|
|
99
|
+
readonly requires: string;
|
|
100
|
+
/** Требования нет в пакете вовсе: промах в шапке ресурса, а не выбор дерева. */
|
|
101
|
+
readonly unknown: boolean;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Связи, разорванные выбором дерева.
|
|
105
|
+
*
|
|
106
|
+
* Не отказ, а предупреждение — и это главное свойство. Дерево вправе закрыть требование своим
|
|
107
|
+
* средством: паттерн исполним и с чужим хуком, если тот делает то же самое. Отказ здесь отбивал
|
|
108
|
+
* бы законную раскладку, а молчание оставляет дерево исправным на вид — сверка зелена на любом
|
|
109
|
+
* подмножестве, сколько бы связок ни было разорвано.
|
|
110
|
+
*/
|
|
111
|
+
export declare function brokenLinks(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IBrokenLink[];
|
|
79
112
|
/**
|
|
80
113
|
* Идентификатор по тому, как ресурс назвали в строке запуска. Принимаются все формы, которыми
|
|
81
114
|
* его называют вслух: `money`, `application/money`, `money.md` и `laws/application/money.md`.
|
package/lib/catalog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/catalog.ts"],"names":[],"mappings":"AAUA,OAAO,EAAS,KAAK,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAS,QAAQ,EAAuD,MAAM,eAAe,CAAC;AAErG,MAAM,WAAW,eAAe;IAC5B,kGAAkG;IAClG,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kFAAkF;IAClF,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/catalog.ts"],"names":[],"mappings":"AAUA,OAAO,EAAS,KAAK,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAS,QAAQ,EAAuD,MAAM,eAAe,CAAC;AAErG,MAAM,WAAW,eAAe;IAC5B,kGAAkG;IAClG,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kFAAkF;IAClF,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAUD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAU1D;AAaD,yFAAyF;AACzF,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI9D;AA8BD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,eAAe,EAAE,CAyBzE;AAED,oFAAoF;AACpF,MAAM,WAAW,UAAU;IACvB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACvD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,GAAG,OAAO,CAU/E;AAED,qFAAqF;AACrF,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,yEAAyE;IACzE,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,SAAS,EAAE,UAAU,GAAG,SAAS,aAAa,EAAE,CAsChH;AAED,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IACxB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,SAAS,EAAE,UAAU,GAAG,SAAS,WAAW,EAAE,CAoB9G;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,MAAM,GAAG,IAAI,CAUpG;AAED,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,yFAAyF;IACzF,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,wFAAwF;AACxF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,kBAAkB,CAchI"}
|
package/lib/catalog.js
CHANGED
|
@@ -10,6 +10,26 @@ import { join } from 'node:path';
|
|
|
10
10
|
import { KINDS } from './config.js';
|
|
11
11
|
import { matchesVariant, readAxes, variantOf, withoutVariant } from './variants.js';
|
|
12
12
|
const TITLE = /^#\s+(\S.*)$/m;
|
|
13
|
+
/**
|
|
14
|
+
* Строка требования в шапке ресурса. Двух видов, потому что ресурсы двух родов: у разметки
|
|
15
|
+
* `**Требует:**`, у исполняемого файла — комментарий `# Требует:`.
|
|
16
|
+
*/
|
|
17
|
+
const REQUIRES = /^(?:#\s*Требует:|\*\*Требует:\*\*)\s*(.+)$/m;
|
|
18
|
+
/**
|
|
19
|
+
* Что ресурс о себе объявил. Имена читаются идентификаторами — `hooks/observe.sh`: короткая
|
|
20
|
+
* форма совпала бы у закона и правила с одним именем, а требование, указавшее не туда, хуже
|
|
21
|
+
* ненайденного.
|
|
22
|
+
*/
|
|
23
|
+
export function requiresOf(text) {
|
|
24
|
+
const found = text.match(REQUIRES);
|
|
25
|
+
if (!found) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
return found[1]
|
|
29
|
+
.split(',')
|
|
30
|
+
.map((name) => name.trim().replace(/^`|`$/g, '').trim())
|
|
31
|
+
.filter((name) => name.length > 0);
|
|
32
|
+
}
|
|
13
33
|
/** Запускается ли файл сам по себе. Спрашиваем систему, а не разбираем биты режима руками. */
|
|
14
34
|
function isExecutable(path) {
|
|
15
35
|
try {
|
|
@@ -67,7 +87,16 @@ export function readCatalog(assetsDir) {
|
|
|
67
87
|
const variant = variantOf(file, axes);
|
|
68
88
|
const name = withoutVariant(file, variant).replace(/\.[^./]+$/, '');
|
|
69
89
|
const executable = isExecutable(path);
|
|
70
|
-
entries.push({
|
|
90
|
+
entries.push({
|
|
91
|
+
id: `${kind}/${file}`,
|
|
92
|
+
kind,
|
|
93
|
+
name,
|
|
94
|
+
title: titleOf(text, name),
|
|
95
|
+
variant,
|
|
96
|
+
text,
|
|
97
|
+
executable,
|
|
98
|
+
requires: requiresOf(text),
|
|
99
|
+
});
|
|
71
100
|
}
|
|
72
101
|
}
|
|
73
102
|
return entries;
|
|
@@ -145,6 +174,31 @@ export function variantGaps(catalog, selection) {
|
|
|
145
174
|
}
|
|
146
175
|
return gaps;
|
|
147
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Связи, разорванные выбором дерева.
|
|
179
|
+
*
|
|
180
|
+
* Не отказ, а предупреждение — и это главное свойство. Дерево вправе закрыть требование своим
|
|
181
|
+
* средством: паттерн исполним и с чужим хуком, если тот делает то же самое. Отказ здесь отбивал
|
|
182
|
+
* бы законную раскладку, а молчание оставляет дерево исправным на вид — сверка зелена на любом
|
|
183
|
+
* подмножестве, сколько бы связок ни было разорвано.
|
|
184
|
+
*/
|
|
185
|
+
export function brokenLinks(catalog, selection) {
|
|
186
|
+
const known = new Set(catalog.map((entry) => entry.id));
|
|
187
|
+
const chosen = new Set(catalog.filter((entry) => isChosen(entry, selection)).map((entry) => entry.id));
|
|
188
|
+
const broken = [];
|
|
189
|
+
for (const entry of catalog) {
|
|
190
|
+
if (!chosen.has(entry.id)) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
for (const required of entry.requires) {
|
|
194
|
+
if (chosen.has(required)) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
broken.push({ id: entry.id, requires: required, unknown: !known.has(required) });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return broken;
|
|
201
|
+
}
|
|
148
202
|
/**
|
|
149
203
|
* Идентификатор по тому, как ресурс назвали в строке запуска. Принимаются все формы, которыми
|
|
150
204
|
* его называют вслух: `money`, `application/money`, `money.md` и `laws/application/money.md`.
|