@deftai/directive-core 0.108.0 → 0.109.1

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 (157) hide show
  1. package/dist/cache/operations.js +1 -1
  2. package/dist/check/gate-lists.d.ts +14 -0
  3. package/dist/check/gate-lists.js +26 -3
  4. package/dist/check/session-completed-ac.d.ts +1 -1
  5. package/dist/check/session-completed-ac.js +1 -1
  6. package/dist/consumer-check-contract/evaluate.d.ts +47 -0
  7. package/dist/consumer-check-contract/evaluate.js +181 -15
  8. package/dist/delivery-attempt/evaluate.d.ts +9 -1
  9. package/dist/delivery-attempt/evaluate.js +69 -0
  10. package/dist/delivery-attempt/index.d.ts +1 -1
  11. package/dist/delivery-attempt/index.js +1 -1
  12. package/dist/deposit/live-procedure-exclusions.d.ts +18 -0
  13. package/dist/deposit/live-procedure-exclusions.js +110 -0
  14. package/dist/deposit/live-procedure-targets.d.ts +45 -0
  15. package/dist/deposit/live-procedure-targets.js +274 -0
  16. package/dist/deposit/python-free.d.ts +6 -0
  17. package/dist/deposit/python-free.js +15 -0
  18. package/dist/deposit/rewrite-deposit-links.d.ts +42 -0
  19. package/dist/deposit/rewrite-deposit-links.js +148 -0
  20. package/dist/deposit/run-stage-content-pack.d.ts +2 -0
  21. package/dist/deposit/run-stage-content-pack.js +3 -0
  22. package/dist/deposit/stage-content-pack.d.ts +17 -0
  23. package/dist/deposit/stage-content-pack.js +91 -0
  24. package/dist/design-critique/citation-grammar.d.ts +7 -0
  25. package/dist/design-critique/citation-grammar.js +1 -1
  26. package/dist/design-critique/completed-arc-record.d.ts +8 -1
  27. package/dist/design-critique/completed-arc-record.js +98 -14
  28. package/dist/hooks/classify/host-session-identity.d.ts +25 -12
  29. package/dist/hooks/classify/host-session-identity.js +64 -49
  30. package/dist/hooks/classify/index.d.ts +2 -2
  31. package/dist/hooks/classify/index.js +2 -2
  32. package/dist/hooks/classify/paths.d.ts +2 -0
  33. package/dist/hooks/classify/paths.js +8 -4
  34. package/dist/hooks/classify/stdin.js +69 -3
  35. package/dist/hooks/dest-form.d.ts +20 -1
  36. package/dist/hooks/dest-form.js +158 -21
  37. package/dist/hooks/dispatcher.d.ts +22 -2
  38. package/dist/hooks/dispatcher.js +338 -66
  39. package/dist/hooks/fixtures/cases.d.ts +6 -0
  40. package/dist/hooks/fixtures/cases.js +53 -0
  41. package/dist/hooks/git-destructive-log.d.ts +32 -0
  42. package/dist/hooks/git-destructive-log.js +46 -0
  43. package/dist/hooks/index.d.ts +3 -0
  44. package/dist/hooks/index.js +3 -0
  45. package/dist/hooks/owner-liveness.d.ts +92 -0
  46. package/dist/hooks/owner-liveness.js +103 -0
  47. package/dist/hooks/shell-write-targets.d.ts +10 -0
  48. package/dist/hooks/shell-write-targets.js +274 -0
  49. package/dist/hooks/tools.d.ts +49 -2
  50. package/dist/hooks/tools.js +96 -1
  51. package/dist/init-deposit/agent-hooks.d.ts +10 -0
  52. package/dist/init-deposit/agent-hooks.js +39 -0
  53. package/dist/init-deposit/gitignore.d.ts +7 -0
  54. package/dist/init-deposit/gitignore.js +24 -0
  55. package/dist/init-deposit/host-tool-coverage.d.ts +53 -0
  56. package/dist/init-deposit/host-tool-coverage.js +150 -0
  57. package/dist/init-deposit/index.d.ts +1 -0
  58. package/dist/init-deposit/index.js +1 -0
  59. package/dist/init-deposit/init-deposit.js +3 -0
  60. package/dist/init-deposit/refresh.js +7 -0
  61. package/dist/init-deposit/runtime-writers.d.ts +14 -0
  62. package/dist/init-deposit/runtime-writers.js +33 -0
  63. package/dist/intake/issue-ingest.d.ts +6 -1
  64. package/dist/intake/issue-ingest.js +21 -2
  65. package/dist/intake/reconcile-issues.js +13 -13
  66. package/dist/lifecycle/brief-envelope.d.ts +25 -0
  67. package/dist/lifecycle/brief-envelope.js +42 -0
  68. package/dist/lifecycle/index.d.ts +1 -0
  69. package/dist/lifecycle/index.js +1 -0
  70. package/dist/literal-acceptance/evaluate.js +14 -5
  71. package/dist/literal-acceptance/index.d.ts +1 -1
  72. package/dist/literal-acceptance/index.js +1 -1
  73. package/dist/literal-acceptance/run.d.ts +2 -0
  74. package/dist/literal-acceptance/run.js +19 -1
  75. package/dist/orchestration/subagent-monitor.d.ts +6 -0
  76. package/dist/orchestration/subagent-monitor.js +23 -1
  77. package/dist/orphan-active/candidate-scope.d.ts +53 -0
  78. package/dist/orphan-active/candidate-scope.js +157 -0
  79. package/dist/orphan-active/evaluate.d.ts +27 -0
  80. package/dist/orphan-active/evaluate.js +63 -6
  81. package/dist/orphan-active/index.d.ts +1 -0
  82. package/dist/orphan-active/index.js +1 -0
  83. package/dist/policy/merge-approval-head.js +7 -6
  84. package/dist/pr-closing-keywords/gh.js +32 -9
  85. package/dist/pr-closing-keywords/main.d.ts +1 -0
  86. package/dist/pr-closing-keywords/main.js +68 -3
  87. package/dist/pr-closing-keywords/types.d.ts +2 -0
  88. package/dist/pr-merge-readiness/gh.js +32 -9
  89. package/dist/pr-protected-issues/gh.js +36 -9
  90. package/dist/pr-wait-mergeable/wrappers.js +3 -3
  91. package/dist/product-first-done-gate/evaluate.js +6 -2
  92. package/dist/product-first-done-gate/types.js +2 -0
  93. package/dist/release/consumer-hard-stops.d.ts +48 -0
  94. package/dist/release/consumer-hard-stops.js +140 -0
  95. package/dist/release/consumer-readiness-disclosure.d.ts +18 -0
  96. package/dist/release/consumer-readiness-disclosure.js +51 -0
  97. package/dist/release/index.d.ts +2 -0
  98. package/dist/release/index.js +2 -0
  99. package/dist/release/pipeline.js +55 -0
  100. package/dist/release/run-consumer-readiness.d.ts +15 -0
  101. package/dist/release/run-consumer-readiness.js +24 -0
  102. package/dist/release/types.d.ts +6 -0
  103. package/dist/review-monitor/github-lease.js +4 -3
  104. package/dist/run-summary/types.d.ts +2 -2
  105. package/dist/scm/build-command.d.ts +2 -2
  106. package/dist/scm/build-command.js +2 -2
  107. package/dist/scm/call-shape.d.ts +25 -0
  108. package/dist/scm/call-shape.js +59 -0
  109. package/dist/scm/call.d.ts +7 -4
  110. package/dist/scm/call.js +41 -11
  111. package/dist/scm/gh-rest.d.ts +25 -12
  112. package/dist/scm/gh-rest.js +65 -15
  113. package/dist/scm/index.d.ts +2 -0
  114. package/dist/scm/index.js +2 -0
  115. package/dist/scm/spawn-status.d.ts +33 -0
  116. package/dist/scm/spawn-status.js +53 -0
  117. package/dist/scope/acceptance-evidence.d.ts +1 -1
  118. package/dist/scope/transition.js +4 -14
  119. package/dist/session/ac-pass-banking.d.ts +2 -2
  120. package/dist/session/ac-pass-banking.js +2 -2
  121. package/dist/session/child-occupancy.d.ts +72 -0
  122. package/dist/session/child-occupancy.js +209 -0
  123. package/dist/session/host-session-owner.d.ts +93 -0
  124. package/dist/session/host-session-owner.js +148 -0
  125. package/dist/session/index.d.ts +2 -0
  126. package/dist/session/index.js +2 -0
  127. package/dist/session/occupancy.d.ts +107 -2
  128. package/dist/session/occupancy.js +272 -34
  129. package/dist/session/verify-ac-session-cache.d.ts +2 -2
  130. package/dist/session/verify-ac-session-cache.js +2 -2
  131. package/dist/swarm/complete-cohort.js +2 -0
  132. package/dist/swarm/pre-dispatch.js +2 -0
  133. package/dist/swarm/subagent-status-dir.d.ts +2 -1
  134. package/dist/swarm/subagent-status-dir.js +11 -2
  135. package/dist/swarm/worktrees.js +2 -0
  136. package/dist/triage/evaluate/worktrees.js +153 -6
  137. package/dist/umbrella-current-shape/index.d.ts +51 -3
  138. package/dist/umbrella-current-shape/index.js +106 -18
  139. package/dist/validate-content/deposit-required.d.ts +39 -0
  140. package/dist/validate-content/deposit-required.js +147 -0
  141. package/dist/validate-content/index.d.ts +1 -0
  142. package/dist/validate-content/index.js +1 -0
  143. package/dist/validate-content/validate-links.d.ts +2 -3
  144. package/dist/validate-content/validate-links.js +29 -2
  145. package/dist/vbrief-activate/activate.d.ts +7 -2
  146. package/dist/vbrief-activate/activate.js +26 -13
  147. package/dist/verify-ac/evaluate.d.ts +9 -0
  148. package/dist/verify-ac/evaluate.js +32 -9
  149. package/dist/verify-env/agent-hooks.d.ts +6 -1
  150. package/dist/verify-env/agent-hooks.js +28 -2
  151. package/dist/verify-source/deposit-closure.d.ts +23 -0
  152. package/dist/verify-source/deposit-closure.js +162 -0
  153. package/dist/verify-source/index.d.ts +2 -0
  154. package/dist/verify-source/index.js +2 -0
  155. package/dist/verify-source/semantic-single-source.d.ts +36 -0
  156. package/dist/verify-source/semantic-single-source.js +349 -0
  157. package/package.json +3 -3
