@deftai/directive-core 0.73.1 → 0.75.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 (71) hide show
  1. package/dist/agents-md-budget/evaluate.d.ts +61 -0
  2. package/dist/agents-md-budget/evaluate.js +321 -19
  3. package/dist/agents-md-budget/index.d.ts +1 -0
  4. package/dist/agents-md-budget/index.js +1 -0
  5. package/dist/agents-md-budget/skill-frontmatter.d.ts +33 -0
  6. package/dist/agents-md-budget/skill-frontmatter.js +115 -0
  7. package/dist/check/orchestrator.d.ts +3 -0
  8. package/dist/check/orchestrator.js +2 -1
  9. package/dist/codebase/map.js +2 -0
  10. package/dist/content-contracts/skills/helpers.d.ts +0 -1
  11. package/dist/content-contracts/skills/helpers.js +1 -11
  12. package/dist/content-contracts/skills/skill-frontmatter.js +8 -1
  13. package/dist/content-contracts/standards/_helpers.js +4 -2
  14. package/dist/content-contracts/standards/_taskfile-helpers.d.ts +2 -0
  15. package/dist/content-contracts/standards/_taskfile-helpers.js +11 -5
  16. package/dist/doctor/checks.d.ts +9 -0
  17. package/dist/doctor/checks.js +67 -0
  18. package/dist/doctor/doctor-state.js +2 -1
  19. package/dist/doctor/main.js +2 -1
  20. package/dist/doctor/paths.js +2 -1
  21. package/dist/eval-health-relocation/evaluate.d.ts +71 -0
  22. package/dist/eval-health-relocation/evaluate.js +252 -0
  23. package/dist/eval-health-relocation/index.d.ts +2 -0
  24. package/dist/eval-health-relocation/index.js +2 -0
  25. package/dist/fs/projection-containment.d.ts +35 -0
  26. package/dist/fs/projection-containment.js +118 -0
  27. package/dist/init-deposit/scaffold.d.ts +21 -0
  28. package/dist/init-deposit/scaffold.js +86 -9
  29. package/dist/intake/issue-emit.js +2 -2
  30. package/dist/policy/agents-md-budget.d.ts +23 -0
  31. package/dist/policy/agents-md-budget.js +75 -4
  32. package/dist/pr-merge-readiness/constants.d.ts +4 -0
  33. package/dist/pr-merge-readiness/constants.js +4 -0
  34. package/dist/pr-merge-readiness/evaluate.js +3 -0
  35. package/dist/pr-merge-readiness/mergeability.d.ts +8 -6
  36. package/dist/pr-merge-readiness/mergeability.js +15 -10
  37. package/dist/pr-merge-readiness/output.js +12 -6
  38. package/dist/pr-merge-readiness/parse.d.ts +1 -0
  39. package/dist/pr-merge-readiness/parse.js +9 -1
  40. package/dist/pr-merge-readiness/types.d.ts +2 -0
  41. package/dist/preflight-cache/evaluate.d.ts +2 -0
  42. package/dist/preflight-cache/evaluate.js +14 -3
  43. package/dist/preflight-cache/index.d.ts +1 -1
  44. package/dist/preflight-cache/index.js +1 -1
  45. package/dist/release/constants.d.ts +2 -0
  46. package/dist/release/constants.js +2 -0
  47. package/dist/release/preflight.d.ts +2 -0
  48. package/dist/release/preflight.js +14 -1
  49. package/dist/release/spawn.js +5 -3
  50. package/dist/scope/decompose.js +4 -3
  51. package/dist/scope/index.d.ts +1 -0
  52. package/dist/scope/index.js +1 -0
  53. package/dist/scope/main.js +24 -1
  54. package/dist/scope/open-umbrella-warning.d.ts +12 -0
  55. package/dist/scope/open-umbrella-warning.js +403 -0
  56. package/dist/scope/vbrief-ref.js +3 -3
  57. package/dist/swarm/complete-cohort.js +6 -6
  58. package/dist/swarm/launch.js +2 -2
  59. package/dist/triage/bootstrap/gitignore.d.ts +1 -1
  60. package/dist/triage/bootstrap/gitignore.js +69 -58
  61. package/dist/triage/queue/cache.d.ts +14 -0
  62. package/dist/triage/queue/cache.js +61 -1
  63. package/dist/validate-content/validate-links.js +2 -2
  64. package/dist/value/readback.d.ts +1 -0
  65. package/dist/value/readback.js +5 -1
  66. package/dist/vbrief-reconcile/umbrellas.js +12 -11
  67. package/dist/verify-env/toolchain-check.js +20 -5
  68. package/dist/verify-env/verify-hooks-installed.d.ts +2 -0
  69. package/dist/verify-env/verify-hooks-installed.js +17 -15
  70. package/dist/xbrief-migrate/migrate-project.js +9 -0
  71. package/package.json +7 -3
@@ -1,9 +1,51 @@
1
+ import { type AgentsMdBudget } from "../policy/agents-md-budget.js";
2
+ import { type SkillFrontmatterMeasure } from "./skill-frontmatter.js";
1
3
  export type OutputStream = "stdout" | "stderr" | "none";
