@rt-tools/agent-kit 0.1.0 → 0.2.0

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.
Files changed (51) hide show
  1. package/README.md +29 -3
  2. package/assets/laws/access.md +3 -12
  3. package/assets/laws/admin-lists.md +9 -21
  4. package/assets/laws/admin-navigation.md +3 -15
  5. package/assets/laws/code-structure.md +5 -16
  6. package/assets/laws/delivery.md +2 -16
  7. package/assets/laws/entity-editing.md +7 -20
  8. package/assets/laws/entity-models.md +10 -17
  9. package/assets/laws/frontend-application.md +3 -14
  10. package/assets/laws/lib-imports.md +0 -13
  11. package/assets/laws/locales.md +2 -11
  12. package/assets/laws/project-documentation.md +7 -20
  13. package/assets/laws/reuse-first.md +0 -9
  14. package/assets/laws/search-visibility.md +0 -13
  15. package/assets/laws/shared-code.md +0 -13
  16. package/assets/laws/verifiability.md +0 -13
  17. package/bin/agent-kit.d.ts +1 -1
  18. package/bin/agent-kit.d.ts.map +1 -1
  19. package/bin/agent-kit.js +63 -7
  20. package/bin/agent-kit.js.map +1 -1
  21. package/bin/prompt.d.ts +9 -0
  22. package/bin/prompt.d.ts.map +1 -0
  23. package/bin/prompt.js +57 -0
  24. package/bin/prompt.js.map +1 -0
  25. package/index.d.ts +2 -0
  26. package/index.d.ts.map +1 -1
  27. package/index.js +2 -0
  28. package/index.js.map +1 -1
  29. package/lib/assets.d.ts +2 -2
  30. package/lib/assets.d.ts.map +1 -1
  31. package/lib/assets.js +14 -29
  32. package/lib/assets.js.map +1 -1
  33. package/lib/catalog.d.ts +44 -0
  34. package/lib/catalog.d.ts.map +1 -0
  35. package/lib/catalog.js +86 -0
  36. package/lib/catalog.js.map +1 -0
  37. package/lib/commands.d.ts +13 -1
  38. package/lib/commands.d.ts.map +1 -1
  39. package/lib/commands.js +85 -9
  40. package/lib/commands.js.map +1 -1
  41. package/lib/config.d.ts +5 -0
  42. package/lib/config.d.ts.map +1 -1
  43. package/lib/config.js +10 -5
  44. package/lib/config.js.map +1 -1
  45. package/lib/picker.d.ts +47 -0
  46. package/lib/picker.d.ts.map +1 -0
  47. package/lib/picker.js +112 -0
  48. package/lib/picker.js.map +1 -0
  49. package/package.json +1 -1
  50. package/rt-tools-agent-kit-0.2.0.tgz +0 -0
  51. package/rt-tools-agent-kit-0.1.0.tgz +0 -0
package/bin/agent-kit.js CHANGED
@@ -1,23 +1,34 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Точка входа. Разбирает аргументы, зовёт команду и печатает то, что она вернула, — своей
4
- * работы у неё нет: всё, что решает, живёт в `commands.ts` и проверяется спеками.
3
+ * Точка входа. Разбирает аргументы, добывает выбор проекта, зовёт команду и печатает то, что
4
+ * она вернула. Своей работы у неё нет: всё, что решает, живёт в `commands.ts` и проверяется
5
+ * спеками — кроме одного, чего команде знать не по чину: откуда взялся выбор законов. У строки
6
+ * запуска это флаг, у терминала — вопрос, у прогона без терминала нет ни того ни другого.
5
7
  */
6
8
  import { readFileSync } from 'node:fs';
7
9
  import { dirname, join, resolve } from 'node:path';
8
10
  import { fileURLToPath } from 'node:url';
9
11
  import process from 'node:process';
10
- import { doctor, init, sync } from '../lib/commands.js';
12
+ import { readCatalog, resolveSelection } from '../lib/catalog.js';
13
+ import { doctor, init, list, sync } from '../lib/commands.js';
11
14
  import { packageRootFrom } from '../lib/package-root.js';
15
+ import { ask, canAsk } from './prompt.js';
12
16
  const USAGE = [
13
17
  'agent-kit <команда>',
14
18
  '',
15
19
  ' init завести .claude/rt-kit.json и каталог надстроек',
20
+ ' list что везёт пакет и что из этого взято здесь',
16
21
  ' sync разложить ресурсы пакета в дерево проекта',
17
22
  ' sync --check ничего не писать, отказать при расхождении — для гейта пуша',
18
23
  ' doctor рассказать о состоянии раскладки, ничего не меняя',
19
24
  '',
20
25
  ' --root <путь> корень проекта; по умолчанию текущий каталог',
26
+ '',
27
+ 'Выбор законов при `init`:',
28
+ '',
29
+ ' --all взять все законы, ни о чём не спрашивая',
30
+ ' --laws a,b,c взять только названные; имена — из `agent-kit list`',
31
+ ' без обоих спросить галочками; без терминала — отказ',
21
32
  ];
