@deftai/directive-core 0.66.2 → 0.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/dist/agents-md-advisory/evaluate.d.ts +44 -0
  2. package/dist/agents-md-advisory/evaluate.js +115 -0
  3. package/dist/agents-md-advisory/index.d.ts +2 -0
  4. package/dist/agents-md-advisory/index.js +2 -0
  5. package/dist/agents-md-budget/evaluate.d.ts +38 -0
  6. package/dist/agents-md-budget/evaluate.js +152 -0
  7. package/dist/agents-md-budget/index.d.ts +2 -0
  8. package/dist/agents-md-budget/index.js +2 -0
  9. package/dist/check/index.d.ts +1 -1
  10. package/dist/check/index.js +1 -1
  11. package/dist/check/orchestrator.d.ts +14 -4
  12. package/dist/check/orchestrator.js +30 -6
  13. package/dist/codebase/map.d.ts +1 -0
  14. package/dist/codebase/map.js +5 -2
  15. package/dist/doctor/checks.js +11 -11
  16. package/dist/doctor/constants.d.ts +7 -2
  17. package/dist/doctor/constants.js +17 -1
  18. package/dist/doctor/flags.js +5 -1
  19. package/dist/doctor/main.js +99 -8
  20. package/dist/doctor/payload-staleness.js +15 -6
  21. package/dist/doctor/types.d.ts +7 -0
  22. package/dist/forward-coverage/evaluate.d.ts +55 -0
  23. package/dist/forward-coverage/evaluate.js +254 -0
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.js +1 -0
  26. package/dist/init-deposit/constants.d.ts +14 -0
  27. package/dist/init-deposit/constants.js +10 -0
  28. package/dist/init-deposit/hygiene.d.ts +50 -0
  29. package/dist/init-deposit/hygiene.js +176 -0
  30. package/dist/init-deposit/init-deposit.d.ts +1 -0
  31. package/dist/init-deposit/init-deposit.js +7 -1
  32. package/dist/init-deposit/refresh.d.ts +2 -0
  33. package/dist/init-deposit/refresh.js +48 -30
  34. package/dist/init-deposit/scaffold.d.ts +3 -4
  35. package/dist/init-deposit/scaffold.js +3 -24
  36. package/dist/intake/github-auth-modes.d.ts +1 -0
  37. package/dist/intake/github-auth-modes.js +1 -0
  38. package/dist/intake/issue-ingest.d.ts +6 -2
  39. package/dist/intake/issue-ingest.js +65 -9
  40. package/dist/integration-e2e/helpers.d.ts +0 -1
  41. package/dist/integration-e2e/helpers.js +3 -7
  42. package/dist/layout/lifecycle-ref.d.ts +15 -0
  43. package/dist/layout/lifecycle-ref.js +55 -0
  44. package/dist/platform/agents-consumer-header.d.ts +13 -0
  45. package/dist/platform/agents-consumer-header.js +57 -0
  46. package/dist/platform/agents-md.d.ts +28 -0
  47. package/dist/platform/agents-md.js +38 -1
  48. package/dist/platform/index.d.ts +1 -0
  49. package/dist/platform/index.js +1 -0
  50. package/dist/policy/agents-md-advisory.d.ts +52 -0
  51. package/dist/policy/agents-md-advisory.js +63 -0
  52. package/dist/policy/agents-md-budget.d.ts +24 -0
  53. package/dist/policy/agents-md-budget.js +89 -0
  54. package/dist/policy/index.d.ts +1 -0
  55. package/dist/policy/index.js +1 -0
  56. package/dist/policy/resolve.js +56 -46
  57. package/dist/pr-merge-readiness/ci-gate.d.ts +28 -0
  58. package/dist/pr-merge-readiness/ci-gate.js +79 -0
  59. package/dist/pr-merge-readiness/compute.d.ts +15 -1
  60. package/dist/pr-merge-readiness/compute.js +129 -9
  61. package/dist/pr-merge-readiness/gh.d.ts +8 -0
  62. package/dist/pr-merge-readiness/gh.js +30 -5
  63. package/dist/pr-merge-readiness/index.d.ts +5 -1
  64. package/dist/pr-merge-readiness/index.js +3 -0
  65. package/dist/pr-merge-readiness/main.d.ts +3 -0
  66. package/dist/pr-merge-readiness/main.js +73 -6
  67. package/dist/pr-merge-readiness/mergeability.d.ts +43 -0
  68. package/dist/pr-merge-readiness/mergeability.js +105 -0
  69. package/dist/pr-merge-readiness/output.js +20 -0
  70. package/dist/pr-merge-readiness/slizard-gate.d.ts +47 -0
  71. package/dist/pr-merge-readiness/slizard-gate.js +119 -0
  72. package/dist/pr-monitor/index.d.ts +1 -1
  73. package/dist/pr-monitor/index.js +1 -1
  74. package/dist/pr-monitor/monitor.d.ts +8 -2
  75. package/dist/pr-monitor/monitor.js +35 -6
  76. package/dist/pr-wait-mergeable/wrappers.d.ts +12 -1
  77. package/dist/pr-wait-mergeable/wrappers.js +13 -5
  78. package/dist/pr-watch/constants.d.ts +33 -0
  79. package/dist/pr-watch/constants.js +33 -0
  80. package/dist/pr-watch/index.d.ts +6 -0
  81. package/dist/pr-watch/index.js +6 -0
  82. package/dist/pr-watch/main.d.ts +21 -0
  83. package/dist/pr-watch/main.js +206 -0
  84. package/dist/pr-watch/probe.d.ts +13 -0
  85. package/dist/pr-watch/probe.js +100 -0
  86. package/dist/pr-watch/types.d.ts +47 -0
  87. package/dist/pr-watch/types.js +2 -0
  88. package/dist/pr-watch/watch.d.ts +12 -0
  89. package/dist/pr-watch/watch.js +122 -0
  90. package/dist/preflight-cache/evaluate.d.ts +1 -0
  91. package/dist/preflight-cache/evaluate.js +17 -10
  92. package/dist/render/project-render.d.ts +16 -2
  93. package/dist/render/project-render.js +155 -97
  94. package/dist/scope/capacity-stamp.d.ts +24 -2
  95. package/dist/scope/capacity-stamp.js +80 -28
  96. package/dist/scope/decomposed-refs.js +2 -2
  97. package/dist/scope/project-definition-sync.js +3 -3
  98. package/dist/scope/vbrief-ref.d.ts +2 -1
  99. package/dist/scope/vbrief-ref.js +3 -2
  100. package/dist/spec-authority/constants.d.ts +6 -0
  101. package/dist/spec-authority/constants.js +11 -0
  102. package/dist/spec-authority/resolver.js +3 -3
  103. package/dist/swarm/finalize-cohort-cli.d.ts +5 -0
  104. package/dist/swarm/finalize-cohort-cli.js +100 -0
  105. package/dist/swarm/finalize-cohort.d.ts +39 -0
  106. package/dist/swarm/finalize-cohort.js +589 -0
  107. package/dist/swarm/index.d.ts +2 -0
  108. package/dist/swarm/index.js +2 -0
  109. package/dist/swarm/launch.d.ts +13 -0
  110. package/dist/swarm/launch.js +19 -7
  111. package/dist/swarm/routing-verify.d.ts +1 -1
  112. package/dist/swarm/routing-verify.js +11 -10
  113. package/dist/swarm/routing.d.ts +9 -0
  114. package/dist/swarm/routing.js +46 -0
  115. package/dist/swarm/verify-review-clean-cli.js +25 -1
  116. package/dist/swarm/verify-review-clean.d.ts +9 -1
  117. package/dist/swarm/verify-review-clean.js +76 -3
  118. package/dist/triage/queue/index.d.ts +1 -0
  119. package/dist/triage/queue/index.js +1 -0
  120. package/dist/triage/queue/project.d.ts +6 -1
  121. package/dist/triage/queue/project.js +9 -4
  122. package/dist/triage/queue/reconcile-live-state.d.ts +42 -0
  123. package/dist/triage/queue/reconcile-live-state.js +54 -0
  124. package/dist/triage/scope/mutations-core.js +91 -81
  125. package/dist/triage/scope/resolve.js +4 -3
  126. package/dist/triage/scope-drift/add-ignore.js +33 -32
  127. package/dist/triage/scope-drift/scope-rules.js +3 -3
  128. package/dist/triage/subscribe/index.js +51 -45
  129. package/dist/triage/welcome/writers.js +79 -64
  130. package/dist/vbrief-build/project-definition-io.js +8 -3
  131. package/dist/vbrief-validate/paths.js +8 -4
  132. package/dist/vbrief-validate/precutover.d.ts +1 -1
  133. package/dist/vbrief-validate/precutover.js +12 -10
  134. package/dist/verify-source/biome-config.d.ts +41 -0
  135. package/dist/verify-source/biome-config.js +97 -0
  136. package/dist/verify-source/index.d.ts +1 -0
  137. package/dist/verify-source/index.js +1 -0
  138. package/dist/xbrief-migrate/agents-header.d.ts +69 -0
  139. package/dist/xbrief-migrate/agents-header.js +179 -0
  140. package/dist/xbrief-migrate/fs-helpers.d.ts +3 -0
  141. package/dist/xbrief-migrate/fs-helpers.js +11 -0
  142. package/dist/xbrief-migrate/index.d.ts +1 -0
  143. package/dist/xbrief-migrate/index.js +1 -0
  144. package/dist/xbrief-migrate/migrate-project.js +29 -10
  145. package/package.json +15 -7
  146. package/dist/install-upgrade/index.d.ts +0 -13
  147. package/dist/install-upgrade/index.js +0 -251
