@deftai/directive-core 0.83.0 → 0.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache/fetch.d.ts +17 -0
- package/dist/cache/fetch.js +50 -0
- package/dist/cache/main.js +36 -2
- package/dist/cache/task-cache/constants.d.ts +4 -0
- package/dist/cache/task-cache/constants.js +4 -0
- package/dist/cache/task-cache/executor.d.ts +9 -0
- package/dist/cache/task-cache/executor.js +51 -0
- package/dist/cache/task-cache/hash.d.ts +17 -0
- package/dist/cache/task-cache/hash.js +92 -0
- package/dist/cache/task-cache/index.d.ts +14 -0
- package/dist/cache/task-cache/index.js +15 -0
- package/dist/cache/task-cache/lint.d.ts +4 -0
- package/dist/cache/task-cache/lint.js +55 -0
- package/dist/cache/task-cache/registry.d.ts +7 -0
- package/dist/cache/task-cache/registry.js +67 -0
- package/dist/cache/task-cache/store.d.ts +10 -0
- package/dist/cache/task-cache/store.js +48 -0
- package/dist/cache/task-cache/types.d.ts +48 -0
- package/dist/cache/task-cache/types.js +3 -0
- package/dist/check/cached-orchestrator.d.ts +16 -0
- package/dist/check/cached-orchestrator.js +76 -0
- package/dist/check/context.d.ts +30 -0
- package/dist/check/context.js +28 -0
- package/dist/check/gate-lists.d.ts +18 -0
- package/dist/check/gate-lists.js +68 -0
- package/dist/check/index.d.ts +4 -1
- package/dist/check/index.js +3 -0
- package/dist/check/orchestrator.d.ts +3 -45
- package/dist/check/orchestrator.js +8 -46
- package/dist/check/runner-detect.d.ts +20 -0
- package/dist/check/runner-detect.js +131 -0
- package/dist/doctor/checks.js +16 -1
- package/dist/doctor/constants.d.ts +2 -0
- package/dist/doctor/constants.js +8 -4
- package/dist/doctor/index.d.ts +1 -0
- package/dist/doctor/index.js +1 -0
- package/dist/doctor/main.js +13 -4
- package/dist/doctor/npm-registry.d.ts +23 -0
- package/dist/doctor/npm-registry.js +128 -0
- package/dist/doctor/npm-view.d.ts +10 -0
- package/dist/doctor/npm-view.js +31 -0
- package/dist/doctor/payload-staleness.js +2 -9
- package/dist/doctor/signpost-checks.d.ts +2 -1
- package/dist/doctor/signpost-checks.js +2 -0
- package/dist/doctor/types.d.ts +9 -0
- package/dist/eval/readback.js +6 -1
- package/dist/eval-health-relocation/evaluate.js +15 -1
- package/dist/fs/projection-containment.js +1 -1
- package/dist/hooks/cursor-hooks.d.ts +6 -20
- package/dist/hooks/cursor-hooks.js +7 -92
- package/dist/hooks/dispatcher.d.ts +2 -0
- package/dist/hooks/dispatcher.js +48 -6
- package/dist/init-deposit/agent-hooks.d.ts +2 -2
- package/dist/init-deposit/agent-hooks.js +15 -37
- package/dist/init-deposit/gitignore.js +1 -0
- package/dist/init-deposit/hygiene.d.ts +22 -0
- package/dist/init-deposit/hygiene.js +136 -4
- package/dist/init-deposit/legacy-detect.d.ts +25 -1
- package/dist/init-deposit/legacy-detect.js +143 -11
- package/dist/init-deposit/refresh.d.ts +2 -0
- package/dist/init-deposit/refresh.js +18 -5
- package/dist/policy/index.d.ts +2 -0
- package/dist/policy/index.js +2 -0
- package/dist/policy/org-force-on-migration.d.ts +44 -0
- package/dist/policy/org-force-on-migration.js +240 -0
- package/dist/policy/product-signal.d.ts +3 -1
- package/dist/policy/product-signal.js +15 -4
- package/dist/policy/value-feedback.d.ts +18 -1
- package/dist/policy/value-feedback.js +78 -5
- package/dist/preflight-cache/evaluate.js +12 -1
- package/dist/product-signal/consent-prompt.d.ts +13 -0
- package/dist/product-signal/consent-prompt.js +30 -0
- package/dist/product-signal/submit.js +22 -1
- package/dist/render/index.d.ts +1 -0
- package/dist/render/index.js +1 -0
- package/dist/render/rule-map-template.d.ts +2 -0
- package/dist/render/rule-map-template.js +407 -0
- package/dist/render/rule-map.d.ts +2 -0
- package/dist/render/rule-map.js +703 -0
- package/dist/review-monitor/constants.d.ts +7 -1
- package/dist/review-monitor/constants.js +40 -14
- package/dist/review-monitor/github-lease.d.ts +45 -0
- package/dist/review-monitor/github-lease.js +106 -0
- package/dist/review-monitor/index.d.ts +2 -0
- package/dist/review-monitor/index.js +2 -0
- package/dist/review-monitor/lease-comment.d.ts +36 -0
- package/dist/review-monitor/lease-comment.js +152 -0
- package/dist/review-monitor/record.d.ts +61 -13
- package/dist/review-monitor/record.js +379 -132
- package/dist/review-monitor/verify.d.ts +2 -0
- package/dist/review-monitor/verify.js +32 -13
- package/dist/scm/gh-rest.d.ts +7 -1
- package/dist/scm/gh-rest.js +35 -0
- package/dist/scope/decompose.js +9 -3
- package/dist/session/git.d.ts +2 -0
- package/dist/session/git.js +14 -0
- package/dist/session/session-start.js +5 -0
- package/dist/session/verify-session-ritual.js +39 -5
- package/dist/swarm/routing-set-cli.js +16 -5
- package/dist/swarm/routing.d.ts +1 -1
- package/dist/swarm/routing.js +3 -1
- package/dist/task-surface/index.js +53 -13
- package/dist/value/readback.js +6 -1
- package/dist/xbrief-migrate/constants.d.ts +6 -0
- package/dist/xbrief-migrate/constants.js +6 -0
- package/dist/xbrief-migrate/index.d.ts +2 -2
- package/dist/xbrief-migrate/index.js +2 -2
- package/dist/xbrief-migrate/migrate-project.d.ts +7 -0
- package/dist/xbrief-migrate/migrate-project.js +18 -1
- package/package.json +7 -3
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { existsSync, statSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { sweepScratchDirs } from "../orchestration/subagent-monitor.js";
|
|
4
|
+
import { resolveRepo } from "../triage/queue/repo.js";
|
|
4
5
|
import { EXIT_CONFIG_ERROR, EXIT_NOT_READY, EXIT_READY, MONITORING_TIER_1, MONITORING_TIER_3, } from "./constants.js";
|
|
5
|
-
import { defaultSubagentStatusDir,
|
|
6
|
+
import { defaultSubagentStatusDir, fetchActiveMonitorFromGithub, readReviewMonitorFile, reviewMonitorPath, } from "./record.js";
|
|
6
7
|
import { isTier1, probeMonitoringTier } from "./tier-detection.js";
|
|
7
8
|
function spawnRedirect(probe) {
|
|
8
9
|
const primitive = probe.primitive ?? "sub-agent";
|
|
@@ -59,6 +60,8 @@ export function evaluateReviewMonitorGate(args) {
|
|
|
59
60
|
const callSite = args.callSite ?? "unspecified";
|
|
60
61
|
const staleMinutes = args.staleMinutes ?? 30;
|
|
61
62
|
const now = args.now ?? new Date();
|
|
63
|
+
// Legacy `.deft/review-monitor.json` is obsolete (#2814); explicit no-op read documents migration.
|
|
64
|
+
readReviewMonitorFile(reviewMonitorPath(projectRoot));
|
|
62
65
|
if (args.approach3 === true) {
|
|
63
66
|
if (isTier1(tier)) {
|
|
64
67
|
return {
|
|
@@ -103,33 +106,43 @@ export function evaluateReviewMonitorGate(args) {
|
|
|
103
106
|
callSite,
|
|
104
107
|
};
|
|
105
108
|
}
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
if (data === null) {
|
|
109
|
+
const repo = resolveRepo(args.repo ?? null, projectRoot);
|
|
110
|
+
if (repo === null) {
|
|
109
111
|
return {
|
|
110
112
|
exitCode: EXIT_CONFIG_ERROR,
|
|
111
|
-
message:
|
|
113
|
+
message: "verify_review_monitor: could not resolve owner/repo — pass --repo OWNER/REPO or run inside a git repo with origin",
|
|
112
114
|
tier,
|
|
113
115
|
monitorRecord: null,
|
|
114
116
|
heartbeatActive: false,
|
|
115
117
|
callSite,
|
|
116
118
|
};
|
|
117
119
|
}
|
|
118
|
-
const
|
|
120
|
+
const githubMonitor = fetchActiveMonitorFromGithub(repo, args.pr, {
|
|
119
121
|
now,
|
|
120
|
-
staleMinutes,
|
|
121
122
|
headSha: args.headSha ?? null,
|
|
123
|
+
seams: args.seams,
|
|
122
124
|
});
|
|
125
|
+
if (githubMonitor !== null && typeof githubMonitor === "object" && "error" in githubMonitor) {
|
|
126
|
+
return {
|
|
127
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
128
|
+
message: `verify_review_monitor: ${githubMonitor.error}`,
|
|
129
|
+
tier,
|
|
130
|
+
monitorRecord: null,
|
|
131
|
+
heartbeatActive: false,
|
|
132
|
+
callSite,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const monitorRecord = githubMonitor;
|
|
123
136
|
const heartbeatActive = hasActivePollingHeartbeat(projectRoot, args.pr, {
|
|
124
137
|
now,
|
|
125
138
|
staleMinutes,
|
|
126
139
|
});
|
|
127
|
-
if (monitorRecord !== null
|
|
128
|
-
const via = monitorRecord !== null ? "review-monitor record" : "subagent heartbeat";
|
|
140
|
+
if (monitorRecord !== null) {
|
|
129
141
|
return {
|
|
130
142
|
exitCode: EXIT_READY,
|
|
131
|
-
message: `verify_review_monitor: active review-
|
|
132
|
-
`(
|
|
143
|
+
message: `verify_review_monitor: active GitHub review-owner lease for PR #${args.pr} ` +
|
|
144
|
+
`(monitor_agent_id=${monitorRecord.monitor_agent_id}, owner=${monitorRecord.owner}, ` +
|
|
145
|
+
`call-site=${callSite}, tier=1, descriptor=${tier.descriptor ?? "unknown"}).`,
|
|
133
146
|
tier,
|
|
134
147
|
monitorRecord,
|
|
135
148
|
heartbeatActive,
|
|
@@ -141,15 +154,20 @@ export function evaluateReviewMonitorGate(args) {
|
|
|
141
154
|
: callSite === "swarm-phase6-cascade"
|
|
142
155
|
? "Swarm Phase 6 post force-push (#380)"
|
|
143
156
|
: "Solo drive-to merge-ready / review-cycle ownership";
|
|
157
|
+
const heartbeatHint = heartbeatActive
|
|
158
|
+
? " Note: local subagent heartbeat is present but is not a GitHub review-owner lease (#2814).\n"
|
|
159
|
+
: "";
|
|
144
160
|
return {
|
|
145
161
|
exitCode: EXIT_NOT_READY,
|
|
146
|
-
message: `verify_review_monitor: Tier 1 available but no active review-
|
|
162
|
+
message: `verify_review_monitor: Tier 1 available but no active GitHub review-owner lease for PR #${args.pr} (#2814).\n` +
|
|
163
|
+
heartbeatHint +
|
|
147
164
|
` Call site: ${siteHint}.\n` +
|
|
148
165
|
` Detected descriptor=${tier.descriptor ?? "unknown"} primitive=${tier.primitive ?? "none"}.\n` +
|
|
166
|
+
` Legacy .deft/review-monitor.json is ignored.\n` +
|
|
149
167
|
` ${spawnRedirect(tier)}`,
|
|
150
168
|
tier,
|
|
151
169
|
monitorRecord: null,
|
|
152
|
-
heartbeatActive
|
|
170
|
+
heartbeatActive,
|
|
153
171
|
callSite,
|
|
154
172
|
};
|
|
155
173
|
}
|
|
@@ -160,6 +178,7 @@ export function verifyResultToJson(result) {
|
|
|
160
178
|
heartbeat_active: result.heartbeatActive,
|
|
161
179
|
message: result.message,
|
|
162
180
|
monitor_agent_id: result.monitorRecord?.monitor_agent_id ?? null,
|
|
181
|
+
monitor_owner: result.monitorRecord?.owner ?? null,
|
|
163
182
|
monitor_record: result.monitorRecord,
|
|
164
183
|
ready: result.exitCode === EXIT_READY,
|
|
165
184
|
tier: result.tier.tier,
|
package/dist/scm/gh-rest.d.ts
CHANGED
|
@@ -53,9 +53,15 @@ export interface RestIssueListOptions {
|
|
|
53
53
|
export declare function restIssueList(repo: string, options?: RestIssueListOptions, seams?: GhRestSeams): Record<string, unknown>[];
|
|
54
54
|
export declare const REST_MAX_PER_PAGE = 100;
|
|
55
55
|
export declare const REST_PAGINATION_MAX_PAGES = 100;
|
|
56
|
-
export declare const PUBLIC_HELPERS: readonly ["restCreateIssue", "restPostComment", "restUpdateIssue", "restCreateLabel", "restCloseIssue", "restOpenPr", "restMergePr", "restIssueView", "restPrView", "restIssueList", "restIssueListPaginated"];
|
|
56
|
+
export declare const PUBLIC_HELPERS: readonly ["restCreateIssue", "restPostComment", "restUpdateComment", "restDeleteComment", "restGetUser", "restUpdateIssue", "restCreateLabel", "restCloseIssue", "restOpenPr", "restMergePr", "restIssueView", "restPrView", "restIssueList", "restIssueListPaginated"];
|
|
57
57
|
export declare function restCreateIssue(repo: string, title: string, body: string, labels?: readonly string[], seams?: GhRestSeams): Record<string, unknown>;
|
|
58
58
|
export declare function restPostComment(repo: string, n: number, body: string, seams?: GhRestSeams): Record<string, unknown>;
|
|
59
|
+
/** `PATCH /repos/{owner}/{repo}/issues/comments/{id}` -- edit a PR/issue comment in place. */
|
|
60
|
+
export declare function restUpdateComment(repo: string, commentId: number, body: string, seams?: GhRestSeams): Record<string, unknown>;
|
|
61
|
+
/** `DELETE /repos/{owner}/{repo}/issues/comments/{id}` -- remove a duplicate claim comment. */
|
|
62
|
+
export declare function restDeleteComment(repo: string, commentId: number, seams?: GhRestSeams): Record<string, unknown>;
|
|
63
|
+
/** `GET /user` -- authenticated GitHub login for review-owner claims. */
|
|
64
|
+
export declare function restGetUser(seams?: GhRestSeams): Record<string, unknown>;
|
|
59
65
|
export declare function restUpdateIssue(repo: string, n: number, patch: Record<string, unknown>, seams?: GhRestSeams): Record<string, unknown>;
|
|
60
66
|
export declare function restCreateLabel(repo: string, name: string, color: string, description: string, seams?: GhRestSeams): Record<string, unknown>;
|
|
61
67
|
export declare function restCloseIssue(repo: string, n: number, reason?: string | null, seams?: GhRestSeams): Record<string, unknown>;
|
package/dist/scm/gh-rest.js
CHANGED
|
@@ -171,6 +171,9 @@ export const REST_PAGINATION_MAX_PAGES = 100;
|
|
|
171
171
|
export const PUBLIC_HELPERS = [
|
|
172
172
|
"restCreateIssue",
|
|
173
173
|
"restPostComment",
|
|
174
|
+
"restUpdateComment",
|
|
175
|
+
"restDeleteComment",
|
|
176
|
+
"restGetUser",
|
|
174
177
|
"restUpdateIssue",
|
|
175
178
|
"restCreateLabel",
|
|
176
179
|
"restCloseIssue",
|
|
@@ -226,6 +229,38 @@ export function restPostComment(repo, n, body, seams = {}) {
|
|
|
226
229
|
...seams,
|
|
227
230
|
});
|
|
228
231
|
}
|
|
232
|
+
/** `PATCH /repos/{owner}/{repo}/issues/comments/{id}` -- edit a PR/issue comment in place. */
|
|
233
|
+
export function restUpdateComment(repo, commentId, body, seams = {}) {
|
|
234
|
+
const [owner, name] = splitRepo(repo);
|
|
235
|
+
const endpoint = `repos/${owner}/${name}/issues/comments/${commentId}`;
|
|
236
|
+
return execMutation([endpoint, "--method", "PATCH"], {
|
|
237
|
+
endpoint,
|
|
238
|
+
payload: { body },
|
|
239
|
+
hint: "verify repo permissions and comment id; check gh auth status",
|
|
240
|
+
...seams,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/** `DELETE /repos/{owner}/{repo}/issues/comments/{id}` -- remove a duplicate claim comment. */
|
|
244
|
+
export function restDeleteComment(repo, commentId, seams = {}) {
|
|
245
|
+
const [owner, name] = splitRepo(repo);
|
|
246
|
+
const endpoint = `repos/${owner}/${name}/issues/comments/${commentId}`;
|
|
247
|
+
return execMutation([endpoint, "--method", "DELETE"], {
|
|
248
|
+
endpoint,
|
|
249
|
+
payload: {},
|
|
250
|
+
hint: "verify repo permissions and comment id; check gh auth status",
|
|
251
|
+
...seams,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
/** `GET /user` -- authenticated GitHub login for review-owner claims. */
|
|
255
|
+
export function restGetUser(seams = {}) {
|
|
256
|
+
return execApi(["user"], {
|
|
257
|
+
endpoint: "user",
|
|
258
|
+
payload: null,
|
|
259
|
+
hint: "verify gh auth status (`gh auth login`)",
|
|
260
|
+
runGhApiFn: seams.runGhApiFn,
|
|
261
|
+
whichFn: seams.whichFn,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
229
264
|
export function restUpdateIssue(repo, n, patch, seams = {}) {
|
|
230
265
|
const [owner, name] = splitRepo(repo);
|
|
231
266
|
const endpoint = `repos/${owner}/${name}/issues/${n}`;
|
package/dist/scope/decompose.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { accessSync, constants, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
11
11
|
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
12
|
+
import { assertWriteTargetSafe, ProjectionContainmentError } from "../fs/projection-containment.js";
|
|
12
13
|
import { hasArtifactSuffix, LEGACY_ARTIFACT_DIR, MIGRATED_ARTIFACT_DIR, resolveLifecycleRoot, } from "../layout/resolve.js";
|
|
13
14
|
import { referenceWithDefaultTrust, slugify } from "../vbrief-build/build.js";
|
|
14
15
|
import { EMITTED_VBRIEF_VERSION } from "../vbrief-build/constants.js";
|
|
@@ -52,7 +53,8 @@ function loadJson(path) {
|
|
|
52
53
|
}
|
|
53
54
|
return data;
|
|
54
55
|
}
|
|
55
|
-
function writeJson(path, data) {
|
|
56
|
+
function writeJson(projectRoot, path, data) {
|
|
57
|
+
assertWriteTargetSafe(projectRoot, path);
|
|
56
58
|
mkdirSync(dirname(path), { recursive: true });
|
|
57
59
|
writeFileSync(path, formatBriefJson(data), "utf8");
|
|
58
60
|
}
|
|
@@ -871,7 +873,7 @@ export function applyDecomposition(opts) {
|
|
|
871
873
|
}
|
|
872
874
|
for (let i = 0; i < childPaths.length; i += 1) {
|
|
873
875
|
// biome-ignore lint/style/noNonNullAssertion: loop bound ensures these exist
|
|
874
|
-
writeJson(childPaths[i].target, childDocs[i]);
|
|
876
|
+
writeJson(projectRoot, childPaths[i].target, childDocs[i]);
|
|
875
877
|
}
|
|
876
878
|
let parentPlan = parent.plan;
|
|
877
879
|
if (parentPlan === null || parentPlan === undefined) {
|
|
@@ -911,7 +913,7 @@ export function applyDecomposition(opts) {
|
|
|
911
913
|
planObj.references = dedupeReferences(references
|
|
912
914
|
.filter((r) => typeof r === "object" && r !== null && !Array.isArray(r))
|
|
913
915
|
.map((r) => r));
|
|
914
|
-
writeJson(parentPath, parent);
|
|
916
|
+
writeJson(projectRoot, parentPath, parent);
|
|
915
917
|
actions.push(`UPDATE ${parentRel} references`);
|
|
916
918
|
return actions;
|
|
917
919
|
}
|
|
@@ -1022,6 +1024,10 @@ export function decomposeMain(argv) {
|
|
|
1022
1024
|
process.stderr.write(`ERROR: ${err.message}\n`);
|
|
1023
1025
|
return 1;
|
|
1024
1026
|
}
|
|
1027
|
+
if (err instanceof ProjectionContainmentError) {
|
|
1028
|
+
process.stderr.write(`ERROR: ${err.message}\n`);
|
|
1029
|
+
return 2;
|
|
1030
|
+
}
|
|
1025
1031
|
process.stderr.write(`ERROR: ${String(err)}\n`);
|
|
1026
1032
|
return 1;
|
|
1027
1033
|
}
|
package/dist/session/git.d.ts
CHANGED
|
@@ -10,5 +10,7 @@ export declare function gitHead(projectRoot: string, runGit?: GitRunner): {
|
|
|
10
10
|
error: string | null;
|
|
11
11
|
};
|
|
12
12
|
export declare function worktreePath(projectRoot: string, runGit?: GitRunner): string;
|
|
13
|
+
/** True when `ancestor` is reachable from `descendant` (same commit counts). */
|
|
14
|
+
export declare function gitIsAncestor(projectRoot: string, ancestor: string, descendant: string, runGit?: GitRunner): boolean | null;
|
|
13
15
|
export declare function detectBranch(projectRoot: string, runGit?: GitRunner): string | null;
|
|
14
16
|
//# sourceMappingURL=git.d.ts.map
|
package/dist/session/git.js
CHANGED
|
@@ -35,6 +35,20 @@ export function worktreePath(projectRoot, runGit = defaultGitRunner) {
|
|
|
35
35
|
}
|
|
36
36
|
return resolve(projectRoot);
|
|
37
37
|
}
|
|
38
|
+
/** True when `ancestor` is reachable from `descendant` (same commit counts). */
|
|
39
|
+
export function gitIsAncestor(projectRoot, ancestor, descendant, runGit = defaultGitRunner) {
|
|
40
|
+
if (ancestor === descendant) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
const { code } = runGit(projectRoot, ["merge-base", "--is-ancestor", ancestor, descendant]);
|
|
44
|
+
if (code === 0) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (code === 1) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
38
52
|
export function detectBranch(projectRoot, runGit = defaultGitRunner) {
|
|
39
53
|
const sym = runGit(projectRoot, ["symbolic-ref", "--short", "HEAD"]);
|
|
40
54
|
if (sym.code === 0 && sym.stdout.trim()) {
|
|
@@ -5,6 +5,7 @@ import { MIGRATE_COMPLETION_NUDGE, shouldEmitMigrateNudge } from "../init-deposi
|
|
|
5
5
|
import { detectEnvironmentContext, environmentContextToDict, formatEnvironmentContext, } from "../platform/shell-context.js";
|
|
6
6
|
import { disclosureLine } from "../policy/disclosure.js";
|
|
7
7
|
import { resolvePolicy } from "../policy/resolve.js";
|
|
8
|
+
import { maybeFormatProductSignalConsentPrompt } from "../product-signal/consent-prompt.js";
|
|
8
9
|
import { maybeRunStalenessTickler } from "../staleness-tickler/run.js";
|
|
9
10
|
import { runDefaultMode } from "../triage/welcome/default-mode.js";
|
|
10
11
|
import { resolveUserMdPath } from "../user-config/resolve-user-md.js";
|
|
@@ -380,6 +381,10 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
380
381
|
catch {
|
|
381
382
|
// observability only — session start must not abort on transient eval readback I/O
|
|
382
383
|
}
|
|
384
|
+
const consentPrompt = maybeFormatProductSignalConsentPrompt({ projectRoot });
|
|
385
|
+
if (consentPrompt.length > 0) {
|
|
386
|
+
lines.push(consentPrompt.trimEnd());
|
|
387
|
+
}
|
|
383
388
|
const payload = newRitualStatePayload({
|
|
384
389
|
sessionId: (options.newSessionId ?? randomUUID)(),
|
|
385
390
|
gitHead: gitHeadValue,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { formatFrameworkCommand } from "../render/framework-commands.js";
|
|
3
|
-
import { defaultGitRunner, gitHead, worktreePath } from "./git.js";
|
|
3
|
+
import { defaultGitRunner, gitHead, gitIsAncestor, worktreePath } from "./git.js";
|
|
4
4
|
import { pythonJsonDump } from "./json.js";
|
|
5
5
|
import { ENV_SESSION_POSTURE, readOnlyPostureMessage, resolveSessionPosture, ritualStateIsPostureAuthority, } from "./posture.js";
|
|
6
6
|
import { defaultRitualRunner } from "./ritual-entrypoint.js";
|
|
@@ -59,6 +59,10 @@ function runGatedStep(projectRoot, payload, stepName, runner, now) {
|
|
|
59
59
|
}
|
|
60
60
|
return null;
|
|
61
61
|
}
|
|
62
|
+
function headDriftRecoveryMessage() {
|
|
63
|
+
return (`session ritual state is stale because git HEAD changed discontinuously. ` +
|
|
64
|
+
`Run \`${formatFrameworkCommand(["session:start"])}\` again.`);
|
|
65
|
+
}
|
|
62
66
|
function evaluateLoadedState(projectRoot, state, input) {
|
|
63
67
|
const runGit = input.runGit ?? defaultGitRunner;
|
|
64
68
|
const { head: currentHead, error: headError } = gitHead(projectRoot, runGit);
|
|
@@ -73,10 +77,22 @@ function evaluateLoadedState(projectRoot, state, input) {
|
|
|
73
77
|
];
|
|
74
78
|
}
|
|
75
79
|
if (state.gitHead !== currentHead) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
const forward = gitIsAncestor(projectRoot, state.gitHead, currentHead, runGit);
|
|
81
|
+
if (forward === null) {
|
|
82
|
+
return [2, "could not verify git history for session ritual"];
|
|
83
|
+
}
|
|
84
|
+
if (!forward) {
|
|
85
|
+
return [1, headDriftRecoveryMessage()];
|
|
86
|
+
}
|
|
87
|
+
if (input.rebindForwardHead) {
|
|
88
|
+
const payload = { ...state.raw, git_head: currentHead };
|
|
89
|
+
try {
|
|
90
|
+
writeRitualState(projectRoot, payload);
|
|
91
|
+
}
|
|
92
|
+
catch (exc) {
|
|
93
|
+
return [2, `could not rebind session ritual git HEAD: ${String(exc)}`];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
80
96
|
}
|
|
81
97
|
const staleness = resolveSessionRitualStalenessHours(projectRoot);
|
|
82
98
|
if (staleness.source === "default-on-error") {
|
|
@@ -157,6 +173,7 @@ export function inspectSessionRitual(projectRoot, options = {}) {
|
|
|
157
173
|
tier,
|
|
158
174
|
now: options.now ?? new Date(),
|
|
159
175
|
runGit: options.runGit,
|
|
176
|
+
rebindForwardHead: false,
|
|
160
177
|
});
|
|
161
178
|
return {
|
|
162
179
|
code,
|
|
@@ -246,6 +263,7 @@ export function verifySessionRitual(projectRoot, options = {}) {
|
|
|
246
263
|
tier: "quick",
|
|
247
264
|
now: instant,
|
|
248
265
|
runGit: options.runGit,
|
|
266
|
+
rebindForwardHead: true,
|
|
249
267
|
});
|
|
250
268
|
if (precheckCode !== 0) {
|
|
251
269
|
return {
|
|
@@ -259,6 +277,21 @@ export function verifySessionRitual(projectRoot, options = {}) {
|
|
|
259
277
|
ritualStateRequired,
|
|
260
278
|
};
|
|
261
279
|
}
|
|
280
|
+
const reloadedAfterPrecheck = readRitualState(projectRoot);
|
|
281
|
+
state = reloadedAfterPrecheck[0];
|
|
282
|
+
err = reloadedAfterPrecheck[1];
|
|
283
|
+
if (state === null) {
|
|
284
|
+
return {
|
|
285
|
+
code: 2,
|
|
286
|
+
message: err ?? "ritual state invalid after precheck",
|
|
287
|
+
tier,
|
|
288
|
+
statePath,
|
|
289
|
+
bypassed: false,
|
|
290
|
+
wouldFailCode: null,
|
|
291
|
+
posture,
|
|
292
|
+
ritualStateRequired,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
262
295
|
const payload = { ...state.raw };
|
|
263
296
|
const gated = { ...payload.gated_steps };
|
|
264
297
|
payload.gated_steps = gated;
|
|
@@ -303,6 +336,7 @@ export function verifySessionRitual(projectRoot, options = {}) {
|
|
|
303
336
|
tier,
|
|
304
337
|
now: instant,
|
|
305
338
|
runGit: options.runGit,
|
|
339
|
+
rebindForwardHead: true,
|
|
306
340
|
});
|
|
307
341
|
if (isBypassed) {
|
|
308
342
|
return {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { PROJECTION_CONTAINMENT_REFUSED_EXIT_CODE, ProjectionContainmentError, } from "../fs/projection-containment.js";
|
|
4
5
|
import { getPlatformCapabilities } from "../intake/platform-capabilities.js";
|
|
5
6
|
import { EXIT_CONFIG_ERROR, EXIT_OK } from "./constants.js";
|
|
6
7
|
import { dispatchProviderFromRuntime, HARNESS_BOUND_PROVIDERS, ROUTING_MODE_HARNESS_DEFAULT, ROUTING_MODE_PINNED, resolveRoutingPath, SWARM_WORKER_ROLES, writeModelDecision, } from "./routing.js";
|
|
@@ -66,11 +67,21 @@ export function routingSetMain(argv = process.argv.slice(2)) {
|
|
|
66
67
|
"so only --harness-default is recordable here.\n");
|
|
67
68
|
return EXIT_CONFIG_ERROR;
|
|
68
69
|
}
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
const root = resolve(projectRoot);
|
|
71
|
+
const path = resolveRoutingPath(root);
|
|
72
|
+
try {
|
|
73
|
+
writeModelDecision(root, path, resolvedProvider, role, {
|
|
74
|
+
model,
|
|
75
|
+
mode: harnessDefault ? ROUTING_MODE_HARNESS_DEFAULT : ROUTING_MODE_PINNED,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
if (err instanceof ProjectionContainmentError) {
|
|
80
|
+
process.stderr.write(`ERROR: ${err.message}\n`);
|
|
81
|
+
return PROJECTION_CONTAINMENT_REFUSED_EXIT_CODE;
|
|
82
|
+
}
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
74
85
|
const modelText = model ?? "<harness default>";
|
|
75
86
|
process.stdout.write(`Recorded route: provider '${resolvedProvider}', role '${role}' -> model ${modelText}.\n` +
|
|
76
87
|
`Route file: ${path}\n`);
|
package/dist/swarm/routing.d.ts
CHANGED
|
@@ -62,5 +62,5 @@ export declare function resolveDispatchProvider(environ?: NodeJS.ProcessEnv): st
|
|
|
62
62
|
* `decidedAt` when the caller did not supply one. Used by the interactive
|
|
63
63
|
* resolver path (resolver step 5) and the `swarm:routing-set` task.
|
|
64
64
|
*/
|
|
65
|
-
export declare function writeModelDecision(path: string, provider: string, role: string, decision: RouteDecision): void;
|
|
65
|
+
export declare function writeModelDecision(projectRoot: string, path: string, provider: string, role: string, decision: RouteDecision): void;
|
|
66
66
|
//# sourceMappingURL=routing.d.ts.map
|
package/dist/swarm/routing.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { execFileSync } from "node:child_process";
|
|
17
17
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
18
18
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
19
|
+
import { assertWriteTargetSafe } from "../fs/projection-containment.js";
|
|
19
20
|
/**
|
|
20
21
|
* The fixed worker-role vocabulary (reused from #1531). No separate tier
|
|
21
22
|
* vocabulary to start; decisions are strictly per-role.
|
|
@@ -201,9 +202,10 @@ function assertSafeRoutingKey(kind, key) {
|
|
|
201
202
|
* `decidedAt` when the caller did not supply one. Used by the interactive
|
|
202
203
|
* resolver path (resolver step 5) and the `swarm:routing-set` task.
|
|
203
204
|
*/
|
|
204
|
-
export function writeModelDecision(path, provider, role, decision) {
|
|
205
|
+
export function writeModelDecision(projectRoot, path, provider, role, decision) {
|
|
205
206
|
assertSafeRoutingKey("provider", provider);
|
|
206
207
|
assertSafeRoutingKey("role", role);
|
|
208
|
+
assertWriteTargetSafe(projectRoot, path);
|
|
207
209
|
const { data } = loadRoutingFile(path);
|
|
208
210
|
// Null-prototype write targets so a computed provider/role key can only ever
|
|
209
211
|
// set an own property and can never reach `Object.prototype`, even if the
|
|
@@ -2,10 +2,24 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { LEGACY_VBRIEF_VERSION } from "@deftai/directive-types";
|
|
5
|
+
import { assertProjectionContained, assertWriteTargetSafe, ProjectionContainmentError, } from "../fs/projection-containment.js";
|
|
5
6
|
const UNRELEASED_RE = /## \[Unreleased\][ \t]*\n([\s\S]*?)(?=\n## \[|$)/;
|
|
6
7
|
const CHANGE_NAME_RE = /^[\w][\w-]*$/;
|
|
7
8
|
const COMMIT_TYPES = "feat|fix|docs|chore|refactor|test|style|perf|ci|build|revert";
|
|
8
9
|
const COMMIT_SUBJECT_RE = new RegExp(`^(${COMMIT_TYPES})(\\(.+\\))?!?: .+`);
|
|
10
|
+
/** Refuse task-surface writes that escape via repo-controlled symlinks (#2807). */
|
|
11
|
+
function projectionTarget(projectDir, ...relSegments) {
|
|
12
|
+
const target = join(resolve(projectDir), ...relSegments);
|
|
13
|
+
assertProjectionContained(projectDir, target);
|
|
14
|
+
return target;
|
|
15
|
+
}
|
|
16
|
+
function containmentExitCode(io, err) {
|
|
17
|
+
if (err instanceof ProjectionContainmentError) {
|
|
18
|
+
io.writeErr(`FAIL: ${err.message}\n`);
|
|
19
|
+
return 2;
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
9
23
|
function proposalTemplate(name) {
|
|
10
24
|
return {
|
|
11
25
|
vBRIEFInfo: { version: LEGACY_VBRIEF_VERSION },
|
|
@@ -67,19 +81,35 @@ export function runChangeInit(projectRoot, name, io) {
|
|
|
67
81
|
io.writeOut("FAIL: Name must contain only alphanumeric characters, underscores, and hyphens\n");
|
|
68
82
|
return 1;
|
|
69
83
|
}
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
const projectAbs = resolve(projectRoot);
|
|
85
|
+
try {
|
|
86
|
+
const base = projectionTarget(projectAbs, join("history", "changes", trimmed));
|
|
87
|
+
if (existsSync(base)) {
|
|
88
|
+
io.writeOut(`FAIL: ${base} already exists\n`);
|
|
89
|
+
return 1;
|
|
90
|
+
}
|
|
91
|
+
const specsDir = join(base, "specs");
|
|
92
|
+
assertProjectionContained(projectAbs, specsDir);
|
|
93
|
+
mkdirSync(specsDir, { recursive: true });
|
|
94
|
+
const proposalPath = join(base, "proposal.xbrief.json");
|
|
95
|
+
const tasksPath = join(base, "tasks.xbrief.json");
|
|
96
|
+
assertWriteTargetSafe(projectAbs, proposalPath);
|
|
97
|
+
assertWriteTargetSafe(projectAbs, tasksPath);
|
|
98
|
+
writeFileSync(proposalPath, `${JSON.stringify(proposalTemplate(trimmed), null, 2)}\n`, "utf8");
|
|
99
|
+
writeFileSync(tasksPath, `${JSON.stringify(tasksTemplate(trimmed), null, 2)}\n`, "utf8");
|
|
100
|
+
io.writeOut(`OK: Created change proposal at ${base}/\n`);
|
|
101
|
+
for (const file of ["proposal.xbrief.json", "tasks.xbrief.json", "specs/"]) {
|
|
102
|
+
io.writeOut(` - ${file}\n`);
|
|
103
|
+
}
|
|
104
|
+
return 0;
|
|
74
105
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
106
|
+
catch (err) {
|
|
107
|
+
const code = containmentExitCode(io, err);
|
|
108
|
+
if (code !== null) {
|
|
109
|
+
return code;
|
|
110
|
+
}
|
|
111
|
+
throw err;
|
|
81
112
|
}
|
|
82
|
-
return 0;
|
|
83
113
|
}
|
|
84
114
|
/** Port of ``task commit:lint`` inline Python (#2022 Phase 2). */
|
|
85
115
|
export function runCommitLint(projectRoot, io) {
|
|
@@ -119,8 +149,18 @@ export function runInstallUninstall(projectRoot, io) {
|
|
|
119
149
|
const filtered = lines.filter((line) => !line.startsWith("See deft/main.md") && !line.startsWith("Skills: deft/skills/"));
|
|
120
150
|
const next = filtered.join("");
|
|
121
151
|
if (next !== original) {
|
|
122
|
-
|
|
123
|
-
|
|
152
|
+
try {
|
|
153
|
+
assertWriteTargetSafe(resolve(projectRoot), path);
|
|
154
|
+
writeFileSync(path, next, "utf8");
|
|
155
|
+
io.writeOut("Removed deft entry from AGENTS.md\n");
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
const code = containmentExitCode(io, err);
|
|
159
|
+
if (code !== null) {
|
|
160
|
+
return code;
|
|
161
|
+
}
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
124
164
|
}
|
|
125
165
|
else {
|
|
126
166
|
io.writeOut("No deft entry found in AGENTS.md\n");
|
package/dist/value/readback.js
CHANGED
|
@@ -2,6 +2,7 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { runningInsideDeftRepo } from "../doctor/paths.js";
|
|
4
4
|
import { ALL_ATTRIBUTION_EVENT_NAMES } from "../events/attribution-constants.js";
|
|
5
|
+
import { assertWriteTargetSafe, ProjectionContainmentError } from "../fs/projection-containment.js";
|
|
5
6
|
import { DEFAULT_EVENT_LOG, readEvents } from "../lifecycle/events.js";
|
|
6
7
|
import { policyColonInvocation } from "../policy/policy-invocation.js";
|
|
7
8
|
import { isValueFeedbackPathAllowed, resolveValueFeedback, } from "../policy/value-feedback.js";
|
|
@@ -229,10 +230,14 @@ function appendReadbackHistory(projectRoot, eventId, line, options = {}) {
|
|
|
229
230
|
line,
|
|
230
231
|
};
|
|
231
232
|
try {
|
|
233
|
+
assertWriteTargetSafe(projectRoot, path);
|
|
232
234
|
mkdirSync(join(path, ".."), { recursive: true });
|
|
233
235
|
appendFileSync(path, `${JSON.stringify(record)}\n`, "utf8");
|
|
234
236
|
}
|
|
235
|
-
catch {
|
|
237
|
+
catch (err) {
|
|
238
|
+
if (err instanceof ProjectionContainmentError) {
|
|
239
|
+
throw err;
|
|
240
|
+
}
|
|
236
241
|
// observability only
|
|
237
242
|
}
|
|
238
243
|
}
|
|
@@ -4,6 +4,12 @@ export { LEGACY_VBRIEF_VERSION, VBRIEF_REFERENCE_PREFIX, VBRIEF_VERSION, XBRIEF_
|
|
|
4
4
|
export declare const V08_CONTAINER_ITEM_TYPES: Set<string>;
|
|
5
5
|
export declare const LEGACY_ARTIFACT_DIR: "vbrief";
|
|
6
6
|
export declare const MIGRATED_ARTIFACT_DIR: "xbrief";
|
|
7
|
+
/**
|
|
8
|
+
* Obsolete framework support narrative at the lifecycle root. Legacy
|
|
9
|
+
* `migrate:xbrief` copied this to `xbrief/vbrief.md` with broken framework
|
|
10
|
+
* links; consumer projections use `xbrief.md` instead (#2806).
|
|
11
|
+
*/
|
|
12
|
+
export declare const OBSOLETE_FRAMEWORK_NARRATIVE_FILENAME: "vbrief.md";
|
|
7
13
|
export declare const LEGACY_ARTIFACT_SUFFIX: ".vbrief.json";
|
|
8
14
|
export declare const MIGRATED_ARTIFACT_SUFFIX: ".xbrief.json";
|
|
9
15
|
export declare const LEGACY_INFO_ROOT_KEY: "vBRIEFInfo";
|
|
@@ -5,6 +5,12 @@ export { LEGACY_VBRIEF_VERSION, VBRIEF_REFERENCE_PREFIX, VBRIEF_VERSION, XBRIEF_
|
|
|
5
5
|
export const V08_CONTAINER_ITEM_TYPES = new Set([...VALID_PLAN_ITEM_TYPES].filter((t) => t !== "task"));
|
|
6
6
|
export const LEGACY_ARTIFACT_DIR = "vbrief";
|
|
7
7
|
export const MIGRATED_ARTIFACT_DIR = "xbrief";
|
|
8
|
+
/**
|
|
9
|
+
* Obsolete framework support narrative at the lifecycle root. Legacy
|
|
10
|
+
* `migrate:xbrief` copied this to `xbrief/vbrief.md` with broken framework
|
|
11
|
+
* links; consumer projections use `xbrief.md` instead (#2806).
|
|
12
|
+
*/
|
|
13
|
+
export const OBSOLETE_FRAMEWORK_NARRATIVE_FILENAME = "vbrief.md";
|
|
8
14
|
export const LEGACY_ARTIFACT_SUFFIX = ".vbrief.json";
|
|
9
15
|
export const MIGRATED_ARTIFACT_SUFFIX = ".xbrief.json";
|
|
10
16
|
export const LEGACY_INFO_ROOT_KEY = "vBRIEFInfo";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { detectStaleUnmanagedHeader, type HeaderPatchOutcome, type HeaderRewriteResult, type HeaderTokenReplacement, LEGACY_HEADER_TOKENS, patchAgentsMdHeader, renderHeaderPatchSummary, renderStaleHeaderLine, rewriteUnmanagedHeaderTokens, type StaleHeaderDetection, } from "./agents-header.js";
|
|
2
|
-
export { VBRIEF_DEPRECATION_MARKER_BODY, VBRIEF_DEPRECATION_MARKER_FILENAME, VBRIEF_DEPRECATION_MARKER_SENTINEL, } from "./constants.js";
|
|
2
|
+
export { OBSOLETE_FRAMEWORK_NARRATIVE_FILENAME, VBRIEF_DEPRECATION_MARKER_BODY, VBRIEF_DEPRECATION_MARKER_FILENAME, VBRIEF_DEPRECATION_MARKER_SENTINEL, } from "./constants.js";
|
|
3
3
|
export { detectLegacyVbriefLayout, detectXbriefConvergence, type LegacyVbriefLayoutDetection, type XbriefConvergenceDetection, type XbriefConvergenceState, } from "./detect.js";
|
|
4
4
|
export { BUILTIN_ALLOW_LIST, type DriftEvaluateOptions, type DriftEvaluateResult, type DriftFinding, type DriftScanMode, evaluateXbriefDrift, LEGACY_REFERENCE_PREFIX, scanCorpusToken, } from "./drift-gate.js";
|
|
5
5
|
export { hasVbriefDeprecationMarker, isDirectory, isEffectivelyEmptyDir, } from "./fs-helpers.js";
|
|
6
|
-
export { convergeLegacyVbriefRoot, emitXbriefMigration, runXbriefMigration, runXbriefMigrationCli, type VbriefConvergeAction, type XbriefMigrationArgs, type XbriefMigrationIo, type XbriefMigrationOutcome, } from "./migrate-project.js";
|
|
6
|
+
export { convergeLegacyVbriefRoot, emitXbriefMigration, removeStaleMigratedFrameworkNarrative, runXbriefMigration, runXbriefMigrationCli, shouldOmitLegacyMigrationFile, type VbriefConvergeAction, type XbriefMigrationArgs, type XbriefMigrationIo, type XbriefMigrationOutcome, } from "./migrate-project.js";
|
|
7
7
|
export { renderXbriefMigrationLine, xbriefMigrationGuidance } from "./signpost.js";
|
|
8
8
|
export { assertFeatureEmissionAllowed, assertLayoutAwareWritePath, FeatureEmissionRejectedError, type JsonObject, type JsonValue, readDeclaredArtifactVersion, resolveLayoutAwareRelativePath, rewriteEmbeddedTokens, SplitLayoutRejectedError, TransformError, transformArtifactV06ToV08, transformArtifactV06ToV08Transactional, } from "./transforms.js";
|
|
9
9
|
export { isPatchOnlyUpgrade, parseSemverPrefix } from "./version.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { detectStaleUnmanagedHeader, LEGACY_HEADER_TOKENS, patchAgentsMdHeader, renderHeaderPatchSummary, renderStaleHeaderLine, rewriteUnmanagedHeaderTokens, } from "./agents-header.js";
|
|
2
|
-
export { VBRIEF_DEPRECATION_MARKER_BODY, VBRIEF_DEPRECATION_MARKER_FILENAME, VBRIEF_DEPRECATION_MARKER_SENTINEL, } from "./constants.js";
|
|
2
|
+
export { OBSOLETE_FRAMEWORK_NARRATIVE_FILENAME, VBRIEF_DEPRECATION_MARKER_BODY, VBRIEF_DEPRECATION_MARKER_FILENAME, VBRIEF_DEPRECATION_MARKER_SENTINEL, } from "./constants.js";
|
|
3
3
|
export { detectLegacyVbriefLayout, detectXbriefConvergence, } from "./detect.js";
|
|
4
4
|
export { BUILTIN_ALLOW_LIST, evaluateXbriefDrift, LEGACY_REFERENCE_PREFIX, scanCorpusToken, } from "./drift-gate.js";
|
|
5
5
|
export { hasVbriefDeprecationMarker, isDirectory, isEffectivelyEmptyDir, } from "./fs-helpers.js";
|
|
6
|
-
export { convergeLegacyVbriefRoot, emitXbriefMigration, runXbriefMigration, runXbriefMigrationCli, } from "./migrate-project.js";
|
|
6
|
+
export { convergeLegacyVbriefRoot, emitXbriefMigration, removeStaleMigratedFrameworkNarrative, runXbriefMigration, runXbriefMigrationCli, shouldOmitLegacyMigrationFile, } from "./migrate-project.js";
|
|
7
7
|
export { renderXbriefMigrationLine, xbriefMigrationGuidance } from "./signpost.js";
|
|
8
8
|
export { assertFeatureEmissionAllowed, assertLayoutAwareWritePath, FeatureEmissionRejectedError, readDeclaredArtifactVersion, resolveLayoutAwareRelativePath, rewriteEmbeddedTokens, SplitLayoutRejectedError, TransformError, transformArtifactV06ToV08, transformArtifactV06ToV08Transactional, } from "./transforms.js";
|
|
9
9
|
export { isPatchOnlyUpgrade, parseSemverPrefix } from "./version.js";
|
|
@@ -33,6 +33,13 @@ export type XbriefMigrationOutcome = {
|
|
|
33
33
|
readonly kind: "config";
|
|
34
34
|
readonly message: string;
|
|
35
35
|
};
|
|
36
|
+
/** True when a legacy lifecycle file is an obsolete framework narrative (#2806). */
|
|
37
|
+
export declare function shouldOmitLegacyMigrationFile(relativePath: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Remove a stale `xbrief/vbrief.md` left by an earlier migrate:xbrief run.
|
|
40
|
+
* Project JSON records and `xbrief/schemas/` are untouched (#2806).
|
|
41
|
+
*/
|
|
42
|
+
export declare function removeStaleMigratedFrameworkNarrative(projectRoot: string): boolean;
|
|
36
43
|
/**
|
|
37
44
|
* Converge a leftover legacy `vbrief/` root to an unambiguous state (#2270).
|
|
38
45
|
* An effectively-empty tree is removed by default (single canonical root); a
|