4
+ /**
5
+ * Layered absolute north-star for the always-on managed surface (#2372 / #2450 / #2452).
6
+ *
7
+ * `ABSOLUTE_MANAGED_MAX_BYTES` is the relocation north-star (<=8192 B / ~2k tok).
8
+ * When `plan.policy.agentsMdBudget.absoluteMaxBytes` is set, growth past that seeded
9
+ * ratchet fails closed; distance-to-north-star is always reported. Optional release-gate
10
+ * north-star enforcement: DEFT_AGENTS_MD_BUDGET_ENFORCE_NORTH_STAR=1 (waiver:
11
+ * DEFT_ALLOW_ABSOLUTE_BUDGET_WAIVER=1).
12
+ *
13
+ * DD-3 (harness-injected skill frontmatter) is measured and itemized (#2463).
14
+ * Managed `absoluteMaxBytes` remains the fail-closed ratchet for the rendered
15
+ * managed section; skill-frontmatter caps are advisory unless
16
+ * `skillFrontmatterMaxBytes` is set (or enforced via env).
17
+ */
18
+ export declare const ABSOLUTE_MANAGED_MAX_BYTES = 8192;
19
+ export declare const ABSOLUTE_MANAGED_MAX_TOKENS = 2000;
20
+ /** Bootstrap host hooks are 0 B until #2438 ships. */
21
+ export declare const BOOTSTRAP_HOOK_BYTES = 0;
22
+ /** Rough UTF-8 bytes-per-token estimate for advisory reporting (~8192 B ≈ ~2048 tok). */
23
+ export declare const ABSOLUTE_BYTES_PER_TOKEN_ESTIMATE = 4;
24
+ /** Byte + estimated-token measure of the managed section body. */
25
+ export interface ManagedSectionMeasure {
26
+ readonly bytes: number;
27
+ readonly estimatedTokens: number;
28
+ }
29
+ /** Itemized always-on bootstrap surface (managed + DD-3 + hooks). */
30
+ export interface BootstrapMeasure {
31
+ readonly managed: ManagedSectionMeasure;
32
+ readonly skillFrontmatter: SkillFrontmatterMeasure;
33
+ readonly bootstrapHookBytes: number;
34
+ readonly totalBytes: number;
35
+ readonly totalEstimatedTokens: number;
36
+ }
2
37
  /** Result of verify:agents-md-budget evaluation; three-state exit contract. */
3
38
  export interface EvaluateResult {
4
39
  readonly code: 0 | 1 | 2;
5
40
  readonly message: string;
6
41
  readonly stream: OutputStream;
42
+ /** North-star distance note (#2452); may accompany success or failure paths. */
43
+ readonly northStarMessage?: string;
44
+ readonly northStarStream?: OutputStream;
45
+ /** @deprecated Use northStarMessage — retained for one release of CLI compat. */
46
+ readonly advisoryMessage?: string;
47
+ /** @deprecated Use northStarStream — retained for one release of CLI compat. */
48
+ readonly advisoryStream?: OutputStream;
7
49
  }