@@ -0,0 +1,44 @@
1
+ import { type RegionCounts } from "../agents-md-budget/evaluate.js";
2
+ import { type AgentsMdAdvisorySource } from "../policy/agents-md-advisory.js";
3
+ export type OutputStream = "stdout" | "stderr" | "none";
4
+ /**
5
+ * Result of the consumer AGENTS.md advisory evaluation (#2155).
6
+ *
7
+ * `code` is 0 in the DEFAULT advisory posture no matter what -- the advisory
8
+ * MUST NEVER fail-close a consumer build. Non-zero codes are only ever produced
9
+ * in the explicit `--enforce` opt-in posture (1 = over the hard cap the
10
+ * consumer asked for; 2 = a config problem that blocks an enforced check).
11
+ */
12
+ export interface AdvisoryEvaluateResult {
13
+ readonly code: 0 | 1 | 2;
14
+ readonly message: string;
15
+ readonly stream: OutputStream;
16
+ /** True when the unmanaged region exceeds the soft budget. */
17
+ readonly over: boolean;
18
+ /** Region counts, or null when AGENTS.md was missing / unreadable / malformed. */
19
+ readonly counts: RegionCounts | null;
20
+ /** The resolved soft budget the unmanaged region was compared against. */
21
+ readonly softMaxLines: number;
22
+ /** Provenance of the soft budget (typed / default / default-on-error). */
23
+ readonly source: AgentsMdAdvisorySource;
24
+ }
25
+ export interface AdvisoryEvaluateOptions {
26
+ /**
27
+ * Opt-in hard-cap posture (#1419 enforce tier). When true, an over-budget
28
+ * unmanaged region exits 1 and a config problem exits 2. Default (false) is
29
+ * the advisory posture that always exits 0.
30
+ */
31
+ readonly enforce?: boolean;
32
+ /** Suppress the within-budget "OK" line (used by aggregate callers). */
33
+ readonly quiet?: boolean;
34
+ }
35
+ /**
36
+ * Evaluate the consumer AGENTS.md against its soft, unmanaged-focused budget.
37
+ *
38
+ * Reuses the #645 region counter (`countRegions`) so the framework-owned
39
+ * managed section is EXCLUDED from the comparison -- only the consumer-authored
40
+ * unmanaged region is measured. In the default advisory posture the result code
41
+ * is always 0; `--enforce` promotes over-budget / config problems to non-zero.
42
+ */
43
+ export declare function evaluate(projectRoot: string, options?: AdvisoryEvaluateOptions): AdvisoryEvaluateResult;
44
+ //# sourceMappingURL=evaluate.d.ts.map
@@ -0,0 +1,115 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { countRegions } from "../agents-md-budget/evaluate.js";
4
+ import { resolveAgentsMdAdvisory, } from "../policy/agents-md-advisory.js";
5
+ const GUIDANCE_LINE = " AGENTS.md is a map, not a manual (#1882): push project detail into a\n" +
6
+ " reference doc and leave a pointer, rather than growing AGENTS.md. See\n" +
7
+ " content/docs/good-agents-md.md.";
8
+ function overMessage(counts, softMax, enforce, fieldName) {
9
+ const over = counts.unmanaged - softMax;
10
+ if (enforce) {
11
+ return (`❌ agents-md-advisory: AGENTS.md unmanaged (project-authored) region is ` +
12
+ `${counts.unmanaged} lines, over the enforced cap of ${softMax} (OVER by ${over}).\n` +
13
+ `${GUIDANCE_LINE}\n` +
14
+ ` Raise ${fieldName} in PROJECT-DEFINITION to accept the growth.`);
15
+ }
16
+ return (`⚠ agents-md-advisory: AGENTS.md unmanaged (project-authored) region is ` +
17
+ `${counts.unmanaged} lines, over the soft budget of ${softMax} (OVER by ${over}). ` +
18
+ "This is advisory only -- your build is NOT affected.\n" +
19
+ `${GUIDANCE_LINE}\n` +
20
+ ` This is your knob: raise ${fieldName} in PROJECT-DEFINITION to accept the\n` +
21
+ " growth and silence this note.");
22
+ }
23
+ const FIELD_NAME = "plan.policy.agentsMdAdvisory.unmanagedSoftMaxLines";
24
+ /**
25
+ * Evaluate the consumer AGENTS.md against its soft, unmanaged-focused budget.
26
+ *
27
+ * Reuses the #645 region counter (`countRegions`) so the framework-owned
28
+ * managed section is EXCLUDED from the comparison -- only the consumer-authored
29
+ * unmanaged region is measured. In the default advisory posture the result code
30
+ * is always 0; `--enforce` promotes over-budget / config problems to non-zero.
31
+ */
32
+ export function evaluate(projectRoot, options = {}) {
33
+ const root = resolve(projectRoot);
34
+ const enforce = options.enforce ?? false;
35
+ const quiet = options.quiet ?? false;
36
+ const advisory = resolveAgentsMdAdvisory(root);
37
+ const softMax = advisory.config.unmanagedSoftMaxLines;
38
+ const agentsPath = join(root, "AGENTS.md");
39
+ if (!existsSync(agentsPath)) {
40
+ const message = `agents-md-advisory: no AGENTS.md found at ${agentsPath}`;
41
+ return {
42
+ code: enforce ? 2 : 0,
43
+ message: enforce ? `❌ ${message}` : quiet ? "" : `${message} (advisory; skipped).`,
44
+ stream: enforce ? "stderr" : quiet ? "none" : "stdout",
45
+ over: false,
46
+ counts: null,
47
+ softMaxLines: softMax,
48
+ source: advisory.source,
49
+ };
50
+ }
51
+ let text;
52
+ try {
53
+ text = readFileSync(agentsPath, { encoding: "utf8" });
54
+ }
55
+ catch (err) {
56
+ const message = `agents-md-advisory: AGENTS.md at ${agentsPath} cannot be read: ${String(err)}`;
57
+ return {
58
+ code: enforce ? 2 : 0,
59
+ message: enforce ? `❌ ${message}` : quiet ? "" : `${message} (advisory; skipped).`,
60
+ stream: enforce ? "stderr" : quiet ? "none" : "stdout",
61
+ over: false,
62
+ counts: null,
63
+ softMaxLines: softMax,
64
+ source: advisory.source,
65
+ };
66
+ }
67
+ const regionResult = countRegions(text);
68
+ if ("error" in regionResult) {
69
+ const message = `agents-md-advisory: ${regionResult.error}`;
70
+ return {
71
+ code: enforce ? 2 : 0,
72
+ message: enforce ? `❌ ${message}` : quiet ? "" : `${message} (advisory; skipped).`,
73
+ stream: enforce ? "stderr" : quiet ? "none" : "stdout",
74
+ over: false,
75
+ counts: null,
76
+ softMaxLines: softMax,
77
+ source: advisory.source,
78
+ };
79
+ }
80
+ const counts = regionResult.counts;
81
+ const over = counts.unmanaged > softMax;
82
+ if (!over) {
83
+ if (quiet) {
84
+ return {
85
+ code: 0,
86
+ message: "",
87
+ stream: "none",
88
+ over: false,
89
+ counts,
90
+ softMaxLines: softMax,
91
+ source: advisory.source,
92
+ };
93
+ }
94
+ return {
95
+ code: 0,
96
+ message: `✓ agents-md-advisory: AGENTS.md unmanaged region ${counts.unmanaged}/${softMax} lines ` +
97
+ `within soft budget (managed ${counts.managed} excluded; advisory).`,
98
+ stream: "stdout",
99
+ over: false,
100
+ counts,
101
+ softMaxLines: softMax,
102
+ source: advisory.source,
103
+ };
104
+ }
105
+ return {
106
+ code: enforce ? 1 : 0,
107
+ message: overMessage(counts, softMax, enforce, FIELD_NAME),
108
+ stream: "stderr",
109
+ over: true,
110
+ counts,
111
+ softMaxLines: softMax,
112
+ source: advisory.source,
113
+ };
114
+ }
115
+ //# sourceMappingURL=evaluate.js.map
@@ -0,0 +1,2 @@
1
+ export * from "./evaluate.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,2 @@
1
+ export * from "./evaluate.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,38 @@
1
+ export type OutputStream = "stdout" | "stderr" | "none";
2
+ /** Result of verify:agents-md-budget evaluation; three-state exit contract. */
3
+ export interface EvaluateResult {
4
+ readonly code: 0 | 1 | 2;
5
+ readonly message: string;
6
+ readonly stream: OutputStream;
7
+ }
8
+ export interface EvaluateOptions {
9
+ readonly quiet?: boolean;
10
+ }
11
+ /** Per-region line counts of an AGENTS.md file. */
12
+ export interface RegionCounts {
13
+ readonly total: number;
14
+ readonly managed: number;
15
+ readonly unmanaged: number;
16
+ }
17
+ /**
18
+ * Split AGENTS.md into managed / unmanaged line counts.
19
+ *
20
+ * Returns `{ counts }` on success, or `{ error }` when the managed markers are
21
+ * malformed (exactly one marker present, or close-before-open). A file with no
22
+ * markers at all is valid: the whole file counts as unmanaged.
23
+ */
24
+ export declare function countRegions(text: string): {
25
+ counts: RegionCounts;
26
+ } | {
27
+ error: string;
28
+ };
29
+ /**
30
+ * Pure evaluator for the AGENTS.md line-budget ratchet gate (#645).
31
+ *
32
+ * Counts the managed section and the unmanaged region separately (the #1309
33
+ * propagation duplicates content across the marker) and fails when either
34
+ * region exceeds its typed per-region budget. Ships green because the budget is
35
+ * seeded at current size; growth past the ratchet fails.
36
+ */
37
+ export declare function evaluate(projectRoot: string, options?: EvaluateOptions): EvaluateResult;
38
+ //# sourceMappingURL=evaluate.d.ts.map
@@ -0,0 +1,152 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { AGENTS_MANAGED_CLOSE } from "../platform/constants.js";
4
+ import { resolveAgentsMdBudget } from "../policy/agents-md-budget.js";
5
+ const OPEN_MARKER_PREFIX = "<!-- deft:managed-section";
6
+ /**
7
+ * Split AGENTS.md into managed / unmanaged line counts.
8
+ *
9
+ * Returns `{ counts }` on success, or `{ error }` when the managed markers are
10
+ * malformed (exactly one marker present, or close-before-open). A file with no
11
+ * markers at all is valid: the whole file counts as unmanaged.
12
+ */
13
+ export function countRegions(text) {
14
+ const lines = text.replace(/\r\n/g, "\n").split("\n");
15
+ // Ignore the trailing empty element produced by a final newline.
16
+ if (lines.length > 0 && lines[lines.length - 1] === "") {
17
+ lines.pop();
18
+ }
19
+ const total = lines.length;
20
+ const openLine = lines.findIndex((l) => l.startsWith(OPEN_MARKER_PREFIX));
21
+ const closeLine = lines.findIndex((l) => l.trim().startsWith(AGENTS_MANAGED_CLOSE));
22
+ const openCount = lines.filter((l) => l.startsWith(OPEN_MARKER_PREFIX)).length;
23
+ const closeCount = lines.filter((l) => l.trim().startsWith(AGENTS_MANAGED_CLOSE)).length;
24
+ if (openLine === -1 && closeLine === -1) {
25
+ return { counts: { total, managed: 0, unmanaged: total } };
26
+ }
27
+ // Malformed if a marker is missing, close precedes open, or either marker is
28
+ // duplicated -- the contract promises exactly one open/close pair.
29
+ if (openLine === -1 ||
30
+ closeLine === -1 ||
31
+ closeLine < openLine ||
32
+ openCount > 1 ||
33
+ closeCount > 1) {
34
+ return {
35
+ error: "AGENTS.md managed-section markers are malformed " +
36
+ `(open@${openLine === -1 ? "none" : openLine + 1}×${openCount}, ` +
37
+ `close@${closeLine === -1 ? "none" : closeLine + 1}×${closeCount}); ` +
38
+ "expected a single <!-- deft:managed-section ... --> ... " +
39
+ "<!-- /deft:managed-section --> pair.",
40
+ };
41
+ }
42
+ const managed = closeLine - openLine + 1;
43
+ return { counts: { total, managed, unmanaged: total - managed } };
44
+ }
45
+ function formatRefusal(counts, managedMax, unmanagedMax, projectRoot) {
46
+ const over = [];
47
+ if (counts.managed > managedMax) {
48
+ over.push(` managed region: ${counts.managed}/${managedMax} lines (OVER by ${counts.managed - managedMax})`);
49
+ }
50
+ if (counts.unmanaged > unmanagedMax) {
51
+ over.push(` unmanaged region: ${counts.unmanaged}/${unmanagedMax} lines (OVER by ${counts.unmanaged - unmanagedMax})`);
52
+ }
53
+ return (`❌ verify:agents-md-budget: AGENTS.md grew past its ratchet ` +
54
+ `(project_root=${projectRoot}).\n` +
55
+ `${over.join("\n")}\n` +
56
+ " AGENTS.md is a map, not a manual (#1882): push detail into a\n" +
57
+ " reference doc (main.md / a pack / docs/) and leave a pointer,\n" +
58
+ " rather than expanding AGENTS.md. See REFERENCES.md.\n" +
59
+ " If the growth is deliberate, raise the matching line in\n" +
60
+ " plan.policy.agentsMdBudget in PROJECT-DEFINITION (a reviewed diff). (#645)");
61
+ }
62
+ /**
63
+ * Pure evaluator for the AGENTS.md line-budget ratchet gate (#645).
64
+ *
65
+ * Counts the managed section and the unmanaged region separately (the #1309
66
+ * propagation duplicates content across the marker) and fails when either
67
+ * region exceeds its typed per-region budget. Ships green because the budget is
68
+ * seeded at current size; growth past the ratchet fails.
69
+ */
70
+ export function evaluate(projectRoot, options = {}) {
71
+ const root = resolve(projectRoot);
72
+ const quiet = options.quiet ?? false;
73
+ const budgetResult = resolveAgentsMdBudget(root);
74
+ if (budgetResult.source === "default-on-error") {
75
+ return {
76
+ code: 2,
77
+ message: `❌ verify:agents-md-budget: PROJECT-DEFINITION malformed: ${budgetResult.error}`,
78
+ stream: "stderr",
79
+ };
80
+ }
81
+ const agentsPath = join(root, "AGENTS.md");
82
+ if (!existsSync(agentsPath)) {
83
+ return {
84
+ code: 2,
85
+ message: `❌ verify:agents-md-budget: AGENTS.md not found at ${agentsPath}`,
86
+ stream: "stderr",
87
+ };
88
+ }
89
+ let text;
90
+ try {
91
+ text = readFileSync(agentsPath, { encoding: "utf8" });
92
+ }
93
+ catch (err) {
94
+ return {
95
+ code: 2,
96
+ message: `❌ verify:agents-md-budget: AGENTS.md at ${agentsPath} cannot be read: ${String(err)}`,
97
+ stream: "stderr",
98
+ };
99
+ }
100
+ const regionResult = countRegions(text);
101
+ if ("error" in regionResult) {
102
+ return {
103
+ code: 2,
104
+ message: `❌ verify:agents-md-budget: ${regionResult.error}`,
105
+ stream: "stderr",
106
+ };
107
+ }
108
+ const counts = regionResult.counts;
109
+ if (budgetResult.source === "unset") {
110
+ if (quiet) {
111
+ return { code: 0, message: "", stream: "none" };
112
+ }
113
+ return {
114
+ code: 0,
115
+ message: "⚠ verify:agents-md-budget: no plan.policy.agentsMdBudget configured " +
116
+ `(managed=${counts.managed}, unmanaged=${counts.unmanaged} lines).\n` +
117
+ " Seed a ratchet at current size to freeze growth (#645): set\n" +
118
+ " plan.policy.agentsMdBudget.{managedMaxLines,unmanagedMaxLines} in " +
119
+ "PROJECT-DEFINITION.",
120
+ stream: "stderr",
121
+ };
122
+ }
123
+ /* v8 ignore start -- defensive: source "typed" always carries a non-null budget. */
124
+ if (budgetResult.budget === null) {
125
+ return {
126
+ code: 2,
127
+ message: "❌ verify:agents-md-budget: unexpected null budget for typed source",
128
+ stream: "stderr",
129
+ };
130
+ }
131
+ /* v8 ignore stop */
132
+ const budget = budgetResult.budget;
133
+ const overManaged = counts.managed > budget.managedMaxLines;
134
+ const overUnmanaged = counts.unmanaged > budget.unmanagedMaxLines;
135
+ if (!overManaged && !overUnmanaged) {
136
+ if (quiet) {
137
+ return { code: 0, message: "", stream: "none" };
138
+ }
139
+ return {
140
+ code: 0,
141
+ message: `✓ verify:agents-md-budget: managed ${counts.managed}/${budget.managedMaxLines}, ` +
142
+ `unmanaged ${counts.unmanaged}/${budget.unmanagedMaxLines} lines (within ratchet).`,
143
+ stream: "stdout",
144
+ };
145
+ }
146
+ return {
147
+ code: 1,
148
+ message: formatRefusal(counts, budget.managedMaxLines, budget.unmanagedMaxLines, root),
149
+ stream: "stderr",
150
+ };
151
+ }
152
+ //# sourceMappingURL=evaluate.js.map
@@ -0,0 +1,2 @@
1
+ export * from "./evaluate.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,2 @@
1
+ export * from "./evaluate.js";
2
+ //# sourceMappingURL=index.js.map
@@ -1,3 +1,3 @@
1
1
  export type { CheckOrchestratorSeams } from "./orchestrator.js";
