@deftai/directive-core 0.106.0 → 0.107.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/dist/check/gate-lists.js +2 -0
- package/dist/design-critique/exclusive-chip.d.ts +33 -0
- package/dist/design-critique/exclusive-chip.js +68 -0
- package/dist/design-critique/parent-audit.d.ts +59 -0
- package/dist/design-critique/parent-audit.js +121 -0
- package/dist/doctor/checks.d.ts +6 -0
- package/dist/doctor/checks.js +35 -0
- package/dist/hooks/dest-form.d.ts +46 -0
- package/dist/hooks/dest-form.js +575 -0
- package/dist/hooks/dispatcher.d.ts +17 -1
- package/dist/hooks/dispatcher.js +82 -11
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/intake/github-auth-modes-cli.js +14 -2
- package/dist/intake/github-auth-modes.d.ts +54 -9
- package/dist/intake/github-auth-modes.js +326 -123
- package/dist/lifecycle/completed-tracked-on-delivery.d.ts +26 -0
- package/dist/lifecycle/completed-tracked-on-delivery.js +37 -14
- package/dist/lifecycle/completed-write-guard.d.ts +48 -0
- package/dist/lifecycle/completed-write-guard.js +373 -0
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.js +1 -0
- package/dist/literal-acceptance/capture.d.ts +16 -0
- package/dist/literal-acceptance/capture.js +75 -7
- package/dist/literal-acceptance/evaluate.js +25 -3
- package/dist/literal-acceptance/index.d.ts +2 -2
- package/dist/literal-acceptance/index.js +2 -2
- package/dist/literal-acceptance/run.js +9 -3
- package/dist/literal-acceptance/types.d.ts +6 -0
- package/dist/literal-acceptance/types.js +6 -0
- package/dist/policy/runtime-authority.d.ts +18 -0
- package/dist/policy/runtime-authority.js +11 -0
- package/dist/policy/write-fence.js +3 -0
- package/dist/preflight-cache/evaluate.js +9 -3
- package/dist/product-first-done-gate/evaluate.js +36 -5
- package/dist/scm/design-critique-chip.d.ts +37 -0
- package/dist/scm/design-critique-chip.js +171 -0
- package/dist/scm/index.d.ts +1 -0
- package/dist/scm/index.js +1 -0
- package/dist/scm/main.d.ts +3 -0
- package/dist/scm/main.js +15 -1
- package/dist/scm/readiness-cli.d.ts +2 -0
- package/dist/scm/readiness-cli.js +42 -1
- package/dist/scm/readiness.d.ts +2 -1
- package/dist/scm/readiness.js +7 -1
- package/dist/scope/decompose.js +2 -1
- package/dist/scope/delivery-evidence.d.ts +27 -1
- package/dist/scope/index.d.ts +1 -0
- package/dist/scope/index.js +1 -0
- package/dist/scope/lifecycle-write.d.ts +24 -0
- package/dist/scope/lifecycle-write.js +64 -0
- package/dist/scope/main.js +3 -1
- package/dist/scope/transition.js +90 -11
- package/dist/scope-provenance/evaluate.js +5 -1
- package/dist/session/git.d.ts +18 -0
- package/dist/session/git.js +81 -1
- package/dist/session/occupancy.d.ts +2 -1
- package/dist/session/occupancy.js +11 -4
- package/dist/session/session-start.d.ts +12 -0
- package/dist/session/session-start.js +17 -0
- package/dist/session/verify-session-ritual.d.ts +22 -3
- package/dist/session/verify-session-ritual.js +29 -4
- package/dist/swarm/launch.d.ts +69 -0
- package/dist/swarm/launch.js +191 -1
- package/dist/triage/actions/index.d.ts +4 -0
- package/dist/triage/actions/index.js +6 -0
- package/dist/triage/evaluate/evaluate.d.ts +11 -0
- package/dist/triage/evaluate/evaluate.js +169 -0
- package/dist/triage/evaluate/github.d.ts +16 -0
- package/dist/triage/evaluate/github.js +153 -0
- package/dist/triage/evaluate/index.d.ts +9 -0
- package/dist/triage/evaluate/index.js +8 -0
- package/dist/triage/evaluate/paths.d.ts +7 -0
- package/dist/triage/evaluate/paths.js +25 -0
- package/dist/triage/evaluate/sink.d.ts +5 -0
- package/dist/triage/evaluate/sink.js +52 -0
- package/dist/triage/evaluate/types.d.ts +93 -0
- package/dist/triage/evaluate/types.js +13 -0
- package/dist/triage/evaluate/validity.d.ts +8 -0
- package/dist/triage/evaluate/validity.js +87 -0
- package/dist/triage/evaluate/value.d.ts +12 -0
- package/dist/triage/evaluate/value.js +33 -0
- package/dist/triage/evaluate/wip-census.d.ts +5 -0
- package/dist/triage/evaluate/wip-census.js +39 -0
- package/dist/triage/evaluate/worktrees.d.ts +8 -0
- package/dist/triage/evaluate/worktrees.js +68 -0
- package/dist/triage/evaluate/xbrief-refs.d.ts +8 -0
- package/dist/triage/evaluate/xbrief-refs.js +60 -0
- package/dist/triage/help/registry-data.d.ts +21 -7
- package/dist/triage/help/registry-data.js +53 -5
- package/dist/triage/index.d.ts +1 -0
- package/dist/triage/index.js +1 -0
- package/dist/value/feedback-file.d.ts +17 -2
- package/dist/value/feedback-file.js +80 -6
- package/dist/vbrief-reconcile/labels.d.ts +1 -0
- package/dist/vbrief-reconcile/labels.js +30 -0
- package/dist/vbrief-validation/story-quality.d.ts +7 -0
- package/dist/vbrief-validation/story-quality.js +8 -1
- package/dist/xbrief-migrate/agents-header.js +69 -7
- package/package.json +3 -3
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const ISSUE_URI_RE = /\/issues\/(\d+)\b/u;
|
|
4
|
+
const ISSUE_HASH_RE = /(?:Issue\s+)?#(\d+)\b/u;
|
|
5
|
+
function numbersFromUnknown(value, into) {
|
|
6
|
+
if (typeof value === "string") {
|
|
7
|
+
for (const re of [ISSUE_URI_RE, ISSUE_HASH_RE]) {
|
|
8
|
+
const match = re.exec(value);
|
|
9
|
+
if (match?.[1] !== undefined) {
|
|
10
|
+
into.add(Number.parseInt(match[1], 10));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
for (const item of value) {
|
|
17
|
+
numbersFromUnknown(item, into);
|
|
18
|
+
}
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (value !== null && typeof value === "object") {
|
|
22
|
+
for (const nested of Object.values(value)) {
|
|
23
|
+
numbersFromUnknown(nested, into);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function issueNumbersFromXbriefJson(raw) {
|
|
28
|
+
const found = new Set();
|
|
29
|
+
if (raw === null || typeof raw !== "object") {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const plan = raw.plan;
|
|
33
|
+
numbersFromUnknown(plan?.references, found);
|
|
34
|
+
return [...found];
|
|
35
|
+
}
|
|
36
|
+
export function listXbriefHits(root, folder) {
|
|
37
|
+
const dir = join(root, "xbrief", folder);
|
|
38
|
+
if (!existsSync(dir)) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
const hits = [];
|
|
42
|
+
for (const name of readdirSync(dir)) {
|
|
43
|
+
if (!name.endsWith(".xbrief.json") && !name.endsWith(".vbrief.json")) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const path = join(dir, name);
|
|
47
|
+
let parsed;
|
|
48
|
+
try {
|
|
49
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
for (const issue of issueNumbersFromXbriefJson(parsed)) {
|
|
55
|
+
hits.push({ issue, path, folder });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return hits;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=xbrief-refs.js.map
|
|
@@ -23,6 +23,17 @@ export declare const registryData: {
|
|
|
23
23
|
readonly see_also: readonly ["task triage:bootstrap", "task cache:fetch-all", "#1119 / D5"];
|
|
24
24
|
readonly placeholder: false;
|
|
25
25
|
};
|
|
26
|
+
readonly "task triage:evaluate": {
|
|
27
|
+
readonly name: "task triage:evaluate";
|
|
28
|
+
readonly summary: "Isolated validity + parent WIP census + value advice";
|
|
29
|
+
readonly refs: "(#3648)";
|
|
30
|
+
readonly description: "Stage A issue-eval: detached origin/master worktrees own validity and ADR/contract reads; the parent on the live working set owns WIP census (xbrief/active, xbrief/pending, plan-sequence); GitHub REST owns open PRs/issues/duplicates. Writes a gitignored sink under .deft-scratch/issue-eval/<sha12>/<invocation-id>/. No GitHub writes. No candidates-log row. Value may set critique-recommend: and must not emit the reserved design-critique clearance line. Fan-out default 4, override --concurrency N.";
|
|
31
|
+
readonly usage: "task triage:evaluate -- <N...> [--concurrency N] [--repo OWNER/NAME] [--json]";
|
|
32
|
+
readonly flags: readonly [readonly ["<N...>", "(required)", "Issue numbers to evaluate."], readonly ["--concurrency N", "4", "Parallel evaluator cap (bind, not a measured existing cap)."], readonly ["--repo OWNER/NAME", "(git remote / env)", "Upstream repo for REST reads."], readonly ["--json", "(off)", "Emit the structured EvaluateResult."], readonly ["--project-root PATH", "(cwd)", "Project root override (Taskfile threads USER_WORKING_DIR)."]];
|
|
33
|
+
readonly examples: readonly ["task triage:evaluate -- 3648", "task triage:evaluate -- 10 11 --concurrency 2 --json"];
|
|
34
|
+
readonly see_also: readonly ["task triage:queue", "task triage:accept", "skills/deft-directive-issue-eval/SKILL.md", "#3648"];
|
|
35
|
+
readonly placeholder: false;
|
|
36
|
+
};
|
|
26
37
|
readonly "task triage:accept": {
|
|
27
38
|
readonly name: "task triage:accept";
|
|
28
39
|
readonly summary: "Mark issue accepted; chains into scope:promote w/ flag";
|
|
@@ -444,12 +455,12 @@ export declare const registryData: {
|
|
|
444
455
|
readonly "task scope:complete": {
|
|
445
456
|
readonly name: "task scope:complete";
|
|
446
457
|
readonly summary: "active/ -> completed/ (set status completed)";
|
|
447
|
-
readonly refs: "(#845)";
|
|
448
|
-
readonly description: "Mark an active vBRIEF complete and move it to vbrief/completed/. Terminal transition; use scope:undo if you need reversibility (refused on terminal actions per D15).";
|
|
449
|
-
readonly usage: "task scope:complete -- <file>";
|
|
450
|
-
readonly flags: readonly [readonly ["<file>", "(required)", "Path to vBRIEF."], readonly ["--project-root PATH", "(detected)", "Consumer project root override."]];
|
|
451
|
-
readonly examples: readonly ["task scope:complete -- vbrief/active/2026-05-19-foo.vbrief.json"];
|
|
452
|
-
readonly see_also: readonly ["task scope:fail", "task scope:cancel", "#1119 / #845"];
|
|
458
|
+
readonly refs: "(#845, #3041, #3721)";
|
|
459
|
+
readonly description: "Mark an active vBRIEF complete and move it to vbrief/completed/. Terminal transition; use scope:undo if you need reversibility (refused on terminal actions per D15). Code-bearing completion requires delivery evidence: --merge-commit and --pr. Those flags feed the delivery gate; they do not skip literal-AC.";
|
|
460
|
+
readonly usage: "task scope:complete -- <file> [--merge-commit SHA] [--pr N]";
|
|
461
|
+
readonly flags: readonly [readonly ["<file>", "(required)", "Path to vBRIEF."], readonly ["--project-root PATH", "(detected)", "Consumer project root override."], readonly ["--merge-commit SHA", "(required for code-bearing)", "Merge commit SHA for delivery evidence (#3041)."], readonly ["--pr N", "(required for code-bearing)", "Merged PR number for delivery evidence (#3041)."], readonly ["--pr-base BRANCH", "(optional)", "PR base branch for delivery evidence."], readonly ["--delivery-branch BRANCH", "(optional)", "Delivery branch tip for completed-tracked."]];
|
|
462
|
+
readonly examples: readonly ["task scope:complete -- vbrief/active/2026-05-19-foo.vbrief.json", "task scope:complete -- xbrief/active/story.xbrief.json --merge-commit abc123 --pr 42"];
|
|
463
|
+
readonly see_also: readonly ["task scope:fail", "task scope:cancel", "#1119 / #845", "#3041"];
|
|
453
464
|
readonly placeholder: false;
|
|
454
465
|
};
|
|
455
466
|
readonly "task scope:fail": {
|
|
@@ -530,7 +541,7 @@ export declare const registryData: {
|
|
|
530
541
|
readonly placeholder: false;
|
|
531
542
|
};
|
|
532
543
|
};
|
|
533
|
-
readonly categoriesTriage: readonly [readonly ["Session-start", readonly ["task triage:summary", "task verify:cache-fresh"]], readonly ["State verbs (mutate audit log)", readonly ["task triage:accept", "task triage:defer", "task triage:reject", "task triage:needs-ac", "task triage:mark-duplicate", "task triage:reset", "task triage:status", "task triage:history"]], readonly ["Read verbs", readonly ["task triage:queue", "task triage:audit", "task triage:show", "task triage:scope", "task triage:scope-drift", "task triage:classify"]], readonly ["Lifecycle", readonly ["task triage:bootstrap", "task triage:welcome", "task triage:reconcile"]], readonly ["Bulk variants", readonly ["task triage:bulk-accept", "task triage:bulk-reject", "task triage:bulk-defer", "task triage:bulk-needs-ac", "task triage:refresh-active", "task triage:smoketest"]], readonly ["Subscription mutation", readonly ["task triage:subscribe", "task triage:unsubscribe"]], readonly ["Archive / rotation", readonly ["task triage:audit:prune", "task triage:archive-list", "task triage:restore-from-archive", "task triage:audit-log:rotate", "task triage:metrics"]], readonly ["Framework eval (#1703)", readonly ["task eval:health", "task eval:triggers", "task eval:run", "task eval:report"]]];
|
|
544
|
+
readonly categoriesTriage: readonly [readonly ["Session-start", readonly ["task triage:summary", "task verify:cache-fresh"]], readonly ["State verbs (mutate audit log)", readonly ["task triage:accept", "task triage:defer", "task triage:reject", "task triage:needs-ac", "task triage:mark-duplicate", "task triage:reset", "task triage:status", "task triage:history"]], readonly ["Read verbs", readonly ["task triage:queue", "task triage:evaluate", "task triage:audit", "task triage:show", "task triage:scope", "task triage:scope-drift", "task triage:classify"]], readonly ["Lifecycle", readonly ["task triage:bootstrap", "task triage:welcome", "task triage:reconcile"]], readonly ["Bulk variants", readonly ["task triage:bulk-accept", "task triage:bulk-reject", "task triage:bulk-defer", "task triage:bulk-needs-ac", "task triage:refresh-active", "task triage:smoketest"]], readonly ["Subscription mutation", readonly ["task triage:subscribe", "task triage:unsubscribe"]], readonly ["Archive / rotation", readonly ["task triage:audit:prune", "task triage:archive-list", "task triage:restore-from-archive", "task triage:audit-log:rotate", "task triage:metrics"]], readonly ["Framework eval (#1703)", readonly ["task eval:health", "task eval:triggers", "task eval:run", "task eval:report"]]];
|
|
534
545
|
readonly categoriesScope: readonly [readonly ["Promote / demote", readonly ["task scope:promote", "task scope:demote"]], readonly ["Activate / complete", readonly ["task scope:activate", "task scope:complete", "task scope:fail", "task scope:cancel", "task scope:block", "task scope:unblock"]], readonly ["Reversibility", readonly ["task scope:undo", "task scope:restore"]], readonly ["Decomposition", readonly ["task scope:decompose"]]];
|
|
535
546
|
readonly scriptSubcommandMap: {
|
|
536
547
|
readonly triage_actions: {
|
|
@@ -575,6 +586,9 @@ export declare const registryData: {
|
|
|
575
586
|
readonly triage_reconcile: {
|
|
576
587
|
readonly __default__: "task triage:reconcile";
|
|
577
588
|
};
|
|
589
|
+
readonly triage_evaluate: {
|
|
590
|
+
readonly __default__: "task triage:evaluate";
|
|
591
|
+
};
|
|
578
592
|
readonly triage_queue: {
|
|
579
593
|
readonly queue: "task triage:queue";
|
|
580
594
|
readonly show: "task triage:show";
|
|
@@ -34,6 +34,35 @@ export const registryData = {
|
|
|
34
34
|
see_also: ["task triage:bootstrap", "task cache:fetch-all", "#1119 / D5"],
|
|
35
35
|
placeholder: false,
|
|
36
36
|
},
|
|
37
|
+
"task triage:evaluate": {
|
|
38
|
+
name: "task triage:evaluate",
|
|
39
|
+
summary: "Isolated validity + parent WIP census + value advice",
|
|
40
|
+
refs: "(#3648)",
|
|
41
|
+
description: "Stage A issue-eval: detached origin/master worktrees own validity and ADR/contract reads; the parent on the live working set owns WIP census (xbrief/active, xbrief/pending, plan-sequence); GitHub REST owns open PRs/issues/duplicates. Writes a gitignored sink under .deft-scratch/issue-eval/<sha12>/<invocation-id>/. No GitHub writes. No candidates-log row. Value may set critique-recommend: and must not emit the reserved design-critique clearance line. Fan-out default 4, override --concurrency N.",
|
|
42
|
+
usage: "task triage:evaluate -- <N...> [--concurrency N] [--repo OWNER/NAME] [--json]",
|
|
43
|
+
flags: [
|
|
44
|
+
["<N...>", "(required)", "Issue numbers to evaluate."],
|
|
45
|
+
["--concurrency N", "4", "Parallel evaluator cap (bind, not a measured existing cap)."],
|
|
46
|
+
["--repo OWNER/NAME", "(git remote / env)", "Upstream repo for REST reads."],
|
|
47
|
+
["--json", "(off)", "Emit the structured EvaluateResult."],
|
|
48
|
+
[
|
|
49
|
+
"--project-root PATH",
|
|
50
|
+
"(cwd)",
|
|
51
|
+
"Project root override (Taskfile threads USER_WORKING_DIR).",
|
|
52
|
+
],
|
|
53
|
+
],
|
|
54
|
+
examples: [
|
|
55
|
+
"task triage:evaluate -- 3648",
|
|
56
|
+
"task triage:evaluate -- 10 11 --concurrency 2 --json",
|
|
57
|
+
],
|
|
58
|
+
see_also: [
|
|
59
|
+
"task triage:queue",
|
|
60
|
+
"task triage:accept",
|
|
61
|
+
"skills/deft-directive-issue-eval/SKILL.md",
|
|
62
|
+
"#3648",
|
|
63
|
+
],
|
|
64
|
+
placeholder: false,
|
|
65
|
+
},
|
|
37
66
|
"task triage:accept": {
|
|
38
67
|
name: "task triage:accept",
|
|
39
68
|
summary: "Mark issue accepted; chains into scope:promote w/ flag",
|
|
@@ -782,15 +811,30 @@ export const registryData = {
|
|
|
782
811
|
"task scope:complete": {
|
|
783
812
|
name: "task scope:complete",
|
|
784
813
|
summary: "active/ -> completed/ (set status completed)",
|
|
785
|
-
refs: "(#845)",
|
|
786
|
-
description: "Mark an active vBRIEF complete and move it to vbrief/completed/. Terminal transition; use scope:undo if you need reversibility (refused on terminal actions per D15).",
|
|
787
|
-
usage: "task scope:complete -- <file>",
|
|
814
|
+
refs: "(#845, #3041, #3721)",
|
|
815
|
+
description: "Mark an active vBRIEF complete and move it to vbrief/completed/. Terminal transition; use scope:undo if you need reversibility (refused on terminal actions per D15). Code-bearing completion requires delivery evidence: --merge-commit and --pr. Those flags feed the delivery gate; they do not skip literal-AC.",
|
|
816
|
+
usage: "task scope:complete -- <file> [--merge-commit SHA] [--pr N]",
|
|
788
817
|
flags: [
|
|
789
818
|
["<file>", "(required)", "Path to vBRIEF."],
|
|
790
819
|
["--project-root PATH", "(detected)", "Consumer project root override."],
|
|
820
|
+
[
|
|
821
|
+
"--merge-commit SHA",
|
|
822
|
+
"(required for code-bearing)",
|
|
823
|
+
"Merge commit SHA for delivery evidence (#3041).",
|
|
824
|
+
],
|
|
825
|
+
[
|
|
826
|
+
"--pr N",
|
|
827
|
+
"(required for code-bearing)",
|
|
828
|
+
"Merged PR number for delivery evidence (#3041).",
|
|
829
|
+
],
|
|
830
|
+
["--pr-base BRANCH", "(optional)", "PR base branch for delivery evidence."],
|
|
831
|
+
["--delivery-branch BRANCH", "(optional)", "Delivery branch tip for completed-tracked."],
|
|
791
832
|
],
|
|
792
|
-
examples: [
|
|
793
|
-
|
|
833
|
+
examples: [
|
|
834
|
+
"task scope:complete -- vbrief/active/2026-05-19-foo.vbrief.json",
|
|
835
|
+
"task scope:complete -- xbrief/active/story.xbrief.json --merge-commit abc123 --pr 42",
|
|
836
|
+
],
|
|
837
|
+
see_also: ["task scope:fail", "task scope:cancel", "#1119 / #845", "#3041"],
|
|
794
838
|
placeholder: false,
|
|
795
839
|
},
|
|
796
840
|
"task scope:fail": {
|
|
@@ -930,6 +974,7 @@ export const registryData = {
|
|
|
930
974
|
"Read verbs",
|
|
931
975
|
[
|
|
932
976
|
"task triage:queue",
|
|
977
|
+
"task triage:evaluate",
|
|
933
978
|
"task triage:audit",
|
|
934
979
|
"task triage:show",
|
|
935
980
|
"task triage:scope",
|
|
@@ -1024,6 +1069,9 @@ export const registryData = {
|
|
|
1024
1069
|
triage_reconcile: {
|
|
1025
1070
|
__default__: "task triage:reconcile",
|
|
1026
1071
|
},
|
|
1072
|
+
triage_evaluate: {
|
|
1073
|
+
__default__: "task triage:evaluate",
|
|
1074
|
+
},
|
|
1027
1075
|
triage_queue: {
|
|
1028
1076
|
queue: "task triage:queue",
|
|
1029
1077
|
show: "task triage:show",
|
package/dist/triage/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * as bulk from "./bulk/index.js";
|
|
|
13
13
|
export * as cachePath from "./cache-path.js";
|
|
14
14
|
export * from "./cache-path.js";
|
|
15
15
|
export * as classify from "./classify/index.js";
|
|
16
|
+
export * as evaluate from "./evaluate/index.js";
|
|
16
17
|
export * as help from "./help/index.js";
|
|
17
18
|
export * as queue from "./queue/index.js";
|
|
18
19
|
export * as reconcile from "./reconcile/index.js";
|
package/dist/triage/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export * as bulk from "./bulk/index.js";
|
|
|
13
13
|
export * as cachePath from "./cache-path.js";
|
|
14
14
|
export * from "./cache-path.js";
|
|
15
15
|
export * as classify from "./classify/index.js";
|
|
16
|
+
export * as evaluate from "./evaluate/index.js";
|
|
16
17
|
export * as help from "./help/index.js";
|
|
17
18
|
export * as queue from "./queue/index.js";
|
|
18
19
|
export * as reconcile from "./reconcile/index.js";
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { type GhRestSeams } from "../scm/gh-rest.js";
|
|
2
2
|
export declare const DEFAULT_UPSTREAM_REPO = "deftai/directive";
|
|
3
3
|
export declare const FRAMEWORK_GAP_TITLE_PREFIX = "[framework-gap]";
|
|
4
|
+
/** Sole sanctioned title classification (#3713). Does not write `adoption-blocker`. */
|
|
5
|
+
export declare const ADOPTION_BLOCKER_TITLE_TOKEN = "BLOCKER";
|
|
6
|
+
/** Strip source prefix and the optional BLOCKER token so marked/unmarked titles dedup. */
|
|
7
|
+
export declare function stripTitleClassificationTokens(title: string): string;
|
|
4
8
|
export type FeedbackFileOutcome = "draft" | "filed" | "skipped-maintainer" | "blocked-policy" | "blocked-duplicate" | "blocked-no-confirm" | "error-bad-args" | "error-config" | "error-network";
|
|
5
9
|
export interface FeedbackGapInput {
|
|
6
10
|
readonly summary: string;
|
|
@@ -8,6 +12,11 @@ export interface FeedbackGapInput {
|
|
|
8
12
|
readonly expected?: string;
|
|
9
13
|
readonly actual?: string;
|
|
10
14
|
readonly sessionNotes?: string;
|
|
15
|
+
/** Consumer hard-stop judgment. Advisory title token only — never applies a label. */
|
|
16
|
+
readonly adoptionBlocker?: boolean;
|
|
17
|
+
readonly flowAndVersion?: string;
|
|
18
|
+
readonly alternatives?: string;
|
|
19
|
+
readonly recoveryCost?: string;
|
|
11
20
|
}
|
|
12
21
|
export interface FeedbackFileOptions extends FeedbackGapInput {
|
|
13
22
|
readonly projectRoot?: string | null;
|
|
@@ -29,10 +38,12 @@ export interface FeedbackFileResult {
|
|
|
29
38
|
}
|
|
30
39
|
/** True when the resolved project root is the directive maintainer source checkout. */
|
|
31
40
|
export declare function isMaintainerFrameworkRepo(projectRoot: string): boolean;
|
|
32
|
-
/** Normalize a title for duplicate comparison (#1709). */
|
|
41
|
+
/** Normalize a title for duplicate comparison (#1709 / #3713). */
|
|
33
42
|
export declare function normalizeForDedup(title: string): string;
|
|
34
43
|
/** Build the upstream issue title for a framework-gap report. */
|
|
35
|
-
export declare function buildFrameworkGapTitle(summary: string
|
|
44
|
+
export declare function buildFrameworkGapTitle(summary: string, options?: {
|
|
45
|
+
adoptionBlocker?: boolean;
|
|
46
|
+
}): string;
|
|
36
47
|
/** Build the structured framework-gap issue body. */
|
|
37
48
|
export declare function buildFrameworkGapBody(input: FeedbackGapInput): string;
|
|
38
49
|
export interface DuplicateMatch {
|
|
@@ -49,6 +60,10 @@ export interface FeedbackFileCliArgs {
|
|
|
49
60
|
expected?: string;
|
|
50
61
|
actual?: string;
|
|
51
62
|
sessionNotes?: string;
|
|
63
|
+
adoptionBlocker?: boolean;
|
|
64
|
+
flowAndVersion?: string;
|
|
65
|
+
alternatives?: string;
|
|
66
|
+
recoveryCost?: string;
|
|
52
67
|
confirm?: boolean;
|
|
53
68
|
dryRun?: boolean;
|
|
54
69
|
json?: boolean;
|
|
@@ -7,6 +7,21 @@ import { GhRestError, restCreateIssue, restIssueListPaginated, } from "../scm/gh
|
|
|
7
7
|
import { resolveProjectRoot } from "../scope/project-context.js";
|
|
8
8
|
export const DEFAULT_UPSTREAM_REPO = "deftai/directive";
|
|
9
9
|
export const FRAMEWORK_GAP_TITLE_PREFIX = "[framework-gap]";
|
|
10
|
+
/** Sole sanctioned title classification (#3713). Does not write `adoption-blocker`. */
|
|
11
|
+
export const ADOPTION_BLOCKER_TITLE_TOKEN = "BLOCKER";
|
|
12
|
+
const FRAMEWORK_GAP_PREFIX_RE = /^\[framework-gap\]\s*/i;
|
|
13
|
+
const BLOCKER_TITLE_TOKEN_RE = /^BLOCKER\b[:\s-]*/i;
|
|
14
|
+
/** Strip source prefix and the optional BLOCKER token so marked/unmarked titles dedup. */
|
|
15
|
+
export function stripTitleClassificationTokens(title) {
|
|
16
|
+
let rest = title.trim();
|
|
17
|
+
let previous = "";
|
|
18
|
+
while (rest !== previous) {
|
|
19
|
+
previous = rest;
|
|
20
|
+
rest = rest.replace(FRAMEWORK_GAP_PREFIX_RE, "").trim();
|
|
21
|
+
rest = rest.replace(BLOCKER_TITLE_TOKEN_RE, "").trim();
|
|
22
|
+
}
|
|
23
|
+
return rest;
|
|
24
|
+
}
|
|
10
25
|
function sanitizeOneLine(value) {
|
|
11
26
|
return value.replace(/\r?\n/g, " ").trim();
|
|
12
27
|
}
|
|
@@ -18,23 +33,54 @@ function sectionOrPlaceholder(value, placeholder) {
|
|
|
18
33
|
export function isMaintainerFrameworkRepo(projectRoot) {
|
|
19
34
|
return isFrameworkRepoRoot(resolve(projectRoot));
|
|
20
35
|
}
|
|
21
|
-
/** Normalize a title for duplicate comparison (#1709). */
|
|
36
|
+
/** Normalize a title for duplicate comparison (#1709 / #3713). */
|
|
22
37
|
export function normalizeForDedup(title) {
|
|
23
|
-
return title
|
|
24
|
-
.trim()
|
|
25
|
-
.replace(/^\[framework-gap\]\s*/i, "")
|
|
38
|
+
return stripTitleClassificationTokens(title)
|
|
26
39
|
.toLowerCase()
|
|
27
40
|
.replace(/[^\p{L}\p{N}]+/gu, " ")
|
|
28
41
|
.trim();
|
|
29
42
|
}
|
|
30
43
|
/** Build the upstream issue title for a framework-gap report. */
|
|
31
|
-
export function buildFrameworkGapTitle(summary) {
|
|
44
|
+
export function buildFrameworkGapTitle(summary, options) {
|
|
32
45
|
const clean = sanitizeOneLine(summary);
|
|
46
|
+
if (options?.adoptionBlocker) {
|
|
47
|
+
const rest = stripTitleClassificationTokens(clean);
|
|
48
|
+
return `${FRAMEWORK_GAP_TITLE_PREFIX} ${ADOPTION_BLOCKER_TITLE_TOKEN} ${rest}`;
|
|
49
|
+
}
|
|
33
50
|
if (clean.toLowerCase().startsWith(FRAMEWORK_GAP_TITLE_PREFIX.toLowerCase())) {
|
|
34
51
|
return clean;
|
|
35
52
|
}
|
|
36
53
|
return `${FRAMEWORK_GAP_TITLE_PREFIX} ${clean}`;
|
|
37
54
|
}
|
|
55
|
+
function buildAdoptionImpactSection(input) {
|
|
56
|
+
if (!input.adoptionBlocker) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
return [
|
|
60
|
+
"## Adoption impact",
|
|
61
|
+
"",
|
|
62
|
+
"This report is a consumer hard-stop: the consumer cannot complete an intended Directive flow and has no reasonable workaround.",
|
|
63
|
+
"",
|
|
64
|
+
`Title token: \`${ADOPTION_BLOCKER_TITLE_TOKEN}\`. This token does not apply the \`adoption-blocker\` ranking label. A privileged actor applies that label after the body-evidence test.`,
|
|
65
|
+
"",
|
|
66
|
+
"### Affected consumer flow and version",
|
|
67
|
+
"",
|
|
68
|
+
sectionOrPlaceholder(input.flowAndVersion, "_(not provided)_"),
|
|
69
|
+
"",
|
|
70
|
+
"### Documented alternatives attempted",
|
|
71
|
+
"",
|
|
72
|
+
sectionOrPlaceholder(input.alternatives, "_(not provided — why documented alternatives are not a reasonable workaround)_"),
|
|
73
|
+
"",
|
|
74
|
+
"### Observed recovery cost",
|
|
75
|
+
"",
|
|
76
|
+
sectionOrPlaceholder(input.recoveryCost, "_(not provided)_"),
|
|
77
|
+
"",
|
|
78
|
+
"### Triage owner and date",
|
|
79
|
+
"",
|
|
80
|
+
"_(to be filled by a privileged actor when applying `adoption-blocker`)_",
|
|
81
|
+
"",
|
|
82
|
+
];
|
|
83
|
+
}
|
|
38
84
|
/** Build the structured framework-gap issue body. */
|
|
39
85
|
export function buildFrameworkGapBody(input) {
|
|
40
86
|
const summary = sanitizeOneLine(input.summary);
|
|
@@ -59,6 +105,7 @@ export function buildFrameworkGapBody(input) {
|
|
|
59
105
|
"",
|
|
60
106
|
sectionOrPlaceholder(input.sessionNotes, "_(not provided)_"),
|
|
61
107
|
"",
|
|
108
|
+
...buildAdoptionImpactSection(input),
|
|
62
109
|
"---",
|
|
63
110
|
"_Filed via `task feedback:file` from a directive consumer project (Refs #1709 value-feedback gap escalation)._",
|
|
64
111
|
"",
|
|
@@ -126,7 +173,7 @@ export function runFeedbackFile(options) {
|
|
|
126
173
|
}
|
|
127
174
|
const projectRoot = resolve(projectRootRaw);
|
|
128
175
|
const repo = resolveRepo(options);
|
|
129
|
-
const title = buildFrameworkGapTitle(summary);
|
|
176
|
+
const title = buildFrameworkGapTitle(summary, { adoptionBlocker: options.adoptionBlocker });
|
|
130
177
|
const body = buildFrameworkGapBody(options);
|
|
131
178
|
if (isMaintainerFrameworkRepo(projectRoot)) {
|
|
132
179
|
return {
|
|
@@ -254,6 +301,8 @@ export function parseFeedbackFileArgs(argv) {
|
|
|
254
301
|
out.dryRun = true;
|
|
255
302
|
else if (arg === "--json")
|
|
256
303
|
out.json = true;
|
|
304
|
+
else if (arg === "--blocker")
|
|
305
|
+
out.adoptionBlocker = true;
|
|
257
306
|
else if (arg === "--summary") {
|
|
258
307
|
out.summary = argv[++i];
|
|
259
308
|
}
|
|
@@ -287,6 +336,27 @@ export function parseFeedbackFileArgs(argv) {
|
|
|
287
336
|
else if (arg?.startsWith("--session-notes=")) {
|
|
288
337
|
out.sessionNotes = arg.slice("--session-notes=".length);
|
|
289
338
|
}
|
|
339
|
+
else if (arg === "--flow" || arg === "--flow-and-version") {
|
|
340
|
+
out.flowAndVersion = argv[++i];
|
|
341
|
+
}
|
|
342
|
+
else if (arg?.startsWith("--flow=")) {
|
|
343
|
+
out.flowAndVersion = arg.slice("--flow=".length);
|
|
344
|
+
}
|
|
345
|
+
else if (arg?.startsWith("--flow-and-version=")) {
|
|
346
|
+
out.flowAndVersion = arg.slice("--flow-and-version=".length);
|
|
347
|
+
}
|
|
348
|
+
else if (arg === "--alternatives") {
|
|
349
|
+
out.alternatives = argv[++i];
|
|
350
|
+
}
|
|
351
|
+
else if (arg?.startsWith("--alternatives=")) {
|
|
352
|
+
out.alternatives = arg.slice("--alternatives=".length);
|
|
353
|
+
}
|
|
354
|
+
else if (arg === "--recovery-cost") {
|
|
355
|
+
out.recoveryCost = argv[++i];
|
|
356
|
+
}
|
|
357
|
+
else if (arg?.startsWith("--recovery-cost=")) {
|
|
358
|
+
out.recoveryCost = arg.slice("--recovery-cost=".length);
|
|
359
|
+
}
|
|
290
360
|
else if (arg === "--repo") {
|
|
291
361
|
out.repo = argv[++i];
|
|
292
362
|
}
|
|
@@ -327,6 +397,10 @@ export function feedbackFileMain(argv) {
|
|
|
327
397
|
expected: args.expected,
|
|
328
398
|
actual: args.actual,
|
|
329
399
|
sessionNotes: args.sessionNotes,
|
|
400
|
+
adoptionBlocker: args.adoptionBlocker,
|
|
401
|
+
flowAndVersion: args.flowAndVersion,
|
|
402
|
+
alternatives: args.alternatives,
|
|
403
|
+
recoveryCost: args.recoveryCost,
|
|
330
404
|
confirm: args.confirm,
|
|
331
405
|
dryRun: args.dryRun,
|
|
332
406
|
json: args.json,
|
|
@@ -9,6 +9,7 @@ export declare function computeDesiredLabels(plan: Record<string, unknown>, unre
|
|
|
9
9
|
export declare class ScmLabelClient implements LabelClient {
|
|
10
10
|
fetchLabels(repo: string, issueNumber: number): string[];
|
|
11
11
|
apply(repo: string, issueNumber: number, add: readonly string[], remove: readonly string[]): void;
|
|
12
|
+
private applyMut;
|
|
12
13
|
}
|
|
13
14
|
export interface ReconcileLabelsOptions {
|
|
14
15
|
readonly repo?: string | null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { applyDesignCritiqueCatalogChip, isDesignCritiqueCatalogChip, } from "../design-critique/exclusive-chip.js";
|
|
3
4
|
import { hasArtifactSuffix, resolveLifecycleRoot, stripArtifactSuffix } from "../layout/resolve.js";
|
|
4
5
|
import { call } from "../scm/call.js";
|
|
5
6
|
import { extractIssueRef } from "../triage/reconcile/parse-uri.js";
|
|
@@ -69,6 +70,35 @@ export class ScmLabelClient {
|
|
|
69
70
|
return names;
|
|
70
71
|
}
|
|
71
72
|
apply(repo, issueNumber, add, remove) {
|
|
73
|
+
const catalogAdds = add.filter(isDesignCritiqueCatalogChip);
|
|
74
|
+
if (catalogAdds.length > 0) {
|
|
75
|
+
const nextChip = catalogAdds[catalogAdds.length - 1];
|
|
76
|
+
const restAdd = add.filter((name) => !isDesignCritiqueCatalogChip(name));
|
|
77
|
+
const restRemove = remove.filter((name) => !isDesignCritiqueCatalogChip(name));
|
|
78
|
+
let applied = false;
|
|
79
|
+
const inner = {
|
|
80
|
+
fetchLabels: (r, n) => this.fetchLabels(r, n),
|
|
81
|
+
apply: (r, n, a, rem) => {
|
|
82
|
+
applied = true;
|
|
83
|
+
const addSet = new Set([...a, ...restAdd]);
|
|
84
|
+
const removeSet = new Set([...rem, ...restRemove]);
|
|
85
|
+
for (const name of addSet)
|
|
86
|
+
removeSet.delete(name);
|
|
87
|
+
this.applyMut(r, n, [...addSet], [...removeSet]);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
applyDesignCritiqueCatalogChip(inner, repo, issueNumber, nextChip);
|
|
91
|
+
if (!applied && (restAdd.length > 0 || restRemove.length > 0)) {
|
|
92
|
+
this.applyMut(repo, issueNumber, restAdd, restRemove);
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
this.applyMut(repo, issueNumber, add, remove);
|
|
97
|
+
}
|
|
98
|
+
applyMut(repo, issueNumber, add, remove) {
|
|
99
|
+
if (add.length === 0 && remove.length === 0) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
72
102
|
const args = ["edit", String(issueNumber), "--repo", repo];
|
|
73
103
|
for (const name of add)
|
|
74
104
|
args.push("--add-label", name);
|
|
@@ -25,5 +25,12 @@ export interface StoryQualityParams {
|
|
|
25
25
|
readonly swarm: JsonObject;
|
|
26
26
|
readonly concurrentReady?: boolean;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Shared Gate A text for `readiness=ready` + `parallel_safe: false` (#3666 / #3252).
|
|
30
|
+
* Authoring (`scope:decompose`) and launch (`swarm:readiness` / `swarm:launch`)
|
|
31
|
+
* both emit this string. It must not recommend `readiness=sequential` as a
|
|
32
|
+
* launch remediation — Gate B rejects that value on the same call.
|
|
33
|
+
*/
|
|
34
|
+
export declare const READY_REQUIRES_PARALLEL_SAFE = "readiness=ready requires parallel_safe=true. This story is not eligible for concurrent swarm:launch; dispatch non-concurrent work via the interactive swarm-skill solo-worker path (that path is not gated by swarm:readiness exiting 0; exit 0 gates concurrent workers only; see #3669)";
|
|
28
35
|
export declare function storyQualityIssues(params: StoryQualityParams): string[];
|
|
29
36
|
//# sourceMappingURL=story-quality.d.ts.map
|
|
@@ -266,6 +266,13 @@ export function deprecatedSubitemsIssues(items, prefix = "plan.items") {
|
|
|
266
266
|
visit(items, prefix);
|
|
267
267
|
return issues;
|
|
268
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Shared Gate A text for `readiness=ready` + `parallel_safe: false` (#3666 / #3252).
|
|
271
|
+
* Authoring (`scope:decompose`) and launch (`swarm:readiness` / `swarm:launch`)
|
|
272
|
+
* both emit this string. It must not recommend `readiness=sequential` as a
|
|
273
|
+
* launch remediation — Gate B rejects that value on the same call.
|
|
274
|
+
*/
|
|
275
|
+
export const READY_REQUIRES_PARALLEL_SAFE = "readiness=ready requires parallel_safe=true. This story is not eligible for concurrent swarm:launch; dispatch non-concurrent work via the interactive swarm-skill solo-worker path (that path is not gated by swarm:readiness exiting 0; exit 0 gates concurrent workers only; see #3669)";
|
|
269
276
|
export function storyQualityIssues(params) {
|
|
270
277
|
const issues = [];
|
|
271
278
|
const concurrentReady = params.concurrentReady ?? true;
|
|
@@ -304,7 +311,7 @@ export function storyQualityIssues(params) {
|
|
|
304
311
|
issues.push(...fileScopeIssues(params.swarm));
|
|
305
312
|
issues.push(...verifyCommandIssues(params.swarm));
|
|
306
313
|
if (params.swarm.parallel_safe === false) {
|
|
307
|
-
issues.push(
|
|
314
|
+
issues.push(READY_REQUIRES_PARALLEL_SAFE);
|
|
308
315
|
}
|
|
309
316
|
if (params.swarm.file_scope_confidence === "low") {
|
|
310
317
|
issues.push("readiness=ready requires file_scope_confidence above low");
|
|
@@ -6,14 +6,14 @@ import { MIGRATED_ARTIFACT_DIR } from "./constants.js";
|
|
|
6
6
|
import { isDirectory } from "./fs-helpers.js";
|
|
7
7
|
/**
|
|
8
8
|
* Bounded, ordered set of legacy crossover tokens rewritten in the UNMANAGED
|
|
9
|
-
* region of a consumer AGENTS.md after `migrate:xbrief` (#2154 / Option A).
|
|
9
|
+
* region of a consumer AGENTS.md after `migrate:xbrief` (#2154 / Option A / #3637).
|
|
10
10
|
*
|
|
11
11
|
* Each entry is a mechanical path / verb literal — NOT freeform prose. The
|
|
12
12
|
* casing-only `vBRIEF format` product-description token from the issue table is
|
|
13
|
-
* intentionally excluded so freeform prose survives untouched.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* intentionally excluded so freeform prose survives untouched. `.vbrief.json`
|
|
14
|
+
* and `vbrief:preflight` are substring hits. `vbrief/` is left-bound and only
|
|
15
|
+
* a hard hit when a child segment follows (`vbrief/active/...`); bare
|
|
16
|
+
* `vbrief/` and `x-vbrief/` are not rewritten. A second pass is a no-op.
|
|
17
17
|
*/
|
|
18
18
|
/** Refuse migrate header writes that escape via repo-controlled symlinks (#2668). */
|
|
19
19
|
function projectionTarget(projectDir, ...relSegments) {
|
|
@@ -26,6 +26,8 @@ export const LEGACY_HEADER_TOKENS = [
|
|
|
26
26
|
{ legacy: "vbrief:preflight", migrated: "xbrief:preflight" },
|
|
27
27
|
{ legacy: "vbrief/", migrated: "xbrief/" },
|
|
28
28
|
];
|
|
29
|
+
const VBRIEF_DIR = "vbrief/";
|
|
30
|
+
const XBRIEF_DIR = "xbrief/";
|
|
29
31
|
function countOccurrences(haystack, needle) {
|
|
30
32
|
if (needle.length === 0)
|
|
31
33
|
return 0;
|
|
@@ -37,10 +39,70 @@ function countOccurrences(haystack, needle) {
|
|
|
37
39
|
}
|
|
38
40
|
return count;
|
|
39
41
|
}
|
|
42
|
+
/** `x-vbrief/` is not a `vbrief/` hit; ident/hyphen continue the token. */
|
|
43
|
+
function isLeftBoundAt(text, index) {
|
|
44
|
+
if (index === 0)
|
|
45
|
+
return true;
|
|
46
|
+
return !/[A-Za-z0-9_-]/.test(text[index - 1]);
|
|
47
|
+
}
|
|
48
|
+
/** Child path segment after `vbrief/` — not whitespace, fence close, or EOS. */
|
|
49
|
+
function hasVbriefChildSegment(text, index) {
|
|
50
|
+
const after = index + VBRIEF_DIR.length;
|
|
51
|
+
if (after >= text.length)
|
|
52
|
+
return false;
|
|
53
|
+
const ch = text[after];
|
|
54
|
+
return ch !== "/" && ch !== "`" && !/\s/.test(ch);
|
|
55
|
+
}
|
|
56
|
+
function isHardVbriefDirHit(text, index) {
|
|
57
|
+
return isLeftBoundAt(text, index) && hasVbriefChildSegment(text, index);
|
|
58
|
+
}
|
|
59
|
+
function hasHardVbriefDirHit(text) {
|
|
60
|
+
let index = text.indexOf(VBRIEF_DIR);
|
|
61
|
+
while (index !== -1) {
|
|
62
|
+
if (isHardVbriefDirHit(text, index))
|
|
63
|
+
return true;
|
|
64
|
+
index = text.indexOf(VBRIEF_DIR, index + VBRIEF_DIR.length);
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
/** Rewrite left-bound `vbrief/` + child; leave bare `vbrief/` and `x-vbrief/`. */
|
|
69
|
+
function rewriteLeftBoundVbriefDir(slice) {
|
|
70
|
+
let out = "";
|
|
71
|
+
let cursor = 0;
|
|
72
|
+
let count = 0;
|
|
73
|
+
let index = slice.indexOf(VBRIEF_DIR);
|
|
74
|
+
while (index !== -1) {
|
|
75
|
+
out += slice.slice(cursor, index);
|
|
76
|
+
if (isHardVbriefDirHit(slice, index)) {
|
|
77
|
+
out += XBRIEF_DIR;
|
|
78
|
+
count += 1;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
out += VBRIEF_DIR;
|
|
82
|
+
}
|
|
83
|
+
cursor = index + VBRIEF_DIR.length;
|
|
84
|
+
index = slice.indexOf(VBRIEF_DIR, cursor);
|
|
85
|
+
}
|
|
86
|
+
out += slice.slice(cursor);
|
|
87
|
+
return { next: out, count };
|
|
88
|
+
}
|
|
89
|
+
function unmanagedHitsLegacyToken(unmanaged, legacy) {
|
|
90
|
+
if (legacy === VBRIEF_DIR)
|
|
91
|
+
return hasHardVbriefDirHit(unmanaged);
|
|
92
|
+
return unmanaged.includes(legacy);
|
|
93
|
+
}
|
|
40
94
|
/** Rewrite the bounded legacy tokens inside a single unmanaged text slice. */
|
|
41
95
|
function rewriteSlice(slice, tally) {
|
|
42
96
|
let next = slice;
|
|
43
97
|
for (const { legacy, migrated } of LEGACY_HEADER_TOKENS) {
|
|
98
|
+
if (legacy === VBRIEF_DIR) {
|
|
99
|
+
const rewritten = rewriteLeftBoundVbriefDir(next);
|
|
100
|
+
if (rewritten.count === 0)
|
|
101
|
+
continue;
|
|
102
|
+
tally.set(legacy, (tally.get(legacy) ?? 0) + rewritten.count);
|
|
103
|
+
next = rewritten.next;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
44
106
|
const occurrences = countOccurrences(next, legacy);
|
|
45
107
|
if (occurrences === 0)
|
|
46
108
|
continue;
|
|
@@ -172,7 +234,7 @@ export function detectStaleUnmanagedHeader(projectRoot, readText = (path) => {
|
|
|
172
234
|
cursor = end;
|
|
173
235
|
}
|
|
174
236
|
unmanaged += content.slice(cursor);
|
|
175
|
-
const matches = LEGACY_HEADER_TOKENS.filter((t) => unmanaged
|
|
237
|
+
const matches = LEGACY_HEADER_TOKENS.filter((t) => unmanagedHitsLegacyToken(unmanaged, t.legacy)).map((t) => t.legacy);
|
|
176
238
|
return { stale: matches.length > 0, matches };
|
|
177
239
|
}
|
|
178
240
|
/** One-line doctor / ritual signpost for the #2154 stale-header regression. */
|
|
@@ -183,6 +245,6 @@ export function renderStaleHeaderLine(projectRoot, readText) {
|
|
|
183
245
|
}
|
|
184
246
|
return (`AGENTS.md header drift: xbrief/ tree present but the unmanaged AGENTS.md header still ` +
|
|
185
247
|
`references legacy token(s) ${matches.join(", ")}. ` +
|
|
186
|
-
"
|
|
248
|
+
"Hand-edit the unmanaged header path literals.");
|
|
187
249
|
}
|
|
188
250
|
//# sourceMappingURL=agents-header.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.107.0",
|
|
4
4
|
"description": "TypeScript engine core for the Directive framework.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -386,8 +386,8 @@
|
|
|
386
386
|
"provenance": true
|
|
387
387
|
},
|
|
388
388
|
"dependencies": {
|
|
389
|
-
"@deftai/directive-content": "^0.
|
|
390
|
-
"@deftai/directive-types": "^0.
|
|
389
|
+
"@deftai/directive-content": "^0.107.0",
|
|
390
|
+
"@deftai/directive-types": "^0.107.0",
|
|
391
391
|
"archiver": "^8.0.0"
|
|
392
392
|
},
|
|
393
393
|
"scripts": {
|