@rt-tools/agent-kit 0.2.0 → 0.4.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.
- package/README.md +235 -18
- package/assets/agents/business-analyst.md +74 -0
- package/assets/agents/project-manager.md +70 -0
- package/assets/agents/qa-engineer.md +72 -0
- package/assets/agents/skill-curator.md +110 -0
- package/assets/agents/spec-critic.md +44 -0
- package/assets/agents/spec-writer.md +50 -0
- package/assets/checks/board.github.mjs +286 -0
- package/assets/checks/check-board.github.mjs +188 -0
- package/assets/checks/check-doc-paths.mjs +163 -0
- package/assets/checks/check-dupes.mjs +277 -0
- package/assets/checks/check-lib-layers.mjs +573 -0
- package/assets/checks/check-reuse.mjs +208 -0
- package/assets/checks/check-schema-drift.mjs +186 -0
- package/assets/checks/check-specs.mjs +1007 -0
- package/assets/checks/check-styles.mjs +109 -0
- package/assets/checks/rt-kit-checks.config.mjs +134 -0
- package/assets/checks/task-new.github.mjs +198 -0
- package/assets/commands/skill-curator.md +70 -0
- package/assets/defaults/gate-map.sh +100 -0
- package/assets/defaults/project.sh +179 -0
- package/assets/hooks/browser-device-id.sh +20 -0
- package/assets/hooks/browser-guard-device-id.sh +28 -0
- package/assets/hooks/browser-guard-no-asking.sh +27 -0
- package/assets/hooks/browser-guard-no-listing.sh +18 -0
- package/assets/hooks/browser-guard-no-other-drivers.sh +79 -0
- package/assets/hooks/browser-guard-require-select.sh +54 -0
- package/assets/hooks/commit-msg.sh +26 -0
- package/assets/hooks/constitution-index.sh +43 -0
- package/assets/hooks/dev-server-guard.sh +115 -0
- package/assets/hooks/docs-guard.sh +282 -0
- package/assets/hooks/git-guard-delivery.sh +167 -0
- package/assets/hooks/git-guard-main.sh +73 -0
- package/assets/hooks/git-guard-push-tests.sh +94 -0
- package/assets/hooks/glossary-load.sh +23 -0
- package/assets/hooks/lint-after-edit.sh +219 -0
- package/assets/hooks/qa-dataid-guard.sh +121 -0
- package/assets/hooks/reuse-first-guard.sh +154 -0
- package/assets/hooks/skill-gate-rearm.sh +23 -0
- package/assets/hooks/skill-gate.sh +128 -0
- package/assets/hooks/skill-loaded.sh +21 -0
- package/assets/hooks/sql-guard.sh +679 -0
- package/assets/hooks/task-context-load.sh +100 -0
- package/assets/hooks/task-flow-guard.sh +107 -0
- package/assets/laws/{access.md → application/access.md} +1 -4
- package/assets/laws/{locales.md → application/locales.md} +1 -3
- package/assets/laws/application/money.md +41 -0
- package/assets/laws/application/ownership.md +32 -0
- package/assets/laws/{search-visibility.md → application/search-visibility.md} +1 -1
- package/assets/laws/code-structure.md +7 -6
- package/assets/laws/delivery.md +53 -3
- package/assets/laws/entity-editing.md +49 -55
- package/assets/laws/entity-models.md +4 -14
- package/assets/laws/frontend-application.md +5 -5
- package/assets/laws/lib-imports.md +14 -1
- package/assets/laws/lists.md +33 -0
- package/assets/laws/navigation.md +40 -0
- package/assets/laws/project-documentation.md +17 -8
- package/assets/laws/reuse-first.md +26 -21
- package/assets/laws/shared-code.md +13 -1
- package/assets/laws/verifiability.md +17 -1
- package/assets/laws/work-conduct.md +48 -0
- package/assets/patterns/admin-lists-screen.md +131 -0
- package/assets/patterns/admin-nav-item.md +71 -0
- package/assets/patterns/angular-patterns-state.md +101 -0
- package/assets/patterns/api-layer-pair.md +88 -0
- package/assets/patterns/browser-verification-measure.md +86 -0
- package/assets/patterns/browser-verification-stand.md +143 -0
- package/assets/patterns/component-structure-new.md +99 -0
- package/assets/patterns/dependencies-upgrade.md +65 -0
- package/assets/patterns/doc-style-sweep.md +137 -0
- package/assets/patterns/doc-style-write.md +109 -0
- package/assets/patterns/entity-aside.md +136 -0
- package/assets/patterns/entity-models-new.md +124 -0
- package/assets/patterns/entity-store.md +91 -0
- package/assets/patterns/git-workflow-commit.azure.md +259 -0
- package/assets/patterns/git-workflow-commit.github.md +333 -0
- package/assets/patterns/git-workflow-commit.gitlab.md +283 -0
- package/assets/patterns/git-workflow-merge.md +99 -0
- package/assets/patterns/git-workflow-migration.md +88 -0
- package/assets/patterns/git-workflow-restart.md +49 -0
- package/assets/patterns/lib-layers-move.md +95 -0
- package/assets/patterns/lib-layers-new.md +82 -0
- package/assets/patterns/ownership-scope-resolve.md +69 -0
- package/assets/patterns/permissions-procedure.md +71 -0
- package/assets/patterns/platform-access-di.md +84 -0
- package/assets/patterns/pricing-quote.md +71 -0
- package/assets/patterns/reuse-first-extend.md +73 -0
- package/assets/patterns/seo-page.md +104 -0
- package/assets/patterns/seo-verify.md +83 -0
- package/assets/patterns/shared-code-new.md +86 -0
- package/assets/patterns/spec-driven-domain.md +107 -0
- package/assets/patterns/spec-driven-rule.md +127 -0
- package/assets/patterns/styling-bem-component.md +88 -0
- package/assets/patterns/styling-bem-layout.md +73 -0
- package/assets/patterns/task-flow-close.md +90 -0
- package/assets/patterns/task-flow-resume.md +94 -0
- package/assets/patterns/task-flow-start.md +117 -0
- package/assets/patterns/testing-e2e.md +92 -0
- package/assets/patterns/testing-unit.md +117 -0
- package/assets/patterns/translations-key.md +64 -0
- package/assets/patterns/ts-procedure.md +65 -0
- package/assets/rules/angular-patterns.md +71 -0
- package/assets/rules/api-layer.md +71 -0
- package/assets/rules/browser-verification.md +87 -0
- package/assets/rules/component-structure.md +64 -0
- package/assets/rules/dependencies.md +66 -0
- package/assets/rules/doc-style.md +103 -0
- package/assets/rules/entity-conventions.md +78 -0
- package/assets/rules/entity-models.md +70 -0
- package/assets/rules/git-workflow.azure.md +116 -0
- package/assets/rules/git-workflow.github.md +123 -0
- package/assets/rules/git-workflow.gitlab.md +113 -0
- package/assets/rules/lib-layers.md +80 -0
- package/assets/rules/lists.md +73 -0
- package/assets/rules/navigation.md +78 -0
- package/assets/rules/ownership-scope.md +63 -0
- package/assets/rules/permissions.md +70 -0
- package/assets/rules/platform-access.md +77 -0
- package/assets/rules/pricing.md +64 -0
- package/assets/rules/reuse-first.md +83 -0
- package/assets/rules/seo.md +71 -0
- package/assets/rules/shared-code.md +70 -0
- package/assets/rules/spec-driven.md +135 -0
- package/assets/rules/styling-bem.md +74 -0
- package/assets/rules/task-flow.md +110 -0
- package/assets/rules/testing.md +100 -0
- package/assets/rules/translations.md +69 -0
- package/assets/rules/typescript-conventions.md +76 -0
- package/assets/skills/agent-kit.md +81 -0
- package/assets/skills/write-a-skill.md +108 -0
- package/assets/templates/gate-map.sh +45 -0
- package/assets/templates/implementation.md +44 -0
- package/assets/templates/pattern.md +5 -1
- package/assets/templates/project.sh +54 -0
- package/assets/templates/rule.md +12 -23
- package/assets/variants.json +20 -0
- package/assets/workflows/feature.js +134 -0
- package/assets/workflows/plan.js +150 -0
- package/bin/agent-kit.d.ts.map +1 -1
- package/bin/agent-kit.js +78 -5
- package/bin/agent-kit.js.map +1 -1
- package/bin/prompt.d.ts +5 -0
- package/bin/prompt.d.ts.map +1 -1
- package/bin/prompt.js +19 -7
- package/bin/prompt.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/lib/assets.d.ts +14 -1
- package/lib/assets.d.ts.map +1 -1
- package/lib/assets.js +23 -2
- package/lib/assets.js.map +1 -1
- package/lib/catalog.d.ts +52 -5
- package/lib/catalog.d.ts.map +1 -1
- package/lib/catalog.js +104 -16
- package/lib/catalog.js.map +1 -1
- package/lib/commands.d.ts +22 -1
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +218 -11
- package/lib/commands.js.map +1 -1
- package/lib/companion.d.ts +57 -0
- package/lib/companion.d.ts.map +1 -0
- package/lib/companion.js +60 -0
- package/lib/companion.js.map +1 -0
- package/lib/config.d.ts +42 -2
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +60 -2
- package/lib/config.js.map +1 -1
- package/lib/freshness.d.ts +14 -0
- package/lib/freshness.d.ts.map +1 -0
- package/lib/freshness.js +116 -0
- package/lib/freshness.js.map +1 -0
- package/lib/hooks-map.d.ts +24 -0
- package/lib/hooks-map.d.ts.map +1 -0
- package/lib/hooks-map.js +72 -0
- package/lib/hooks-map.js.map +1 -0
- package/lib/integrity.d.ts +36 -0
- package/lib/integrity.d.ts.map +1 -0
- package/lib/integrity.js +44 -0
- package/lib/integrity.js.map +1 -0
- package/lib/picker.d.ts +11 -1
- package/lib/picker.d.ts.map +1 -1
- package/lib/picker.js +44 -6
- package/lib/picker.js.map +1 -1
- package/lib/stamp.d.ts +2 -5
- package/lib/stamp.d.ts.map +1 -1
- package/lib/stamp.js +25 -10
- package/lib/stamp.js.map +1 -1
- package/lib/sync.d.ts +29 -0
- package/lib/sync.d.ts.map +1 -1
- package/lib/sync.js +78 -4
- package/lib/sync.js.map +1 -1
- package/lib/variants.d.ts +44 -0
- package/lib/variants.d.ts.map +1 -0
- package/lib/variants.js +82 -0
- package/lib/variants.js.map +1 -0
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.4.0.tgz +0 -0
- package/assets/laws/admin-lists.md +0 -35
- package/assets/laws/admin-navigation.md +0 -38
- package/rt-tools-agent-kit-0.2.0.tgz +0 -0
package/lib/catalog.d.ts
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import { TKind } from './config.js';
|
|
2
|
+
import { IVariant } from './variants.js';
|
|
2
3
|
export interface IEntryOfCatalog {
|
|
3
|
-
/** Идентификатор: `laws/
|
|
4
|
+
/** Идентификатор: `laws/application/money.md`. Он же путь надстройки и ключ в `only` и `skip`. */
|
|
4
5
|
readonly id: string;
|
|
5
6
|
readonly kind: TKind;
|
|
6
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Имя без рода, вида и расширения: `delivery`, `application/money`. Им ресурс называют в
|
|
9
|
+
* строке запуска, и им же он ложится в дерево проекта.
|
|
10
|
+
*/
|
|
7
11
|
readonly name: string;
|
|
8
12
|
/** Заголовок первой строки файла; у файла без заголовка — его имя. */
|
|
9
13
|
readonly title: string;
|
|
14
|
+
/** Вид ресурса, если он лежит в пакете в нескольких: `git-workflow.github.md`. */
|
|
15
|
+
readonly variant: IVariant | null;
|
|
10
16
|
readonly text: string;
|
|
17
|
+
/**
|
|
18
|
+
* Запускается ли файл сам по себе. Гард зовут по пути, а не через оболочку, и файл без
|
|
19
|
+
* права на запуск отвечает отказом доступа — то есть ненулевым кодом, который читается как
|
|
20
|
+
* «правка отбита». Признак берётся с файла в пакете и переносится на разложенный.
|
|
21
|
+
*/
|
|
22
|
+
readonly executable: boolean;
|
|
11
23
|
}
|
|
12
24
|
/** Заголовок нужен человеку, который выбирает: имена законов ему ни о чём не говорят. */
|
|
13
25
|
export declare function titleOf(text: string, fallback: string): string;
|
|
@@ -17,6 +29,12 @@ export declare function titleOf(text: string, fallback: string): string;
|
|
|
17
29
|
* нечем — ресурс просто не приезжал бы к проекту.
|
|
18
30
|
*/
|
|
19
31
|
export declare function readCatalog(assetsDir: string): readonly IEntryOfCatalog[];
|
|
32
|
+
/** Чем проект ограничил раскладку: выбором, отказом и видами, которые он назвал. */
|
|
33
|
+
export interface ISelection {
|
|
34
|
+
readonly only: readonly string[];
|
|
35
|
+
readonly skip: readonly string[];
|
|
36
|
+
readonly variants: Readonly<Record<string, string>>;
|
|
37
|
+
}
|
|
20
38
|
/**
|
|
21
39
|
* Взят ли ресурс при этом выборе.
|
|
22
40
|
*
|
|
@@ -27,11 +45,40 @@ export declare function readCatalog(assetsDir: string): readonly IEntryOfCatalog
|
|
|
27
45
|
*
|
|
28
46
|
* `skip` вычитает из выбранного. Отказ от одного закона не должен требовать переписать
|
|
29
47
|
* список из пятнадцати, а точечное исключение остаётся точечным.
|
|
48
|
+
*
|
|
49
|
+
* Вид отбирается раньше обоих: ресурс чужого хостинга не «не выбран» — его в этом дереве не
|
|
50
|
+
* существует вовсе, и называть его в `skip` проекту незачем.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isChosen(entry: IEntryOfCatalog, selection: ISelection): boolean;
|
|
53
|
+
/** Ресурс, у которого виды есть, но ни один не отвечает тому, что выбрало дерево. */
|
|
54
|
+
export interface IGapOfVariant {
|
|
55
|
+
readonly kind: TKind;
|
|
56
|
+
/** Имя без вида — под ним ресурс лёг бы в дерево. */
|
|
57
|
+
readonly name: string;
|
|
58
|
+
readonly axis: string;
|
|
59
|
+
/** Что выбрало дерево по этой оси. */
|
|
60
|
+
readonly chosen: string;
|
|
61
|
+
/** Виды, которые у ресурса есть на самом деле. */
|
|
62
|
+
readonly available: readonly string[];
|
|
63
|
+
/** Идентификаторы этих видов: ими же пробел и снимается через `skip`. */
|
|
64
|
+
readonly ids: readonly string[];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Ресурсы, оставшиеся без вида под выбор дерева.
|
|
68
|
+
*
|
|
69
|
+
* Молчаливый пропуск такого ресурса — худший из исходов. Правило поставки едет тремя видами, а
|
|
70
|
+
* команды заведения задачи и сверки очереди — одним: дерево, выбравшее чужой хостинг, получает
|
|
71
|
+
* правило, которое зовёт три команды, и ни одного скрипта под ними. Отказа при этом не бывает —
|
|
72
|
+
* ресурс просто не приезжает, и узнают об этом, когда команда из правила не находится.
|
|
73
|
+
*
|
|
74
|
+
* Отсюда правило: **ресурс с видами, ни один из которых не совпал, — отказ, а не пропуск.**
|
|
75
|
+
* Снимается он двумя способами, и оба явные: завести недостающий вид в пакете либо назвать
|
|
76
|
+
* существующие в `skip` — дерево тем самым говорит, что обходится без этого ресурса.
|
|
30
77
|
*/
|
|
31
|
-
export declare function
|
|
78
|
+
export declare function variantGaps(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IGapOfVariant[];
|
|
32
79
|
/**
|
|
33
|
-
* Идентификатор по тому, как ресурс назвали в строке запуска. Принимаются все
|
|
34
|
-
*
|
|
80
|
+
* Идентификатор по тому, как ресурс назвали в строке запуска. Принимаются все формы, которыми
|
|
81
|
+
* его называют вслух: `money`, `application/money`, `money.md` и `laws/application/money.md`.
|
|
35
82
|
*/
|
|
36
83
|
export declare function idOf(spoken: string, kind: TKind, catalog: readonly IEntryOfCatalog[]): string | null;
|
|
37
84
|
export interface IResolvedSelection {
|
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;
|
|
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;CAChC;AAeD,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,CAgBzE;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;;;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
|
@@ -5,25 +5,52 @@
|
|
|
5
5
|
* `init` спрашивает, какие законы взять, а `list` показывает и то, от чего проект отказался.
|
|
6
6
|
* И там и там нужен полный набор, а `collectAssets` по устройству отдаёт уже отобранный.
|
|
7
7
|
*/
|
|
8
|
-
import { readdirSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { accessSync, constants, readdirSync, readFileSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
10
|
import { KINDS } from './config.js';
|
|
11
|
+
import { matchesVariant, readAxes, variantOf, withoutVariant } from './variants.js';
|
|
11
12
|
const TITLE = /^#\s+(\S.*)$/m;
|
|
13
|
+
/** Запускается ли файл сам по себе. Спрашиваем систему, а не разбираем биты режима руками. */
|
|
14
|
+
function isExecutable(path) {
|
|
15
|
+
try {
|
|
16
|
+
accessSync(path, constants.X_OK);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
12
23
|
/** Заголовок нужен человеку, который выбирает: имена законов ему ни о чём не говорят. */
|
|
13
24
|
export function titleOf(text, fallback) {
|
|
14
25
|
const found = text.match(TITLE);
|
|
15
26
|
return found ? found[1].trim() : fallback;
|
|
16
27
|
}
|
|
17
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Файлы рода — вместе с теми, что лежат в подкаталогах.
|
|
30
|
+
*
|
|
31
|
+
* Обход рекурсивный потому, что у законов два слоя: общий закон верен любому приложению этого
|
|
32
|
+
* класса, а закон приложения — только такому, где есть деньги, локали или владеющая сущность.
|
|
33
|
+
* Слой отражён каталогом, и путь внутри рода едет в идентификатор целиком, чтобы разложенное
|
|
34
|
+
* повторяло раскладку пакета.
|
|
35
|
+
*/
|
|
36
|
+
function filesOf(dir, prefix = '') {
|
|
37
|
+
let entries;
|
|
18
38
|
try {
|
|
19
|
-
|
|
20
|
-
.filter((entry) => entry.isFile())
|
|
21
|
-
.map((entry) => entry.name)
|
|
22
|
-
.sort();
|
|
39
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
23
40
|
}
|
|
24
41
|
catch {
|
|
25
42
|
return [];
|
|
26
43
|
}
|
|
44
|
+
const files = [];
|
|
45
|
+
for (const entry of [...entries].sort((one, two) => one.name.localeCompare(two.name))) {
|
|
46
|
+
if (entry.isFile()) {
|
|
47
|
+
files.push(`${prefix}${entry.name}`);
|
|
48
|
+
}
|
|
49
|
+
else if (entry.isDirectory()) {
|
|
50
|
+
files.push(...filesOf(join(dir, entry.name), `${prefix}${entry.name}/`));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return files;
|
|
27
54
|
}
|
|
28
55
|
/**
|
|
29
56
|
* Всё, что лежит в ресурсах пакета. Список не выписан руками, а читается из каталога:
|
|
@@ -31,12 +58,16 @@ function filesOf(dir) {
|
|
|
31
58
|
* нечем — ресурс просто не приезжал бы к проекту.
|
|
32
59
|
*/
|
|
33
60
|
export function readCatalog(assetsDir) {
|
|
61
|
+
const axes = readAxes(assetsDir);
|
|
34
62
|
const entries = [];
|
|
35
63
|
for (const kind of KINDS) {
|
|
36
64
|
for (const file of filesOf(join(assetsDir, kind))) {
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
65
|
+
const path = join(assetsDir, kind, file);
|
|
66
|
+
const text = readFileSync(path, 'utf8');
|
|
67
|
+
const variant = variantOf(file, axes);
|
|
68
|
+
const name = withoutVariant(file, variant).replace(/\.[^./]+$/, '');
|
|
69
|
+
const executable = isExecutable(path);
|
|
70
|
+
entries.push({ id: `${kind}/${file}`, kind, name, title: titleOf(text, name), variant, text, executable });
|
|
40
71
|
}
|
|
41
72
|
}
|
|
42
73
|
return entries;
|
|
@@ -51,21 +82,78 @@ export function readCatalog(assetsDir) {
|
|
|
51
82
|
*
|
|
52
83
|
* `skip` вычитает из выбранного. Отказ от одного закона не должен требовать переписать
|
|
53
84
|
* список из пятнадцати, а точечное исключение остаётся точечным.
|
|
85
|
+
*
|
|
86
|
+
* Вид отбирается раньше обоих: ресурс чужого хостинга не «не выбран» — его в этом дереве не
|
|
87
|
+
* существует вовсе, и называть его в `skip` проекту незачем.
|
|
54
88
|
*/
|
|
55
|
-
export function isChosen(entry,
|
|
56
|
-
if (
|
|
89
|
+
export function isChosen(entry, selection) {
|
|
90
|
+
if (!matchesVariant(entry.variant, selection.variants)) {
|
|
57
91
|
return false;
|
|
58
92
|
}
|
|
59
|
-
|
|
60
|
-
|
|
93
|
+
if (selection.skip.includes(entry.id)) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const restricted = selection.only.some((id) => id.startsWith(`${entry.kind}/`));
|
|
97
|
+
return !restricted || selection.only.includes(entry.id);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Ресурсы, оставшиеся без вида под выбор дерева.
|
|
101
|
+
*
|
|
102
|
+
* Молчаливый пропуск такого ресурса — худший из исходов. Правило поставки едет тремя видами, а
|
|
103
|
+
* команды заведения задачи и сверки очереди — одним: дерево, выбравшее чужой хостинг, получает
|
|
104
|
+
* правило, которое зовёт три команды, и ни одного скрипта под ними. Отказа при этом не бывает —
|
|
105
|
+
* ресурс просто не приезжает, и узнают об этом, когда команда из правила не находится.
|
|
106
|
+
*
|
|
107
|
+
* Отсюда правило: **ресурс с видами, ни один из которых не совпал, — отказ, а не пропуск.**
|
|
108
|
+
* Снимается он двумя способами, и оба явные: завести недостающий вид в пакете либо назвать
|
|
109
|
+
* существующие в `skip` — дерево тем самым говорит, что обходится без этого ресурса.
|
|
110
|
+
*/
|
|
111
|
+
export function variantGaps(catalog, selection) {
|
|
112
|
+
const groups = new Map();
|
|
113
|
+
for (const entry of catalog) {
|
|
114
|
+
if (entry.variant === null) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const key = `${entry.kind}/${entry.name}`;
|
|
118
|
+
groups.set(key, [...(groups.get(key) ?? []), entry]);
|
|
119
|
+
}
|
|
120
|
+
const gaps = [];
|
|
121
|
+
for (const entries of groups.values()) {
|
|
122
|
+
const axis = entries[0].variant?.axis ?? '';
|
|
123
|
+
if (entries.some((entry) => matchesVariant(entry.variant, selection.variants))) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
// Отказ от ресурса — законный ответ, и повторять его отказом раскладки незачем: дерево
|
|
127
|
+
// уже сказало, что обходится без него.
|
|
128
|
+
if (entries.every((entry) => selection.skip.includes(entry.id))) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
// Род, суженный через `only`, отбирает ресурсы поимённо: не названный в нём ресурс не
|
|
132
|
+
// пропал — его не просили.
|
|
133
|
+
const restricted = selection.only.some((id) => id.startsWith(`${entries[0].kind}/`));
|
|
134
|
+
if (restricted && !entries.some((entry) => selection.only.includes(entry.id))) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
gaps.push({
|
|
138
|
+
kind: entries[0].kind,
|
|
139
|
+
name: entries[0].name,
|
|
140
|
+
axis,
|
|
141
|
+
chosen: selection.variants[axis] ?? '',
|
|
142
|
+
available: entries.map((entry) => entry.variant?.value ?? ''),
|
|
143
|
+
ids: entries.map((entry) => entry.id),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return gaps;
|
|
61
147
|
}
|
|
62
148
|
/**
|
|
63
|
-
* Идентификатор по тому, как ресурс назвали в строке запуска. Принимаются все
|
|
64
|
-
*
|
|
149
|
+
* Идентификатор по тому, как ресурс назвали в строке запуска. Принимаются все формы, которыми
|
|
150
|
+
* его называют вслух: `money`, `application/money`, `money.md` и `laws/application/money.md`.
|
|
65
151
|
*/
|
|
66
152
|
export function idOf(spoken, kind, catalog) {
|
|
67
153
|
const wanted = spoken.trim();
|
|
68
|
-
const
|
|
154
|
+
const shortOf = (entry) => entry.name.split('/').pop() ?? entry.name;
|
|
155
|
+
const found = catalog.find((entry) => entry.kind === kind &&
|
|
156
|
+
(entry.id === wanted || entry.name === wanted || shortOf(entry) === wanted || entry.id.endsWith(`/${wanted}`)));
|
|
69
157
|
return found ? found.id : null;
|
|
70
158
|
}
|
|
71
159
|
/** Разбор `--laws access,delivery`: имя без ответа — промах, и молчать о нём нельзя. */
|
package/lib/catalog.js.map
CHANGED
|
@@ -1 +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"]}
|
|
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 { accessSync, constants, Dirent, readdirSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { KINDS, TKind } from './config.js';\nimport { IAxis, IVariant, matchesVariant, readAxes, variantOf, withoutVariant } from './variants.js';\n\nexport interface IEntryOfCatalog {\n /** Идентификатор: `laws/application/money.md`. Он же путь надстройки и ключ в `only` и `skip`. */\n readonly id: string;\n readonly kind: TKind;\n /**\n * Имя без рода, вида и расширения: `delivery`, `application/money`. Им ресурс называют в\n * строке запуска, и им же он ложится в дерево проекта.\n */\n readonly name: string;\n /** Заголовок первой строки файла; у файла без заголовка — его имя. */\n readonly title: string;\n /** Вид ресурса, если он лежит в пакете в нескольких: `git-workflow.github.md`. */\n readonly variant: IVariant | null;\n readonly text: string;\n /**\n * Запускается ли файл сам по себе. Гард зовут по пути, а не через оболочку, и файл без\n * права на запуск отвечает отказом доступа — то есть ненулевым кодом, который читается как\n * «правка отбита». Признак берётся с файла в пакете и переносится на разложенный.\n */\n readonly executable: boolean;\n}\n\nconst TITLE: RegExp = /^#\\s+(\\S.*)$/m;\n\n/** Запускается ли файл сам по себе. Спрашиваем систему, а не разбираем биты режима руками. */\nfunction isExecutable(path: string): boolean {\n try {\n accessSync(path, constants.X_OK);\n\n return true;\n } catch {\n return false;\n }\n}\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\n/**\n * Файлы рода — вместе с теми, что лежат в подкаталогах.\n *\n * Обход рекурсивный потому, что у законов два слоя: общий закон верен любому приложению этого\n * класса, а закон приложения — только такому, где есть деньги, локали или владеющая сущность.\n * Слой отражён каталогом, и путь внутри рода едет в идентификатор целиком, чтобы разложенное\n * повторяло раскладку пакета.\n */\nfunction filesOf(dir: string, prefix: string = ''): readonly string[] {\n let entries: readonly Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n\n const files: string[] = [];\n for (const entry of [...entries].sort((one: Dirent, two: Dirent): number => one.name.localeCompare(two.name))) {\n if (entry.isFile()) {\n files.push(`${prefix}${entry.name}`);\n } else if (entry.isDirectory()) {\n files.push(...filesOf(join(dir, entry.name), `${prefix}${entry.name}/`));\n }\n }\n\n return files;\n}\n\n/**\n * Всё, что лежит в ресурсах пакета. Список не выписан руками, а читается из каталога:\n * выписанный разошёлся бы с ним на первом же добавленном законе, и заметить это было бы\n * нечем — ресурс просто не приезжал бы к проекту.\n */\nexport function readCatalog(assetsDir: string): readonly IEntryOfCatalog[] {\n const axes: readonly IAxis[] = readAxes(assetsDir);\n const entries: IEntryOfCatalog[] = [];\n\n for (const kind of KINDS) {\n for (const file of filesOf(join(assetsDir, kind))) {\n const path: string = join(assetsDir, kind, file);\n const text: string = readFileSync(path, 'utf8');\n const variant: IVariant | null = variantOf(file, axes);\n const name: string = withoutVariant(file, variant).replace(/\\.[^./]+$/, '');\n const executable: boolean = isExecutable(path);\n entries.push({ id: `${kind}/${file}`, kind, name, title: titleOf(text, name), variant, text, executable });\n }\n }\n\n return entries;\n}\n\n/** Чем проект ограничил раскладку: выбором, отказом и видами, которые он назвал. */\nexport interface ISelection {\n readonly only: readonly string[];\n readonly skip: readonly string[];\n readonly variants: Readonly<Record<string, string>>;\n}\n\n/**\n * Взят ли ресурс при этом выборе.\n *\n * `only` ограничивает **только те роды, которые сам называет**. Проект, выбравший девять\n * законов из пятнадцати, шаблоны при этом не терял бы: перечисляя законы, он говорит о\n * законах, а не обо всём, что пакет везёт. Обратное правило читалось бы как «выбрал законы —\n * остался без шаблонов», и заметил бы это только тот, кто пошёл за шаблоном.\n *\n * `skip` вычитает из выбранного. Отказ от одного закона не должен требовать переписать\n * список из пятнадцати, а точечное исключение остаётся точечным.\n *\n * Вид отбирается раньше обоих: ресурс чужого хостинга не «не выбран» — его в этом дереве не\n * существует вовсе, и называть его в `skip` проекту незачем.\n */\nexport function isChosen(entry: IEntryOfCatalog, selection: ISelection): boolean {\n if (!matchesVariant(entry.variant, selection.variants)) {\n return false;\n }\n if (selection.skip.includes(entry.id)) {\n return false;\n }\n const restricted: boolean = selection.only.some((id: string): boolean => id.startsWith(`${entry.kind}/`));\n\n return !restricted || selection.only.includes(entry.id);\n}\n\n/** Ресурс, у которого виды есть, но ни один не отвечает тому, что выбрало дерево. */\nexport interface IGapOfVariant {\n readonly kind: TKind;\n /** Имя без вида — под ним ресурс лёг бы в дерево. */\n readonly name: string;\n readonly axis: string;\n /** Что выбрало дерево по этой оси. */\n readonly chosen: string;\n /** Виды, которые у ресурса есть на самом деле. */\n readonly available: readonly string[];\n /** Идентификаторы этих видов: ими же пробел и снимается через `skip`. */\n readonly ids: readonly string[];\n}\n\n/**\n * Ресурсы, оставшиеся без вида под выбор дерева.\n *\n * Молчаливый пропуск такого ресурса — худший из исходов. Правило поставки едет тремя видами, а\n * команды заведения задачи и сверки очереди — одним: дерево, выбравшее чужой хостинг, получает\n * правило, которое зовёт три команды, и ни одного скрипта под ними. Отказа при этом не бывает —\n * ресурс просто не приезжает, и узнают об этом, когда команда из правила не находится.\n *\n * Отсюда правило: **ресурс с видами, ни один из которых не совпал, — отказ, а не пропуск.**\n * Снимается он двумя способами, и оба явные: завести недостающий вид в пакете либо назвать\n * существующие в `skip` — дерево тем самым говорит, что обходится без этого ресурса.\n */\nexport function variantGaps(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IGapOfVariant[] {\n const groups: Map<string, IEntryOfCatalog[]> = new Map();\n for (const entry of catalog) {\n if (entry.variant === null) {\n continue;\n }\n const key: string = `${entry.kind}/${entry.name}`;\n groups.set(key, [...(groups.get(key) ?? []), entry]);\n }\n\n const gaps: IGapOfVariant[] = [];\n for (const entries of groups.values()) {\n const axis: string = entries[0].variant?.axis ?? '';\n if (entries.some((entry: IEntryOfCatalog): boolean => matchesVariant(entry.variant, selection.variants))) {\n continue;\n }\n // Отказ от ресурса — законный ответ, и повторять его отказом раскладки незачем: дерево\n // уже сказало, что обходится без него.\n if (entries.every((entry: IEntryOfCatalog): boolean => selection.skip.includes(entry.id))) {\n continue;\n }\n // Род, суженный через `only`, отбирает ресурсы поимённо: не названный в нём ресурс не\n // пропал — его не просили.\n const restricted: boolean = selection.only.some((id: string): boolean => id.startsWith(`${entries[0].kind}/`));\n if (restricted && !entries.some((entry: IEntryOfCatalog): boolean => selection.only.includes(entry.id))) {\n continue;\n }\n gaps.push({\n kind: entries[0].kind,\n name: entries[0].name,\n axis,\n chosen: selection.variants[axis] ?? '',\n available: entries.map((entry: IEntryOfCatalog): string => entry.variant?.value ?? ''),\n ids: entries.map((entry: IEntryOfCatalog): string => entry.id),\n });\n }\n\n return gaps;\n}\n\n/**\n * Идентификатор по тому, как ресурс назвали в строке запуска. Принимаются все формы, которыми\n * его называют вслух: `money`, `application/money`, `money.md` и `laws/application/money.md`.\n */\nexport function idOf(spoken: string, kind: TKind, catalog: readonly IEntryOfCatalog[]): string | null {\n const wanted: string = spoken.trim();\n const shortOf: (entry: IEntryOfCatalog) => string = (entry: IEntryOfCatalog): string => entry.name.split('/').pop() ?? entry.name;\n const found: IEntryOfCatalog | undefined = catalog.find(\n (entry: IEntryOfCatalog): boolean =>\n entry.kind === kind &&\n (entry.id === wanted || entry.name === wanted || shortOf(entry) === 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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { IStaleBuild } from './freshness.js';
|
|
1
2
|
export interface IOutcomeOfCommand {
|
|
2
3
|
readonly code: number;
|
|
3
4
|
readonly lines: readonly string[];
|
|
@@ -7,14 +8,34 @@ export interface IEnvironment {
|
|
|
7
8
|
readonly root: string;
|
|
8
9
|
readonly version: string;
|
|
9
10
|
readonly assetsDir: string;
|
|
11
|
+
/**
|
|
12
|
+
* Ресурсы правлены позже, чем собран пакет, из которого идёт раскладка; `null` — сверять не с
|
|
13
|
+
* чем. Считается на краю, где пакет знает своё расположение: библиотечные модули работают с
|
|
14
|
+
* тем каталогом ресурсов, который им дали, и о существовании исходников не знают.
|
|
15
|
+
*/
|
|
16
|
+
readonly stale?: IStaleBuild | null;
|
|
10
17
|
}
|
|
18
|
+
/** Куда уезжает прежнее содержимое файла, отданного пакету. */
|
|
19
|
+
export declare const KEPT_SUFFIX: string;
|
|
11
20
|
/**
|
|
12
21
|
* Что проект выбрал при заведении конфига. Пустой список — весь набор; выбор приходит сюда
|
|
13
22
|
* готовым, потому что взять его негде, кроме края: у строки запуска флаг, у терминала вопрос,
|
|
14
23
|
* а у неинтерактивного прогона нет ни того ни другого — и решать это команде не по чину.
|
|
15
24
|
*/
|
|
16
|
-
export declare function init(root: string, only?: readonly string[]): IOutcomeOfCommand;
|
|
25
|
+
export declare function init(root: string, only?: readonly string[], variants?: Readonly<Record<string, string>>, assetsDir?: string): IOutcomeOfCommand;
|
|
17
26
|
export declare function sync(env: IEnvironment, check: boolean): IOutcomeOfCommand;
|
|
27
|
+
/**
|
|
28
|
+
* Отдать пакету файл, который лежит в дереве не от него.
|
|
29
|
+
*
|
|
30
|
+
* Пока команды не было, `sync` отказывал на такой файл поимённо и предлагал «перенести правку в
|
|
31
|
+
* надстройку или снять» — то есть работу, которую делают руками, и до неё же сводилась вся
|
|
32
|
+
* установка в живое дерево. При этом один чужой файл останавливал раскладку целиком.
|
|
33
|
+
*
|
|
34
|
+
* Молчаливой перезаписи здесь нет и быть не может: в файле лежит то, чего в дереве больше нигде
|
|
35
|
+
* нет. Поэтому переход — это переименование прежнего содержимого рядом и повторный `sync`:
|
|
36
|
+
* пакет кладёт своё, а прежнее остаётся на диске под именем с пометкой и ждёт разбора.
|
|
37
|
+
*/
|
|
38
|
+
export declare function adopt(env: IEnvironment, names: readonly string[]): IOutcomeOfCommand;
|
|
18
39
|
export declare function doctor(env: IEnvironment): IOutcomeOfCommand;
|
|
19
40
|
/**
|
|
20
41
|
* Что пакет везёт и что из этого взято здесь.
|
package/lib/commands.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/commands.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/commands.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAO7C,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;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACvC;AAoDD,+DAA+D;AAC/D,eAAO,MAAM,WAAW,EAAE,MAAyB,CAAC;AAuFpD;;;;GAIG;AACH,wBAAgB,IAAI,CAChB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,QAAQ,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EAC/C,SAAS,GAAE,MAAW,GACvB,iBAAiB,CAgCnB;AAgBD,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,iBAAiB,CA2EzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,CA+CpF;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA2C3D;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAkDzD"}
|