2
- export { dispatchTaskCheck, isFrameworkSourceContext, resolveCheckTarget } from "./orchestrator.js";
2
+ export { dispatchTaskCheck, isFrameworkRepoRoot, isFrameworkSourceContext, resolveCheckTarget, } from "./orchestrator.js";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,2 @@
1
- export { dispatchTaskCheck, isFrameworkSourceContext, resolveCheckTarget } from "./orchestrator.js";
1
+ export { dispatchTaskCheck, isFrameworkRepoRoot, isFrameworkSourceContext, resolveCheckTarget, } from "./orchestrator.js";
2
2
  //# sourceMappingURL=index.js.map
@@ -24,13 +24,23 @@ export interface CheckOrchestratorSeams {
24
24
  error?: Error;
25
25
  };
26
26
  }
27
+ /**
28
+ * True when `path` is the directive framework source checkout root (not a
29
+ * vendored `.deft/core` content deposit). Used to distinguish a maintainer
30
+ * running `task check` from a subdirectory (#2220) from a consumer install.
31
+ */
32
+ export declare function isFrameworkRepoRoot(path: string): boolean;
27
33
  /**
28
34
  * Return true when running in the framework's own source checkout (#1519).
29
35
  *
30
- * Mirrors `is_framework_source_context` from _project_context.py:
31
- * equality of lexical absolute roots is the stable distinction. We do NOT
32
- * resolve symlinks here -- a consumer project may symlink `.deft/core` to a
33
- * local framework checkout and should still run the consumer-safe gate.
36
+ * Mirrors `is_framework_source_context` from _project_context.py with one
37
+ * extension (#2220): when the Taskfile lives at the framework repo root,
38
+ * `task check` may be invoked from a subdirectory (`USER_WORKING_DIR` !=
39
+ * `TASKFILE_DIR`). Those invocations must still route to
40
+ * `check:framework-source` so the biome lane runs. We do NOT resolve
41
+ * symlinks on the framework root -- a consumer project may symlink
42
+ * `.deft/core` to a local framework checkout and should still run the
43
+ * consumer-safe gate (the deposit path lacks `packages/cli`).
34
44
  */
