@deftai/directive-core 0.61.2 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/check-updates/index.d.ts +49 -0
- package/dist/check-updates/index.js +323 -0
- package/dist/install-upgrade/index.js +3 -3
- package/dist/migrate-preflight/index.d.ts +1 -2
- package/dist/migrate-preflight/index.js +16 -36
- package/dist/release/build-dist-runner.d.ts +2 -0
- package/dist/release/build-dist-runner.js +21 -0
- package/dist/release/build-dist.d.ts +14 -0
- package/dist/release/build-dist.js +171 -0
- package/dist/release/constants.d.ts +1 -1
- package/dist/release/constants.js +1 -6
- package/dist/release/index.d.ts +2 -3
- package/dist/release/index.js +2 -3
- package/dist/release/native-steps.d.ts +5 -0
- package/dist/release/native-steps.js +72 -0
- package/dist/release/pipeline.js +8 -27
- package/dist/release-e2e/rollback-bridge.d.ts +1 -1
- package/dist/release-e2e/rollback-bridge.js +3 -17
- package/dist/render/framework-commands.d.ts +2 -2
- package/dist/render/framework-commands.js +102 -84
- package/dist/umbrella-current-shape/index.d.ts +87 -0
- package/dist/umbrella-current-shape/index.js +329 -0
- package/dist/vbrief-validate/precutover.d.ts +3 -0
- package/dist/vbrief-validate/precutover.js +8 -1
- package/dist/verify-env/verify-hooks-installed.d.ts +2 -0
- package/dist/verify-env/verify-hooks-installed.js +20 -4
- package/package.json +15 -3
- package/dist/release/pyproject-sync.d.ts +0 -6
- package/dist/release/pyproject-sync.js +0 -52
- package/dist/release/pyproject.d.ts +0 -3
- package/dist/release/pyproject.js +0 -33
- package/dist/release/python-steps.d.ts +0 -6
- package/dist/release/python-steps.js +0 -143
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Default subprocess timeout for `git ls-remote` (seconds). */
|
|
2
|
+
export declare const REMOTE_PROBE_DEFAULT_TIMEOUT = 5;
|
|
3
|
+
/** Baked-in canonical upstream (#1320). Never probe consumer origin. */
|
|
4
|
+
export declare const DEFT_UPSTREAM_URL = "https://github.com/deftai/directive.git";
|
|
5
|
+
/** Reject git ls-remote targets that could be parsed as options (#CodeQL). */
|
|
6
|
+
export declare function isSafeGitLsRemoteTarget(url: string): boolean;
|
|
7
|
+
/** Return a trimmed upstream URL safe for `git ls-remote`, or null when rejected. */
|
|
8
|
+
export declare function sanitizeGitLsRemoteTarget(url: string): string | null;
|
|
9
|
+
export type RemoteProbeStatus = "ok" | "behind" | "skipped" | "no-upstream" | "no-tags" | "error";
|
|
10
|
+
export interface RemoteProbeResult {
|
|
11
|
+
readonly status: RemoteProbeStatus;
|
|
12
|
+
readonly current: string;
|
|
13
|
+
readonly remote?: string;
|
|
14
|
+
readonly upstream_url?: string;
|
|
15
|
+
readonly reason?: string;
|
|
16
|
+
readonly error?: string;
|
|
17
|
+
}
|
|
18
|
+
export type SemverKey = readonly [number, number, number, number, string];
|
|
19
|
+
export interface GitRunner {
|
|
20
|
+
lsRemoteTags(upstreamUrl: string, timeoutMs: number): string[] | "timeout" | "os-error";
|
|
21
|
+
}
|
|
22
|
+
export declare function parseSemverTag(tag: string): SemverKey | null;
|
|
23
|
+
export declare function maxSemverTag(tags: readonly string[]): string | null;
|
|
24
|
+
export declare function resolveProbeCurrentVersion(projectRoot: string, frameworkRoot?: string): string;
|
|
25
|
+
export declare function resolveUpstreamUrl(projectRoot: string): string;
|
|
26
|
+
export declare function resolveProbeTimeout(env?: NodeJS.ProcessEnv): number;
|
|
27
|
+
export declare function noNetworkActive(env?: NodeJS.ProcessEnv): boolean;
|
|
28
|
+
export declare function parseLsRemoteTags(stdout: string): string[];
|
|
29
|
+
export declare function defaultGitRunner(): GitRunner;
|
|
30
|
+
export interface RunRemoteProbeOptions {
|
|
31
|
+
readonly projectRoot: string;
|
|
32
|
+
readonly frameworkRoot?: string;
|
|
33
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
34
|
+
readonly git?: GitRunner;
|
|
35
|
+
}
|
|
36
|
+
export declare function runRemoteProbe(options: RunRemoteProbeOptions): RemoteProbeResult;
|
|
37
|
+
export interface EmitCheckUpdatesOptions {
|
|
38
|
+
readonly jsonMode: boolean;
|
|
39
|
+
readonly writeOut: (text: string) => void;
|
|
40
|
+
}
|
|
41
|
+
export declare function emitCheckUpdates(result: RemoteProbeResult, options: EmitCheckUpdatesOptions): number;
|
|
42
|
+
export declare function runCheckUpdates(argv: readonly string[], options?: {
|
|
43
|
+
projectRoot?: string;
|
|
44
|
+
frameworkRoot?: string;
|
|
45
|
+
env?: NodeJS.ProcessEnv;
|
|
46
|
+
git?: GitRunner;
|
|
47
|
+
writeOut?: (text: string) => void;
|
|
48
|
+
}): number;
|
|
49
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { manifestTagToVersion, parseInstallManifest } from "../doctor/manifest.js";
|
|
5
|
+
import { resolveVersion } from "../doctor/paths.js";
|
|
6
|
+
/** Default subprocess timeout for `git ls-remote` (seconds). */
|
|
7
|
+
export const REMOTE_PROBE_DEFAULT_TIMEOUT = 5.0;
|
|
8
|
+
/** Baked-in canonical upstream (#1320). Never probe consumer origin. */
|
|
9
|
+
export const DEFT_UPSTREAM_URL = "https://github.com/deftai/directive.git";
|
|
10
|
+
const MANIFEST_UPSTREAM_URL_KEYS = [
|
|
11
|
+
"source_url",
|
|
12
|
+
"url",
|
|
13
|
+
"upstream_url",
|
|
14
|
+
"upstream",
|
|
15
|
+
"origin",
|
|
16
|
+
];
|
|
17
|
+
/** Reject git ls-remote targets that could be parsed as options (#CodeQL). */
|
|
18
|
+
export function isSafeGitLsRemoteTarget(url) {
|
|
19
|
+
return sanitizeGitLsRemoteTarget(url) !== null;
|
|
20
|
+
}
|
|
21
|
+
/** Return a trimmed upstream URL safe for `git ls-remote`, or null when rejected. */
|
|
22
|
+
export function sanitizeGitLsRemoteTarget(url) {
|
|
23
|
+
const trimmed = url.trim();
|
|
24
|
+
if (!trimmed || trimmed.startsWith("-")) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
if (/^https?:\/\/\S+$/i.test(trimmed)) {
|
|
28
|
+
return trimmed;
|
|
29
|
+
}
|
|
30
|
+
if (/^git@[\w.-]+:[\w./-]+\.git$/i.test(trimmed)) {
|
|
31
|
+
return trimmed;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function normalizePrereleaseForSort(pre) {
|
|
36
|
+
const dot = pre.indexOf(".");
|
|
37
|
+
if (dot <= 0) {
|
|
38
|
+
return pre;
|
|
39
|
+
}
|
|
40
|
+
const kind = pre.slice(0, dot).toLowerCase();
|
|
41
|
+
if (kind !== "alpha" && kind !== "beta" && kind !== "rc") {
|
|
42
|
+
return pre;
|
|
43
|
+
}
|
|
44
|
+
const afterKind = pre.slice(dot + 1);
|
|
45
|
+
let numEnd = 0;
|
|
46
|
+
while (numEnd < afterKind.length) {
|
|
47
|
+
const ch = afterKind[numEnd];
|
|
48
|
+
if (ch === undefined || ch < "0" || ch > "9") {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
numEnd += 1;
|
|
52
|
+
}
|
|
53
|
+
if (numEnd === 0) {
|
|
54
|
+
return pre;
|
|
55
|
+
}
|
|
56
|
+
const padded = afterKind.slice(0, numEnd).padStart(8, "0");
|
|
57
|
+
return `${kind}.${padded}${afterKind.slice(numEnd)}`;
|
|
58
|
+
}
|
|
59
|
+
const SEMVER_TAG_RE = /^v?(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(?<pre>-[\w][\w.-]*)?$/;
|
|
60
|
+
export function parseSemverTag(tag) {
|
|
61
|
+
if (!tag) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const match = SEMVER_TAG_RE.exec(tag.trim());
|
|
65
|
+
if (!match?.groups) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const pre = match.groups.pre ?? "";
|
|
69
|
+
const preStripped = pre.replace(/^-/, "");
|
|
70
|
+
return [
|
|
71
|
+
Number(match.groups.major),
|
|
72
|
+
Number(match.groups.minor),
|
|
73
|
+
Number(match.groups.patch),
|
|
74
|
+
pre ? 0 : 1,
|
|
75
|
+
normalizePrereleaseForSort(preStripped),
|
|
76
|
+
];
|
|
77
|
+
}
|
|
78
|
+
export function maxSemverTag(tags) {
|
|
79
|
+
const parsed = [];
|
|
80
|
+
for (const tag of tags) {
|
|
81
|
+
const key = parseSemverTag(tag);
|
|
82
|
+
if (key !== null) {
|
|
83
|
+
parsed.push({ key, tag });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (parsed.length === 0) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
parsed.sort((a, b) => {
|
|
90
|
+
for (let i = 0; i < a.key.length; i += 1) {
|
|
91
|
+
const av = a.key[i];
|
|
92
|
+
const bv = b.key[i];
|
|
93
|
+
if (typeof av === "number" && typeof bv === "number" && av !== bv) {
|
|
94
|
+
return av - bv;
|
|
95
|
+
}
|
|
96
|
+
if (typeof av === "string" && typeof bv === "string" && av !== bv) {
|
|
97
|
+
return av < bv ? -1 : 1;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return 0;
|
|
101
|
+
});
|
|
102
|
+
return parsed[parsed.length - 1]?.tag ?? null;
|
|
103
|
+
}
|
|
104
|
+
function readVendoredManifest(projectRoot) {
|
|
105
|
+
for (const candidate of [
|
|
106
|
+
join(projectRoot, ".deft", "core", "VERSION"),
|
|
107
|
+
join(projectRoot, ".deft", "VERSION"),
|
|
108
|
+
join(projectRoot, "deft", "VERSION"),
|
|
109
|
+
]) {
|
|
110
|
+
if (!existsSync(candidate)) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
return parseInstallManifest(readFileSync(candidate, "utf8"));
|
|
115
|
+
}
|
|
116
|
+
catch { }
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
export function resolveProbeCurrentVersion(projectRoot, frameworkRoot) {
|
|
121
|
+
const manifest = readVendoredManifest(projectRoot);
|
|
122
|
+
if (manifest) {
|
|
123
|
+
const derived = manifestTagToVersion(manifest);
|
|
124
|
+
if (derived) {
|
|
125
|
+
return derived;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return resolveVersion(frameworkRoot).replace(/^v/, "");
|
|
129
|
+
}
|
|
130
|
+
export function resolveUpstreamUrl(projectRoot) {
|
|
131
|
+
const manifest = readVendoredManifest(projectRoot);
|
|
132
|
+
if (manifest) {
|
|
133
|
+
for (const key of MANIFEST_UPSTREAM_URL_KEYS) {
|
|
134
|
+
const value = manifest[key];
|
|
135
|
+
if (typeof value === "string" && value.trim()) {
|
|
136
|
+
const safe = sanitizeGitLsRemoteTarget(value);
|
|
137
|
+
if (safe) {
|
|
138
|
+
return safe;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return DEFT_UPSTREAM_URL;
|
|
144
|
+
}
|
|
145
|
+
export function resolveProbeTimeout(env = process.env) {
|
|
146
|
+
const raw = (env.DEFT_REMOTE_PROBE_TIMEOUT ?? "").trim();
|
|
147
|
+
if (!raw) {
|
|
148
|
+
return REMOTE_PROBE_DEFAULT_TIMEOUT;
|
|
149
|
+
}
|
|
150
|
+
const value = Number(raw);
|
|
151
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
152
|
+
return REMOTE_PROBE_DEFAULT_TIMEOUT;
|
|
153
|
+
}
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
export function noNetworkActive(env = process.env) {
|
|
157
|
+
return (env.DEFT_NO_NETWORK ?? "").trim() === "1";
|
|
158
|
+
}
|
|
159
|
+
export function parseLsRemoteTags(stdout) {
|
|
160
|
+
const tags = [];
|
|
161
|
+
for (const line of stdout.split("\n")) {
|
|
162
|
+
const marker = "\trefs/tags/";
|
|
163
|
+
const idx = line.indexOf(marker);
|
|
164
|
+
if (idx < 0) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const tag = line.slice(idx + marker.length).trim();
|
|
168
|
+
if (tag) {
|
|
169
|
+
tags.push(tag);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return tags;
|
|
173
|
+
}
|
|
174
|
+
export function defaultGitRunner() {
|
|
175
|
+
return {
|
|
176
|
+
lsRemoteTags(upstreamUrl, timeoutMs) {
|
|
177
|
+
const safeUrl = sanitizeGitLsRemoteTarget(upstreamUrl);
|
|
178
|
+
if (!safeUrl) {
|
|
179
|
+
return "os-error";
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
const stdout = execFileSync("git", ["ls-remote", "--tags", "--refs", "--", safeUrl], {
|
|
183
|
+
encoding: "utf8",
|
|
184
|
+
timeout: timeoutMs,
|
|
185
|
+
});
|
|
186
|
+
return parseLsRemoteTags(stdout);
|
|
187
|
+
}
|
|
188
|
+
catch (exc) {
|
|
189
|
+
if (exc instanceof Error &&
|
|
190
|
+
(exc.message.includes("ETIMEDOUT") || exc.message.includes("timed out"))) {
|
|
191
|
+
return "timeout";
|
|
192
|
+
}
|
|
193
|
+
return "os-error";
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
export function runRemoteProbe(options) {
|
|
199
|
+
const env = options.env ?? process.env;
|
|
200
|
+
const current = resolveProbeCurrentVersion(options.projectRoot, options.frameworkRoot);
|
|
201
|
+
if (noNetworkActive(env)) {
|
|
202
|
+
return {
|
|
203
|
+
status: "skipped",
|
|
204
|
+
reason: "DEFT_NO_NETWORK=1",
|
|
205
|
+
current,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const timeoutSec = resolveProbeTimeout(env);
|
|
209
|
+
const upstreamUrl = resolveUpstreamUrl(options.projectRoot);
|
|
210
|
+
const git = options.git ?? defaultGitRunner();
|
|
211
|
+
const tagsResult = git.lsRemoteTags(upstreamUrl, Math.ceil(timeoutSec * 1000));
|
|
212
|
+
if (tagsResult === "timeout") {
|
|
213
|
+
return {
|
|
214
|
+
status: "error",
|
|
215
|
+
current,
|
|
216
|
+
upstream_url: upstreamUrl,
|
|
217
|
+
error: "timeout",
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (tagsResult === "os-error") {
|
|
221
|
+
return {
|
|
222
|
+
status: "error",
|
|
223
|
+
current,
|
|
224
|
+
upstream_url: upstreamUrl,
|
|
225
|
+
error: "Error: git unavailable",
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const remoteTag = maxSemverTag(tagsResult);
|
|
229
|
+
if (remoteTag === null) {
|
|
230
|
+
return {
|
|
231
|
+
status: "no-tags",
|
|
232
|
+
current,
|
|
233
|
+
upstream_url: upstreamUrl,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
const remoteKey = parseSemverTag(remoteTag);
|
|
237
|
+
const currentKey = parseSemverTag(current.startsWith("v") ? current : `v${current}`);
|
|
238
|
+
if (currentKey !== null && remoteKey !== null && compareSemverKeys(remoteKey, currentKey) > 0) {
|
|
239
|
+
return {
|
|
240
|
+
status: "behind",
|
|
241
|
+
current,
|
|
242
|
+
remote: remoteTag,
|
|
243
|
+
upstream_url: upstreamUrl,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
status: "ok",
|
|
248
|
+
current,
|
|
249
|
+
remote: remoteTag,
|
|
250
|
+
upstream_url: upstreamUrl,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function compareSemverKeys(a, b) {
|
|
254
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
255
|
+
const av = a[i];
|
|
256
|
+
const bv = b[i];
|
|
257
|
+
if (av === bv) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (typeof av === "number" && typeof bv === "number") {
|
|
261
|
+
return av - bv;
|
|
262
|
+
}
|
|
263
|
+
return String(av) < String(bv) ? -1 : 1;
|
|
264
|
+
}
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
export function emitCheckUpdates(result, options) {
|
|
268
|
+
if (options.jsonMode) {
|
|
269
|
+
const payload = {
|
|
270
|
+
status: result.status,
|
|
271
|
+
current: result.current,
|
|
272
|
+
};
|
|
273
|
+
if (result.remote !== undefined) {
|
|
274
|
+
payload.remote = result.remote;
|
|
275
|
+
}
|
|
276
|
+
if (result.upstream_url !== undefined) {
|
|
277
|
+
payload.upstream_url = result.upstream_url;
|
|
278
|
+
}
|
|
279
|
+
if (result.reason !== undefined) {
|
|
280
|
+
payload.reason = result.reason;
|
|
281
|
+
}
|
|
282
|
+
if (result.error !== undefined) {
|
|
283
|
+
payload.error = result.error;
|
|
284
|
+
}
|
|
285
|
+
options.writeOut(`${JSON.stringify(payload, Object.keys(payload).sort())}\n`);
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
const current = result.current;
|
|
289
|
+
switch (result.status) {
|
|
290
|
+
case "ok":
|
|
291
|
+
options.writeOut(`OK upstream=${result.remote}\n`);
|
|
292
|
+
break;
|
|
293
|
+
case "behind":
|
|
294
|
+
options.writeOut(`BEHIND upstream=${result.remote} current=v${current} commits-behind=unknown\n`);
|
|
295
|
+
break;
|
|
296
|
+
case "skipped":
|
|
297
|
+
options.writeOut(`SKIPPED reason=DEFT_NO_NETWORK current=v${current}\n`);
|
|
298
|
+
break;
|
|
299
|
+
case "no-upstream":
|
|
300
|
+
options.writeOut(`NO-UPSTREAM current=v${current}\n`);
|
|
301
|
+
break;
|
|
302
|
+
case "no-tags":
|
|
303
|
+
options.writeOut(`NO-TAGS current=v${current} upstream=${result.upstream_url ?? ""}\n`);
|
|
304
|
+
break;
|
|
305
|
+
default:
|
|
306
|
+
options.writeOut(`ERROR current=v${current} upstream=${result.upstream_url ?? ""} error=${result.error ?? "unknown"}\n`);
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return result.status === "behind" ? 1 : 0;
|
|
311
|
+
}
|
|
312
|
+
export function runCheckUpdates(argv, options = {}) {
|
|
313
|
+
const jsonMode = argv.includes("--json");
|
|
314
|
+
const result = runRemoteProbe({
|
|
315
|
+
projectRoot: options.projectRoot ?? process.cwd(),
|
|
316
|
+
frameworkRoot: options.frameworkRoot,
|
|
317
|
+
env: options.env,
|
|
318
|
+
git: options.git,
|
|
319
|
+
});
|
|
320
|
+
const writeOut = options.writeOut ?? ((text) => process.stdout.write(text));
|
|
321
|
+
return emitCheckUpdates(result, { jsonMode, writeOut });
|
|
322
|
+
}
|
|
323
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -6,7 +6,7 @@ import { buildInstallManifestText, CANONICAL_INSTALL_ROOT, } from "../init-depos
|
|
|
6
6
|
import { agentsRefreshPlan } from "../platform/agents-md.js";
|
|
7
7
|
import { DEV_FALLBACK } from "../platform/constants.js";
|
|
8
8
|
import { resolveVersion } from "../platform/resolve-version.js";
|
|
9
|
-
import { detectPreCutoverLegacy } from "../vbrief-validate/precutover.js";
|
|
9
|
+
import { detectPreCutoverLegacy, frozenPreCutoverMigrationGuidance, } from "../vbrief-validate/precutover.js";
|
|
10
10
|
const ENGINE_PACKAGE_FALLBACK = "0.0.0";
|
|
11
11
|
/**
|
|
12
12
|
* Resolve the framework version for an upgrade (#2053).
|
|
@@ -193,7 +193,7 @@ export function runInstallUpgrade(args, io) {
|
|
|
193
193
|
}
|
|
194
194
|
const legacy = detectPreCutoverLegacy(projectRoot);
|
|
195
195
|
if (legacy.length > 0) {
|
|
196
|
-
io.writeOut(`Pre-v0.20 document model detected (${legacy.join(", ")}).
|
|
196
|
+
io.writeOut(`Pre-v0.20 document model detected (${legacy.join(", ")}). ${frozenPreCutoverMigrationGuidance()}\n`);
|
|
197
197
|
}
|
|
198
198
|
const vbriefDir = join(projectRoot, "vbrief");
|
|
199
199
|
const targetDir = existsSync(vbriefDir) && statSync(vbriefDir).isDirectory() ? vbriefDir : projectRoot;
|
|
@@ -216,7 +216,7 @@ export function runInstallUpgrade(args, io) {
|
|
|
216
216
|
else {
|
|
217
217
|
io.writeOut(`Updated .deft-version from ${recorded} to ${normalizedVersion}.\n`);
|
|
218
218
|
}
|
|
219
|
-
io.writeOut(
|
|
219
|
+
io.writeOut(`If legacy SPECIFICATION.md or PROJECT.md content remains, see UPGRADING.md § Frozen pre-v0.20 document-model migration (#2068).\n`);
|
|
220
220
|
return runAgentsRefresh(projectRoot, agentsRoot, io);
|
|
221
221
|
}
|
|
222
222
|
//# sourceMappingURL=index.js.map
|
|
@@ -18,11 +18,10 @@ export type MigratePreflightOutcome = {
|
|
|
18
18
|
readonly exitCode: 0 | 1;
|
|
19
19
|
readonly results: readonly CheckResult[];
|
|
20
20
|
} | MigratePreflightConfigError;
|
|
21
|
-
export declare function checkUv(which?: (cmd: string) => string | null): CheckResult;
|
|
22
21
|
export declare function checkLayout(deftRoot: string, projectRoot: string): CheckResult;
|
|
23
22
|
export declare function checkGitClean(projectRoot: string): CheckResult;
|
|
24
23
|
export declare function checkDocumentModel(projectRoot: string): CheckResult;
|
|
25
|
-
export declare function evaluate(deftRoot: string, projectRoot: string
|
|
24
|
+
export declare function evaluate(deftRoot: string, projectRoot: string): {
|
|
26
25
|
exitCode: 0 | 1;
|
|
27
26
|
results: CheckResult[];
|
|
28
27
|
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import { detectPreCutoverLegacy, isCurrentGeneratedSpecification, isGeneratedSpecificationExport, missingLifecycleFolders, } from "../vbrief-validate/precutover.js";
|
|
4
|
+
import { detectPreCutoverLegacy, frozenPreCutoverMigrationGuidance, isCurrentGeneratedSpecification, isGeneratedSpecificationExport, missingLifecycleFolders, } from "../vbrief-validate/precutover.js";
|
|
6
5
|
function resolveContentRoot(frameworkRoot) {
|
|
7
6
|
const nested = join(frameworkRoot, "content");
|
|
8
7
|
try {
|
|
@@ -14,25 +13,7 @@ function resolveContentRoot(frameworkRoot) {
|
|
|
14
13
|
}
|
|
15
14
|
return frameworkRoot;
|
|
16
15
|
}
|
|
17
|
-
export function checkUv(which = defaultWhich) {
|
|
18
|
-
if (which("uv") !== null) {
|
|
19
|
-
return { name: "uv", status: "PASS", message: "uv is on PATH." };
|
|
20
|
-
}
|
|
21
|
-
return {
|
|
22
|
-
name: "uv",
|
|
23
|
-
status: "FAIL",
|
|
24
|
-
message: "uv is not on PATH. Install from https://docs.astral.sh/uv/ and re-run.",
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
16
|
export function checkLayout(deftRoot, projectRoot) {
|
|
28
|
-
const migrator = join(deftRoot, "scripts", "migrate_vbrief.py");
|
|
29
|
-
if (!existsSync(migrator) || !statSync(migrator).isFile()) {
|
|
30
|
-
return {
|
|
31
|
-
name: "layout",
|
|
32
|
-
status: "FAIL",
|
|
33
|
-
message: `Migrator script missing at ${migrator}. The framework checkout appears incomplete or pre-v0.20; refresh per deft/QUICK-START.md.`,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
17
|
const schemasDir = join(resolveContentRoot(deftRoot), "vbrief", "schemas");
|
|
37
18
|
if (!existsSync(schemasDir) || !statSync(schemasDir).isDirectory()) {
|
|
38
19
|
return {
|
|
@@ -46,13 +27,13 @@ export function checkLayout(deftRoot, projectRoot) {
|
|
|
46
27
|
return {
|
|
47
28
|
name: "layout",
|
|
48
29
|
status: "WARN",
|
|
49
|
-
message: `Project vbrief/ not present at ${projectVbrief} --
|
|
30
|
+
message: `Project vbrief/ not present at ${projectVbrief} -- expected for greenfield projects.`,
|
|
50
31
|
};
|
|
51
32
|
}
|
|
52
33
|
return {
|
|
53
34
|
name: "layout",
|
|
54
35
|
status: "PASS",
|
|
55
|
-
message: `Framework
|
|
36
|
+
message: `Framework schemas present; project vbrief/ at ${projectVbrief}.`,
|
|
56
37
|
};
|
|
57
38
|
}
|
|
58
39
|
export function checkGitClean(projectRoot) {
|
|
@@ -65,7 +46,7 @@ export function checkGitClean(projectRoot) {
|
|
|
65
46
|
return {
|
|
66
47
|
name: "git-clean",
|
|
67
48
|
status: "WARN",
|
|
68
|
-
message: "Working tree is dirty.
|
|
49
|
+
message: "Working tree is dirty. Commit or stash before running a frozen-release migration.",
|
|
69
50
|
};
|
|
70
51
|
}
|
|
71
52
|
return { name: "git-clean", status: "PASS", message: "Working tree is clean." };
|
|
@@ -76,7 +57,7 @@ export function checkGitClean(projectRoot) {
|
|
|
76
57
|
return {
|
|
77
58
|
name: "git-clean",
|
|
78
59
|
status: "WARN",
|
|
79
|
-
message: "git executable not on PATH; skipping working-tree check.
|
|
60
|
+
message: "git executable not on PATH; skipping working-tree check.",
|
|
80
61
|
};
|
|
81
62
|
}
|
|
82
63
|
return {
|
|
@@ -91,8 +72,8 @@ export function checkDocumentModel(projectRoot) {
|
|
|
91
72
|
if (legacy.length > 0) {
|
|
92
73
|
return {
|
|
93
74
|
name: "document-model",
|
|
94
|
-
status: "
|
|
95
|
-
message: `
|
|
75
|
+
status: "FAIL",
|
|
76
|
+
message: `Pre-v0.20 document model detected (${legacy.join(", ")}). ${frozenPreCutoverMigrationGuidance()}`,
|
|
96
77
|
};
|
|
97
78
|
}
|
|
98
79
|
const specPath = join(projectRoot, "SPECIFICATION.md");
|
|
@@ -117,8 +98,8 @@ export function checkDocumentModel(projectRoot) {
|
|
|
117
98
|
if (isCurrentGeneratedSpecification(projectRoot, content)) {
|
|
118
99
|
return {
|
|
119
100
|
name: "document-model",
|
|
120
|
-
status: "
|
|
121
|
-
message: "Current generated SPECIFICATION.md detected (source: vbrief/specification.vbrief.json);
|
|
101
|
+
status: "PASS",
|
|
102
|
+
message: "Current generated SPECIFICATION.md detected (source: vbrief/specification.vbrief.json); pre-v0.20 migration is not needed.",
|
|
122
103
|
};
|
|
123
104
|
}
|
|
124
105
|
}
|
|
@@ -128,20 +109,19 @@ export function checkDocumentModel(projectRoot) {
|
|
|
128
109
|
if (missing.length > 0) {
|
|
129
110
|
return {
|
|
130
111
|
name: "document-model",
|
|
131
|
-
status: "
|
|
132
|
-
message: `Partial vBRIEF layout detected; missing lifecycle folder(s): ${missing.join(", ")}
|
|
112
|
+
status: "FAIL",
|
|
113
|
+
message: `Partial vBRIEF layout detected; missing lifecycle folder(s): ${missing.join(", ")}. Create the folders or follow ${frozenPreCutoverMigrationGuidance()}`,
|
|
133
114
|
};
|
|
134
115
|
}
|
|
135
116
|
}
|
|
136
117
|
return {
|
|
137
118
|
name: "document-model",
|
|
138
|
-
status: "
|
|
139
|
-
message: "No
|
|
119
|
+
status: "PASS",
|
|
120
|
+
message: "No pre-v0.20 document-model artifacts detected.",
|
|
140
121
|
};
|
|
141
122
|
}
|
|
142
|
-
export function evaluate(deftRoot, projectRoot
|
|
123
|
+
export function evaluate(deftRoot, projectRoot) {
|
|
143
124
|
const results = [
|
|
144
|
-
checkUv(which),
|
|
145
125
|
checkLayout(deftRoot, projectRoot),
|
|
146
126
|
checkDocumentModel(projectRoot),
|
|
147
127
|
checkGitClean(projectRoot),
|
|
@@ -183,10 +163,10 @@ export function emitMigratePreflight(outcome, io, quiet) {
|
|
|
183
163
|
io.writeOut(line);
|
|
184
164
|
}
|
|
185
165
|
if (outcome.exitCode === 1) {
|
|
186
|
-
io.writeErr("migrate:preflight FAILED --
|
|
166
|
+
io.writeErr("migrate:preflight FAILED -- pre-v0.20 document model or incomplete vBRIEF layout. Resolve using UPGRADING.md § Frozen pre-v0.20 document-model migration (#2068).\n");
|
|
187
167
|
}
|
|
188
168
|
else {
|
|
189
|
-
io.writeOut("migrate:preflight OK --
|
|
169
|
+
io.writeOut("migrate:preflight OK -- no pre-v0.20 document-model migration required.\n");
|
|
190
170
|
}
|
|
191
171
|
return outcome.exitCode;
|
|
192
172
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subprocess entry for sync callers (release pipeline, task build).
|
|
3
|
+
* Top-level await keeps the process alive until the archive is written.
|
|
4
|
+
*/
|
|
5
|
+
import { buildArchive, selectFormat } from "./build-dist.js";
|
|
6
|
+
const version = process.argv[2];
|
|
7
|
+
const root = process.argv[3];
|
|
8
|
+
if (!version || !root) {
|
|
9
|
+
process.stderr.write("usage: build-dist-runner <version> <root>\n");
|
|
10
|
+
process.exit(2);
|
|
11
|
+
}
|
|
12
|
+
const fmt = selectFormat(process.env.DEFT_BUILD_FORMAT);
|
|
13
|
+
try {
|
|
14
|
+
const out = await buildArchive(root, version, fmt);
|
|
15
|
+
process.stdout.write(`${out}\n`);
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
process.stderr.write(`${String(err)}\n`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=build-dist-runner.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const DEFAULT_EXCLUDES: Set<string>;
|
|
2
|
+
export declare const DEFAULT_EXCLUDED_PATH_PREFIXES: readonly ["history/archive", "vbrief/completed", "vbrief/cancelled"];
|
|
3
|
+
export declare const ARCHIVE_ROOT = "deft";
|
|
4
|
+
export declare const CONTENT_PREFIX = "content/";
|
|
5
|
+
export declare function iterSourceFiles(root: string, excludes?: ReadonlySet<string>, excludedPrefixes?: readonly string[]): Array<{
|
|
6
|
+
absPath: string;
|
|
7
|
+
archiveRel: string;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function selectFormat(arg: string | null | undefined): "tar" | "zip";
|
|
10
|
+
export declare function outputPath(root: string, version: string, fmt: "tar" | "zip"): string;
|
|
11
|
+
export declare function buildArchive(root: string, version: string, fmt: "tar" | "zip", extraExcludes?: readonly string[]): Promise<string>;
|
|
12
|
+
export declare function parseExtraExcludes(raw: string): string[];
|
|
13
|
+
export declare function main(argv: readonly string[]): Promise<number>;
|
|
14
|
+
//# sourceMappingURL=build-dist.d.ts.map
|