@@ -0,0 +1,110 @@
1
+ /**
2
+ * C3 live-procedure exclusion declaration (#3602 / #3899).
3
+ *
4
+ * History, examples, and prohibitions are skipped by this list, not by
5
+ * matching prose patterns. A file that names a deleted Python script in
6
+ * order to forbid treating it as a live path must appear here as
7
+ * `prohibition` or C3 will mis-fire.
8
+ */
9
+ export const LIVE_PROCEDURE_EXCLUSIONS = [
10
+ {
11
+ path: "scm/github.md",
12
+ kind: "prohibition",
13
+ reason: "Names deleted Python scripts to forbid treating them as live implementation paths (#2022).",
14
+ },
15
+ {
16
+ path: "UPGRADING.md",
17
+ kind: "history",
18
+ reason: "Migration history of Python helpers on pinned pre-Python-free releases.",
19
+ },
20
+ {
21
+ path: "deployments/aws/via-elastic-beanstalk.md",
22
+ kind: "example",
23
+ reason: "Consumer AWS sample `scripts/create_admin.py` is not a deposit helper.",
24
+ },
25
+ {
26
+ path: "conventions/machine-generated-banner.md",
27
+ kind: "history",
28
+ reason: "Registry of historical Python writers that produced the banner; not a live consumer procedure.",
29
+ },
30
+ {
31
+ path: "docs/BROWNFIELD.md",
32
+ kind: "history",
33
+ reason: "Brownfield migration history of the Python migrator on pinned releases.",
34
+ },
35
+ {
36
+ path: "languages/python.md",
37
+ kind: "example",
38
+ reason: "Python language pack names consumer application files, not deposit helpers.",
39
+ },
40
+ {
41
+ path: "languages/kotlin.md",
42
+ kind: "example",
43
+ reason: "Kotlin stdlib `run` scoping function is not the deposit Python launcher.",
44
+ },
45
+ {
46
+ path: "deployments/aws/via-lambda.md",
47
+ kind: "example",
48
+ reason: "AWS Lambda sample `src/app.py` is a consumer application, not a deposit helper.",
49
+ },
50
+ {
51
+ path: "deployments/fly-io/via-dockerfile.md",
52
+ kind: "example",
53
+ reason: "Fly.io sample `app.py` / `manage.py` are consumer application files.",
54
+ },
55
+ {
56
+ path: "coding/toolchain.md",
57
+ kind: "example",
58
+ reason: "Cites framework test paths that pin the toolchain contract; not a live helper.",
59
+ },
60
+ {
61
+ path: "coding/testing.md",
62
+ kind: "example",
63
+ reason: "Documents `_test.py` naming for consumer Python tests, not a deposit helper.",
64
+ },
65
+ {
66
+ path: "contracts/deterministic-questions.md",
67
+ kind: "example",
68
+ reason: "Cites the contract's own test path; not a live consumer helper.",
69
+ },
70
+ {
71
+ path: "conventions/task-caching.md",
72
+ kind: "example",
73
+ reason: "Cites framework tests that pin task-caching; not a live helper.",
74
+ },
75
+ {
76
+ path: "events/README.md",
77
+ kind: "history",
78
+ reason: "Maintainer event-registry history naming retired Python writers and their tests.",
79
+ },
80
+ {
81
+ path: "templates/agents-entry.placeholders.md",
82
+ kind: "example",
83
+ reason: "Cites the agents-entry contract test; not a live helper.",
84
+ },
85
+ {
86
+ path: "templates/swarm-greptile-poller-prompt.md",
87
+ kind: "example",
88
+ reason: "Cites the poller-template contract test; not a live helper.",
89
+ },
90
+ {
91
+ path: "verification/plan-checking.md",
92
+ kind: "example",
93
+ reason: "Swarm-spec example names `tests/test_auth.py` as consumer test layout.",
94
+ },
95
+ {
96
+ path: "swarm/swarm.md",
97
+ kind: "example",
98
+ reason: "Swarm-spec example names `src/auth.py` / `tests/test_auth.py` as consumer files.",
99
+ },
100
+ {
101
+ path: "skills/deft-directive-setup/SKILL.md",
102
+ kind: "example",
103
+ reason: "Setup skill Python example names `src/ui.py` as a consumer application file.",
104
+ },
105
+ ];
106
+ const EXCLUSION_PATHS = new Set(LIVE_PROCEDURE_EXCLUSIONS.map((entry) => entry.path));
107
+ export function isDeclaredLiveProcedureExclusion(relativePath) {
108
+ return EXCLUSION_PATHS.has(relativePath.replace(/\\/g, "/"));
109
+ }
110
+ //# sourceMappingURL=live-procedure-exclusions.js.map
@@ -0,0 +1,45 @@
1
+ /**
2
+ * C3 live-procedure target validation (#3602 / #3899).
3
+ *
4
+ * A shipped live procedure must not name a helper the deposit does not
5
+ * contain. Python helpers are identified via python-free; markdown is
6
+ * walked with the validate-links skip set and extractLinkTargets. History,
7
+ * examples, and prohibitions are skipped by declaration.
8
+ *
9
+ * Metric: unique live-invalid helper targets (not occurrences, not matching
10
+ * lines). Prefer a zero unique-target assertion; do not freeze raw counts.
11
+ */
12
+ export interface LiveProcedureHit {
13
+ readonly file: string;
14
+ readonly line: number;
15
+ readonly target: string;
16
+ }
17
+ export interface ExtraMarkdownFile {
18
+ readonly relativePath: string;
19
+ readonly absolutePath: string;
20
+ }
21
+ export interface EvaluateLiveProcedureTargetsOptions {
22
+ readonly stagedRoot: string;
23
+ readonly extraFiles?: readonly ExtraMarkdownFile[];
24
+ }
25
+ /**
26
+ * Metric used by C3: unique live-invalid helper targets.
27
+ * Not occurrence count, not matching-line count.
28
+ */
29
+ export declare const LIVE_PROCEDURE_METRIC: "unique-targets";
30
+ export interface LiveProcedureEvaluation {
31
+ readonly metric: typeof LIVE_PROCEDURE_METRIC;
32
+ readonly hits: readonly LiveProcedureHit[];
33
+ readonly uniqueTargets: readonly string[];
34
+ }
35
+ export declare function normalizePythonHelperTarget(raw: string): string | null;
36
+ export declare function evaluateLiveProcedureTargets(options: EvaluateLiveProcedureTargetsOptions): LiveProcedureEvaluation;
37
+ export declare function extraDepositMarkdownFiles(root: string): ExtraMarkdownFile[];
38
+ export declare class LiveProcedureTargetsError extends Error {
39
+ readonly evaluation: LiveProcedureEvaluation;
40
+ constructor(evaluation: LiveProcedureEvaluation);
41
+ }
42
+ /** Fail closed when a deposit's live procedures name pruned Python helpers (#3602 C3). */
43
+ export declare function assertLiveProcedureDepositClean(depositDir: string): void;
44
+ export declare function formatLiveProcedureFailure(result: LiveProcedureEvaluation): string;
45
+ //# sourceMappingURL=live-procedure-targets.d.ts.map
@@ -0,0 +1,274 @@
1
+ /**
2
+ * C3 live-procedure target validation (#3602 / #3899).
3
+ *
4
+ * A shipped live procedure must not name a helper the deposit does not
5
+ * contain. Python helpers are identified via python-free; markdown is
6
+ * walked with the validate-links skip set and extractLinkTargets. History,
7
+ * examples, and prohibitions are skipped by declaration.
8
+ *
9
+ * Metric: unique live-invalid helper targets (not occurrences, not matching
10
+ * lines). Prefer a zero unique-target assertion; do not freeze raw counts.
11
+ */
12
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
13
+ import { join, relative, resolve, sep } from "node:path";
14
+ import { NON_PRODUCT_DIRS } from "../fs/non-product-dirs.js";
15
+ import { extractLinkTargets, shouldSkipLinkTarget } from "../validate-content/link-parser.js";
16
+ import { isDeclaredLiveProcedureExclusion } from "./live-procedure-exclusions.js";
17
+ import { isPrunedPythonArtifactPath } from "./python-free.js";
18
+ const SKIP_DIRS = new Set([...NON_PRODUCT_DIRS, ".planning", "specs"]);
19
+ /**
20
+ * Metric used by C3: unique live-invalid helper targets.
21
+ * Not occurrence count, not matching-line count.
22
+ */
23
+ export const LIVE_PROCEDURE_METRIC = "unique-targets";
24
+ function shouldSkipPath(parts) {
25
+ if (parts.some((p) => SKIP_DIRS.has(p)))
26
+ return true;
27
+ return parts.includes("history") && parts.includes("archive");
28
+ }
29
+ function toPosix(rel) {
30
+ return rel.split(sep).join("/");
31
+ }
32
+ function collectMarkdownFiles(root) {
33
+ const out = [];
34
+ const walk = (dir, parts) => {
35
+ if (shouldSkipPath(parts))
36
+ return;
37
+ let entries;
38
+ try {
39
+ entries = readdirSync(dir, { withFileTypes: true });
40
+ }
41
+ catch {
42
+ return;
43
+ }
44
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
45
+ const full = join(dir, entry.name);
46
+ const nextParts = [...parts, entry.name];
47
+ if (shouldSkipPath(nextParts))
48
+ continue;
49
+ if (entry.isDirectory()) {
50
+ walk(full, nextParts);
51
+ }
52
+ else if (entry.isFile() && entry.name.endsWith(".md")) {
53
+ out.push(full);
54
+ }
55
+ }
56
+ };
57
+ walk(root, []);
58
+ return out.sort();
59
+ }
60
+ const PATH_CHAR = /[A-Za-z0-9_.-]/;
61
+ const PY_HELPER_TOKEN = /^(?:[A-Za-z0-9_.-]+\/)+[A-Za-z0-9_.-]+\.py[c]?$/;
62
+ const RUN_SHIM_TOKEN = /^(?:\.deft\/core\/run|deft\/run|run)$/;
63
+ /** Known pruned deposit helpers named without a directory. Not consumer app.py. */
64
+ const PRUNED_BARE_HELPER_BASENAMES = new Set([
65
+ "gh_rest.py",
66
+ "ip_risk.py",
67
+ "preflight_implementation.py",
68
+ "preflight_gh.py",
69
+ "migrate_vbrief.py",
70
+ "slug_normalize.py",
71
+ "slice_record.py",
72
+ "validate_strategy_output.py",
73
+ "_precutover.py",
74
+ "swarm_mint_jwt.py",
75
+ ]);
76
+ function stripRelativePrefix(posix) {
77
+ let out = posix;
78
+ while (out.startsWith("./"))
79
+ out = out.slice(2);
80
+ while (out.startsWith("../"))
81
+ out = out.slice(3);
82
+ return out;
83
+ }
84
+ export function normalizePythonHelperTarget(raw) {
85
+ const clean = (raw.split("#")[0] ?? "").split("?")[0] ?? "";
86
+ const posix = clean.replace(/\\/g, "/");
87
+ if (posix.includes("://"))
88
+ return null;
89
+ const stripped = stripRelativePrefix(posix);
90
+ if (stripped.includes(".."))
91
+ return null;
92
+ const scriptsIdx = stripped.lastIndexOf("scripts/");
93
+ const token = scriptsIdx >= 0 ? stripped.slice(scriptsIdx) : stripped;
94
+ if (RUN_SHIM_TOKEN.test(token) || RUN_SHIM_TOKEN.test(stripped)) {
95
+ if (stripped === ".deft/core/run" || stripped.endsWith("/.deft/core/run")) {
96
+ return ".deft/core/run";
97
+ }
98
+ if (stripped === "deft/run" || stripped.endsWith("/deft/run"))
99
+ return "deft/run";
100
+ if (token === "run" || stripped === "run")
101
+ return "run";
102
+ }
103
+ if (!token.includes("/") && PRUNED_BARE_HELPER_BASENAMES.has(token)) {
104
+ return token;
105
+ }
106
+ if (!PY_HELPER_TOKEN.test(token))
107
+ return null;
108
+ return token;
109
+ }
110
+ function isPathChar(ch) {
111
+ if (ch === undefined)
112
+ return false;
113
+ return PATH_CHAR.test(ch) || ch === "/" || ch === "\\";
114
+ }
115
+ function extractBacktickPythonHelpers(line) {
116
+ const out = [];
117
+ const suffixes = [".pyc", ".py"];
118
+ for (const suffix of suffixes) {
119
+ let from = 0;
120
+ while (from < line.length) {
121
+ const idx = line.indexOf(suffix, from);
122
+ if (idx < 0)
123
+ break;
124
+ const after = line[idx + suffix.length];
125
+ if (isPathChar(after)) {
126
+ from = idx + suffix.length;
127
+ continue;
128
+ }
129
+ let start = idx;
130
+ while (start > 0 && isPathChar(line[start - 1]))
131
+ start -= 1;
132
+ const token = line.slice(start, idx + suffix.length);
133
+ from = idx + suffix.length;
134
+ const normalized = normalizePythonHelperTarget(token);
135
+ if (normalized)
136
+ out.push(normalized);
137
+ }
138
+ }
139
+ for (const spelling of [".deft/core/run", "deft/run"]) {
140
+ if (line.includes(spelling)) {
141
+ const normalized = normalizePythonHelperTarget(spelling);
142
+ if (normalized)
143
+ out.push(normalized);
144
+ }
145
+ }
146
+ let tick = 0;
147
+ while (tick < line.length) {
148
+ const open = line.indexOf("`", tick);
149
+ if (open < 0)
150
+ break;
151
+ const close = line.indexOf("`", open + 1);
152
+ if (close < 0)
153
+ break;
154
+ const inner = line.slice(open + 1, close).trim();
155
+ const parts = inner.split(/\s+/).filter((p) => p.length > 0);
156
+ const first = (parts[0] ?? "").replace(/^\.\//, "");
157
+ const arg = parts[1];
158
+ const launcherArgs = new Set([
159
+ "bootstrap",
160
+ "spec",
161
+ "init",
162
+ "project",
163
+ "upgrade",
164
+ "doctor",
165
+ "validate",
166
+ "reset",
167
+ ]);
168
+ if (first === "run" && (arg === undefined || launcherArgs.has(arg))) {
169
+ const normalized = normalizePythonHelperTarget("run");
170
+ if (normalized)
171
+ out.push(normalized);
172
+ }
173
+ tick = close + 1;
174
+ }
175
+ return out;
176
+ }
177
+ function extractLineTargets(line) {
178
+ const found = new Set();
179
+ for (const raw of extractLinkTargets(line)) {
180
+ if (raw.startsWith("http://") ||
181
+ raw.startsWith("https://") ||
182
+ raw.startsWith("mailto:") ||
183
+ raw.startsWith("#")) {
184
+ continue;
185
+ }
186
+ if (shouldSkipLinkTarget(raw))
187
+ continue;
188
+ const normalized = normalizePythonHelperTarget(raw);
189
+ if (normalized)
190
+ found.add(normalized);
191
+ }
192
+ for (const token of extractBacktickPythonHelpers(line))
193
+ found.add(token);
194
+ return [...found];
195
+ }
196
+ function scanMarkdownFile(absolutePath, relativePath) {
197
+ if (isDeclaredLiveProcedureExclusion(relativePath))
198
+ return [];
199
+ let text;
200
+ try {
201
+ text = readFileSync(absolutePath, "utf8");
202
+ }
203
+ catch {
204
+ return [];
205
+ }
206
+ const hits = [];
207
+ const lines = text.split("\n");
208
+ for (let i = 0; i < lines.length; i += 1) {
209
+ const line = lines[i] ?? "";
210
+ for (const target of extractLineTargets(line)) {
211
+ if (!isPrunedPythonArtifactPath(target))
212
+ continue;
213
+ hits.push({ file: relativePath, line: i + 1, target });
214
+ }
215
+ }
216
+ return hits;
217
+ }
218
+ export function evaluateLiveProcedureTargets(options) {
219
+ const stagedRoot = resolve(options.stagedRoot);
220
+ const hits = [];
221
+ if (existsSync(stagedRoot)) {
222
+ for (const md of collectMarkdownFiles(stagedRoot)) {
223
+ const rel = toPosix(relative(stagedRoot, md));
224
+ hits.push(...scanMarkdownFile(md, rel));
225
+ }
226
+ }
227
+ for (const extra of options.extraFiles ?? []) {
228
+ const rel = extra.relativePath.replace(/\\/g, "/");
229
+ hits.push(...scanMarkdownFile(extra.absolutePath, rel));
230
+ }
231
+ const unique = [...new Set(hits.map((h) => h.target))].sort();
232
+ return { metric: LIVE_PROCEDURE_METRIC, hits, uniqueTargets: unique };
233
+ }
234
+ export function extraDepositMarkdownFiles(root) {
235
+ const extras = [];
236
+ for (const name of ["main.md", "SKILL.md"]) {
237
+ const absolutePath = join(root, name);
238
+ if (existsSync(absolutePath)) {
239
+ extras.push({ relativePath: name, absolutePath });
240
+ }
241
+ }
242
+ return extras;
243
+ }
244
+ export class LiveProcedureTargetsError extends Error {
245
+ evaluation;
246
+ constructor(evaluation) {
247
+ super(formatLiveProcedureFailure(evaluation));
248
+ this.name = "LiveProcedureTargetsError";
249
+ this.evaluation = evaluation;
250
+ }
251
+ }
252
+ /** Fail closed when a deposit's live procedures name pruned Python helpers (#3602 C3). */
253
+ export function assertLiveProcedureDepositClean(depositDir) {
254
+ const result = evaluateLiveProcedureTargets({
255
+ stagedRoot: depositDir,
256
+ extraFiles: extraDepositMarkdownFiles(depositDir),
257
+ });
258
+ if (result.uniqueTargets.length > 0) {
259
+ throw new LiveProcedureTargetsError(result);
260
+ }
261
+ }
262
+ export function formatLiveProcedureFailure(result) {
263
+ const lines = [
264
+ `C3 live-procedure target validation failed: ${result.uniqueTargets.length} unique live-invalid helper target(s) (metric=${result.metric}).`,
265
+ ];
266
+ for (const hit of result.hits.slice(0, 40)) {
267
+ lines.push(` ${hit.file}:${hit.line} -> ${hit.target}`);
268
+ }
269
+ if (result.hits.length > 40) {
270
+ lines.push(` ... ${result.hits.length - 40} more occurrence(s)`);
271
+ }
272
+ return lines.join("\n");
273
+ }
274
+ //# sourceMappingURL=live-procedure-targets.js.map
@@ -5,6 +5,12 @@
5
5
  * Python-only artifacts that must not ship to npm consumers: the legacy
6
6
  * `scripts/` tree, `.py` files, and Python `run` shims.
7
7
  */
8
+ /** True when a named helper path is a Python artifact the deposit prunes (#3602 C3). */
9
+ export declare function isPythonHelperPath(relPath: string): boolean;
10
+ /** Path spellings of the Python `run` launcher python-free removes (#3602 C3). */
11
+ export declare function isPythonRunShimPath(relPath: string): boolean;
12
+ /** True when C3 should treat a named path as a pruned Python deposit helper. */
13
+ export declare function isPrunedPythonArtifactPath(relPath: string): boolean;
8
14
  export interface PythonArtifact {
9
15
  readonly path: string;
10
16
  readonly kind: "py-file" | "scripts-tree" | "run-shim";
@@ -11,6 +11,21 @@ import { join, relative } from "node:path";
11
11
  import { containedRemove } from "../fs/contained-write.js";
12
12
  const PY_SUFFIX = ".py";
13
13
  const PYC_SUFFIX = ".pyc";
14
+ /** True when a named helper path is a Python artifact the deposit prunes (#3602 C3). */
15
+ export function isPythonHelperPath(relPath) {
16
+ const posix = relPath.replace(/\\/g, "/");
17
+ const base = posix.split("/").pop() ?? posix;
18
+ return base.endsWith(PY_SUFFIX) || base.endsWith(PYC_SUFFIX);
19
+ }
20
+ /** Path spellings of the Python `run` launcher python-free removes (#3602 C3). */
21
+ export function isPythonRunShimPath(relPath) {
22
+ const posix = relPath.replace(/\\/g, "/").replace(/^\.\//, "");
23
+ return posix === "run" || posix === ".deft/core/run" || posix === "deft/run";
24
+ }
25
+ /** True when C3 should treat a named path as a pruned Python deposit helper. */
26
+ export function isPrunedPythonArtifactPath(relPath) {
27
+ return isPythonHelperPath(relPath) || isPythonRunShimPath(relPath);
28
+ }
14
29
  function isPythonRunShim(_path, head) {
15
30
  if (!head.startsWith("#!"))
16
31
  return false;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Flatten-aware rewrite of relative markdown links from the source tree onto
3
+ * the C1 packed layout (#3937).
4
+ *
5
+ * Source checkout: content/X lives under content/; root main.md / SKILL.md stay
6
+ * at the repo root. Deposit: content/ children flatten to the payload root, and
7
+ * harness entries land beside them. A naive `./content/` strip cannot express
8
+ * the reverse `../../main.md` escape. This rewrite maps each relative target
9
+ * through the source path, then emits a deposit-relative href.
10
+ */
11
+ export declare const DEPOSIT_LINK_REWRITE_VERSION = "1";
12
+ export declare const REWRITE_MARKER_PREFIX = "<!-- deft:deposit-link-rewrite";
13
+ export declare function isUrlLikeTarget(target: string): boolean;
14
+ /** Map a repo-relative source path to its packed-deposit relative path. */
15
+ export declare function mapSourceToPackRelative(sourceRel: string): string | null;
16
+ /** Inverse: packed path -> source path the file was copied from. */
17
+ export declare function sourceRelForPackRel(packRel: string): string;
18
+ export declare function resolveSourceTargetRel(sourceFileRel: string, rawPath: string): string;
19
+ export declare function splitLinkHash(target: string): {
20
+ path: string;
21
+ suffix: string;
22
+ };
23
+ export interface RewriteLinkResult {
24
+ readonly next: string;
25
+ readonly rewritten: boolean;
26
+ readonly packMapped: boolean;
27
+ }
28
+ export declare function rewriteRelativeLink(options: {
29
+ readonly sourceFileRel: string;
30
+ readonly packFileRel: string;
31
+ readonly target: string;
32
+ }): RewriteLinkResult;
33
+ export declare function rewriteMarker(sourceRel: string): string;
34
+ export declare function rewriteMarkdownDepositLinks(options: {
35
+ readonly content: string;
36
+ readonly sourceFileRel: string;
37
+ readonly packFileRel: string;
38
+ }): {
39
+ content: string;
40
+ rewriteCount: number;
41
+ };
42
+ //# sourceMappingURL=rewrite-deposit-links.d.ts.map
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Flatten-aware rewrite of relative markdown links from the source tree onto
3
+ * the C1 packed layout (#3937).
4
+ *
5
+ * Source checkout: content/X lives under content/; root main.md / SKILL.md stay
6
+ * at the repo root. Deposit: content/ children flatten to the payload root, and
7
+ * harness entries land beside them. A naive `./content/` strip cannot express
8
+ * the reverse `../../main.md` escape. This rewrite maps each relative target
9
+ * through the source path, then emits a deposit-relative href.
10
+ */
11
+ import { posix } from "node:path";
12
+ import { extractLinkTargets, shouldSkipLinkTarget } from "../validate-content/link-parser.js";
13
+ export const DEPOSIT_LINK_REWRITE_VERSION = "1";
14
+ export const REWRITE_MARKER_PREFIX = "<!-- deft:deposit-link-rewrite";
15
+ const HARNESS_PACK_FILES = new Set(["main.md", "SKILL.md", "Taskfile.yml"]);
16
+ export function isUrlLikeTarget(target) {
17
+ return (target.startsWith("http://") ||
18
+ target.startsWith("https://") ||
19
+ target.startsWith("mailto:") ||
20
+ target.startsWith("#"));
21
+ }
22
+ /** Map a repo-relative source path to its packed-deposit relative path. */
23
+ export function mapSourceToPackRelative(sourceRel) {
24
+ let n = sourceRel.replace(/\\/g, "/");
25
+ if (n.length > 1 && n.endsWith("/"))
26
+ n = n.slice(0, -1);
27
+ if (n === "." || n === "")
28
+ return ".";
29
+ if (HARNESS_PACK_FILES.has(n) || n.startsWith("tasks/") || n.startsWith(".githooks/")) {
30
+ return n;
31
+ }
32
+ if (n === "content")
33
+ return ".";
34
+ if (n.startsWith("content/"))
35
+ return n.slice("content/".length);
36
+ return null;
37
+ }
38
+ /** Inverse: packed path -> source path the file was copied from. */
39
+ export function sourceRelForPackRel(packRel) {
40
+ const n = packRel.replace(/\\/g, "/");
41
+ if (HARNESS_PACK_FILES.has(n) || n.startsWith("tasks/") || n.startsWith(".githooks/")) {
42
+ return n;
43
+ }
44
+ return `content/${n}`;
45
+ }
46
+ export function resolveSourceTargetRel(sourceFileRel, rawPath) {
47
+ const sourceDir = posix.dirname(sourceFileRel.replace(/\\/g, "/"));
48
+ return posix.normalize(posix.join(sourceDir, rawPath)).replace(/\\/g, "/");
49
+ }
50
+ export function splitLinkHash(target) {
51
+ const hash = target.indexOf("#");
52
+ const query = target.indexOf("?");
53
+ let cut = -1;
54
+ if (hash === -1)
55
+ cut = query;
56
+ else if (query === -1)
57
+ cut = hash;
58
+ else
59
+ cut = Math.min(hash, query);
60
+ if (cut === -1)
61
+ return { path: target, suffix: "" };
62
+ return { path: target.slice(0, cut), suffix: target.slice(cut) };
63
+ }
64
+ export function rewriteRelativeLink(options) {
65
+ const { sourceFileRel, packFileRel, target } = options;
66
+ if (isUrlLikeTarget(target) || shouldSkipLinkTarget(target)) {
67
+ return { next: target, rewritten: false, packMapped: true };
68
+ }
69
+ const { path: rawPath, suffix } = splitLinkHash(target);
70
+ if (!rawPath)
71
+ return { next: target, rewritten: false, packMapped: true };
72
+ const trailingSlash = rawPath.endsWith("/");
73
+ const sourceTarget = resolveSourceTargetRel(sourceFileRel, rawPath);
74
+ if (sourceTarget === ".." || sourceTarget.startsWith("../")) {
75
+ return { next: target, rewritten: false, packMapped: false };
76
+ }
77
+ const packTarget = mapSourceToPackRelative(sourceTarget);
78
+ if (packTarget === null) {
79
+ return { next: target, rewritten: false, packMapped: false };
80
+ }
81
+ const packDir = posix.dirname(packFileRel.replace(/\\/g, "/"));
82
+ let rel = posix.relative(packDir, packTarget).replace(/\\/g, "/");
83
+ if (rel === "")
84
+ rel = ".";
85
+ if (trailingSlash && rel !== "." && !rel.endsWith("/"))
86
+ rel = `${rel}/`;
87
+ if (rawPath.startsWith("./") && !rel.startsWith(".") && !rel.startsWith("/")) {
88
+ rel = `./${rel}`;
89
+ }
90
+ const next = `${rel}${suffix}`;
91
+ return { next, rewritten: next !== target, packMapped: true };
92
+ }
93
+ export function rewriteMarker(sourceRel) {
94
+ return `${REWRITE_MARKER_PREFIX} v=${DEPOSIT_LINK_REWRITE_VERSION} source="${sourceRel.replace(/\\/g, "/")}" -->`;
95
+ }
96
+ export function rewriteMarkdownDepositLinks(options) {
97
+ const lines = options.content.split("\n");
98
+ let rewriteCount = 0;
99
+ const out = [];
100
+ for (const line of lines) {
101
+ const targets = extractLinkTargets(line);
102
+ if (targets.length === 0) {
103
+ out.push(line);
104
+ continue;
105
+ }
106
+ let nextLine = line;
107
+ for (let i = targets.length - 1; i >= 0; i -= 1) {
108
+ const target = targets[i];
109
+ if (target === undefined)
110
+ continue;
111
+ const result = rewriteRelativeLink({
112
+ sourceFileRel: options.sourceFileRel,
113
+ packFileRel: options.packFileRel,
114
+ target,
115
+ });
116
+ if (!result.rewritten)
117
+ continue;
118
+ const needle = `](${target})`;
119
+ const at = nextLine.lastIndexOf(needle);
120
+ if (at === -1)
121
+ continue;
122
+ nextLine = `${nextLine.slice(0, at)}](${result.next})${nextLine.slice(at + needle.length)}`;
123
+ rewriteCount += 1;
124
+ }
125
+ out.push(nextLine);
126
+ }
127
+ if (rewriteCount === 0) {
128
+ return { content: options.content, rewriteCount: 0 };
129
+ }
130
+ let joined = out.join("\n");
131
+ if (!joined.includes(REWRITE_MARKER_PREFIX)) {
132
+ const marker = rewriteMarker(options.sourceFileRel);
133
+ if (joined.startsWith("<!--")) {
134
+ const end = joined.indexOf("-->");
135
+ if (end !== -1) {
136
+ joined = `${joined.slice(0, end + 3)}\n${marker}${joined.slice(end + 3)}`;
137
+ }
138
+ else {
139
+ joined = `${marker}\n${joined}`;
140
+ }
141
+ }
142
+ else {
143
+ joined = `${marker}\n${joined}`;
144
+ }
145
+ }
146
+ return { content: joined, rewriteCount };
147
+ }
148
+ //# sourceMappingURL=rewrite-deposit-links.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=run-stage-content-pack.d.ts.map
@@ -0,0 +1,3 @@
1
+ import { runPrepackFromContentPackage } from "./stage-content-pack.js";
2
+ runPrepackFromContentPackage(process.cwd());
3
+ //# sourceMappingURL=run-stage-content-pack.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Stage the @deftai/directive-content pack the same way prepack does, then
3
+ * rewrite relative markdown links onto the flattened deposit layout (#3937).
4
+ *
5
+ * Must not write into the source tree: destDir is the pack root (packages/content
6
+ * during prepack, or an out-of-tree temp dir in tests).
7
+ */
8
+ /** Copy content/ + harness + engine entries, then rewrite markdown links. */
9
+ export declare function stageContentPack(options: {
10
+ readonly repoRoot: string;
11
+ readonly destDir: string;
12
+ }): {
13
+ filesRewritten: number;
14
+ };
15
+ /** prepack cwd is packages/content. */
16
+ export declare function runPrepackFromContentPackage(pkgDir: string): void;
17
+ //# sourceMappingURL=stage-content-pack.d.ts.map