35
45
  export declare function isFrameworkSourceContext(frameworkRoot: string, projectRoot: string): boolean;
36
46
  /**
@@ -12,17 +12,41 @@
12
12
  * 2 -- config error (missing args, task spawn error, etc.)
13
13
  */
14
14
  import { spawnSync } from "node:child_process";
15
- import { join, resolve } from "node:path";
15
+ import { existsSync } from "node:fs";
16
+ import { join, resolve, sep } from "node:path";
17
+ /**
18
+ * True when `path` is the directive framework source checkout root (not a
19
+ * vendored `.deft/core` content deposit). Used to distinguish a maintainer
20
+ * running `task check` from a subdirectory (#2220) from a consumer install.
21
+ */
22
+ export function isFrameworkRepoRoot(path) {
23
+ const root = resolve(path);
24
+ return (existsSync(join(root, "packages", "cli", "package.json")) &&
25
+ existsSync(join(root, "biome.json")) &&
26
+ existsSync(join(root, "Taskfile.yml")));
27
+ }
16
28
  /**
17
29
  * Return true when running in the framework's own source checkout (#1519).
18
30
  *
19
- * Mirrors `is_framework_source_context` from _project_context.py:
20
- * equality of lexical absolute roots is the stable distinction. We do NOT
21
- * resolve symlinks here -- a consumer project may symlink `.deft/core` to a
22
- * local framework checkout and should still run the consumer-safe gate.
31
+ * Mirrors `is_framework_source_context` from _project_context.py with one
32
+ * extension (#2220): when the Taskfile lives at the framework repo root,
33
+ * `task check` may be invoked from a subdirectory (`USER_WORKING_DIR` !=
34
+ * `TASKFILE_DIR`). Those invocations must still route to
35
+ * `check:framework-source` so the biome lane runs. We do NOT resolve
36
+ * symlinks on the framework root -- a consumer project may symlink
37
+ * `.deft/core` to a local framework checkout and should still run the
38
+ * consumer-safe gate (the deposit path lacks `packages/cli`).
23
39
  */
