@deftai/directive-core 0.72.0 → 0.73.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.
- package/dist/branch/evaluate.js +2 -1
- package/dist/category-b-namespace/index.js +16 -2
- package/dist/codebase/default-extractor.js +9 -1
- package/dist/codebase/map.js +1 -1
- package/dist/deposit/copy-tree.js +31 -7
- package/dist/doctor/checks.d.ts +9 -0
- package/dist/doctor/checks.js +85 -5
- package/dist/doctor/main.js +2 -1
- package/dist/eval/health.js +7 -1
- package/dist/eval/readback.js +7 -1
- package/dist/init-deposit/gitignore.js +6 -0
- package/dist/init-deposit/hygiene.d.ts +30 -0
- package/dist/init-deposit/hygiene.js +60 -1
- package/dist/init-deposit/refresh.d.ts +16 -2
- package/dist/init-deposit/refresh.js +162 -52
- package/dist/init-deposit/scaffold.d.ts +11 -1
- package/dist/init-deposit/scaffold.js +9 -8
- package/dist/integration-e2e/helpers.js +7 -7
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/index.js +1 -1
- package/dist/layout/resolve.d.ts +47 -31
- package/dist/layout/resolve.js +93 -36
- package/dist/lifecycle/lifecycle-hygiene.d.ts +1 -1
- package/dist/lifecycle/lifecycle-hygiene.js +1 -1
- package/dist/migrate-preflight/index.js +10 -10
- package/dist/policy/disclosure.d.ts +1 -0
- package/dist/policy/disclosure.js +1 -0
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.js +1 -0
- package/dist/policy/policy-invocation.d.ts +5 -0
- package/dist/policy/policy-invocation.js +9 -0
- package/dist/policy/resolve.d.ts +2 -2
- package/dist/policy/resolve.js +15 -5
- package/dist/policy/value-feedback.js +6 -3
- package/dist/policy/wip.d.ts +1 -1
- package/dist/policy/wip.js +15 -3
- package/dist/preflight-cache/evaluate.js +1 -1
- package/dist/release/native-steps.js +14 -2
- package/dist/render/export-spec.js +2 -2
- package/dist/render/project-render.js +40 -9
- package/dist/scope/decompose.js +12 -2
- package/dist/session/resume-conditions.js +2 -2
- package/dist/session/ritual-sentinel.d.ts +1 -1
- package/dist/session/ritual-sentinel.js +6 -4
- package/dist/slice/constants.d.ts +1 -1
- package/dist/slice/constants.js +1 -1
- package/dist/slice/project-context.js +1 -1
- package/dist/spec-authority/resolver.js +9 -2
- package/dist/swarm/complete-cohort.js +14 -1
- package/dist/swarm/constants.d.ts +1 -1
- package/dist/swarm/constants.js +2 -1
- package/dist/swarm/readiness.js +33 -3
- package/dist/swarm/subagent-backend.js +2 -1
- package/dist/task-surface/index.js +3 -3
- package/dist/triage/actions/candidates-log.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +22 -10
- package/dist/triage/bootstrap/index.d.ts +1 -1
- package/dist/triage/bootstrap/index.js +9 -3
- package/dist/triage/cache-path.d.ts +1 -1
- package/dist/triage/cache-path.js +32 -6
- package/dist/triage/classify/index.d.ts +1 -1
- package/dist/triage/classify/index.js +11 -4
- package/dist/triage/help/registry-data.d.ts +2 -2
- package/dist/triage/help/registry-data.js +2 -2
- package/dist/triage/queue/constants.d.ts +4 -4
- package/dist/triage/queue/constants.js +4 -4
- package/dist/triage/queue/scope-walk.js +7 -1
- package/dist/triage/reconcile/reconcile.js +23 -3
- package/dist/triage/reconcile/types.d.ts +1 -1
- package/dist/triage/reconcile/types.js +1 -1
- package/dist/triage/refresh/refresh.js +9 -2
- package/dist/triage/scope/constants.d.ts +2 -2
- package/dist/triage/scope/constants.js +2 -2
- package/dist/triage/scope/renderers.js +7 -1
- package/dist/triage/smoketest/index.js +9 -6
- package/dist/triage/subscribe/index.d.ts +2 -2
- package/dist/triage/subscribe/index.js +2 -2
- package/dist/triage/summary/index.d.ts +2 -2
- package/dist/triage/summary/index.js +1 -1
- package/dist/triage/summary/reconcilable.d.ts +1 -1
- package/dist/triage/welcome/constants.d.ts +2 -2
- package/dist/triage/welcome/constants.js +2 -2
- package/dist/triage/welcome/onboard.js +8 -1
- package/dist/triage/welcome/prior-state.js +14 -2
- package/dist/triage/welcome/summary.js +14 -2
- package/dist/triage/welcome/writers.js +7 -1
- package/dist/ts-check-lane/run-lane.d.ts +4 -0
- package/dist/ts-check-lane/run-lane.js +20 -6
- package/dist/validate-content/validate-strategy-output.js +11 -1
- package/dist/value/feedback-file.js +3 -2
- package/dist/value/readback.js +3 -2
- package/dist/vbrief-build/parity-scenarios.js +3 -3
- package/dist/vbrief-reconcile/labels.js +16 -1
- package/dist/vbrief-reconcile/parity-scenarios.js +10 -10
- package/dist/vbrief-reconcile/umbrellas.js +16 -1
- package/dist/vbrief-validate/main.js +13 -1
- package/dist/vbrief-validate/precutover.js +33 -5
- package/dist/vbrief-validate/staleness.d.ts +1 -1
- package/dist/vbrief-validate/staleness.js +5 -2
- package/dist/verify-source/code-structure-validate.js +7 -1
- package/dist/xbrief-migrate/signpost.d.ts +4 -4
- package/dist/xbrief-migrate/signpost.js +11 -15
- package/package.json +3 -3
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Refs #1942, #1430, #1671.
|
|
9
9
|
*/
|
|
10
|
+
import { execFileSync } from "node:child_process";
|
|
10
11
|
import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
11
12
|
import { platform as osPlatform } from "node:os";
|
|
12
13
|
import { join, resolve } from "node:path";
|
|
@@ -20,8 +21,8 @@ import { resolveLifecycleRoot } from "../layout/resolve.js";
|
|
|
20
21
|
import { DEV_FALLBACK } from "../platform/constants.js";
|
|
21
22
|
import { checkLocalEngineIntegrity, classify, ENGINE_PACKAGE, plan, renderGlobalInstall, resolveEngine, } from "../resolution/index.js";
|
|
22
23
|
import { gitPorcelain } from "../story-ready/git.js";
|
|
23
|
-
import { ensureInitGitignoreLines } from "./gitignore.js";
|
|
24
|
-
import { depositStagePaths, isInstallerManagedPath, printCommitGuidance } from "./hygiene.js";
|
|
24
|
+
import { ensureInitGitignoreLines, isDepositTrackedInGit } from "./gitignore.js";
|
|
25
|
+
import { depositStagePaths, isInstallerManagedPath, printCommitGuidance, pruneStrayDepositPaths, } from "./hygiene.js";
|
|
25
26
|
import { parseInitArgv } from "./init-deposit.js";
|
|
26
27
|
import { buildLegacyRefusalJson, buildLegacyRefusalMessage, detectLegacyLayout, LEGACY_LAYOUT_REFUSED_EXIT_CODE, LegacyLayoutRefusedError, } from "./legacy-detect.js";
|
|
27
28
|
import { printMigrateNudgeIfNeeded } from "./migrate.js";
|
|
@@ -155,14 +156,20 @@ function syncBareVersionMarker(projectDir, version) {
|
|
|
155
156
|
const normalized = normalizeVersion(version);
|
|
156
157
|
if (!normalized || normalized === DEV_FALLBACK)
|
|
157
158
|
return;
|
|
158
|
-
|
|
159
|
+
let vbriefDir = null;
|
|
160
|
+
try {
|
|
161
|
+
vbriefDir = resolveLifecycleRoot(projectDir);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// No xbrief/ layout present — write the root-level derivative instead.
|
|
165
|
+
}
|
|
159
166
|
let targetDir = projectDir;
|
|
160
167
|
try {
|
|
161
|
-
if (statSync(vbriefDir).isDirectory())
|
|
168
|
+
if (vbriefDir !== null && statSync(vbriefDir).isDirectory())
|
|
162
169
|
targetDir = vbriefDir;
|
|
163
170
|
}
|
|
164
171
|
catch {
|
|
165
|
-
//
|
|
172
|
+
// xbrief/ absent — write the root-level derivative instead
|
|
166
173
|
}
|
|
167
174
|
try {
|
|
168
175
|
mkdirSync(targetDir, { recursive: true });
|
|
@@ -212,11 +219,12 @@ function unquoteGitPath(path) {
|
|
|
212
219
|
}
|
|
213
220
|
return path;
|
|
214
221
|
}
|
|
215
|
-
function
|
|
216
|
-
const
|
|
222
|
+
function porcelainStatusEntries(porcelain) {
|
|
223
|
+
const entries = [];
|
|
217
224
|
for (const line of porcelain.split("\n")) {
|
|
218
225
|
if (line.length < 4)
|
|
219
226
|
continue;
|
|
227
|
+
const status = line.slice(0, 2);
|
|
220
228
|
let rest = line.slice(3);
|
|
221
229
|
const arrow = rest.indexOf(" -> ");
|
|
222
230
|
if (arrow >= 0)
|
|
@@ -224,43 +232,102 @@ function porcelainStatusPaths(porcelain) {
|
|
|
224
232
|
const trimmed = unquoteGitPath(rest.trim());
|
|
225
233
|
if (!trimmed)
|
|
226
234
|
continue;
|
|
227
|
-
|
|
235
|
+
entries.push({ status, path: trimmed.replace(/\\/g, "/") });
|
|
228
236
|
}
|
|
229
|
-
return
|
|
237
|
+
return entries;
|
|
230
238
|
}
|
|
231
|
-
function
|
|
239
|
+
function classifyChangedEntries(changed) {
|
|
232
240
|
const core = [];
|
|
233
241
|
const installerManaged = [];
|
|
234
|
-
for (const
|
|
242
|
+
for (const entry of changed) {
|
|
243
|
+
const { path } = entry;
|
|
235
244
|
if (!path)
|
|
236
245
|
continue;
|
|
237
246
|
if (path.startsWith(".deft/core/") || path === ".deft/core") {
|
|
238
|
-
core.push(
|
|
247
|
+
core.push(entry);
|
|
239
248
|
}
|
|
240
249
|
else if (isInstallerManagedPath(path)) {
|
|
241
|
-
installerManaged.push(
|
|
250
|
+
installerManaged.push(entry);
|
|
242
251
|
}
|
|
243
252
|
}
|
|
244
253
|
return { core, installerManaged };
|
|
245
254
|
}
|
|
255
|
+
function isCrlfNoiseCandidate(status) {
|
|
256
|
+
return status.includes("M") && /^[ M]+$/.test(status);
|
|
257
|
+
}
|
|
258
|
+
function normalizeGitNameList(output) {
|
|
259
|
+
return output
|
|
260
|
+
.split("\n")
|
|
261
|
+
.map((line) => line.trim().replace(/\\/g, "/"))
|
|
262
|
+
.filter(Boolean);
|
|
263
|
+
}
|
|
264
|
+
function gitSemanticDiffNames(projectRoot, paths) {
|
|
265
|
+
if (paths.length === 0)
|
|
266
|
+
return [];
|
|
267
|
+
try {
|
|
268
|
+
const args = ["diff", "--ignore-cr-at-eol", "--name-only", "--", ...paths];
|
|
269
|
+
const worktree = execFileSync("git", args, {
|
|
270
|
+
cwd: projectRoot,
|
|
271
|
+
encoding: "utf8",
|
|
272
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
273
|
+
});
|
|
274
|
+
const cached = execFileSync("git", ["diff", "--cached", "--ignore-cr-at-eol", "--name-only", "--", ...paths], {
|
|
275
|
+
cwd: projectRoot,
|
|
276
|
+
encoding: "utf8",
|
|
277
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
278
|
+
});
|
|
279
|
+
return [...new Set([...normalizeGitNameList(worktree), ...normalizeGitNameList(cached)])];
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
246
285
|
/** Framework-managed uncommitted paths after refresh (#1671). */
|
|
247
|
-
export function frameworkRefreshSideEffects(projectDir,
|
|
286
|
+
export function frameworkRefreshSideEffects(projectDir, options) {
|
|
287
|
+
const readPorcelain = options?.readPorcelain ?? gitPorcelain;
|
|
288
|
+
const readSemanticDiffNames = options?.readSemanticDiffNames ?? gitSemanticDiffNames;
|
|
248
289
|
const porcelain = readPorcelain(projectDir);
|
|
249
290
|
if (porcelain === null)
|
|
250
|
-
return [];
|
|
251
|
-
const changed =
|
|
252
|
-
const { core, installerManaged } =
|
|
253
|
-
const
|
|
254
|
-
|
|
291
|
+
return { files: [], crlfOnlyCoreFiles: [] };
|
|
292
|
+
const changed = porcelainStatusEntries(porcelain);
|
|
293
|
+
const { core, installerManaged } = classifyChangedEntries(changed);
|
|
294
|
+
const semanticCoreNames = core.length > 0
|
|
295
|
+
? readSemanticDiffNames(projectDir, core.map((entry) => entry.path))
|
|
296
|
+
: [];
|
|
297
|
+
const semanticCoreSet = semanticCoreNames === null ? null : new Set(semanticCoreNames);
|
|
298
|
+
const coreFiles = [];
|
|
299
|
+
const crlfOnlyCoreFiles = [];
|
|
300
|
+
for (const entry of core) {
|
|
301
|
+
if (semanticCoreSet !== null &&
|
|
302
|
+
isCrlfNoiseCandidate(entry.status) &&
|
|
303
|
+
!semanticCoreSet.has(entry.path)) {
|
|
304
|
+
crlfOnlyCoreFiles.push(entry.path);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
coreFiles.push(entry.path);
|
|
308
|
+
}
|
|
309
|
+
const files = [...coreFiles, ...installerManaged.map((entry) => entry.path)].sort();
|
|
310
|
+
return { files, crlfOnlyCoreFiles: crlfOnlyCoreFiles.sort() };
|
|
255
311
|
}
|
|
256
|
-
export function printRefreshSideEffects(io,
|
|
257
|
-
if (
|
|
312
|
+
export function printRefreshSideEffects(io, effects) {
|
|
313
|
+
if (effects.crlfOnlyCoreFiles.length > 0) {
|
|
314
|
+
io.printf("\nWindows line-ending note (#2118): suppressed .deft/core CRLF/LF-only noise; " +
|
|
315
|
+
"ensure .gitattributes contains `.deft/core/** text eol=lf`.\n");
|
|
316
|
+
}
|
|
317
|
+
if (effects.files.length === 0)
|
|
258
318
|
return;
|
|
319
|
+
if (effects.payloadSwapped === false) {
|
|
320
|
+
io.printf("\nFramework-managed files still have semantic uncommitted changes:\n");
|
|
321
|
+
for (const file of effects.files) {
|
|
322
|
+
io.printf(` ${file}\n`);
|
|
323
|
+
}
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
259
326
|
io.printf("\nAGENTS.md refresh side effects (#1671): the refresh and framework payload swap\n");
|
|
260
327
|
io.printf("left these framework files with uncommitted changes -- they belong in the\n");
|
|
261
328
|
io.printf("framework deposit commit (the installer stages them before printing the\n");
|
|
262
329
|
io.printf("`git add` list below, so there are no post-stage stragglers):\n");
|
|
263
|
-
for (const file of files) {
|
|
330
|
+
for (const file of effects.files) {
|
|
264
331
|
io.printf(` ${file}\n`);
|
|
265
332
|
}
|
|
266
333
|
}
|
|
@@ -301,7 +368,8 @@ export function buildUpdateSummaryJson(result, options, updateState) {
|
|
|
301
368
|
user_config_dir: "",
|
|
302
369
|
skills_created: false,
|
|
303
370
|
payload_layout: "vendored",
|
|
304
|
-
strategy:
|
|
371
|
+
strategy: result.strategy,
|
|
372
|
+
already_current: result.alreadyCurrent,
|
|
305
373
|
dirty_tree: false,
|
|
306
374
|
dirty_files: [],
|
|
307
375
|
staged_paths: result.stagedPaths,
|
|
@@ -313,9 +381,12 @@ export function buildUpdateSummaryJson(result, options, updateState) {
|
|
|
313
381
|
};
|
|
314
382
|
}
|
|
315
383
|
export function printUpdateComplete(result, io, updateState) {
|
|
316
|
-
io.printf(
|
|
384
|
+
io.printf(result.alreadyCurrent
|
|
385
|
+
? "\nOK Deft framework payload already current.\n\n"
|
|
386
|
+
: "\nOK Deft framework payload refreshed.\n\n");
|
|
317
387
|
io.printf(` Location : ${result.deftDir}\n`);
|
|
318
388
|
io.printf(` Content : v${normalizeVersion(result.contentVersion)}\n`);
|
|
389
|
+
io.printf(` Strategy : ${result.strategy}\n`);
|
|
319
390
|
if (updateState) {
|
|
320
391
|
io.printf(` State : ${updateState}\n`);
|
|
321
392
|
}
|
|
@@ -350,29 +421,49 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
350
421
|
const engineVersion = readEngine();
|
|
351
422
|
const contentVersion = readContentPackageVersion(contentRoot, readPackageVersion);
|
|
352
423
|
const versionSkewNotice = buildVersionSkewNotice(engineVersion, contentVersion, previousDepositVersion);
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
424
|
+
const alreadyCurrent = previousDepositVersion !== null &&
|
|
425
|
+
normalizeVersion(previousDepositVersion) === normalizeVersion(contentVersion);
|
|
426
|
+
const strategy = alreadyCurrent ? "no-op" : "file-swap";
|
|
427
|
+
if (alreadyCurrent) {
|
|
428
|
+
io.printf("[deft update] Framework payload already current; skipping payload copy.\n");
|
|
429
|
+
migrateLegacyInstallManifest(projectDir, join(deftDir, "VERSION"));
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
await copyContent(contentRoot, deftDir);
|
|
433
|
+
await prunePythonArtifactsFromDeposit(deftDir, projectDir, io);
|
|
434
|
+
// #2347: prune framework-source paths that the content package does not ship.
|
|
435
|
+
// The additive file-swap never removes them, causing the deposit-hygiene
|
|
436
|
+
// advisory to persist across every upgrade until manually cleaned.
|
|
437
|
+
await pruneStrayDepositPaths(deftDir, contentRoot, io);
|
|
438
|
+
const nowIso = seams.nowIso ?? (() => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"));
|
|
439
|
+
const manifestFields = {
|
|
440
|
+
ref: contentVersion.startsWith("v") ? contentVersion : `v${contentVersion}`,
|
|
441
|
+
sha: "content-package",
|
|
442
|
+
tag: contentVersion.startsWith("v") ? contentVersion : `v${contentVersion}`,
|
|
443
|
+
installRoot: CANONICAL_INSTALL_ROOT,
|
|
444
|
+
fetchedAt: nowIso(),
|
|
445
|
+
fetchedBy: "directive-update",
|
|
446
|
+
...(previousManagedBy ? { managedBy: previousManagedBy } : {}),
|
|
447
|
+
};
|
|
448
|
+
const writtenManifestPath = writeInstallManifest(projectDir, deftDir, manifestFields);
|
|
449
|
+
// #2064: retire a stale legacy .deft/VERSION now that the canonical
|
|
450
|
+
// .deft/core/VERSION has been rewritten (folded in from install-upgrade so no
|
|
451
|
+
// manifest behavior is lost by the redirect). Best-effort; never fatal.
|
|
452
|
+
migrateLegacyInstallManifest(projectDir, writtenManifestPath);
|
|
453
|
+
// #2055: regenerate the bare .deft-version derivative so it agrees with the
|
|
454
|
+
// freshly written manifest tag (otherwise doctor's manifest-agreement check
|
|
455
|
+
// fails and the operator must hand-edit the marker). No-op refreshes skip
|
|
456
|
+
// this projection because the manifest tag was not rewritten (#2118).
|
|
457
|
+
syncBareVersionMarker(projectDir, contentVersion);
|
|
458
|
+
}
|
|
374
459
|
const agentsMdUpdated = writeAgentsMd(projectDir, deftDir, io);
|
|
375
|
-
|
|
460
|
+
// #2148: the deft-core-guard CI workflow is only meaningful when the deposit
|
|
461
|
+
// is git-tracked (committed vendor layout). On an npm-managed (gitignored)
|
|
462
|
+
// deposit it creates untracked noise after every `directive update`. Probe
|
|
463
|
+
// git-tracked status once and share the result with the gitignore upkeep below.
|
|
464
|
+
const depositTracked = isDepositTrackedInGit(projectDir, seams.gitLsFiles);
|
|
465
|
+
const skipGuardWorkflow = depositTracked !== true;
|
|
466
|
+
await depositNeutralization(projectDir, io, { skipGuardWorkflow });
|
|
376
467
|
// #2266: non-destructive `.gitignore` upkeep for framework-owned paths. This
|
|
377
468
|
// NEVER un-tracks a committed deposit -- `ensureInitGitignoreLines` only writes
|
|
378
469
|
// `.gitignore` and leaves an already-tracked `.deft/core` tracked. The
|
|
@@ -383,12 +474,27 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
383
474
|
if (args.nonInteractive) {
|
|
384
475
|
taskfileWired = ensureTaskfile(projectDir, io);
|
|
385
476
|
}
|
|
386
|
-
const { stagePaths, staged, stagedPaths } = depositStagePaths(projectDir, {
|
|
387
|
-
includeTaskfile: taskfileWired,
|
|
388
|
-
});
|
|
389
|
-
printCommitGuidance(io, stagePaths, staged);
|
|
390
477
|
const readPorcelain = seams.gitPorcelain ?? gitPorcelain;
|
|
391
|
-
|
|
478
|
+
const effects = frameworkRefreshSideEffects(projectDir, {
|
|
479
|
+
readPorcelain,
|
|
480
|
+
readSemanticDiffNames: seams.gitSemanticDiffNames ?? gitSemanticDiffNames,
|
|
481
|
+
});
|
|
482
|
+
let stagedPaths = [];
|
|
483
|
+
if (!alreadyCurrent || effects.files.length > 0) {
|
|
484
|
+
const stagedResult = depositStagePaths(projectDir, {
|
|
485
|
+
includeTaskfile: taskfileWired,
|
|
486
|
+
includeCore: !alreadyCurrent,
|
|
487
|
+
});
|
|
488
|
+
stagedPaths = stagedResult.stagedPaths;
|
|
489
|
+
if (!alreadyCurrent) {
|
|
490
|
+
printCommitGuidance(io, stagedResult.stagePaths, stagedResult.staged);
|
|
491
|
+
}
|
|
492
|
+
else if (stagedResult.stagedPaths.length > 0) {
|
|
493
|
+
io.printf("\nUpdated installer-managed projections for the current framework deposit:\n");
|
|
494
|
+
io.printf(` git add ${stagedResult.stagedPaths.join(" ")}\n`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
printRefreshSideEffects(io, { ...effects, payloadSwapped: !alreadyCurrent });
|
|
392
498
|
if (versionSkewNotice) {
|
|
393
499
|
io.printf(`${versionSkewNotice}\n`);
|
|
394
500
|
}
|
|
@@ -398,6 +504,8 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
398
504
|
contentVersion,
|
|
399
505
|
engineVersion,
|
|
400
506
|
previousDepositVersion,
|
|
507
|
+
alreadyCurrent,
|
|
508
|
+
strategy,
|
|
401
509
|
agentsMdUpdated,
|
|
402
510
|
versionSkewNotice,
|
|
403
511
|
legacyLayout: false,
|
|
@@ -499,7 +607,9 @@ export async function runRefreshDepositCli(options) {
|
|
|
499
607
|
}
|
|
500
608
|
try {
|
|
501
609
|
const result = await runRefreshDeposit(options, io, options.seams);
|
|
502
|
-
const state =
|
|
610
|
+
const state = result.alreadyCurrent
|
|
611
|
+
? "current"
|
|
612
|
+
: classification?.state;
|
|
503
613
|
if (options.jsonOut) {
|
|
504
614
|
options.writeOut(`${JSON.stringify(buildUpdateSummaryJson(result, options, state), null, 2)}\n`);
|
|
505
615
|
printUpdateComplete(result, { printf: options.writeErr }, state);
|
|
@@ -67,6 +67,16 @@ export declare function ensureCodeqlPathsIgnore(projectDir: string, io: InitDepo
|
|
|
67
67
|
export declare function ensureCoreGuardWorkflow(projectDir: string, io: InitDepositIo): boolean;
|
|
68
68
|
export declare function pruneFrameworkSelfTests(projectDir: string, io: InitDepositIo): Promise<boolean>;
|
|
69
69
|
export declare function pruneVendoredTsTests(projectDir: string, io: InitDepositIo): Promise<number>;
|
|
70
|
+
export interface DepositNeutralizationOptions {
|
|
71
|
+
/**
|
|
72
|
+
* #2148: skip depositing `.github/workflows/deft-core-guard.yml` when the
|
|
73
|
+
* framework deposit is not git-tracked (npm-managed / gitignored layout). The
|
|
74
|
+
* guard CI check is meaningless when there is no committed `.deft/core/**` to
|
|
75
|
+
* guard; skipping it prevents the file from re-appearing as untracked noise
|
|
76
|
+
* after every `directive update`.
|
|
77
|
+
*/
|
|
78
|
+
readonly skipGuardWorkflow?: boolean;
|
|
79
|
+
}
|
|
70
80
|
/** Best-effort #1430 neutralization deposit (mirrors depositNeutralization). */
|
|
71
|
-
export declare function depositNeutralization(projectDir: string, io: InitDepositIo): Promise<void>;
|
|
81
|
+
export declare function depositNeutralization(projectDir: string, io: InitDepositIo, options?: DepositNeutralizationOptions): Promise<void>;
|
|
72
82
|
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -10,8 +10,8 @@ import { mkdir, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
|
10
10
|
import { platform } from "node:os";
|
|
11
11
|
import { dirname, join, relative } from "node:path";
|
|
12
12
|
import { copyTree } from "../deposit/copy-tree.js";
|
|
13
|
-
import { resolveLifecycleRoot } from "../layout/resolve.js";
|
|
14
13
|
import { agentsRefreshPlan } from "../platform/agents-md.js";
|
|
14
|
+
import { MIGRATED_ARTIFACT_DIR } from "../xbrief-migrate/constants.js";
|
|
15
15
|
import { CANONICAL_INSTALL_ROOT } from "./constants.js";
|
|
16
16
|
import { installerManagedGuardEre } from "./hygiene.js";
|
|
17
17
|
export { CANONICAL_INSTALL_ROOT };
|
|
@@ -22,6 +22,7 @@ const FRAMEWORK_SELF_TEST_REL = ".deft/core/tests";
|
|
|
22
22
|
const VENDORED_TS_PACKAGES_REL = ".deft/core/packages";
|
|
23
23
|
const VENDORED_TS_TEST_RE = /\.(test|spec)\.(c|m)?[jt]sx?$/i;
|
|
24
24
|
const CORE_GITATTRIBUTES_LINES = [
|
|
25
|
+
`${CORE_GLOB} text eol=lf`,
|
|
25
26
|
`${CORE_GLOB} linguist-generated=true`,
|
|
26
27
|
`${CORE_GLOB} linguist-vendored=true`,
|
|
27
28
|
];
|
|
@@ -322,7 +323,7 @@ function vbriefLifecycleDirsPresent(consumerVbrief) {
|
|
|
322
323
|
});
|
|
323
324
|
}
|
|
324
325
|
export async function writeConsumerVbrief(projectDir, deftDir, io) {
|
|
325
|
-
const consumerVbrief =
|
|
326
|
+
const consumerVbrief = join(projectDir, MIGRATED_ARTIFACT_DIR);
|
|
326
327
|
const schemasDst = join(consumerVbrief, "schemas");
|
|
327
328
|
const vbriefMdDst = join(consumerVbrief, "vbrief.md");
|
|
328
329
|
const schemasPresent = existsSync(schemasDst) && statSync(schemasDst).isDirectory();
|
|
@@ -584,7 +585,7 @@ export function ensureGitattributes(projectDir, io) {
|
|
|
584
585
|
const present = new Set(existing.split("\n").map((line) => line.trim()));
|
|
585
586
|
const additions = CORE_GITATTRIBUTES_LINES.filter((line) => !present.has(line));
|
|
586
587
|
if (additions.length === 0) {
|
|
587
|
-
io.printf(`.gitattributes already marks ${CORE_GLOB} as generated/vendored — skipping.\n`);
|
|
588
|
+
io.printf(`.gitattributes already marks ${CORE_GLOB} as LF-pinned/generated/vendored — skipping.\n`);
|
|
588
589
|
return false;
|
|
589
590
|
}
|
|
590
591
|
let body = existing;
|
|
@@ -594,13 +595,13 @@ export function ensureGitattributes(projectDir, io) {
|
|
|
594
595
|
body += "\n";
|
|
595
596
|
body +=
|
|
596
597
|
"# Deft framework: the vendored payload is packaged framework code, not\n" +
|
|
597
|
-
"# consumer source.
|
|
598
|
-
"# diffs treat .deft/core/** as machine-managed (#1430).\n";
|
|
598
|
+
"# consumer source. Pin LF endings and mark it generated + vendored so\n" +
|
|
599
|
+
"# Git does not rewrite it and diffs treat .deft/core/** as machine-managed (#1430, #2118).\n";
|
|
599
600
|
for (const add of additions) {
|
|
600
601
|
body += `${add}\n`;
|
|
601
602
|
}
|
|
602
603
|
writeFileSync(path, body, "utf8");
|
|
603
|
-
io.printf(`.gitattributes updated with
|
|
604
|
+
io.printf(`.gitattributes updated with Deft core markers: ${additions.join(", ")}\n`);
|
|
604
605
|
return true;
|
|
605
606
|
}
|
|
606
607
|
function greptilePatternPresent(patterns, glob) {
|
|
@@ -774,12 +775,12 @@ export async function pruneVendoredTsTests(projectDir, io) {
|
|
|
774
775
|
return removed;
|
|
775
776
|
}
|
|
776
777
|
/** Best-effort #1430 neutralization deposit (mirrors depositNeutralization). */
|
|
777
|
-
export async function depositNeutralization(projectDir, io) {
|
|
778
|
+
export async function depositNeutralization(projectDir, io, options = {}) {
|
|
778
779
|
const steps = [
|
|
779
780
|
() => ensureGitattributes(projectDir, io),
|
|
780
781
|
() => ensureGreptileIgnore(projectDir, io),
|
|
781
782
|
() => ensureCodeqlPathsIgnore(projectDir, io),
|
|
782
|
-
() => ensureCoreGuardWorkflow(projectDir, io),
|
|
783
|
+
...(options.skipGuardWorkflow ? [] : [() => ensureCoreGuardWorkflow(projectDir, io)]),
|
|
783
784
|
() => pruneFrameworkSelfTests(projectDir, io),
|
|
784
785
|
async () => (await pruneVendoredTsTests(projectDir, io)) > 0,
|
|
785
786
|
];
|
|
@@ -77,12 +77,12 @@ export function populateCacheLayout(cacheRoot, repo, issueNumbers) {
|
|
|
77
77
|
}
|
|
78
78
|
export function createConsumerProject(parent) {
|
|
79
79
|
const root = join(parent, "consumer");
|
|
80
|
-
mkdirSync(join(root, "
|
|
81
|
-
mkdirSync(join(root, "
|
|
82
|
-
mkdirSync(join(root, "
|
|
83
|
-
mkdirSync(join(root, "
|
|
84
|
-
mkdirSync(join(root, "
|
|
85
|
-
writeFileSync(join(root, "
|
|
80
|
+
mkdirSync(join(root, "xbrief", "proposed"), { recursive: true });
|
|
81
|
+
mkdirSync(join(root, "xbrief", "pending"), { recursive: true });
|
|
82
|
+
mkdirSync(join(root, "xbrief", "active"), { recursive: true });
|
|
83
|
+
mkdirSync(join(root, "xbrief", "completed"), { recursive: true });
|
|
84
|
+
mkdirSync(join(root, "xbrief", "cancelled"), { recursive: true });
|
|
85
|
+
writeFileSync(join(root, "xbrief", "specification.xbrief.json"), `${JSON.stringify({
|
|
86
86
|
vBRIEFInfo: { version: "0.5" },
|
|
87
87
|
plan: {
|
|
88
88
|
title: "Consumer Project",
|
|
@@ -96,7 +96,7 @@ export function createConsumerProject(parent) {
|
|
|
96
96
|
return root;
|
|
97
97
|
}
|
|
98
98
|
export function writeScopeVbrief(projectRoot, folder, filename, status = "proposed") {
|
|
99
|
-
const target = join(projectRoot, "
|
|
99
|
+
const target = join(projectRoot, "xbrief", folder, filename);
|
|
100
100
|
writeFileSync(target, `${JSON.stringify({
|
|
101
101
|
vBRIEFInfo: { version: "0.5" },
|
|
102
102
|
plan: {
|
package/dist/layout/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { ARTIFACT_SUFFIXES, hasArtifactSuffix, isLifecycleArtifactPath,
|
|
1
|
+
export { ARTIFACT_SUFFIXES, hasArtifactSuffix, isLifecycleArtifactPath, LIFECYCLE_DIR_NAMES, type LifecycleLayout, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY, projectDefinitionFilename, projectDefinitionRelPath, resolveAuditDir, resolveAuditPath, resolveEvalDir, resolveEvalPath, resolveLifecycleFolder, resolveLifecycleLayout, resolveLifecycleRoot, resolveProjectDefinitionPath, resolveSpecArtifactPath, specArtifactRelPath, stripArtifactSuffix, } from "./resolve.js";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/layout/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { ARTIFACT_SUFFIXES, hasArtifactSuffix, isLifecycleArtifactPath,
|
|
1
|
+
export { ARTIFACT_SUFFIXES, hasArtifactSuffix, isLifecycleArtifactPath, LIFECYCLE_DIR_NAMES, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY, projectDefinitionFilename, projectDefinitionRelPath, resolveAuditDir, resolveAuditPath, resolveEvalDir, resolveEvalPath, resolveLifecycleFolder, resolveLifecycleLayout, resolveLifecycleRoot, resolveProjectDefinitionPath, resolveSpecArtifactPath, specArtifactRelPath, stripArtifactSuffix, } from "./resolve.js";
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/layout/resolve.d.ts
CHANGED
|
@@ -1,28 +1,39 @@
|
|
|
1
|
-
import { LEGACY_ARTIFACT_DIR,
|
|
2
|
-
export { LEGACY_ARTIFACT_DIR,
|
|
1
|
+
import { LEGACY_ARTIFACT_DIR, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY } from "../xbrief-migrate/constants.js";
|
|
2
|
+
export { LEGACY_ARTIFACT_DIR, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY, };
|
|
3
3
|
/**
|
|
4
|
-
* Lifecycle directory names recognized by the layout-aware resolver
|
|
5
|
-
*
|
|
4
|
+
* Lifecycle directory names recognized by the layout-aware resolver and path
|
|
5
|
+
* validators. Includes both `xbrief` (canonical) and `vbrief` (legacy) so that
|
|
6
|
+
* path-validation helpers remain accurate during and after migration.
|
|
6
7
|
*/
|
|
7
8
|
export declare const LIFECYCLE_DIR_NAMES: readonly ["xbrief", "vbrief"];
|
|
8
9
|
/**
|
|
9
|
-
* Artifact filename suffixes recognized by the layout-aware resolver
|
|
10
|
-
*
|
|
10
|
+
* Artifact filename suffixes recognized by the layout-aware resolver and path
|
|
11
|
+
* validators. Includes both `.xbrief.json` (canonical) and `.vbrief.json`
|
|
12
|
+
* (legacy) so that path-validation helpers remain accurate during and after
|
|
13
|
+
* migration.
|
|
11
14
|
*/
|
|
12
15
|
export declare const ARTIFACT_SUFFIXES: readonly [".xbrief.json", ".vbrief.json"];
|
|
13
|
-
/** The on-disk lifecycle layout an engine call site should resolve against (#2109). */
|
|
16
|
+
/** The on-disk lifecycle layout an engine call site should resolve against (#2109 / #2112). */
|
|
14
17
|
export interface LifecycleLayout {
|
|
15
|
-
/** The lifecycle directory name -- `xbrief`
|
|
16
|
-
readonly artifactDir: typeof MIGRATED_ARTIFACT_DIR
|
|
17
|
-
/** The artifact filename suffix
|
|
18
|
-
readonly artifactSuffix: typeof MIGRATED_ARTIFACT_SUFFIX
|
|
19
|
-
/** The
|
|
20
|
-
readonly infoRootKey: typeof MIGRATED_INFO_ROOT_KEY
|
|
21
|
-
/** Absolute path to the resolved lifecycle root (`<projectRoot
|
|
18
|
+
/** The lifecycle directory name -- always `xbrief` after the vbrief read path removal (#2112). */
|
|
19
|
+
readonly artifactDir: typeof MIGRATED_ARTIFACT_DIR;
|
|
20
|
+
/** The artifact filename suffix -- always `.xbrief.json` after #2112. */
|
|
21
|
+
readonly artifactSuffix: typeof MIGRATED_ARTIFACT_SUFFIX;
|
|
22
|
+
/** The `xBRIEFInfo` root key. */
|
|
23
|
+
readonly infoRootKey: typeof MIGRATED_INFO_ROOT_KEY;
|
|
24
|
+
/** Absolute path to the resolved lifecycle root (`<projectRoot>/xbrief`). */
|
|
22
25
|
readonly root: string;
|
|
23
|
-
/**
|
|
24
|
-
readonly migrated:
|
|
26
|
+
/** Always `true` -- the legacy vbrief read path was removed in #2112. */
|
|
27
|
+
readonly migrated: true;
|
|
25
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Returns the layout root for non-layout-sensitive helpers (#2112).
|
|
31
|
+
*
|
|
32
|
+
* - If a valid `xbrief/` layout exists, returns its root.
|
|
33
|
+
* - If only `vbrief/` exists (legacy-only), throws so the caller surfaces the migrate hint.
|
|
34
|
+
* - If neither exists (empty/new project), falls back to `<projectRoot>/xbrief/` canonical path.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveLayoutRootOrCanonical(projectRoot: string): string;
|
|
26
37
|
/** True when `name` ends with any recognized artifact suffix (.xbrief.json or .vbrief.json). */
|
|
27
38
|
export declare function hasArtifactSuffix(name: string): boolean;
|
|
28
39
|
/**
|
|
@@ -31,24 +42,30 @@ export declare function hasArtifactSuffix(name: string): boolean;
|
|
|
31
42
|
*/
|
|
32
43
|
export declare function stripArtifactSuffix(name: string): string;
|
|
33
44
|
/**
|
|
34
|
-
* True when a POSIX-style path is a lifecycle artifact
|
|
35
|
-
*
|
|
45
|
+
* True when a POSIX-style path is a lifecycle artifact path carrying a
|
|
46
|
+
* recognized lifecycle directory prefix (`xbrief/` or `vbrief/`) and a
|
|
47
|
+
* recognized artifact suffix. The `vbrief/` prefix and `.vbrief.json` suffix
|
|
48
|
+
* remain recognized for path-validation purposes during and after migration.
|
|
36
49
|
*/
|
|
37
50
|
export declare function isLifecycleArtifactPath(posix: string): boolean;
|
|
38
51
|
/**
|
|
39
|
-
* Resolve the active lifecycle layout for `projectRoot` (#2109 part 1).
|
|
52
|
+
* Resolve the active lifecycle layout for `projectRoot` (#2109 part 1 / #2112).
|
|
53
|
+
*
|
|
54
|
+
* Requires the migrated `xbrief/` layout: the `xbrief/` directory must exist
|
|
55
|
+
* AND contain at least one `.xbrief.json` artifact. If no such layout is found,
|
|
56
|
+
* throws with a clear error directing the operator to run `deft migrate:xbrief`.
|
|
57
|
+
*
|
|
58
|
+
* The legacy `vbrief/` read-path fallback was removed in #2112 (0.73.0 MINOR).
|
|
59
|
+
* Projects that have not migrated must run `deft migrate:xbrief` first.
|
|
40
60
|
*
|
|
41
|
-
*
|
|
42
|
-
* exists AND it contains at least one `.xbrief.json` artifact; otherwise falls
|
|
43
|
-
* back to the legacy `vbrief/` layout. With only `vbrief/` present (today's
|
|
44
|
-
* repo) the result is the legacy layout, so existing behavior is unchanged.
|
|
61
|
+
* @throws {Error} When no `xbrief/` layout with `.xbrief.json` artifacts exists.
|
|
45
62
|
*/
|
|
46
63
|
export declare function resolveLifecycleLayout(projectRoot: string): LifecycleLayout;
|
|
47
64
|
/** Convenience accessor for the absolute resolved lifecycle root directory. */
|
|
48
65
|
export declare function resolveLifecycleRoot(projectRoot: string): string;
|
|
49
66
|
/**
|
|
50
|
-
* Absolute path to a lifecycle folder under the resolved layout root
|
|
51
|
-
* (e.g. `<root
|
|
67
|
+
* Absolute path to a lifecycle folder under the resolved `xbrief/` layout root
|
|
68
|
+
* (e.g. `<root>/xbrief/active`). Layout-aware (#2109 part 2a / #2112).
|
|
52
69
|
*/
|
|
53
70
|
export declare function resolveLifecycleFolder(projectRoot: string, folder: string): string;
|
|
54
71
|
/** Absolute path to the layout-aware `.eval/` directory for version-eval results (#1703). */
|
|
@@ -68,20 +85,19 @@ export declare function projectDefinitionFilename(projectRoot: string): string;
|
|
|
68
85
|
export declare function resolveProjectDefinitionPath(projectRoot: string): string;
|
|
69
86
|
/**
|
|
70
87
|
* POSIX-style display path to the resolved PROJECT-DEFINITION artifact relative
|
|
71
|
-
* to the project root (e.g. `
|
|
88
|
+
* to the project root (e.g. `xbrief/PROJECT-DEFINITION.xbrief.json`).
|
|
72
89
|
*/
|
|
73
90
|
export declare function projectDefinitionRelPath(projectRoot: string): string;
|
|
74
91
|
/**
|
|
75
|
-
* Absolute path to the resolved specification artifact (#2132).
|
|
92
|
+
* Absolute path to the resolved specification artifact (#2132 / #2112).
|
|
76
93
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* layout resolver used by the #2109 Part-2a call-site sweep.
|
|
94
|
+
* Returns `xbrief/specification.xbrief.json`; throws when no migrated xbrief/
|
|
95
|
+
* layout is present (see `resolveLifecycleLayout`).
|
|
80
96
|
*/
|
|
81
97
|
export declare function resolveSpecArtifactPath(projectRoot: string): string;
|
|
82
98
|
/**
|
|
83
99
|
* POSIX-style display path to the resolved specification artifact relative to
|
|
84
|
-
* the project root (
|
|
100
|
+
* the project root (`xbrief/specification.xbrief.json`).
|
|
85
101
|
*/
|
|
86
102
|
export declare function specArtifactRelPath(projectRoot: string): string;
|
|
87
103
|
//# sourceMappingURL=resolve.d.ts.map
|