@rt-tools/agent-kit 0.8.0 → 0.8.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 +24 -19
- package/assets/agents/qa-engineer.md +1 -1
- package/assets/checks/board.github.mjs +56 -17
- package/assets/checks/check-board.github.mjs +49 -5
- package/assets/checks/check-reuse.mjs +9 -7
- package/assets/checks/task-new.github.mjs +33 -5
- package/assets/commands/agent-kit-digest.md +10 -5
- package/assets/commands/next-session.md +4 -4
- package/assets/commands/skill-curator.md +11 -9
- package/assets/defaults/gate-map.sh +11 -4
- package/assets/defaults/project.sh +46 -0
- package/assets/docs/GLOSSARY.md +28 -26
- package/assets/hooks/docs-guard.sh +19 -3
- package/assets/hooks/git-guard-delivery.sh +106 -13
- package/assets/hooks/proposal-guard.sh +93 -0
- package/assets/hooks/reuse-first-guard.sh +68 -14
- package/assets/hooks/skill-gate-layers.sh +1 -1
- package/assets/hooks/skill-gate.sh +26 -0
- package/assets/hooks/task-flow-guard.sh +44 -18
- package/assets/hooks/window-fill-guard.sh +1 -1
- package/assets/laws/delivery.md +13 -7
- package/assets/laws/project-documentation.md +21 -0
- package/assets/laws/verifiability.md +6 -1
- package/assets/laws/work-conduct.md +67 -3
- package/assets/patterns/git-workflow-commit.azure.md +10 -2
- package/assets/patterns/git-workflow-commit.github.md +15 -2
- package/assets/patterns/git-workflow-commit.gitlab.md +10 -2
- package/assets/patterns/git-workflow-merge.md +1 -1
- package/assets/patterns/reuse-first-extend.md +12 -3
- package/assets/patterns/spec-driven-domain.md +7 -1
- package/assets/patterns/spec-driven-rule.md +6 -0
- package/assets/patterns/task-flow-close.md +78 -9
- package/assets/patterns/task-flow-handoff.md +27 -4
- package/assets/patterns/task-flow-resume.md +23 -5
- package/assets/patterns/task-flow-start.md +5 -1
- package/assets/rules/browser-verification.md +10 -1
- package/assets/rules/doc-style.md +13 -5
- package/assets/rules/git-workflow.azure.md +12 -7
- package/assets/rules/git-workflow.github.md +31 -13
- package/assets/rules/git-workflow.gitlab.md +12 -7
- package/assets/rules/reuse-first.md +1 -1
- package/assets/rules/spec-driven.md +4 -0
- package/assets/rules/task-flow.md +47 -22
- package/assets/rules/testing.md +19 -0
- package/assets/rules/typescript-conventions.md +12 -0
- package/assets/skills/agent-kit-extend.md +173 -0
- package/assets/skills/agent-kit.md +62 -10
- package/assets/traits.json +14 -0
- package/bin/agent-kit.d.ts.map +1 -1
- package/bin/agent-kit.js +31 -16
- package/bin/agent-kit.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/argv.d.ts +17 -0
- package/lib/argv.d.ts.map +1 -0
- package/lib/argv.js +44 -0
- package/lib/argv.js.map +1 -0
- package/lib/cargo.d.ts +88 -0
- package/lib/cargo.d.ts.map +1 -0
- package/lib/cargo.js +16 -0
- package/lib/cargo.js.map +1 -0
- package/lib/catalog.d.ts +18 -1
- package/lib/catalog.d.ts.map +1 -1
- package/lib/catalog.js +12 -2
- package/lib/catalog.js.map +1 -1
- package/lib/commands.d.ts +0 -26
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +78 -122
- package/lib/commands.js.map +1 -1
- package/lib/companion.d.ts +37 -0
- package/lib/companion.d.ts.map +1 -1
- package/lib/companion.js +42 -1
- package/lib/companion.js.map +1 -1
- package/lib/config.d.ts +28 -0
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +20 -0
- package/lib/config.js.map +1 -1
- package/lib/ship.d.ts +39 -0
- package/lib/ship.d.ts.map +1 -0
- package/lib/ship.js +87 -0
- package/lib/ship.js.map +1 -0
- package/lib/shipment.d.ts +60 -0
- package/lib/shipment.d.ts.map +1 -0
- package/lib/shipment.js +247 -0
- package/lib/shipment.js.map +1 -0
- package/lib/snapshot.d.ts +30 -0
- package/lib/snapshot.d.ts.map +1 -0
- package/lib/snapshot.js +73 -0
- package/lib/snapshot.js.map +1 -0
- package/lib/traits.d.ts +32 -0
- package/lib/traits.d.ts.map +1 -0
- package/lib/traits.js +82 -0
- package/lib/traits.js.map +1 -0
- package/package.json +6 -2
- package/rt-tools-agent-kit-0.8.2.tgz +0 -0
- package/lib/submit.d.ts +0 -24
- package/lib/submit.d.ts.map +0 -1
- package/lib/submit.js +0 -26
- package/lib/submit.js.map +0 -1
- package/rt-tools-agent-kit-0.8.0.tgz +0 -0
- /package/assets/rules/{entity-conventions.md → entity-conventions.needs-admin.md} +0 -0
- /package/assets/rules/{observability.md → observability.needs-app.md} +0 -0
package/lib/commands.js
CHANGED
|
@@ -7,14 +7,14 @@ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from '
|
|
|
7
7
|
import { join } from 'node:path';
|
|
8
8
|
import { collectAssets } from './assets.js';
|
|
9
9
|
import { cascadeCuts, isChosen, readCatalog } from './catalog.js';
|
|
10
|
-
import { isUnfilled } from './companion.js';
|
|
11
|
-
import { CONFIG_PATH, DEFAULT_LAYOUT, KINDS, OVERRIDES_DIR, PROFILE_FILE, readConfig, RT_KIT_DIR
|
|
10
|
+
import { debtLine, isUnfilled, pathOf as companionPathOf, unaddressedOf } from './companion.js';
|
|
11
|
+
import { CONFIG_PATH, DEFAULT_LAYOUT, KINDS, OVERRIDES_DIR, PROFILE_FILE, readConfig, RT_KIT_DIR } from './config.js';
|
|
12
12
|
import { hooksSection, SETTINGS_PATH } from './hooks-map.js';
|
|
13
13
|
import { DEFAULT_DAYS, KEEP_DAYS, OBSERVATIONS_DIR, readObservations, summarize } from './observations.js';
|
|
14
14
|
import { isRefusal } from './plan.js';
|
|
15
|
-
import {
|
|
16
|
-
import { FEEDBACK_LABEL } from './submit.js';
|
|
15
|
+
import { laidOutSkills } from './snapshot.js';
|
|
17
16
|
import { pendingOf, planSync, runSync } from './sync.js';
|
|
17
|
+
import { answersRequirement, readTraits, unknownTraits } from './traits.js';
|
|
18
18
|
import { placeholdersOf } from './vars.js';
|
|
19
19
|
import { readAxes, unansweredAxes } from './variants.js';
|
|
20
20
|
/** Что с файлом сделала раскладка. Прошедшее время здесь правда: `sync` уже записал. */
|
|
@@ -43,6 +43,8 @@ const CUT_BY_CASCADE = 'снят каскадом';
|
|
|
43
43
|
const SKIPPED = 'пропущен';
|
|
44
44
|
/** Ресурс чужого вида: в этом дереве его не существует, а не «от него отказались». */
|
|
45
45
|
const OTHER_VARIANT = 'другой вид';
|
|
46
|
+
/** Ресурс, которому нужно свойство дерева: дерево его не отвергало — свойства у него нет. */
|
|
47
|
+
const NEEDS_TRAIT = 'нужно свойство';
|
|
46
48
|
const KIND_TITLE = {
|
|
47
49
|
laws: 'ЗАКОНЫ',
|
|
48
50
|
rules: 'ПРАВИЛА',
|
|
@@ -135,6 +137,17 @@ function profileLines(root, assetsDir, config) {
|
|
|
135
137
|
}
|
|
136
138
|
const holes = (result) => [...result.missing].map(([asset, names]) => ` ${asset}: нет значений для ${names.map((name) => `{{${name}}}`).join(', ')}`);
|
|
137
139
|
const unfilled = (result) => result.companions.filter(isUnfilled);
|
|
140
|
+
/**
|
|
141
|
+
* Строка про компаньон, которому нечего сказать.
|
|
142
|
+
*
|
|
143
|
+
* У правила, требующего свойства, которого дерево не назвало, это не черновик, а лишний файл:
|
|
144
|
+
* заполнять его нечем — ни одной статье такого правила здесь не отвечает ни файл, ни символ.
|
|
145
|
+
* Такое правило доезжает сюда только выбором поимённо, и верное действие тут обратное
|
|
146
|
+
* заполнению — снять ресурс строкой отказа.
|
|
147
|
+
*/
|
|
148
|
+
const companionLine = (entry) => entry.needs === null
|
|
149
|
+
? ` ${entry.path} — ${COMPANION_WORD[entry.state]}`
|
|
150
|
+
: ` ${entry.path} — правилу нужно свойство «${entry.needs}»: заполнять нечем, снимай ресурс строкой в \`skip\``;
|
|
138
151
|
/** Ось без ответа: чем её спрашивают и из чего выбирают. */
|
|
139
152
|
const axisLines = (axes) => axes.flatMap((axis) => [
|
|
140
153
|
` ${axis.name} — ${axis.question}`,
|
|
@@ -161,6 +174,23 @@ const gapLines = (result) => result.gaps.flatMap((gap) => [
|
|
|
161
174
|
` ${gap.kind}/${gap.name} — есть только под ${gap.axis}: ${gap.available.join(', ')}, а выбран «${gap.chosen}»`,
|
|
162
175
|
` либо заведи вид под «${gap.chosen}», либо назови в skip: ${gap.ids.join(', ')}`,
|
|
163
176
|
]);
|
|
177
|
+
/**
|
|
178
|
+
* Свойства, названные не по перечню пакета, — и деревом, и ресурсами.
|
|
179
|
+
*
|
|
180
|
+
* Обе стороны собираются одной функцией, потому что промах у них общий: имя свойства написано
|
|
181
|
+
* так, как его никто не объявлял. Разница только в том, где оно написано, и потому в строке
|
|
182
|
+
* отказа стоит место, а не одно имя.
|
|
183
|
+
*/
|
|
184
|
+
function strangeTraits(config, assetsDir) {
|
|
185
|
+
const traits = readTraits(assetsDir);
|
|
186
|
+
const lines = unknownTraits(config.has, traits).map((trait) => ` \`${trait}\` — названо деревом в \`has\`, а пакет такого свойства не объявлял`);
|
|
187
|
+
for (const entry of readCatalog(assetsDir)) {
|
|
188
|
+
if (entry.needs !== null && unknownTraits([entry.needs], traits).length) {
|
|
189
|
+
lines.push(` \`${entry.needs}\` — требует ${entry.id}, а пакет такого свойства не объявлял`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return lines.length ? [...lines, ` объявленные свойства: ${traits.map((trait) => trait.value).join(', ')}`] : [];
|
|
193
|
+
}
|
|
164
194
|
/**
|
|
165
195
|
* Гарды, которых нет в настройке агента, и готовый кусок для неё.
|
|
166
196
|
*
|
|
@@ -265,7 +295,7 @@ const describe = (result) => [
|
|
|
265
295
|
...unboundLines(result),
|
|
266
296
|
...warnings(result),
|
|
267
297
|
...pendingOf(result).map((entry) => ` ${entry.path} — ${STATE_WORD[entry.outcome]}`),
|
|
268
|
-
...unfilled(result).map(
|
|
298
|
+
...unfilled(result).map(companionLine),
|
|
269
299
|
];
|
|
270
300
|
/** Имена дырок во всех ресурсах, которые дерево берёт. Без конфига — ни одной: выбор неизвестен. */
|
|
271
301
|
function placeholdersIn(config, assetsDir) {
|
|
@@ -317,6 +347,26 @@ export function init(root, only = [], variants = {}, assetsDir = '') {
|
|
|
317
347
|
],
|
|
318
348
|
};
|
|
319
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Долг, добавленный этой раскладкой: статьи разложенных правил, у которых в компаньонах дерева
|
|
352
|
+
* нет адреса.
|
|
353
|
+
*
|
|
354
|
+
* Называется он здесь, а не отдельной сверкой, ровно потому, что через день его уже не отличить
|
|
355
|
+
* от накопленного: сверка видит расхождение, но не знает, чьим обновлением оно приехало.
|
|
356
|
+
*/
|
|
357
|
+
function debtLines(config, root, assetsDir) {
|
|
358
|
+
const found = [];
|
|
359
|
+
for (const asset of collectAssets(config, assetsDir)) {
|
|
360
|
+
if (asset.kind !== 'rules') {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const path = join(root, companionPathOf(asset));
|
|
364
|
+
const existing = existsSync(path) ? readFileSync(path, 'utf8') : null;
|
|
365
|
+
found.push(unaddressedOf(asset.name, asset.text, existing));
|
|
366
|
+
}
|
|
367
|
+
const line = debtLine(found);
|
|
368
|
+
return line === null ? [] : [line, 'адрес статье дописывается в компаньоне рядом с правилом'];
|
|
369
|
+
}
|
|
320
370
|
/**
|
|
321
371
|
* Отказ на устаревшей сборке. Раскладка из неё положила бы прежнюю редакцию ресурса и назвала
|
|
322
372
|
* это сделанным: неправда дороже отказа — её замечают, когда правленое правило не действует.
|
|
@@ -346,6 +396,13 @@ export function sync(env, check) {
|
|
|
346
396
|
if (unanswered.length) {
|
|
347
397
|
return { code: 1, lines: ['раскладка не начата: не выбран вид', ...axisLines(unanswered)] };
|
|
348
398
|
}
|
|
399
|
+
// Незнакомое свойство — опечатка, и молчать о ней нельзя ни с одной стороны. У дерева она
|
|
400
|
+
// означает, что помеченного им ресурса оно не получит вовсе; у ресурса — что он не ляжет
|
|
401
|
+
// никуда и никогда, а причину в имени файла не разглядеть.
|
|
402
|
+
const strange = strangeTraits(config, assetsDir);
|
|
403
|
+
if (strange.length) {
|
|
404
|
+
return { code: 1, lines: ['раскладка не начата: свойство дерева не объявлено пакетом', ...strange] };
|
|
405
|
+
}
|
|
349
406
|
if (check) {
|
|
350
407
|
const result = planSync(config, root, version, assetsDir);
|
|
351
408
|
const pending = pendingOf(result);
|
|
@@ -399,6 +456,7 @@ export function sync(env, check) {
|
|
|
399
456
|
? [`разложено файлов: ${result.written.length}`, ...result.written.map((path) => ` ${path}`)]
|
|
400
457
|
: ['всё уже разложено']),
|
|
401
458
|
...unboundLines(result),
|
|
459
|
+
...debtLines(config, root, assetsDir),
|
|
402
460
|
...warnings(result),
|
|
403
461
|
],
|
|
404
462
|
};
|
|
@@ -466,10 +524,6 @@ const countLines = (counted) => {
|
|
|
466
524
|
const width = counted.reduce((found, entry) => Math.max(found, entry.name.length), 0);
|
|
467
525
|
return counted.map((entry) => ` ${entry.name.padEnd(width)} ${entry.count}`);
|
|
468
526
|
};
|
|
469
|
-
/** Имена скилов, разложенных в это дерево: правила, паттерны и скилы без закона. */
|
|
470
|
-
const skillsOf = (config, assetsDir) => collectAssets(config, assetsDir)
|
|
471
|
-
.filter((asset) => SKILL_KINDS.includes(asset.kind))
|
|
472
|
-
.map((asset) => asset.name);
|
|
473
527
|
/**
|
|
474
528
|
* Сводка наблюдений за отрезок дней.
|
|
475
529
|
*
|
|
@@ -496,7 +550,7 @@ export function stats(env, options) {
|
|
|
496
550
|
}
|
|
497
551
|
const days = options.days > 0 ? options.days : DEFAULT_DAYS;
|
|
498
552
|
const result = readObservations(root, options.today, days);
|
|
499
|
-
const summary = summarize(result.observations,
|
|
553
|
+
const summary = summarize(result.observations, laidOutSkills(config, assetsDir), days);
|
|
500
554
|
if (options.json) {
|
|
501
555
|
return { code: 0, lines: [JSON.stringify({ ...summary, swept: result.swept })] };
|
|
502
556
|
}
|
|
@@ -530,7 +584,7 @@ export function stats(env, options) {
|
|
|
530
584
|
...(summary.unused.length
|
|
531
585
|
? [
|
|
532
586
|
'',
|
|
533
|
-
`разложено и не загружено ни разу: ${summary.unused.length} из ${
|
|
587
|
+
`разложено и не загружено ни разу: ${summary.unused.length} из ${laidOutSkills(config, assetsDir).length}`,
|
|
534
588
|
...summary.unused.slice(0, UNUSED_SHOWN).map((name) => ` ${name}`),
|
|
535
589
|
// Список говорится не весь, и об этом говорится вслух: молчаливый обрыв
|
|
536
590
|
// читается как «вот они все», и правило, не попавшее в первую дюжину,
|
|
@@ -563,115 +617,6 @@ export function stats(env, options) {
|
|
|
563
617
|
],
|
|
564
618
|
};
|
|
565
619
|
}
|
|
566
|
-
/** Тело записи: место, повод, готовый текст — и сводка под ними. */
|
|
567
|
-
const issueBody = (proposal, summary, version) => [
|
|
568
|
-
`Ресурс: \`${proposal.resource}\``,
|
|
569
|
-
`Пакет: v${version}`,
|
|
570
|
-
'',
|
|
571
|
-
proposal.body,
|
|
572
|
-
'',
|
|
573
|
-
'## Наблюдения дерева, из которого пришло предложение',
|
|
574
|
-
'',
|
|
575
|
-
'```',
|
|
576
|
-
...summary,
|
|
577
|
-
'```',
|
|
578
|
-
'',
|
|
579
|
-
'Заведено `agent-kit propose`. Дерево, из которого оно пришло, здесь не называется намеренно.',
|
|
580
|
-
].join('\n');
|
|
581
|
-
/**
|
|
582
|
-
* Отправка предложений, адресованных пакету.
|
|
583
|
-
*
|
|
584
|
-
* Наружу уезжает только адрес «пакет»: «компаньон» и «дерево» — про имена и надстройки этого
|
|
585
|
-
* дерева, и в репозитории пакета им делать нечего. Перед отправкой текст сверяется на адрес
|
|
586
|
-
* дерева: файл уезжает в чужой репозиторий целиком, и проверка здесь дешевле, чем разбор потом.
|
|
587
|
-
*/
|
|
588
|
-
export function propose(env, options) {
|
|
589
|
-
const { root, version } = env;
|
|
590
|
-
const config = readConfig(root);
|
|
591
|
-
if (!config) {
|
|
592
|
-
return { code: 1, lines: [NO_CONFIG] };
|
|
593
|
-
}
|
|
594
|
-
if (!options.repository) {
|
|
595
|
-
return { code: 1, lines: ['куда отправлять — неизвестно: в манифесте пакета нет адреса репозитория'] };
|
|
596
|
-
}
|
|
597
|
-
const all = readProposals(root);
|
|
598
|
-
const mine = all.filter((entry) => entry.address === TO_PACKAGE && !entry.sent);
|
|
599
|
-
if (!mine.length) {
|
|
600
|
-
const others = all.filter((entry) => entry.address !== TO_PACKAGE).length;
|
|
601
|
-
const sent = all.filter((entry) => Boolean(entry.sent)).length;
|
|
602
|
-
return {
|
|
603
|
-
code: 0,
|
|
604
|
-
lines: [
|
|
605
|
-
'отправлять нечего: предложений с адресом «пакет» и без пометки об отправке нет',
|
|
606
|
-
...(others ? [`с другими адресами: ${others} — они правятся здесь же, надстройкой и компаньоном`] : []),
|
|
607
|
-
...(sent ? [`уже отправлено: ${sent}`] : []),
|
|
608
|
-
...(all.length ? [] : [`предложения кладутся в ${PROPOSALS_DIR}/ — форма в шаблоне \`proposal.md\``]),
|
|
609
|
-
],
|
|
610
|
-
};
|
|
611
|
-
}
|
|
612
|
-
// Утечка отбивает отправку целиком, а не свой блок: файл разбирает человек, и «уехало два из
|
|
613
|
-
// трёх» он прочтёт как «всё в порядке».
|
|
614
|
-
const marks = marksOf(root, options.remote);
|
|
615
|
-
const leaked = mine.flatMap((proposal) => leaksIn(proposal.body, marks).map((leak) => ({ proposal, leak })));
|
|
616
|
-
if (leaked.length) {
|
|
617
|
-
return {
|
|
618
|
-
code: 1,
|
|
619
|
-
lines: [
|
|
620
|
-
`отправка не начата: в тексте предложений назван адрес этого дерева — ${leaked.length}`,
|
|
621
|
-
...leaked.map(({ proposal, leak }) => ` ${proposal.file}:${proposal.line + leak.line} — ${leak.why}\n ${leak.text}`),
|
|
622
|
-
'предложение уезжает в чужой репозиторий целиком: правь текст, а не обходи проверку',
|
|
623
|
-
],
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
|
-
if (options.dryRun) {
|
|
627
|
-
return {
|
|
628
|
-
code: 0,
|
|
629
|
-
lines: [
|
|
630
|
-
`уехало бы записей: ${mine.length} → ${options.repository}, метка ${FEEDBACK_LABEL}`,
|
|
631
|
-
...mine.map((entry) => ` ${entry.resource} (${entry.file}:${entry.line})`),
|
|
632
|
-
'сводка наблюдений едет вместе с ними',
|
|
633
|
-
],
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
const done = [];
|
|
637
|
-
for (const proposal of mine) {
|
|
638
|
-
let url = '';
|
|
639
|
-
try {
|
|
640
|
-
url = options.submit({
|
|
641
|
-
repository: options.repository,
|
|
642
|
-
title: `предложение: ${proposal.resource}`,
|
|
643
|
-
body: issueBody(proposal, options.summary, version),
|
|
644
|
-
label: FEEDBACK_LABEL,
|
|
645
|
-
});
|
|
646
|
-
}
|
|
647
|
-
catch (error) {
|
|
648
|
-
return {
|
|
649
|
-
code: 1,
|
|
650
|
-
lines: [
|
|
651
|
-
...(done.length ? [`отправлено до отказа: ${done.length}`, ...done] : []),
|
|
652
|
-
`отправка оборвалась на ${proposal.resource}: ${error.message.split('\n')[0]}`,
|
|
653
|
-
// Три причины покрывают почти все отказы, и ни одну из них не видно из
|
|
654
|
-
// сообщения помощника: он говорит про метку, доступ и сам себя одинаково глухо.
|
|
655
|
-
`проверь: помощник хостинга стоит и вошёл в учётную запись; в ${options.repository} заведена метка ${FEEDBACK_LABEL}; у учётной записи есть право заводить записи`,
|
|
656
|
-
'файл предложений остался на месте — отправленное помечено, остальное уедет повторным запуском',
|
|
657
|
-
],
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
|
-
// Пометка ложится сразу, а не после всех: оборвавшаяся отправка иначе увозит половину
|
|
661
|
-
// предложений и не оставляет следа, по которому видно, какую именно.
|
|
662
|
-
const path = join(root, proposal.file);
|
|
663
|
-
writeFileSync(path, markSent(readFileSync(path, 'utf8'), proposal, url), 'utf8');
|
|
664
|
-
done.push(` ${proposal.resource} → ${url}`);
|
|
665
|
-
}
|
|
666
|
-
return {
|
|
667
|
-
code: 0,
|
|
668
|
-
lines: [
|
|
669
|
-
`заведено записей: ${done.length} в ${options.repository}`,
|
|
670
|
-
...done,
|
|
671
|
-
'разбирается сведением: `/agent-kit-digest` в репозитории пакета — оно и заводит задачу',
|
|
672
|
-
],
|
|
673
|
-
};
|
|
674
|
-
}
|
|
675
620
|
export function doctor(env) {
|
|
676
621
|
const { root, version, assetsDir } = env;
|
|
677
622
|
const config = readConfig(root);
|
|
@@ -698,15 +643,21 @@ export function doctor(env) {
|
|
|
698
643
|
// не отвергало — оно ушло вслед за родителем, и искать его в отказе читатель пойдёт зря.
|
|
699
644
|
const cuts = cascadeCuts(catalog, config);
|
|
700
645
|
const cut = new Set(cuts.map((one) => one.id));
|
|
646
|
+
// Ресурс с неотвеченным требованием в «не выбрано» не идёт по той же причине, что и чужой
|
|
647
|
+
// вид: дерево его не отвергало — свойства, без которого он бессмыслен, у него просто нет.
|
|
648
|
+
const needing = catalog.filter((entry) => entry.needs !== null && !answersRequirement(entry.needs, config.has) && !config.skip.includes(entry.id));
|
|
649
|
+
const needsTrait = needing.length;
|
|
701
650
|
// Невыбранное называется поимённо: число «не выбрано: 73» не отвечает ни на один вопрос,
|
|
702
651
|
// ради которого его читают, — ни какого ресурса не хватает, ни требуется ли он соседу.
|
|
703
652
|
const unchosen = catalog.filter((entry) => !isChosen(entry, config) &&
|
|
704
653
|
!config.skip.includes(entry.id) &&
|
|
705
|
-
foreignVariant.every((one) => one.id !== entry.id)
|
|
654
|
+
foreignVariant.every((one) => one.id !== entry.id) &&
|
|
655
|
+
needing.every((one) => one.id !== entry.id));
|
|
706
656
|
const lines = [
|
|
707
657
|
`пакет v${version}, везёт ресурсов ${catalog.length}, взято ${taken}`,
|
|
708
|
-
`не выбрано: ${catalog.length - taken - skipped - other - cut.size}, пропущено: ${skipped}, другой вид: ${other}, снято каскадом: ${cut.size}`,
|
|
658
|
+
`не выбрано: ${catalog.length - taken - skipped - other - cut.size - needsTrait}, пропущено: ${skipped}, другой вид: ${other}, снято каскадом: ${cut.size}, нужно свойство: ${needsTrait}`,
|
|
709
659
|
...unchosen.map((entry) => ` не выбран: ${entry.id}`),
|
|
660
|
+
...needing.map((entry) => ` нужно свойство «${entry.needs}»: ${entry.id}`),
|
|
710
661
|
...cuts.map((one) => ` снят каскадом: ${one.id} — вслед за ${one.parent}${one.parent === one.root ? '' : `, отвергнут ${one.root}`}`),
|
|
711
662
|
...profileLines(root, assetsDir, config),
|
|
712
663
|
`значений в конфиге: ${Object.keys(config.vars).length}`,
|
|
@@ -747,6 +698,11 @@ export function list(env) {
|
|
|
747
698
|
if (config.skip.includes(entry.id)) {
|
|
748
699
|
return SKIPPED;
|
|
749
700
|
}
|
|
701
|
+
// Неотвеченное требование — не отказ дерева: оно этого ресурса не выбирало и не
|
|
702
|
+
// отвергало, а свойства, без которого ресурс бессмыслен, у него просто нет.
|
|
703
|
+
if (entry.needs !== null && !answersRequirement(entry.needs, config.has)) {
|
|
704
|
+
return `${NEEDS_TRAIT}: ${entry.needs}`;
|
|
705
|
+
}
|
|
750
706
|
if (!isChosen(entry, config)) {
|
|
751
707
|
return NOT_CHOSEN;
|
|
752
708
|
}
|