24
40
  export function isFrameworkSourceContext(frameworkRoot, projectRoot) {
25
- return resolve(frameworkRoot) === resolve(projectRoot);
41
+ const fw = resolve(frameworkRoot);
42
+ const pr = resolve(projectRoot);
43
+ if (fw === pr) {
44
+ return true;
45
+ }
46
+ if (!isFrameworkRepoRoot(fw)) {
47
+ return false;
48
+ }
49
+ return pr.startsWith(`${fw}${sep}`);
26
50
  }
27
51
  /**
28
52
  * Select the Taskfile target for the given context.
@@ -1,6 +1,7 @@
1
1
  export declare const GENERATED_SENTINEL = "AUTO-GENERATED by task codebase:map";
2
2
  export declare const DEFAULT_OUTPUT_PATH = ".planning/codebase/MAP.md";
3
3
  export declare const BANNER_TEMPLATE: string;
4
+ export declare function markdownText(value: unknown): string;
4
5
  export declare function projectionOutputPath(projectRoot: string, explicitOutput?: string | null): string;
5
6
  export declare function renderCodebaseMap(artifact: Record<string, unknown>): string;
6
7
  export declare function isDeftGenerated(path: string): boolean;
@@ -13,9 +13,12 @@ export const BANNER_TEMPLATE = "<!-- AUTO-GENERATED by task codebase:map -- DO N
13
13
  "<!-- Source of truth: vbrief/PROJECT-DEFINITION.vbrief.json " +
14
14
  "plan.architecture.codeStructure -->\n" +
15
15
  "<!-- Regenerate with: task codebase:map -->\n";
16
- function markdownText(value) {
16
+ export function markdownText(value) {
17
17
  const text = value === null || value === undefined ? "" : String(value);
18
- return text.replace(/\|/g, "\\|").replace(/\n/g, " ").trim();
18
+ // Escape the backslash escape character FIRST, then the `|` table delimiter.
19
+ // Escaping `|` alone lets a pre-existing backslash bypass the delimiter
20
+ // escape (CodeQL js/incomplete-sanitization).
21
+ return text.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\n/g, " ").trim();
19
22
  }
20
23
  function code(value) {
21
24
  const text = markdownText(value);
@@ -34,14 +34,14 @@ export function checkQuickStartResolves(projectRoot, installRoot, seams = {}) {
34
34
  `install root is '${installRoot}' but the file is missing. ` +
35
35
  "Run `.deft/core/run agents:refresh` (Unix) / " +
36
36
  "`.deft\\core\\run agents:refresh` (Windows) to align AGENTS.md " +
37
- "with the on-disk install root, OR run `task upgrade` to " +
37
+ "with the on-disk install root, OR run `deft update` to " +
38
38
  "re-pull the framework if the on-disk install is missing. " +
39
39
  "See UPGRADING.md for the canonical drift-repair walkthrough.",
40
40
  data: {
41
41
  path: qsPath,
42
42
  install_root: installRoot,
43
43
  suggested_fix: ".deft/core/run agents:refresh",
44
- suggested_fix_alt: "task upgrade",
44
+ suggested_fix_alt: "deft update",
45
45
  },
46
46
  };
47
47
  }
@@ -89,13 +89,13 @@ export function checkSkillPathsResolve(projectRoot, agentsMdText, seams = {}) {
89
89
  status: "fail",
90
90
  detail: `${missing.length} skill path(s) do not resolve; ${redirectStubs.length} stub redirect(s). ` +
91
91
  `${parts.join("; ")}. Run \`.deft/core/run agents:refresh\` (Unix) / ` +
92
- "`.deft\\core\\run agents:refresh` (Windows) to rewrite the managed AGENTS.md block so skill paths match the on-disk framework, OR run `task upgrade` if the on-disk skills are missing entirely. See UPGRADING.md for the drift-repair walkthrough.",
92
+ "`.deft\\core\\run agents:refresh` (Windows) to rewrite the managed AGENTS.md block so skill paths match the on-disk framework, OR run `deft update` if the on-disk skills are missing entirely. See UPGRADING.md for the drift-repair walkthrough.",
93
93
  data: {
94
94
  referenced,
95
95
  missing,
96
96
  redirect_stubs: redirectStubs,
97
97
  suggested_fix: ".deft/core/run agents:refresh",
98
- suggested_fix_alt: "task upgrade",
98
+ suggested_fix_alt: "deft update",
99
99
  },
100
100
  };
101
101
  }
@@ -111,7 +111,7 @@ export function checkManifestAgreement(projectRoot, installRoot, seams = {}) {
111
111
  return {
112
112
  name: "manifest-agreement",
113
113
  status: "fail",
114
- detail: `Two install manifests disagree: .deft/core/VERSION (tag='${coreVer}') vs legacy .deft/VERSION (tag='${legacyVer}'). The canonical manifest is .deft/core/VERSION -- run \`task upgrade\` to migrate the stale .deft/VERSION (backed up as .deft/VERSION.premigrate). See UPGRADING.md for the canonical drift-repair walkthrough.`,
114
+ detail: `Two install manifests disagree: .deft/core/VERSION (tag='${coreVer}') vs legacy .deft/VERSION (tag='${legacyVer}'). The canonical manifest is .deft/core/VERSION -- run \`deft update\` to migrate the stale .deft/VERSION (backed up as .deft/VERSION.premigrate). See UPGRADING.md for the canonical drift-repair walkthrough.`,
115
115
  data: {
116
116
  dual_manifest_drift: true,
117
117
  core_manifest_path: coreManifest,
@@ -119,7 +119,7 @@ export function checkManifestAgreement(projectRoot, installRoot, seams = {}) {
119
119
  core_version: coreVer,
120
120
  legacy_version: legacyVer,
121
121
  authoritative: "manifest",
122
- suggested_fix: "task upgrade",
122
+ suggested_fix: "deft update",
123
123
  },
124
124
  };
125
125
  }
@@ -149,13 +149,13 @@ export function checkManifestAgreement(projectRoot, installRoot, seams = {}) {
149
149
  return {
150
150
  name: "manifest-agreement",
151
151
  status: "fail",
152
- detail: `Bare .deft-version exists at ${barePath} but YAML manifest is missing at ${expectedManifestPath}. Run \`task upgrade\` to write the canonical manifest (#1046 PR-B AC-4). See UPGRADING.md for the v0.27.x -> v0.28 transition walkthrough.`,
152
+ detail: `Bare .deft-version exists at ${barePath} but YAML manifest is missing at ${expectedManifestPath}. Run \`deft update\` to write the canonical manifest (#1046 PR-B AC-4). See UPGRADING.md for the v0.27.x -> v0.28 transition walkthrough.`,
153
153
  data: {
154
154
  manifest_path: manifestPath,
155
155
  expected_manifest_path: expectedManifestPath,
156
156
  bare_path: barePath,
157
157
  bare_value: bareText?.trim() ?? null,
158
- suggested_fix: "task upgrade",
158
+ suggested_fix: "deft update",
159
159
  },
160
160
  };
161
161
  }
@@ -165,7 +165,7 @@ export function checkManifestAgreement(projectRoot, installRoot, seams = {}) {
165
165
  return {
166
166
  name: "manifest-agreement",
167
167
  status: "pass",
168
- detail: `YAML manifest at ${manifestPath} present; bare .deft-version absent (derived value: '${derived}' from manifest tag). Run \`task upgrade\` to regenerate the derivative.`,
168
+ detail: `YAML manifest at ${manifestPath} present; bare .deft-version absent (derived value: '${derived}' from manifest tag). Run \`deft update\` to regenerate the derivative.`,
169
169
  data: {
170
170
  manifest_path: manifestPath,
171
171
  manifest,
@@ -205,14 +205,14 @@ export function checkManifestAgreement(projectRoot, installRoot, seams = {}) {
205
205
  return {
206
206
  name: "manifest-agreement",
207
207
  status: "fail",
208
- detail: `Drift detected: YAML manifest tag='${derived}' does NOT agree with bare .deft-version='${bareValue}'. Per #1046 PR-B AC-4 the YAML manifest is the canonical source -- run \`task upgrade\` to regenerate the bare derivative from the manifest, OR manually update ${manifestPath} if the bare value is correct. See UPGRADING.md for the canonical drift-repair walkthrough.`,
208
+ detail: `Drift detected: YAML manifest tag='${derived}' does NOT agree with bare .deft-version='${bareValue}'. Per #1046 PR-B AC-4 the YAML manifest is the canonical source -- run \`deft update\` to regenerate the bare derivative from the manifest, OR manually update ${manifestPath} if the bare value is correct. See UPGRADING.md for the canonical drift-repair walkthrough.`,
209
209
  data: {
210
210
  manifest_path: manifestPath,
211
211
  bare_path: barePath,
212
212
  derived_version: derived,
213
213
  bare_value: bareValue,
214
214
  authoritative: "manifest",
215
- suggested_fix: "task upgrade",
215
+ suggested_fix: "deft update",
216
216
  },
217
217
  };
218
218
  }
@@ -6,7 +6,11 @@ export declare const DEPRECATED_REDIRECT_SENTINEL = "<!-- deft:deprecated-redire
6
6
  export declare const DEPRECATED_SKILL_REDIRECT_SENTINEL = "<!-- deft:deprecated-skill-redirect -->";
7
7
  export declare const REDIRECT_STUB_HEADER_LINES = 8;
8
8
  export declare const TASKFILE_INCLUDE_SNIPPET = "version: '3'\n\nincludes:\n deft:\n taskfile: ./.deft/core/Taskfile.yml\n optional: true\n";
9
- export declare const DOCTOR_ALLOWED_FLAGS: readonly ["--session", "--fix", "--repair", "--repair-taskfile", "--json", "--quiet", "--full", "--project-root", "-h", "--help"];
9
+ export declare const DOCTOR_ALLOWED_FLAGS: readonly ["--session", "--fix", "--repair", "--repair-taskfile", "--json", "--quiet", "--full", "--network", "--project-root", "-h", "--help"];
10
+ /** npm consumer deposit after #2022 Phase 3 -- Python scripts/ tree is intentionally absent. */
11
+ export declare const NPM_PACKAGE_NAME = "@deftai/directive";
12
+ export declare const NETWORK_DISCLOSURE_LINE: string;
13
+ export declare const PAYLOAD_STALENESS_OFFLINE_SKIP_MESSAGE: string;
10
14
  export declare const EXPECTED_FRAMEWORK_DIRS: readonly ["tasks", "scripts", "vbrief"];