8
50
  export interface EvaluateOptions {
9
51
  readonly quiet?: boolean;
@@ -26,6 +68,25 @@ export declare function countRegions(text: string): {
26
68
  } | {
27
69
  error: string;
28
70
  };
71
+ /**
72
+ * Extract the managed-section span (open marker through close marker, inclusive).
73
+ *
74
+ * Returns an empty section when no markers are present — consistent with
75
+ * `countRegions` treating markerless files as entirely unmanaged.
76
+ */
77
+ export declare function extractManagedSection(text: string): {
78
+ section: string;
79
+ } | {
80
+ error: string;
81
+ };
82
+ /** Measure UTF-8 byte length and a rough token estimate for the managed section. */
83
+ export declare function measureManagedSection(text: string): ManagedSectionMeasure | {
84
+ error: string;
85
+ };
86
+ /** Measure managed + DD-3 skill frontmatter + bootstrap hooks. */
87
+ export declare function measureBootstrapSurface(projectRoot: string, managedText: string, budget: AgentsMdBudget | null): BootstrapMeasure | {
88
+ error: string;
89
+ };
29
90
  /**
30
91
  * Pure evaluator for the AGENTS.md line-budget ratchet gate (#645).
31
92
  *
@@ -1,7 +1,28 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join, resolve } from "node:path";
3
3
  import { AGENTS_MANAGED_CLOSE } from "../platform/constants.js";
4
- import { resolveAgentsMdBudget } from "../policy/agents-md-budget.js";
4
+ import { resolveAgentsMdBudget, } from "../policy/agents-md-budget.js";
5
+ import { measureSkillFrontmatter } from "./skill-frontmatter.js";
6
+ /**
7
+ * Layered absolute north-star for the always-on managed surface (#2372 / #2450 / #2452).
8
+ *
9
+ * `ABSOLUTE_MANAGED_MAX_BYTES` is the relocation north-star (<=8192 B / ~2k tok).
10
+ * When `plan.policy.agentsMdBudget.absoluteMaxBytes` is set, growth past that seeded
11
+ * ratchet fails closed; distance-to-north-star is always reported. Optional release-gate
12
+ * north-star enforcement: DEFT_AGENTS_MD_BUDGET_ENFORCE_NORTH_STAR=1 (waiver:
13
+ * DEFT_ALLOW_ABSOLUTE_BUDGET_WAIVER=1).
14
+ *
15
+ * DD-3 (harness-injected skill frontmatter) is measured and itemized (#2463).
16
+ * Managed `absoluteMaxBytes` remains the fail-closed ratchet for the rendered
17
+ * managed section; skill-frontmatter caps are advisory unless
18
+ * `skillFrontmatterMaxBytes` is set (or enforced via env).
19
+ */
20
+ export const ABSOLUTE_MANAGED_MAX_BYTES = 8192;
21
+ export const ABSOLUTE_MANAGED_MAX_TOKENS = 2000;
22
+ /** Bootstrap host hooks are 0 B until #2438 ships. */
23
+ export const BOOTSTRAP_HOOK_BYTES = 0;
24
+ /** Rough UTF-8 bytes-per-token estimate for advisory reporting (~8192 B ≈ ~2048 tok). */
25
+ export const ABSOLUTE_BYTES_PER_TOKEN_ESTIMATE = 4;
5
26
  const OPEN_MARKER_PREFIX = "<!-- deft:managed-section";
6
27
  /**
7
28
  * Split AGENTS.md into managed / unmanaged line counts.
@@ -42,6 +63,238 @@ export function countRegions(text) {
42
63
  const managed = closeLine - openLine + 1;
43
64
  return { counts: { total, managed, unmanaged: total - managed } };
44
65
  }
66
+ /**
67
+ * Extract the managed-section span (open marker through close marker, inclusive).
68
+ *
69
+ * Returns an empty section when no markers are present — consistent with
70
+ * `countRegions` treating markerless files as entirely unmanaged.
71
+ */
72
+ export function extractManagedSection(text) {
73
+ const normalized = text.replace(/\r\n/g, "\n");
74
+ const lines = normalized.split("\n");
75
+ if (lines.length > 0 && lines[lines.length - 1] === "") {
76
+ lines.pop();
77
+ }
78
+ const openLine = lines.findIndex((l) => l.startsWith(OPEN_MARKER_PREFIX));
79
+ const closeLine = lines.findIndex((l) => l.trim().startsWith(AGENTS_MANAGED_CLOSE));
80
+ const openCount = lines.filter((l) => l.startsWith(OPEN_MARKER_PREFIX)).length;
81
+ const closeCount = lines.filter((l) => l.trim().startsWith(AGENTS_MANAGED_CLOSE)).length;
82
+ if (openLine === -1 && closeLine === -1) {
83
+ return { section: "" };
84
+ }
85
+ if (openLine === -1 ||
86
+ closeLine === -1 ||
87
+ closeLine < openLine ||
88
+ openCount > 1 ||
89
+ closeCount > 1) {
90
+ return {
91
+ error: "AGENTS.md managed-section markers are malformed " +
92
+ `(open@${openLine === -1 ? "none" : openLine + 1}×${openCount}, ` +
93
+ `close@${closeLine === -1 ? "none" : closeLine + 1}×${closeCount}); ` +
94
+ "expected a single <!-- deft:managed-section ... --> ... " +
95
+ "<!-- /deft:managed-section --> pair.",
96
+ };
97
+ }
98
+ return { section: lines.slice(openLine, closeLine + 1).join("\n") };
99
+ }
100
+ /** Measure UTF-8 byte length and a rough token estimate for the managed section. */
101
+ export function measureManagedSection(text) {
102
+ const extracted = extractManagedSection(text);
103
+ if ("error" in extracted) {
104
+ return extracted;
105
+ }
106
+ const bytes = Buffer.byteLength(extracted.section, "utf8");
107
+ return {
108
+ bytes,
109
+ estimatedTokens: Math.ceil(bytes / ABSOLUTE_BYTES_PER_TOKEN_ESTIMATE),
110
+ };
111
+ }
112
+ function resolveHarnessProfile(budget, projectRoot) {
113
+ const env = process.env.DEFT_AGENTS_MD_BUDGET_HARNESS_PROFILE?.trim();
114
+ if (env === "cursor" || env === "none") {
115
+ return env;
116
+ }
117
+ if (budget?.harnessProfile !== undefined) {
118
+ return budget.harnessProfile;
119
+ }
120
+ const skillsRoot = join(projectRoot, "content", "skills");
121
+ if (existsSync(skillsRoot)) {
122
+ return "cursor";
123
+ }
124
+ return "none";
125
+ }
126
+ function resolveSkillFrontmatterTier(budget) {
127
+ const env = process.env.DEFT_AGENTS_MD_BUDGET_SKILL_TIER?.trim();
128
+ if (env === "daily-core" || env === "all" || env === "none") {
129
+ return env;
130
+ }
131
+ return budget?.skillFrontmatterTier ?? "all";
132
+ }
133
+ /** Measure managed + DD-3 skill frontmatter + bootstrap hooks. */
134
+ export function measureBootstrapSurface(projectRoot, managedText, budget) {
135
+ const managedResult = measureManagedSection(managedText);
136
+ if ("error" in managedResult) {
137
+ return managedResult;
138
+ }
139
+ const harnessProfile = resolveHarnessProfile(budget, projectRoot);
140
+ const tier = resolveSkillFrontmatterTier(budget);
141
+ const skillFrontmatter = measureSkillFrontmatter(projectRoot, {
142
+ harnessProfile,
143
+ tier,
144
+ bytesPerToken: ABSOLUTE_BYTES_PER_TOKEN_ESTIMATE,
145
+ });
146
+ const totalBytes = managedResult.bytes + skillFrontmatter.bytes + BOOTSTRAP_HOOK_BYTES;
147
+ return {
148
+ managed: managedResult,
149
+ skillFrontmatter,
150
+ bootstrapHookBytes: BOOTSTRAP_HOOK_BYTES,
151
+ totalBytes,
152
+ totalEstimatedTokens: Math.ceil(totalBytes / ABSOLUTE_BYTES_PER_TOKEN_ESTIMATE),
153
+ };
154
+ }
155
+ function formatNorthStarOverageBytes(overBytes, overTokens) {
156
+ const parts = [];
157
+ if (overBytes > 0) {
158
+ parts.push(`${overBytes} bytes over`);
159
+ }
160
+ if (overTokens > 0) {
161
+ parts.push(`~${overTokens} tok over`);
162
+ }
163
+ return parts.join(", ");
164
+ }
165
+ function formatNorthStarOverage(measure) {
166
+ return formatNorthStarOverageBytes(measure.bytes - ABSOLUTE_MANAGED_MAX_BYTES, measure.estimatedTokens - ABSOLUTE_MANAGED_MAX_TOKENS);
167
+ }
168
+ function formatCombinedNorthStarOverage(bootstrap) {
169
+ return formatNorthStarOverageBytes(bootstrap.totalBytes - ABSOLUTE_MANAGED_MAX_BYTES, bootstrap.totalEstimatedTokens - ABSOLUTE_MANAGED_MAX_TOKENS);
170
+ }
171
+ function formatNorthStarDistance(measure) {
172
+ const overBytes = measure.bytes - ABSOLUTE_MANAGED_MAX_BYTES;
173
+ const overTokens = measure.estimatedTokens - ABSOLUTE_MANAGED_MAX_TOKENS;
174
+ if (overBytes <= 0 && overTokens <= 0) {
175
+ return (`north-star: managed ${measure.bytes} bytes (~${measure.estimatedTokens} tok) within ` +
176
+ `≤${ABSOLUTE_MANAGED_MAX_BYTES} B / ~${ABSOLUTE_MANAGED_MAX_TOKENS} tok target.`);
177
+ }
178
+ return (`north-star: managed ≤${ABSOLUTE_MANAGED_MAX_BYTES} B / ~${ABSOLUTE_MANAGED_MAX_TOKENS} tok ` +
179
+ `(current ${measure.bytes} bytes / ~${measure.estimatedTokens} tok — ` +
180
+ `${formatNorthStarOverage(measure)}).`);
181
+ }
182
+ function formatCombinedNorthStarDistance(bootstrap) {
183
+ const overBytes = bootstrap.totalBytes - ABSOLUTE_MANAGED_MAX_BYTES;
184
+ const overTokens = bootstrap.totalEstimatedTokens - ABSOLUTE_MANAGED_MAX_TOKENS;
185
+ if (overBytes <= 0 && overTokens <= 0) {
186
+ return (`north-star: combined always-on ${bootstrap.totalBytes} bytes ` +
187
+ `(~${bootstrap.totalEstimatedTokens} tok) within ` +
188
+ `≤${ABSOLUTE_MANAGED_MAX_BYTES} B / ~${ABSOLUTE_MANAGED_MAX_TOKENS} tok target.`);
189
+ }
190
+ return (`north-star: combined always-on ≤${ABSOLUTE_MANAGED_MAX_BYTES} B / ~${ABSOLUTE_MANAGED_MAX_TOKENS} tok ` +
191
+ `(current ${bootstrap.totalBytes} bytes / ~${bootstrap.totalEstimatedTokens} tok — ` +
192
+ `${formatCombinedNorthStarOverage(bootstrap)}).`);
193
+ }
194
+ function formatBootstrapItemization(bootstrap) {
195
+ const { managed, skillFrontmatter, bootstrapHookBytes } = bootstrap;
196
+ const tierLabel = skillFrontmatter.harnessProfile === "none"
197
+ ? "none"
198
+ : `${skillFrontmatter.harnessProfile}/${skillFrontmatter.tier}`;
199
+ const skillPart = skillFrontmatter.bytes > 0
200
+ ? `skill-frontmatter ${skillFrontmatter.bytes} B (${tierLabel}, ${skillFrontmatter.skillCount} skills)`
201
+ : `skill-frontmatter 0 B (${tierLabel})`;
202
+ return (`bootstrap: managed ${managed.bytes} B, ${skillPart}, hooks ${bootstrapHookBytes} B; ` +
203
+ `combined ${bootstrap.totalBytes} B (~${bootstrap.totalEstimatedTokens} tok)`);
204
+ }
205
+ function formatAbsoluteAdvisory(bootstrap) {
206
+ const measure = bootstrap.managed;
207
+ return (`⚠ verify:agents-md-budget: always-on bootstrap advisory — ` +
208
+ `${formatBootstrapItemization(bootstrap)}.\n` +
209
+ ` Managed section alone: ${measure.bytes} bytes (~${measure.estimatedTokens} tok) exceeds the north-star ` +
210
+ `of ${ABSOLUTE_MANAGED_MAX_BYTES} bytes / ~${ABSOLUTE_MANAGED_MAX_TOKENS} tok ` +
211
+ `(OVER: ${formatNorthStarOverage(measure)}).\n` +
212
+ ` ${formatCombinedNorthStarDistance(bootstrap)}\n` +
213
+ " Advisory only — set plan.policy.agentsMdBudget.absoluteMaxBytes to enable " +
214
+ "fail-closed managed growth ratchet (#2452).\n" +
215
+ " Optional DD-3 ratchet: plan.policy.agentsMdBudget.skillFrontmatterMaxBytes (#2463).\n" +
216
+ " Tier daily-core skills via OpenPackage or plan.policy.agentsMdBudget.skillFrontmatterTier.\n" +
217
+ " Remediation: UPGRADING.md § Always-on bootstrap budget (DD-3).\n" +
218
+ " The relative line ratchet (#645) remains fail-closed.");
219
+ }
220
+ function formatAbsoluteRefusal(bootstrap, absoluteMaxBytes, projectRoot) {
221
+ const measure = bootstrap.managed;
222
+ const overBytes = measure.bytes - absoluteMaxBytes;
223
+ return (`❌ verify:agents-md-budget: managed section grew past its absolute byte ratchet ` +
224
+ `(project_root=${projectRoot}).\n` +
225
+ ` managed section: ${measure.bytes}/${absoluteMaxBytes} bytes (OVER by ${overBytes})\n` +
226
+ ` ${formatBootstrapItemization(bootstrap)}\n` +
227
+ ` ${formatNorthStarDistance(measure)}\n` +
228
+ ` ${formatCombinedNorthStarDistance(bootstrap)}\n` +
229
+ " AGENTS.md is a map, not a manual (#1882): push detail into a\n" +
230
+ " reference doc (main.md / a pack / docs/) and leave a pointer,\n" +
231
+ " rather than expanding AGENTS.md. See REFERENCES.md.\n" +
232
+ " If the growth is deliberate, raise absoluteMaxBytes in\n" +
233
+ " plan.policy.agentsMdBudget in PROJECT-DEFINITION (a reviewed diff). (#2452)");
234
+ }
235
+ function formatSkillFrontmatterRefusal(bootstrap, skillFrontmatterMaxBytes, projectRoot) {
236
+ const { skillFrontmatter } = bootstrap;
237
+ const overBytes = skillFrontmatter.bytes - skillFrontmatterMaxBytes;
238
+ return (`❌ verify:agents-md-budget: skill frontmatter grew past its DD-3 byte ratchet ` +
239
+ `(project_root=${projectRoot}).\n` +
240
+ ` skill-frontmatter: ${skillFrontmatter.bytes}/${skillFrontmatterMaxBytes} bytes ` +
241
+ `(OVER by ${overBytes}; tier ${skillFrontmatter.tier}, ${skillFrontmatter.skillCount} skills)\n` +
242
+ ` ${formatBootstrapItemization(bootstrap)}\n` +
243
+ ` ${formatCombinedNorthStarDistance(bootstrap)}\n` +
244
+ " Tier deferred skills via OpenPackage (daily-core vs advanced) or shorten\n" +
245
+ " SKILL.md descriptions. See UPGRADING.md § Always-on bootstrap budget (DD-3).\n" +
246
+ " If the growth is deliberate, raise skillFrontmatterMaxBytes in\n" +
247
+ " plan.policy.agentsMdBudget in PROJECT-DEFINITION (a reviewed diff). (#2463)");
248
+ }
249
+ function formatNorthStarRefusal(bootstrap, projectRoot) {
250
+ const measure = bootstrap.managed;
251
+ const overBytes = bootstrap.totalBytes - ABSOLUTE_MANAGED_MAX_BYTES;
252
+ return (`❌ verify:agents-md-budget: combined always-on surface exceeds the north-star ceiling ` +
253
+ `(project_root=${projectRoot}, release-gate mode).\n` +
254
+ ` ${formatBootstrapItemization(bootstrap)}\n` +
255
+ ` managed section: ${measure.bytes}/${ABSOLUTE_MANAGED_MAX_BYTES} bytes\n` +
256
+ ` combined OVER by ${overBytes} bytes vs north-star\n` +
257
+ " Thin the managed section and/or tier DD-3 skills toward the <=8192 B target,\n" +
258
+ " or set DEFT_ALLOW_ABSOLUTE_BUDGET_WAIVER=1 for a time-boxed operator waiver (#2452).");
259
+ }
260
+ function enforceNorthStarEnabled() {
261
+ return process.env.DEFT_AGENTS_MD_BUDGET_ENFORCE_NORTH_STAR === "1";
262
+ }
263
+ function northStarWaiverActive() {
264
+ return process.env.DEFT_ALLOW_ABSOLUTE_BUDGET_WAIVER === "1";
265
+ }
266
+ function attachNorthStarNote(result, bootstrap, options) {
267
+ const measure = bootstrap.managed;
268
+ const overManagedBytes = measure.bytes > ABSOLUTE_MANAGED_MAX_BYTES;
269
+ const overManagedTokens = measure.estimatedTokens > ABSOLUTE_MANAGED_MAX_TOKENS;
270
+ const overCombinedBytes = bootstrap.totalBytes > ABSOLUTE_MANAGED_MAX_BYTES;
271
+ const overCombinedTokens = bootstrap.totalEstimatedTokens > ABSOLUTE_MANAGED_MAX_TOKENS;
272
+ if (options.advisoryOnly) {
273
+ if (!overManagedBytes && !overManagedTokens && !overCombinedBytes && !overCombinedTokens) {
274
+ return result;
275
+ }
276
+ return {
277
+ ...result,
278
+ northStarMessage: formatAbsoluteAdvisory(bootstrap),
279
+ northStarStream: "stderr",
280
+ advisoryMessage: formatAbsoluteAdvisory(bootstrap),
281
+ advisoryStream: "stderr",
282
+ };
283
+ }
284
+ const distance = `${formatNorthStarDistance(measure)}\n ${formatCombinedNorthStarDistance(bootstrap)}`;
285
+ return {
286
+ ...result,
287
+ northStarMessage: distance,
288
+ northStarStream: "stderr",
289
+ advisoryMessage: distance,
290
+ advisoryStream: "stderr",
291
+ };
292
+ }
293
+ function formatAbsoluteSummary(bootstrap, absoluteMaxBytes) {
294
+ const measure = bootstrap.managed;
295
+ return (`absolute managed ${measure.bytes}/${absoluteMaxBytes} bytes (~${measure.estimatedTokens} tok); ` +
296
+ `${formatBootstrapItemization(bootstrap)}`);
297
+ }
45
298
  function formatRefusal(counts, managedMax, unmanagedMax, projectRoot) {
46
299
  const over = [];
47
300
  if (counts.managed > managedMax) {
@@ -106,19 +359,35 @@ export function evaluate(projectRoot, options = {}) {
106
359
  };
107
360
  }
108
361
  const counts = regionResult.counts;
362
+ const bootstrapResult = measureBootstrapSurface(root, text, budgetResult.budget);
363
+ if ("error" in bootstrapResult) {
364
+ return {
365
+ code: 2,
366
+ message: `❌ verify:agents-md-budget: ${bootstrapResult.error}`,
367
+ stream: "stderr",
368
+ };
369
+ }
370
+ const bootstrap = bootstrapResult;
371
+ const measure = bootstrap.managed;
372
+ const absoluteMaxBytes = budgetResult.budget?.absoluteMaxBytes;
373
+ const advisoryOnly = absoluteMaxBytes === undefined;
374
+ const skillFrontmatterMaxBytes = budgetResult.budget?.skillFrontmatterMaxBytes;
109
375
  if (budgetResult.source === "unset") {
110
376
  if (quiet) {
111
- return { code: 0, message: "", stream: "none" };
377
+ return attachNorthStarNote({ code: 0, message: "", stream: "none" }, bootstrap, {
378
+ advisoryOnly,
379
+ });
112
380
  }
113
- return {
381
+ return attachNorthStarNote({
114
382
  code: 0,
115
383
  message: "⚠ verify:agents-md-budget: no plan.policy.agentsMdBudget configured " +
116
384
  `(managed=${counts.managed}, unmanaged=${counts.unmanaged} lines).\n` +
385
+ ` ${formatBootstrapItemization(bootstrap)}.\n` +
117
386
  " Seed a ratchet at current size to freeze growth (#645): set\n" +
118
387
  " plan.policy.agentsMdBudget.{managedMaxLines,unmanagedMaxLines} in " +
119
388
  "PROJECT-DEFINITION.",
120
389
  stream: "stderr",
121
- };
390
+ }, bootstrap, { advisoryOnly });
122
391
  }
123
392
  /* v8 ignore start -- defensive: source "typed" always carries a non-null budget. */
124
393
  if (budgetResult.budget === null) {
@@ -132,21 +401,54 @@ export function evaluate(projectRoot, options = {}) {
132
401
  const budget = budgetResult.budget;
133
402
  const overManaged = counts.managed > budget.managedMaxLines;
134
403
  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
- };
404
+ if (overManaged || overUnmanaged) {
405
+ return attachNorthStarNote({
406
+ code: 1,
407
+ message: formatRefusal(counts, budget.managedMaxLines, budget.unmanagedMaxLines, root),
408
+ stream: "stderr",
409
+ }, bootstrap, { advisoryOnly });
145
410
  }
146
- return {
147
- code: 1,
148
- message: formatRefusal(counts, budget.managedMaxLines, budget.unmanagedMaxLines, root),
149
- stream: "stderr",
150
- };
411
+ if (absoluteMaxBytes !== undefined && measure.bytes > absoluteMaxBytes) {
412
+ return attachNorthStarNote({
413
+ code: 1,
414
+ message: formatAbsoluteRefusal(bootstrap, absoluteMaxBytes, root),
415
+ stream: "stderr",
416
+ }, bootstrap, { advisoryOnly: false });
417
+ }
418
+ if (skillFrontmatterMaxBytes !== undefined &&
419
+ bootstrap.skillFrontmatter.bytes > skillFrontmatterMaxBytes) {
420
+ return attachNorthStarNote({
421
+ code: 1,
422
+ message: formatSkillFrontmatterRefusal(bootstrap, skillFrontmatterMaxBytes, root),
423
+ stream: "stderr",
424
+ }, bootstrap, { advisoryOnly: false });
425
+ }
426
+ const overNorthStarManaged = measure.bytes > ABSOLUTE_MANAGED_MAX_BYTES ||
427
+ measure.estimatedTokens > ABSOLUTE_MANAGED_MAX_TOKENS;
428
+ const overNorthStarCombined = bootstrap.totalBytes > ABSOLUTE_MANAGED_MAX_BYTES ||
429
+ bootstrap.totalEstimatedTokens > ABSOLUTE_MANAGED_MAX_TOKENS;
430
+ if (enforceNorthStarEnabled() &&
431
+ (overNorthStarManaged || overNorthStarCombined) &&
432
+ !northStarWaiverActive()) {
433
+ return attachNorthStarNote({
434
+ code: 1,
435
+ message: formatNorthStarRefusal(bootstrap, root),
436
+ stream: "stderr",
437
+ }, bootstrap, { advisoryOnly: false });
438
+ }
439
+ const absoluteSummary = absoluteMaxBytes !== undefined ? `; ${formatAbsoluteSummary(bootstrap, absoluteMaxBytes)}` : "";
440
+ const bootstrapSummary = absoluteMaxBytes === undefined ? `; ${formatBootstrapItemization(bootstrap)}` : "";
441
+ if (quiet) {
442
+ return attachNorthStarNote({ code: 0, message: "", stream: "none" }, bootstrap, {
443
+ advisoryOnly,
444
+ });
445
+ }
446
+ return attachNorthStarNote({
447
+ code: 0,
448
+ message: `✓ verify:agents-md-budget: managed ${counts.managed}/${budget.managedMaxLines}, ` +
449
+ `unmanaged ${counts.unmanaged}/${budget.unmanagedMaxLines} lines (within ratchet)` +
450
+ `${absoluteSummary}${bootstrapSummary}.`,
451
+ stream: "stdout",
452
+ }, bootstrap, { advisoryOnly });
151
453
  }
152
454
  //# sourceMappingURL=evaluate.js.map
@@ -1,2 +1,3 @@
1
1
  export * from "./evaluate.js";
2
+ export * from "./skill-frontmatter.js";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,3 @@
1
1
  export * from "./evaluate.js";
2
+ export * from "./skill-frontmatter.js";
2
3
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,33 @@
1
+ /** Daily-core tier from spike #2370 / OpenPackage manifest (#2462). */
2
+ export declare const DAILY_CORE_SKILL_NAMES: readonly ["deft-directive-setup", "deft-directive-sync", "deft-directive-build", "deft-directive-pre-pr", "deft-directive-review-cycle", "deft-directive-triage"];
3
+ export type SkillFrontmatterTier = "daily-core" | "all" | "none";
4
+ export type HarnessProfile = "cursor" | "none";
5
+ /** Extract the YAML `description` field from a SKILL.md frontmatter block. */
6
+ export declare function extractSkillDescription(text: string): string;
7
+ /**
8
+ * Cursor `<agent_skill>` injection shape (DD-3 minimum).
9
+ * Uses repo-relative POSIX paths for stable cross-platform byte counts.
10
+ */
11
+ export declare function formatCursorAgentSkill(relPath: string, description: string): string;
12
+ export interface SkillFrontmatterEntry {
13
+ readonly skillName: string;
14
+ readonly relPath: string;
15
+ readonly bytes: number;
16
+ }
17
+ export interface SkillFrontmatterMeasure {
18
+ readonly bytes: number;
19
+ readonly estimatedTokens: number;
20
+ readonly skillCount: number;
21
+ readonly tier: SkillFrontmatterTier;
22
+ readonly harnessProfile: HarnessProfile;
23
+ readonly entries: readonly SkillFrontmatterEntry[];
24
+ }
25
+ export interface MeasureSkillFrontmatterOptions {
26
+ readonly skillsRoot?: string;
27
+ readonly tier?: SkillFrontmatterTier;
28
+ readonly harnessProfile?: HarnessProfile;
29
+ readonly bytesPerToken?: number;
30
+ }
31
+ /** Measure harness-injected skill frontmatter bytes for the configured profile/tier. */
32
+ export declare function measureSkillFrontmatter(projectRoot: string, options?: MeasureSkillFrontmatterOptions): SkillFrontmatterMeasure;
33
+ //# sourceMappingURL=skill-frontmatter.d.ts.map
@@ -0,0 +1,115 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { join, posix } from "node:path";
3
+ /** Daily-core tier from spike #2370 / OpenPackage manifest (#2462). */
4
+ export const DAILY_CORE_SKILL_NAMES = [
5
+ "deft-directive-setup",
6
+ "deft-directive-sync",
7
+ "deft-directive-build",
8
+ "deft-directive-pre-pr",
9
+ "deft-directive-review-cycle",
10
+ "deft-directive-triage",
11
+ ];
12
+ const DAILY_CORE_SET = new Set(DAILY_CORE_SKILL_NAMES);
13
+ /** Extract the YAML `description` field from a SKILL.md frontmatter block. */
14
+ export function extractSkillDescription(text) {
15
+ if (!text.startsWith("---")) {
16
+ return "";
17
+ }
18
+ const closing = text.indexOf("\n---", 3);
19
+ if (closing === -1) {
20
+ return "";
21
+ }
22
+ const frontmatter = text.slice(3, closing).replace(/^\r?\n/, "");
23
+ const lines = frontmatter.split(/\r?\n/);
24
+ for (let i = 0; i < lines.length; i += 1) {
25
+ const line = lines[i] ?? "";
26
+ if (/^description:\s*>(-)?\s*$/.test(line)) {
27
+ const descLines = [];
28
+ for (let j = i + 1; j < lines.length; j += 1) {
29
+ const next = lines[j] ?? "";
30
+ if (!/^[ \t]/.test(next)) {
31
+ break;
32
+ }
33
+ descLines.push(next.replace(/^[ \t]+/, ""));
34
+ }
35
+ return descLines.join("\n").trim();
36
+ }
37
+ const single = line.match(/^description:\s*(.+)$/);
38
+ if (single !== null && single[1] !== undefined) {
39
+ return single[1].trim();
40
+ }
41
+ }
42
+ return "";
43
+ }
44
+ /**
45
+ * Cursor `<agent_skill>` injection shape (DD-3 minimum).
46
+ * Uses repo-relative POSIX paths for stable cross-platform byte counts.
47
+ */
48
+ export function formatCursorAgentSkill(relPath, description) {
49
+ const normalized = relPath.replace(/\\/g, "/");
50
+ return `<agent_skill fullPath="${normalized}">${description}</agent_skill>`;
51
+ }
52
+ function defaultSkillsRoot(projectRoot) {
53
+ return join(projectRoot, "content", "skills");
54
+ }
55
+ function listSkillDirs(skillsRoot) {
56
+ if (!existsSync(skillsRoot)) {
57
+ return [];
58
+ }
59
+ return readdirSync(skillsRoot, { withFileTypes: true })
60
+ .filter((entry) => entry.isDirectory())
61
+ .map((entry) => entry.name)
62
+ .sort();
63
+ }
64
+ function skillMatchesTier(skillName, tier) {
65
+ if (tier === "none") {
66
+ return false;
67
+ }
68
+ if (tier === "all") {
69
+ return true;
70
+ }
71
+ return DAILY_CORE_SET.has(skillName);
72
+ }
73
+ /** Measure harness-injected skill frontmatter bytes for the configured profile/tier. */
74
+ export function measureSkillFrontmatter(projectRoot, options = {}) {
75
+ const harnessProfile = options.harnessProfile ?? "cursor";
76
+ const tier = options.tier ?? "all";
77
+ const bytesPerToken = options.bytesPerToken ?? 4;
78
+ const skillsRoot = options.skillsRoot ?? defaultSkillsRoot(projectRoot);
79
+ if (harnessProfile === "none" || tier === "none") {
80
+ return {
81
+ bytes: 0,
82
+ estimatedTokens: 0,
83
+ skillCount: 0,
84
+ tier,
85
+ harnessProfile,
86
+ entries: [],
87
+ };
88
+ }
89
+ const entries = [];
90
+ for (const skillName of listSkillDirs(skillsRoot)) {
91
+ if (!skillMatchesTier(skillName, tier)) {
92
+ continue;
93
+ }
94
+ const skillPath = join(skillsRoot, skillName, "SKILL.md");
95
+ if (!existsSync(skillPath)) {
96
+ continue;
97
+ }
98
+ const text = readFileSync(skillPath, "utf8");
99
+ const description = extractSkillDescription(text);
100
+ const relPath = posix.join("content", "skills", skillName, "SKILL.md");
101
+ const block = formatCursorAgentSkill(relPath, description);
102
+ const bytes = Buffer.byteLength(block, "utf8");
103
+ entries.push({ skillName, relPath, bytes });
104
+ }
105
+ const bytes = entries.reduce((sum, entry) => sum + entry.bytes, 0);
106
+ return {
107
+ bytes,
108
+ estimatedTokens: Math.ceil(bytes / bytesPerToken),
109
+ skillCount: entries.length,
110
+ tier,
111
+ harnessProfile,
112
+ entries,
113
+ };
114
+ }
115
+ //# sourceMappingURL=skill-frontmatter.js.map
@@ -19,10 +19,13 @@ export interface CheckOrchestratorSeams {
19
19
  readonly spawnFn?: (cmd: string, args: string[], opts: {
20
20
  cwd: string;
21
21
  stdio: string;
22
+ env?: NodeJS.ProcessEnv;
22
23
  }) => {
23
24
  status: number | null;
24
25
  error?: Error;
25
26
  };
27
+ /** Child-process environment (default: process.env). */
28
+ readonly env?: NodeJS.ProcessEnv;
26
29
  }
27
30
  /**
28
31
  * True when `path` is the directive framework source checkout root (not a
@@ -77,6 +77,7 @@ export function dispatchTaskCheck(frameworkRoot, projectRoot, seams = {}) {
77
77
  const result = spawn(taskBin, [target, "--taskfile", taskfilePath], {
78
78
  cwd,
79
79
  stdio: "inherit",
80
+ env: seams.env,
80
81
  });
81
82
  if (result.error !== undefined) {
82
83
  process.stderr.write(`check: failed to invoke task: ${result.error.message}\n`);
@@ -88,7 +89,7 @@ function defaultSpawn(cmd, args, opts) {
88
89
  const result = spawnSync(cmd, args, {
89
90
  cwd: opts.cwd,
90
91
  stdio: opts.stdio,
91
- env: { ...process.env },
92
+ env: opts.env ?? process.env,
92
93
  });
93
94
  return { status: result.status, error: result.error };
94
95
  }
@@ -1,5 +1,6 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
3
+ import { assertProjectionContained } from "../fs/projection-containment.js";
3
4
  import { resolveProjectDefinitionPath } from "../layout/resolve.js";
4
5
  import { extractCodeStructure, loadJsonFile } from "../verify-source/code-structure-validate.js";
5
6
  import { CodeStructureConfigError, configErrorToDict, defaultCodeStructurePath, } from "./default-extractor.js";
@@ -239,6 +240,7 @@ export function writeCodebaseMap(projectRoot, options) {
239
240
  const resolvedOutput = isAbsolute(options.outputPath)
240
241
  ? options.outputPath
241
242
  : join(projectRoot, options.outputPath);
243
+ assertProjectionContained(projectRoot, resolvedOutput);
242
244
  if (!options.force && !isDeftGenerated(resolvedOutput)) {
243
245
  throw new Error(`refusing to overwrite non-generated MAP at ${resolvedOutput}; ` +
244
246
  `missing '${GENERATED_SENTINEL}' banner`);
@@ -19,6 +19,5 @@ export declare function listSkillMdFiles(): string[];
19
19
  export declare const RFC2119_LEGEND = "!=MUST, ~=SHOULD";
20
20
  export declare const PLATFORM_DETECTION_HEADING = "## Platform Detection";
21
21
  export declare const USER_MD_GATE_HEADING = "## USER.md Gate";
22
- export declare const DEPRECATED_SKILL_REDIRECT_STUBS: Set<string>;
23
22
  export declare const REQUIRED_OSES: Set<string>;
24
23
  //# sourceMappingURL=helpers.d.ts.map