22
33
  /**
23
34
  * Единственное место, где пакет узнаёт собственное расположение. `import.meta` живёт только
@@ -33,12 +44,57 @@ function optionOf(argv, name, fallback) {
33
44
  const index = argv.indexOf(name);
34
45
  return index >= 0 && argv[index + 1] ? argv[index + 1] : fallback;
35
46
  }
36
- export function main(argv) {
47
+ const isOutcome = (value) => typeof value === 'object' && value !== null && 'code' in value;
48
+ /**
49
+ * Выбор законов для `init`: список идентификаторов, `null` на брошенном выборе или готовый
50
+ * отказ. Пустой список означает «взять всё», поэтому брошенный выбор отличается от него не
51
+ * значением, а `null`: подставить «всё» вместо неотвеченного вопроса значило бы разложить
52
+ * пятнадцать законов тому, кто как раз просил спросить.
53
+ */
54
+ async function selectionFor(argv, assetsDir) {
55
+ if (argv.includes('--all')) {
56
+ return [];
57
+ }
58
+ const catalog = readCatalog(assetsDir);
59
+ const spoken = optionOf(argv, '--laws', '');
60
+ if (spoken) {
61
+ const named = spoken
62
+ .split(',')
63
+ .map((name) => name.trim())
64
+ .filter(Boolean);
65
+ const { ids, unknown } = resolveSelection(named, 'laws', catalog);
66
+ return unknown.length
67
+ ? { code: 1, lines: [`таких законов пакет не везёт: ${unknown.join(', ')}`, 'что везёт — `agent-kit list`'] }
68
+ : ids;
69
+ }
70
+ if (!canAsk()) {
71
+ return {
72
+ code: 1,
73
+ lines: [
74
+ 'спросить некого: запуск без терминала',
75
+ 'назови законы флагом `--laws a,b,c` или возьми все флагом `--all`',
76
+ 'что везёт пакет — `agent-kit list`',
77
+ ],
78
+ };
79
+ }
80
+ const choices = catalog
81
+ .filter((entry) => entry.kind === 'laws')
82
+ .map((entry) => ({ id: entry.id, name: entry.name, title: entry.title }));
83
+ return ask(choices, 'Какие законы разложить в этом проекте?');
84
+ }
85
+ export async function main(argv) {
37
86
  const command = argv[0] ?? '';
38
87
  const env = environmentOf(resolve(optionOf(argv, '--root', process.cwd())));
39
88
  switch (command) {
40
- case 'init':
41
- return init(env.root);
89
+ case 'init': {
90
+ const selection = await selectionFor(argv, env.assetsDir);
91
+ if (isOutcome(selection)) {
92
+ return selection;
93
+ }
94
+ return selection === null ? { code: 1, lines: ['выбор брошен — ничего не заведено'] } : init(env.root, selection);
95
+ }
96
+ case 'list':
97
+ return list(env);
42
98
  case 'sync':
43
99
  return sync(env, argv.includes('--check'));
44
100
  case 'doctor':
@@ -47,7 +103,7 @@ export function main(argv) {
47
103
  return { code: command ? 1 : 0, lines: command ? [`неизвестная команда «${command}»`, '', ...USAGE] : USAGE };
48
104
  }
49
105
  }
50
- const outcome = main(process.argv.slice(2));
106
+ const outcome = await main(process.argv.slice(2));
51
107
  for (const line of outcome.lines) {
52
108
  // eslint-disable-next-line no-console
53
109
  console.log(line);
@@ -1 +1 @@
1
- {"version":3,"file":"agent-kit.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/bin/agent-kit.ts"],"names":[],"mappings":";AACA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,OAAO,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,MAAM,EAAgB,IAAI,EAAqB,IAAI,EAAE,MAAM,oBAAoB,CAAC;AACzF,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,KAAK,GAAsB;IAC7B,qBAAqB;IACrB,EAAE;IACF,mEAAmE;IACnE,6DAA6D;IAC7D,+EAA+E;IAC/E,qEAAqE;IACrE,EAAE;IACF,gEAAgE;CACnE,CAAC;AAEF;;;;GAIG;AACH,SAAS,aAAa,CAAC,IAAY;IAC/B,MAAM,GAAG,GAAW,eAAe,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC7E,MAAM,OAAO,GAAY,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAyB,CAAC,OAAO,CAAC;IAErH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,CAAC;AAC7D,CAAC;AAED,SAAS,QAAQ,CAAC,IAAuB,EAAE,IAAY,EAAE,QAAgB;IACrE,MAAM,KAAK,GAAW,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzC,OAAO,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAuB;IACxC,MAAM,OAAO,GAAW,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACtC,MAAM,GAAG,GAAiB,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IAE1F,QAAQ,OAAO,EAAE,CAAC;QACd,KAAK,MAAM;YACP,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1B,KAAK,MAAM;YACP,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/C,KAAK,QAAQ;YACT,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;QACvB;YACI,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,wBAAwB,OAAO,GAAG,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;IACtH,CAAC;AACL,CAAC;AAED,MAAM,OAAO,GAAsB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/D,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IAC/B,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AACD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC","sourcesContent":["#!/usr/bin/env node\n/**\n * Точка входа. Разбирает аргументы, зовёт команду и печатает то, что она вернула, своей\n * работы у неё нет: всё, что решает, живёт в `commands.ts` и проверяется спеками.\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 { doctor, IEnvironment, init, IOutcomeOfCommand, sync } from '../lib/commands.js';\nimport { packageRootFrom } from '../lib/package-root.js';\n\nconst USAGE: readonly string[] = [\n 'agent-kit <команда>',\n '',\n ' init завести .claude/rt-kit.json и каталог надстроек',\n ' sync разложить ресурсы пакета в дерево проекта',\n ' sync --check ничего не писать, отказать при расхождении — для гейта пуша',\n ' doctor рассказать о состоянии раскладки, ничего не меняя',\n '',\n ' --root <путь> корень проекта; по умолчанию текущий каталог',\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 version: string = (JSON.parse(readFileSync(join(pkg, 'package.json'), 'utf8')) as { version: string }).version;\n\n return { root, version, assetsDir: join(pkg, 'assets') };\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\nexport function main(argv: readonly string[]): 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 return init(env.root);\n case 'sync':\n return sync(env, argv.includes('--check'));\n case 'doctor':\n return doctor(env);\n default:\n return { code: command ? 1 : 0, lines: command ? [`неизвестная команда «${command}»`, '', ...USAGE] : USAGE };\n }\n}\n\nconst outcome: IOutcomeOfCommand = 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 { 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 { doctor, IEnvironment, init, IOutcomeOfCommand, list, sync } from '../lib/commands.js';\nimport { packageRootFrom } from '../lib/package-root.js';\nimport { IChoice } from '../lib/picker.js';\nimport { ask, 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 '',\n ' --root <путь> корень проекта; по умолчанию текущий каталог',\n '',\n 'Выбор законов при `init`:',\n '',\n ' --all взять все законы, ни о чём не спрашивая',\n ' --laws a,b,c взять только названные; имена — из `agent-kit list`',\n ' без обоих спросить галочками; без терминала — отказ',\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 version: string = (JSON.parse(readFileSync(join(pkg, 'package.json'), 'utf8')) as { version: string }).version;\n\n return { root, version, assetsDir: join(pkg, 'assets') };\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\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\n return selection === null ? { code: 1, lines: ['выбор брошен — ничего не заведено'] } : init(env.root, selection);\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 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"]}
@@ -0,0 +1,9 @@
1
+ import { IChoice } from '../lib/picker.js';
2
+ /** Спросить можно только там, где есть кого спрашивать: в конвейере и в CI — некого. */
3
+ export declare const canAsk: () => boolean;
4
+ /**
5
+ * Выбор галочками. Возвращает выбранные идентификаторы, а на брошенном выборе — `null`:
6
+ * подставлять вместо ответа умолчание нельзя, потому что «взять всё» — тоже решение.
7
+ */
8
+ export declare function ask(choices: readonly IChoice[], question: string): Promise<readonly string[] | null>;
9
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/bin/prompt.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,OAAO,EAAoD,MAAM,kBAAkB,CAAC;AAM7F,wFAAwF;AACxF,eAAO,MAAM,MAAM,EAAE,MAAM,OAA6E,CAAC;AASzG;;;GAGG;AACH,wBAAgB,GAAG,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC,CAoCpG"}
package/bin/prompt.js ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Терминал: сырой режим, перерисовка и восстановление курсора. Решений здесь нет — переход
3
+ * состояния по нажатой клавише живёт в `lib/picker.ts` и проверяется спекой.
4
+ *
5
+ * Всё, что этот модуль обязан сделать помимо перерисовки, — вернуть терминал в прежнее
6
+ * состояние на любом выходе. Оставленный сырой режим ломает не программу, а оболочку после
7
+ * неё: ввод перестаёт отзываться, и виноватым выглядит терминал.
8
+ */
9
+ import process from 'node:process';
10
+ import { pickerOf, pressChunk, renderPicker } from '../lib/picker.js';
11
+ const HIDE_CURSOR = `${String.fromCharCode(27)}[?25l`;
12
+ const SHOW_CURSOR = `${String.fromCharCode(27)}[?25h`;
13
+ const UP_AND_CLEAR = `${String.fromCharCode(27)}[1A${String.fromCharCode(27)}[2K`;
14
+ /** Спросить можно только там, где есть кого спрашивать: в конвейере и в CI — некого. */
15
+ export const canAsk = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);
16
+ function draw(state, question, previous) {
17
+ const lines = renderPicker(state, question);
18
+ process.stdout.write(UP_AND_CLEAR.repeat(previous) + lines.join('\n') + '\n');
19
+ return lines.length;
20
+ }
21
+ /**
22
+ * Выбор галочками. Возвращает выбранные идентификаторы, а на брошенном выборе — `null`:
23
+ * подставлять вместо ответа умолчание нельзя, потому что «взять всё» — тоже решение.
24
+ */
25
+ export function ask(choices, question) {
26
+ if (!choices.length) {
27
+ return Promise.resolve([]);
28
+ }
29
+ return new Promise((resolve) => {
30
+ let state = pickerOf(choices);
31
+ let drawn = 0;
32
+ /** Обработчик приходит доводом, чтобы отпустить терминал ровно тот, что его занял. */
33
+ const stop = (handler) => {
34
+ process.stdin.off('data', handler);
35
+ process.stdin.setRawMode(false);
36
+ process.stdin.pause();
37
+ process.stdout.write(SHOW_CURSOR);
38
+ };
39
+ const onData = (chunk) => {
40
+ state = pressChunk(state, chunk.toString('utf8'));
41
+ if (state.done || state.cancelled) {
42
+ stop(onData);
43
+ resolve(state.cancelled ? null : [...state.chosen]);
44
+ return;
45
+ }
46
+ drawn = draw(state, question, drawn);
47
+ };
48
+ process.stdout.write(HIDE_CURSOR);
49
+ drawn = draw(state, question, 0);
50
+ // Сырой режим: клавиша доходит нажатием, а не строкой по enter, и Ctrl-C приходит
51
+ // знаком в потоке вместо сигнала — потому он и разбирается наравне с остальными.
52
+ process.stdin.setRawMode(true);
53
+ process.stdin.resume();
54
+ process.stdin.on('data', onData);
55
+ });
56
+ }
57
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/bin/prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Терминал: сырой режим, перерисовка и восстановление курсора. Решений здесь нет — переход\n * состояния по нажатой клавише живёт в `lib/picker.ts` и проверяется спекой.\n *\n * Всё, что этот модуль обязан сделать помимо перерисовки, — вернуть терминал в прежнее\n * состояние на любом выходе. Оставленный сырой режим ломает не программу, а оболочку после\n * неё: ввод перестаёт отзываться, и виноватым выглядит терминал.\n */\nimport process from 'node:process';\n\nimport { IChoice, IPickerState, pickerOf, pressChunk, renderPicker } from '../lib/picker.js';\n\nconst HIDE_CURSOR: string = `${String.fromCharCode(27)}[?25l`;\nconst SHOW_CURSOR: string = `${String.fromCharCode(27)}[?25h`;\nconst UP_AND_CLEAR: string = `${String.fromCharCode(27)}[1A${String.fromCharCode(27)}[2K`;\n\n/** Спросить можно только там, где есть кого спрашивать: в конвейере и в CI — некого. */\nexport const canAsk: () => boolean = (): boolean => Boolean(process.stdin.isTTY && process.stdout.isTTY);\n\nfunction draw(state: IPickerState, question: string, previous: number): number {\n const lines: readonly string[] = renderPicker(state, question);\n process.stdout.write(UP_AND_CLEAR.repeat(previous) + lines.join('\\n') + '\\n');\n\n return lines.length;\n}\n\n/**\n * Выбор галочками. Возвращает выбранные идентификаторы, а на брошенном выборе — `null`:\n * подставлять вместо ответа умолчание нельзя, потому что «взять всё» — тоже решение.\n */\nexport function ask(choices: readonly IChoice[], question: string): Promise<readonly string[] | null> {\n if (!choices.length) {\n return Promise.resolve([]);\n }\n\n return new Promise((resolve: (value: readonly string[] | null) => void): void => {\n let state: IPickerState = pickerOf(choices);\n let drawn: number = 0;\n\n /** Обработчик приходит доводом, чтобы отпустить терминал ровно тот, что его занял. */\n const stop: (handler: (chunk: Buffer) => void) => void = (handler: (chunk: Buffer) => void): void => {\n process.stdin.off('data', handler);\n process.stdin.setRawMode(false);\n process.stdin.pause();\n process.stdout.write(SHOW_CURSOR);\n };\n\n const onData: (chunk: Buffer) => void = (chunk: Buffer): void => {\n state = pressChunk(state, chunk.toString('utf8'));\n if (state.done || state.cancelled) {\n stop(onData);\n resolve(state.cancelled ? null : [...state.chosen]);\n\n return;\n }\n drawn = draw(state, question, drawn);\n };\n\n process.stdout.write(HIDE_CURSOR);\n drawn = draw(state, question, 0);\n // Сырой режим: клавиша доходит нажатием, а не строкой по enter, и Ctrl-C приходит\n // знаком в потоке вместо сигнала — потому он и разбирается наравне с остальными.\n process.stdin.setRawMode(true);\n process.stdin.resume();\n process.stdin.on('data', onData);\n });\n}\n"]}
package/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export * from './lib/assets.js';
2
+ export * from './lib/catalog.js';
2
3
  export * from './lib/commands.js';
3
4
  export * from './lib/config.js';
5
+ export * from './lib/picker.js';
4
6
  export * from './lib/plan.js';
5
7
  export * from './lib/sections.js';
6
8
  export * from './lib/stamp.js';
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../projects/agent-kit/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../projects/agent-kit/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC"}
package/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  export * from './lib/assets.js';
2
+ export * from './lib/catalog.js';
2
3
  export * from './lib/commands.js';
3
4
  export * from './lib/config.js';
5
+ export * from './lib/picker.js';
4
6
  export * from './lib/plan.js';
5
7
  export * from './lib/sections.js';
6
8
  export * from './lib/stamp.js';
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../projects/agent-kit/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC","sourcesContent":["export * from './lib/assets.js';\nexport * from './lib/commands.js';\nexport * from './lib/config.js';\nexport * from './lib/plan.js';\nexport * from './lib/sections.js';\nexport * from './lib/stamp.js';\nexport * from './lib/sync.js';\nexport * from './lib/vars.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../projects/agent-kit/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC","sourcesContent":["export * from './lib/assets.js';\nexport * from './lib/catalog.js';\nexport * from './lib/commands.js';\nexport * from './lib/config.js';\nexport * from './lib/picker.js';\nexport * from './lib/plan.js';\nexport * from './lib/sections.js';\nexport * from './lib/stamp.js';\nexport * from './lib/sync.js';\nexport * from './lib/vars.js';\n"]}
package/lib/assets.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { IConfig, TKind } from './config.js';
2
2
  export interface IAsset {
3
- /** Идентификатор: `laws/delivery.md`. Он же путь надстройки и ключ в `skip`. */
3
+ /** Идентификатор: `laws/delivery.md`. Он же путь надстройки и ключ в `only` и `skip`. */
4
4
  readonly id: string;
5
5
  readonly kind: TKind;
6
6
  readonly text: string;
7
7
  /** Путь в дереве проекта, куда ресурс ложится. */
8
8
  readonly target: string;
9
9
  }
10
- export declare function collectAssets(config: IConfig, root: string): readonly IAsset[];
10
+ export declare function collectAssets(config: IConfig, assetsDir: string): readonly IAsset[];
11
11
  //# sourceMappingURL=assets.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/assets.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,OAAO,EAAS,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,WAAW,MAAM;IACnB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CAC3B;AAaD,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAa9E"}
1
+ {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/assets.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,MAAM;IACnB,yFAAyF;IACzF,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CASnF"}
package/lib/assets.js CHANGED
@@ -1,35 +1,20 @@
1
1
  /**
2
- * Что пакет везёт и куда это ложится.
2
+ * Что из везомого пакетом взял этот проект и куда оно ложится.
3
3
  *
4
- * Список ресурсов не выписан руками, а читается из каталога `assets/`: выписанный список
5
- * разошёлся бы с каталогом на первом же добавленном законе, и заметить это было бы нечем —
6
- * ресурс просто не приезжал бы к проекту.
4
+ * Полный набор читает `catalog.ts`, здесь из него остаётся выбранное. Разделено потому, что
5
+ * выбирать приходится раньше, чем раскладывать: `init` спрашивает, какие законы взять, а `list`
6
+ * показывает и то, от чего проект отказался, — обоим нужен набор до отбора.
7
7
  */
8
- import { readdirSync, readFileSync } from 'node:fs';
9
8
  import { join } from 'node:path';
10
- import { KINDS } from './config.js';
11
- function filesOf(dir) {
12
- try {
13
- return readdirSync(dir, { withFileTypes: true })
14
- .filter((entry) => entry.isFile())
15
- .map((entry) => entry.name)
16
- .sort();
17
- }
18
- catch {
19
- return [];
20
- }
21
- }
22
- export function collectAssets(config, root) {
23
- const assets = [];
24
- for (const kind of KINDS) {
25
- for (const name of filesOf(join(root, kind))) {
26
- const id = `${kind}/${name}`;
27
- if (config.skip.includes(id)) {
28
- continue;
29
- }
30
- assets.push({ id, kind, text: readFileSync(join(root, kind, name), 'utf8'), target: join(config.layout[kind], name) });
31
- }
32
- }
33
- return assets;
9
+ import { isChosen, readCatalog } from './catalog.js';
10
+ export function collectAssets(config, assetsDir) {
11
+ return readCatalog(assetsDir)
12
+ .filter((entry) => isChosen(entry, config.only, config.skip))
13
+ .map((entry) => ({
14
+ id: entry.id,
15
+ kind: entry.kind,
16
+ text: entry.text,
17
+ target: join(config.layout[entry.kind], entry.id.slice(entry.kind.length + 1)),
18
+ }));
34
19
  }
35
20
  //# sourceMappingURL=assets.js.map
package/lib/assets.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/assets.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAU,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAW,KAAK,EAAS,MAAM,aAAa,CAAC;AAWpD,SAAS,OAAO,CAAC,GAAW;IACxB,IAAI,CAAC;QACD,OAAO,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;aAC3C,MAAM,CAAC,CAAC,KAAa,EAAW,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;aAClD,GAAG,CAAC,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;aAC1C,IAAI,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAe,EAAE,IAAY;IACvD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;YAC3C,MAAM,EAAE,GAAW,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;YACrC,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC3B,SAAS;YACb,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3H,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC","sourcesContent":["/**\n * Что пакет везёт и куда это ложится.\n *\n * Список ресурсов не выписан руками, а читается из каталога `assets/`: выписанный список\n * разошёлся бы с каталогом на первом же добавленном законе, и заметить это было бы нечем —\n * ресурс просто не приезжал бы к проекту.\n */\nimport { Dirent, readdirSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { IConfig, KINDS, TKind } from './config.js';\n\nexport interface IAsset {\n /** Идентификатор: `laws/delivery.md`. Он же путь надстройки и ключ в `skip`. */\n readonly id: string;\n readonly kind: TKind;\n readonly text: string;\n /** Путь в дереве проекта, куда ресурс ложится. */\n readonly target: string;\n}\n\nfunction filesOf(dir: string): readonly string[] {\n try {\n return readdirSync(dir, { withFileTypes: true })\n .filter((entry: Dirent): boolean => entry.isFile())\n .map((entry: Dirent): string => entry.name)\n .sort();\n } catch {\n return [];\n }\n}\n\nexport function collectAssets(config: IConfig, root: string): readonly IAsset[] {\n const assets: IAsset[] = [];\n for (const kind of KINDS) {\n for (const name of filesOf(join(root, kind))) {\n const id: string = `${kind}/${name}`;\n if (config.skip.includes(id)) {\n continue;\n }\n assets.push({ id, kind, text: readFileSync(join(root, kind, name), 'utf8'), target: join(config.layout[kind], name) });\n }\n }\n\n return assets;\n}\n"]}
1
+ {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/assets.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAmB,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAYtE,MAAM,UAAU,aAAa,CAAC,MAAe,EAAE,SAAiB;IAC5D,OAAO,WAAW,CAAC,SAAS,CAAC;SACxB,MAAM,CAAC,CAAC,KAAsB,EAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;SACtF,GAAG,CAAC,CAAC,KAAsB,EAAU,EAAE,CAAC,CAAC;QACtC,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;KACjF,CAAC,CAAC,CAAC;AACZ,CAAC","sourcesContent":["/**\n * Что из везомого пакетом взял этот проект и куда оно ложится.\n *\n * Полный набор читает `catalog.ts`, здесь из него остаётся выбранное. Разделено потому, что\n * выбирать приходится раньше, чем раскладывать: `init` спрашивает, какие законы взять, а `list`\n * показывает и то, от чего проект отказался, обоим нужен набор до отбора.\n */\nimport { join } from 'node:path';\n\nimport { IEntryOfCatalog, isChosen, readCatalog } from './catalog.js';\nimport { IConfig, TKind } from './config.js';\n\nexport interface IAsset {\n /** Идентификатор: `laws/delivery.md`. Он же путь надстройки и ключ в `only` и `skip`. */\n readonly id: string;\n readonly kind: TKind;\n readonly text: string;\n /** Путь в дереве проекта, куда ресурс ложится. */\n readonly target: string;\n}\n\nexport function collectAssets(config: IConfig, assetsDir: string): readonly IAsset[] {\n return readCatalog(assetsDir)\n .filter((entry: IEntryOfCatalog): boolean => isChosen(entry, config.only, config.skip))\n .map((entry: IEntryOfCatalog): IAsset => ({\n id: entry.id,\n kind: entry.kind,\n text: entry.text,\n target: join(config.layout[entry.kind], entry.id.slice(entry.kind.length + 1)),\n }));\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import { TKind } from './config.js';
2
+ export interface IEntryOfCatalog {
3
+ /** Идентификатор: `laws/delivery.md`. Он же путь надстройки и ключ в `only` и `skip`. */
4
+ readonly id: string;
5
+ readonly kind: TKind;
6
+ /** Имя без рода и расширения: `delivery`. Им ресурс называют в строке запуска. */
7
+ readonly name: string;
8
+ /** Заголовок первой строки файла; у файла без заголовка — его имя. */
9
+ readonly title: string;
10
+ readonly text: string;
11
+ }
12
+ /** Заголовок нужен человеку, который выбирает: имена законов ему ни о чём не говорят. */
13
+ export declare function titleOf(text: string, fallback: string): string;
14
+ /**
15
+ * Всё, что лежит в ресурсах пакета. Список не выписан руками, а читается из каталога:
16
+ * выписанный разошёлся бы с ним на первом же добавленном законе, и заметить это было бы
17
+ * нечем — ресурс просто не приезжал бы к проекту.
18
+ */
19
+ export declare function readCatalog(assetsDir: string): readonly IEntryOfCatalog[];
20
+ /**
21
+ * Взят ли ресурс при этом выборе.
22
+ *
23
+ * `only` ограничивает **только те роды, которые сам называет**. Проект, выбравший девять
24
+ * законов из пятнадцати, шаблоны при этом не терял бы: перечисляя законы, он говорит о
25
+ * законах, а не обо всём, что пакет везёт. Обратное правило читалось бы как «выбрал законы —
26
+ * остался без шаблонов», и заметил бы это только тот, кто пошёл за шаблоном.
27
+ *
28
+ * `skip` вычитает из выбранного. Отказ от одного закона не должен требовать переписать
29
+ * список из пятнадцати, а точечное исключение остаётся точечным.
30
+ */
31
+ export declare function isChosen(entry: IEntryOfCatalog, only: readonly string[], skip: readonly string[]): boolean;
32
+ /**
33
+ * Идентификатор по тому, как ресурс назвали в строке запуска. Принимаются все три формы,
34
+ * которыми его называют вслух: `access`, `access.md` и `laws/access.md`.
35
+ */
36
+ export declare function idOf(spoken: string, kind: TKind, catalog: readonly IEntryOfCatalog[]): string | null;
37
+ export interface IResolvedSelection {
38
+ readonly ids: readonly string[];
39
+ /** Имена, которым в каталоге ничего не отвечает: промах в строке запуска, а не выбор. */
40
+ readonly unknown: readonly string[];
41
+ }
42
+ /** Разбор `--laws access,delivery`: имя без ответа — промах, и молчать о нём нельзя. */
43
+ export declare function resolveSelection(spoken: readonly string[], kind: TKind, catalog: readonly IEntryOfCatalog[]): IResolvedSelection;
44
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +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;AAE3C,MAAM,WAAW,eAAe;IAC5B,yFAAyF;IACzF,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAID,yFAAyF;AACzF,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI9D;AAaD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,eAAe,EAAE,CAWzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAO1G;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,MAAM,GAAG,IAAI,CAQpG;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 ADDED
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Что пакет везёт — прочитанное с диска, до всякого выбора проекта.
3
+ *
4
+ * Каталог нужен отдельно от раскладки, потому что выбирать приходится раньше, чем раскладывать:
5
+ * `init` спрашивает, какие законы взять, а `list` показывает и то, от чего проект отказался.
6
+ * И там и там нужен полный набор, а `collectAssets` по устройству отдаёт уже отобранный.
7
+ */
8
+ import { readdirSync, readFileSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ import { KINDS } from './config.js';
11
+ const TITLE = /^#\s+(\S.*)$/m;
12
+ /** Заголовок нужен человеку, который выбирает: имена законов ему ни о чём не говорят. */
13
+ export function titleOf(text, fallback) {
14
+ const found = text.match(TITLE);
15
+ return found ? found[1].trim() : fallback;
16
+ }
17
+ function filesOf(dir) {
18
+ try {
19
+ return readdirSync(dir, { withFileTypes: true })
20
+ .filter((entry) => entry.isFile())
21
+ .map((entry) => entry.name)
22
+ .sort();
23
+ }
24
+ catch {
25
+ return [];
26
+ }
27
+ }
28
+ /**
29
+ * Всё, что лежит в ресурсах пакета. Список не выписан руками, а читается из каталога:
30
+ * выписанный разошёлся бы с ним на первом же добавленном законе, и заметить это было бы
31
+ * нечем — ресурс просто не приезжал бы к проекту.
32
+ */
33
+ export function readCatalog(assetsDir) {
34
+ const entries = [];
35
+ for (const kind of KINDS) {
36
+ for (const file of filesOf(join(assetsDir, kind))) {
37
+ const text = readFileSync(join(assetsDir, kind, file), 'utf8');
38
+ const name = file.replace(/\.[^.]+$/, '');
39
+ entries.push({ id: `${kind}/${file}`, kind, name, title: titleOf(text, name), text });
40
+ }
41
+ }
42
+ return entries;
43
+ }
44
+ /**
45
+ * Взят ли ресурс при этом выборе.
46
+ *
47
+ * `only` ограничивает **только те роды, которые сам называет**. Проект, выбравший девять
48
+ * законов из пятнадцати, шаблоны при этом не терял бы: перечисляя законы, он говорит о
49
+ * законах, а не обо всём, что пакет везёт. Обратное правило читалось бы как «выбрал законы —
50
+ * остался без шаблонов», и заметил бы это только тот, кто пошёл за шаблоном.
51
+ *
52
+ * `skip` вычитает из выбранного. Отказ от одного закона не должен требовать переписать
53
+ * список из пятнадцати, а точечное исключение остаётся точечным.
54
+ */
55
+ export function isChosen(entry, only, skip) {
56
+ if (skip.includes(entry.id)) {
57
+ return false;
58
+ }
59
+ const restricted = only.some((id) => id.startsWith(`${entry.kind}/`));
60
+ return !restricted || only.includes(entry.id);
61
+ }
62
+ /**
63
+ * Идентификатор по тому, как ресурс назвали в строке запуска. Принимаются все три формы,
64
+ * которыми его называют вслух: `access`, `access.md` и `laws/access.md`.
65
+ */
66
+ export function idOf(spoken, kind, catalog) {
67
+ const wanted = spoken.trim();
68
+ const found = catalog.find((entry) => entry.kind === kind && (entry.id === wanted || entry.name === wanted || entry.id.endsWith(`/${wanted}`)));
69
+ return found ? found.id : null;
70
+ }
71
+ /** Разбор `--laws access,delivery`: имя без ответа — промах, и молчать о нём нельзя. */
72
+ export function resolveSelection(spoken, kind, catalog) {
73
+ const ids = [];
74
+ const unknown = [];
75
+ for (const name of spoken) {
76
+ const id = idOf(name, kind, catalog);
77
+ if (id === null) {
78
+ unknown.push(name.trim());
79
+ }
80
+ else if (!ids.includes(id)) {
81
+ ids.push(id);
82
+ }
83
+ }
84
+ return { ids, unknown };
85
+ }
86
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/catalog.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Что пакет везёт — прочитанное с диска, до всякого выбора проекта.\n *\n * Каталог нужен отдельно от раскладки, потому что выбирать приходится раньше, чем раскладывать:\n * `init` спрашивает, какие законы взять, а `list` показывает и то, от чего проект отказался.\n * И там и там нужен полный набор, а `collectAssets` по устройству отдаёт уже отобранный.\n */\nimport { Dirent, readdirSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { KINDS, TKind } from './config.js';\n\nexport interface IEntryOfCatalog {\n /** Идентификатор: `laws/delivery.md`. Он же путь надстройки и ключ в `only` и `skip`. */\n readonly id: string;\n readonly kind: TKind;\n /** Имя без рода и расширения: `delivery`. Им ресурс называют в строке запуска. */\n readonly name: string;\n /** Заголовок первой строки файла; у файла без заголовка — его имя. */\n readonly title: string;\n readonly text: string;\n}\n\nconst TITLE: RegExp = /^#\\s+(\\S.*)$/m;\n\n/** Заголовок нужен человеку, который выбирает: имена законов ему ни о чём не говорят. */\nexport function titleOf(text: string, fallback: string): string {\n const found: RegExpMatchArray | null = text.match(TITLE);\n\n return found ? found[1].trim() : fallback;\n}\n\nfunction filesOf(dir: string): readonly string[] {\n try {\n return readdirSync(dir, { withFileTypes: true })\n .filter((entry: Dirent): boolean => entry.isFile())\n .map((entry: Dirent): string => entry.name)\n .sort();\n } catch {\n return [];\n }\n}\n\n/**\n * Всё, что лежит в ресурсах пакета. Список не выписан руками, а читается из каталога:\n * выписанный разошёлся бы с ним на первом же добавленном законе, и заметить это было бы\n * нечем — ресурс просто не приезжал бы к проекту.\n */\nexport function readCatalog(assetsDir: string): readonly IEntryOfCatalog[] {\n const entries: IEntryOfCatalog[] = [];\n for (const kind of KINDS) {\n for (const file of filesOf(join(assetsDir, kind))) {\n const text: string = readFileSync(join(assetsDir, kind, file), 'utf8');\n const name: string = file.replace(/\\.[^.]+$/, '');\n entries.push({ id: `${kind}/${file}`, kind, name, title: titleOf(text, name), text });\n }\n }\n\n return entries;\n}\n\n/**\n * Взят ли ресурс при этом выборе.\n *\n * `only` ограничивает **только те роды, которые сам называет**. Проект, выбравший девять\n * законов из пятнадцати, шаблоны при этом не терял бы: перечисляя законы, он говорит о\n * законах, а не обо всём, что пакет везёт. Обратное правило читалось бы как «выбрал законы —\n * остался без шаблонов», и заметил бы это только тот, кто пошёл за шаблоном.\n *\n * `skip` вычитает из выбранного. Отказ от одного закона не должен требовать переписать\n * список из пятнадцати, а точечное исключение остаётся точечным.\n */\nexport function isChosen(entry: IEntryOfCatalog, only: readonly string[], skip: readonly string[]): boolean {\n if (skip.includes(entry.id)) {\n return false;\n }\n const restricted: boolean = only.some((id: string): boolean => id.startsWith(`${entry.kind}/`));\n\n return !restricted || only.includes(entry.id);\n}\n\n/**\n * Идентификатор по тому, как ресурс назвали в строке запуска. Принимаются все три формы,\n * которыми его называют вслух: `access`, `access.md` и `laws/access.md`.\n */\nexport function idOf(spoken: string, kind: TKind, catalog: readonly IEntryOfCatalog[]): string | null {\n const wanted: string = spoken.trim();\n const found: IEntryOfCatalog | undefined = catalog.find(\n (entry: IEntryOfCatalog): boolean =>\n entry.kind === kind && (entry.id === wanted || entry.name === wanted || entry.id.endsWith(`/${wanted}`))\n );\n\n return found ? found.id : null;\n}\n\nexport interface IResolvedSelection {\n readonly ids: readonly string[];\n /** Имена, которым в каталоге ничего не отвечает: промах в строке запуска, а не выбор. */\n readonly unknown: readonly string[];\n}\n\n/** Разбор `--laws access,delivery`: имя без ответа — промах, и молчать о нём нельзя. */\nexport function resolveSelection(spoken: readonly string[], kind: TKind, catalog: readonly IEntryOfCatalog[]): IResolvedSelection {\n const ids: string[] = [];\n const unknown: string[] = [];\n\n for (const name of spoken) {\n const id: string | null = idOf(name, kind, catalog);\n if (id === null) {\n unknown.push(name.trim());\n } else if (!ids.includes(id)) {\n ids.push(id);\n }\n }\n\n return { ids, unknown };\n}\n"]}
package/lib/commands.d.ts CHANGED
@@ -8,7 +8,19 @@ export interface IEnvironment {
8
8
  readonly version: string;
9
9
  readonly assetsDir: string;
10
10
  }
11
- export declare function init(root: string): IOutcomeOfCommand;
11
+ /**
12
+ * Что проект выбрал при заведении конфига. Пустой список — весь набор; выбор приходит сюда
13
+ * готовым, потому что взять его негде, кроме края: у строки запуска флаг, у терминала вопрос,
14
+ * а у неинтерактивного прогона нет ни того ни другого — и решать это команде не по чину.
15
+ */
16
+ export declare function init(root: string, only?: readonly string[]): IOutcomeOfCommand;
12
17
  export declare function sync(env: IEnvironment, check: boolean): IOutcomeOfCommand;
13
18
  export declare function doctor(env: IEnvironment): IOutcomeOfCommand;
19
+ /**
20
+ * Что пакет везёт и что из этого взято здесь.
21
+ *
22
+ * Работает и без конфига: посмотреть, из чего выбирать, нужно раньше, чем выбор сделан, — иначе
23
+ * имена законов взять неоткуда, кроме исходников пакета.
24
+ */
25
+ export declare function list(env: IEnvironment): IOutcomeOfCommand;
14
26
  //# sourceMappingURL=commands.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/commands.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,2FAA2F;AAC3F,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC9B;AAuBD,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAmBpD;AAED,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,iBAAiB,CA0CzE;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAqB3D"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/commands.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,2FAA2F;AAC3F,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC9B;AAgDD;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,SAAS,MAAM,EAAO,GAAG,iBAAiB,CAoBlF;AAED,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,iBAAiB,CA0CzE;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAyB3D;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA+CzD"}