11
15
  /** npm consumer deposit after #2022 Phase 3 -- Python scripts/ tree is intentionally absent. */
12
16
  export declare const CONSUMER_FRAMEWORK_DIRS: readonly ["tasks", "vbrief"];
@@ -14,7 +18,8 @@ export declare const DEFT_REPO_POSITIVE_MARKERS: readonly ["content/templates/ag
14
18
  export declare const EXPECTED_CONTENT_DIRS: readonly ["languages", "strategies", "skills", "templates"];
15
19
  /** Post-freeze canonical upgrade path (#1997 / #2003 / #1912). */
16
20
  export declare const CANONICAL_UPGRADE_COMMAND = "npm i -g @deftai/directive@latest";
17
- export declare const NPM_PACKAGE_NAME = "@deftai/directive";
21
+ /** Vendored npm-managed deposit: global bump plus in-place `.deft/core/` refresh (#2115). */
22
+ export declare const VENDORED_NPM_DEPOSIT_UPGRADE_COMMAND = "npm i -g @deftai/directive@latest && deft update";
18
23
  export declare const CLEAN_WINDOW_HOURS = 24;
19
24
  export declare const DIRTY_WINDOW_HOURS = 4;
20
25
  export declare const ENV_STATE_PATH = "DEFT_DOCTOR_STATE_PATH";
@@ -20,10 +20,25 @@ export const DOCTOR_ALLOWED_FLAGS = [
20
20
  "--json",
21
21
  "--quiet",
22
22
  "--full",
23
+ "--network",
23
24
  "--project-root",
24
25
  "-h",
25
26
  "--help",
26
27
  ];
28
+ /** npm consumer deposit after #2022 Phase 3 -- Python scripts/ tree is intentionally absent. */
29
+ export const NPM_PACKAGE_NAME = "@deftai/directive";
30
+ // #2182: payload-staleness is the only doctor check that can reach a network
31
+ // endpoint (git ls-remote against the framework's git remote, falling back to
32
+ // `npm view` against the npm registry). It is OFF by default (offline tier)
33
+ // and requires the explicit `--network` flag; this line discloses exactly
34
+ // which tool + registry class it may contact BEFORE the check runs, and the
35
+ // skip line tells an offline run how to opt in.
36
+ export const NETWORK_DISCLOSURE_LINE = "[deft doctor] --network: this run may contact your git remote (framework " +
37
+ "repo host) and, as a fallback, the npm registry (registry.npmjs.org) to " +
38
+ `look up the latest ${NPM_PACKAGE_NAME} version.`;
39
+ export const PAYLOAD_STALENESS_OFFLINE_SKIP_MESSAGE = "skip -- offline tier (default). Run `deft doctor --network` to check " +
40
+ "framework currency against your git remote and the npm registry " +
41
+ "(discloses tool + registry before contacting either).";
27
42
  // Engine / lifecycle dirs that stay at the framework root (NOT relocated by
28
43
  // #1875). Shippable-content dirs moved under content/ -- see EXPECTED_CONTENT_DIRS.
29
44
  export const EXPECTED_FRAMEWORK_DIRS = ["tasks", "scripts", "vbrief"];
@@ -45,7 +60,8 @@ export const DEFT_REPO_POSITIVE_MARKERS = [
45
60
  export const EXPECTED_CONTENT_DIRS = ["languages", "strategies", "skills", "templates"];
46
61
  /** Post-freeze canonical upgrade path (#1997 / #2003 / #1912). */
47
62
  export const CANONICAL_UPGRADE_COMMAND = "npm i -g @deftai/directive@latest";
48
- export const NPM_PACKAGE_NAME = "@deftai/directive";
63
+ /** Vendored npm-managed deposit: global bump plus in-place `.deft/core/` refresh (#2115). */
64
+ export const VENDORED_NPM_DEPOSIT_UPGRADE_COMMAND = `${CANONICAL_UPGRADE_COMMAND} && deft update`;
49
65
  export const CLEAN_WINDOW_HOURS = 24;
50
66
  export const DIRTY_WINDOW_HOURS = 4;
51
67
  export const ENV_STATE_PATH = "DEFT_DOCTOR_STATE_PATH";