@deftai/directive-core 0.66.2 → 0.68.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/agents-md-advisory/evaluate.d.ts +44 -0
- package/dist/agents-md-advisory/evaluate.js +115 -0
- package/dist/agents-md-advisory/index.d.ts +2 -0
- package/dist/agents-md-advisory/index.js +2 -0
- package/dist/agents-md-budget/evaluate.d.ts +38 -0
- package/dist/agents-md-budget/evaluate.js +152 -0
- package/dist/agents-md-budget/index.d.ts +2 -0
- package/dist/agents-md-budget/index.js +2 -0
- package/dist/check/index.d.ts +1 -1
- package/dist/check/index.js +1 -1
- package/dist/check/orchestrator.d.ts +14 -4
- package/dist/check/orchestrator.js +30 -6
- package/dist/codebase/map.d.ts +1 -0
- package/dist/codebase/map.js +5 -2
- package/dist/doctor/checks.js +11 -11
- package/dist/doctor/constants.d.ts +7 -2
- package/dist/doctor/constants.js +17 -1
- package/dist/doctor/flags.js +5 -1
- package/dist/doctor/main.js +99 -8
- package/dist/doctor/payload-staleness.js +15 -6
- package/dist/doctor/types.d.ts +7 -0
- package/dist/forward-coverage/evaluate.d.ts +55 -0
- package/dist/forward-coverage/evaluate.js +254 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/init-deposit/constants.d.ts +14 -0
- package/dist/init-deposit/constants.js +10 -0
- package/dist/init-deposit/hygiene.d.ts +50 -0
- package/dist/init-deposit/hygiene.js +176 -0
- package/dist/init-deposit/init-deposit.d.ts +1 -0
- package/dist/init-deposit/init-deposit.js +7 -1
- package/dist/init-deposit/refresh.d.ts +2 -0
- package/dist/init-deposit/refresh.js +48 -30
- package/dist/init-deposit/scaffold.d.ts +3 -4
- package/dist/init-deposit/scaffold.js +3 -24
- package/dist/intake/github-auth-modes.d.ts +1 -0
- package/dist/intake/github-auth-modes.js +1 -0
- package/dist/intake/issue-ingest.d.ts +6 -2
- package/dist/intake/issue-ingest.js +65 -9
- package/dist/integration-e2e/helpers.d.ts +0 -1
- package/dist/integration-e2e/helpers.js +3 -7
- package/dist/layout/lifecycle-ref.d.ts +15 -0
- package/dist/layout/lifecycle-ref.js +55 -0
- package/dist/platform/agents-consumer-header.d.ts +13 -0
- package/dist/platform/agents-consumer-header.js +57 -0
- package/dist/platform/agents-md.d.ts +28 -0
- package/dist/platform/agents-md.js +38 -1
- package/dist/platform/index.d.ts +1 -0
- package/dist/platform/index.js +1 -0
- package/dist/policy/agents-md-advisory.d.ts +52 -0
- package/dist/policy/agents-md-advisory.js +63 -0
- package/dist/policy/agents-md-budget.d.ts +24 -0
- package/dist/policy/agents-md-budget.js +89 -0
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.js +1 -0
- package/dist/policy/resolve.js +56 -46
- package/dist/pr-merge-readiness/ci-gate.d.ts +28 -0
- package/dist/pr-merge-readiness/ci-gate.js +79 -0
- package/dist/pr-merge-readiness/compute.d.ts +15 -1
- package/dist/pr-merge-readiness/compute.js +129 -9
- package/dist/pr-merge-readiness/gh.d.ts +8 -0
- package/dist/pr-merge-readiness/gh.js +30 -5
- package/dist/pr-merge-readiness/index.d.ts +5 -1
- package/dist/pr-merge-readiness/index.js +3 -0
- package/dist/pr-merge-readiness/main.d.ts +3 -0
- package/dist/pr-merge-readiness/main.js +73 -6
- package/dist/pr-merge-readiness/mergeability.d.ts +43 -0
- package/dist/pr-merge-readiness/mergeability.js +105 -0
- package/dist/pr-merge-readiness/output.js +20 -0
- package/dist/pr-merge-readiness/slizard-gate.d.ts +47 -0
- package/dist/pr-merge-readiness/slizard-gate.js +119 -0
- package/dist/pr-monitor/index.d.ts +1 -1
- package/dist/pr-monitor/index.js +1 -1
- package/dist/pr-monitor/monitor.d.ts +8 -2
- package/dist/pr-monitor/monitor.js +35 -6
- package/dist/pr-wait-mergeable/wrappers.d.ts +12 -1
- package/dist/pr-wait-mergeable/wrappers.js +13 -5
- package/dist/pr-watch/constants.d.ts +33 -0
- package/dist/pr-watch/constants.js +33 -0
- package/dist/pr-watch/index.d.ts +6 -0
- package/dist/pr-watch/index.js +6 -0
- package/dist/pr-watch/main.d.ts +21 -0
- package/dist/pr-watch/main.js +206 -0
- package/dist/pr-watch/probe.d.ts +13 -0
- package/dist/pr-watch/probe.js +100 -0
- package/dist/pr-watch/types.d.ts +47 -0
- package/dist/pr-watch/types.js +2 -0
- package/dist/pr-watch/watch.d.ts +12 -0
- package/dist/pr-watch/watch.js +122 -0
- package/dist/preflight-cache/evaluate.d.ts +1 -0
- package/dist/preflight-cache/evaluate.js +17 -10
- package/dist/render/project-render.d.ts +16 -2
- package/dist/render/project-render.js +155 -97
- package/dist/scope/capacity-stamp.d.ts +24 -2
- package/dist/scope/capacity-stamp.js +80 -28
- package/dist/scope/decomposed-refs.js +2 -2
- package/dist/scope/project-definition-sync.js +3 -3
- package/dist/scope/vbrief-ref.d.ts +2 -1
- package/dist/scope/vbrief-ref.js +3 -2
- package/dist/spec-authority/constants.d.ts +6 -0
- package/dist/spec-authority/constants.js +11 -0
- package/dist/spec-authority/resolver.js +3 -3
- package/dist/swarm/finalize-cohort-cli.d.ts +5 -0
- package/dist/swarm/finalize-cohort-cli.js +100 -0
- package/dist/swarm/finalize-cohort.d.ts +39 -0
- package/dist/swarm/finalize-cohort.js +589 -0
- package/dist/swarm/index.d.ts +2 -0
- package/dist/swarm/index.js +2 -0
- package/dist/swarm/launch.d.ts +13 -0
- package/dist/swarm/launch.js +19 -7
- package/dist/swarm/routing-verify.d.ts +1 -1
- package/dist/swarm/routing-verify.js +11 -10
- package/dist/swarm/routing.d.ts +9 -0
- package/dist/swarm/routing.js +46 -0
- package/dist/swarm/verify-review-clean-cli.js +25 -1
- package/dist/swarm/verify-review-clean.d.ts +9 -1
- package/dist/swarm/verify-review-clean.js +76 -3
- package/dist/triage/queue/index.d.ts +1 -0
- package/dist/triage/queue/index.js +1 -0
- package/dist/triage/queue/project.d.ts +6 -1
- package/dist/triage/queue/project.js +9 -4
- package/dist/triage/queue/reconcile-live-state.d.ts +42 -0
- package/dist/triage/queue/reconcile-live-state.js +54 -0
- package/dist/triage/scope/mutations-core.js +91 -81
- package/dist/triage/scope/resolve.js +4 -3
- package/dist/triage/scope-drift/add-ignore.js +33 -32
- package/dist/triage/scope-drift/scope-rules.js +3 -3
- package/dist/triage/subscribe/index.js +51 -45
- package/dist/triage/welcome/writers.js +79 -64
- package/dist/vbrief-build/project-definition-io.js +8 -3
- package/dist/vbrief-validate/paths.js +8 -4
- package/dist/vbrief-validate/precutover.d.ts +1 -1
- package/dist/vbrief-validate/precutover.js +12 -10
- package/dist/verify-source/biome-config.d.ts +41 -0
- package/dist/verify-source/biome-config.js +97 -0
- package/dist/verify-source/index.d.ts +1 -0
- package/dist/verify-source/index.js +1 -0
- package/dist/xbrief-migrate/agents-header.d.ts +69 -0
- package/dist/xbrief-migrate/agents-header.js +179 -0
- package/dist/xbrief-migrate/fs-helpers.d.ts +3 -0
- package/dist/xbrief-migrate/fs-helpers.js +11 -0
- package/dist/xbrief-migrate/index.d.ts +1 -0
- package/dist/xbrief-migrate/index.js +1 -0
- package/dist/xbrief-migrate/migrate-project.js +29 -10
- package/package.json +15 -7
- package/dist/install-upgrade/index.d.ts +0 -13
- package/dist/install-upgrade/index.js +0 -251
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scoped staging + installer-managed allowlist for TS-native init/update (#1453).
|
|
3
|
+
*
|
|
4
|
+
* Mirrors cmd/deft-install/hygiene.go + deposit.go installerManagedMatchers.
|
|
5
|
+
* Refs #1576, #1453, #1430.
|
|
6
|
+
*/
|
|
7
|
+
import { execFileSync } from "node:child_process";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { join, relative } from "node:path";
|
|
10
|
+
import { gitPorcelain } from "../story-ready/git.js";
|
|
11
|
+
import { CANONICAL_INSTALL_ROOT } from "./constants.js";
|
|
12
|
+
export const CODEQL_CONFIG_REL = ".github/codeql/codeql-config.yml";
|
|
13
|
+
export const CORE_GUARD_WORKFLOW_REL = ".github/workflows/deft-core-guard.yml";
|
|
14
|
+
const VBRIEF_LIFECYCLE_DIRS = ["proposed", "pending", "active", "completed", "cancelled"];
|
|
15
|
+
/** Single source of truth for installer-managed paths (#1440 / #1576). */
|
|
16
|
+
export function installerManagedMatchers() {
|
|
17
|
+
return [
|
|
18
|
+
{ exact: "AGENTS.md" },
|
|
19
|
+
{ prefix: ".agents/" },
|
|
20
|
+
{ prefix: ".githooks/" },
|
|
21
|
+
{ exact: ".gitattributes" },
|
|
22
|
+
{ exact: ".gitignore" },
|
|
23
|
+
{ exact: "greptile.json" },
|
|
24
|
+
{ exact: CODEQL_CONFIG_REL },
|
|
25
|
+
{ exact: CORE_GUARD_WORKFLOW_REL },
|
|
26
|
+
{ exact: "Taskfile.yml" },
|
|
27
|
+
{ exact: "vbrief/.deft-version" },
|
|
28
|
+
{ exact: "vbrief/vbrief.md" },
|
|
29
|
+
{ prefix: "vbrief/schemas/" },
|
|
30
|
+
{ prefix: "vbrief/migration/" },
|
|
31
|
+
...VBRIEF_LIFECYCLE_DIRS.map((sub) => ({ exact: `vbrief/${sub}/.gitkeep` })),
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
function escapeEre(value) {
|
|
35
|
+
return value.replace(/[.^$*+?()[\]{}|\\]/g, "\\$&");
|
|
36
|
+
}
|
|
37
|
+
function matcherToEre(matcher) {
|
|
38
|
+
if (matcher.exact)
|
|
39
|
+
return `^${escapeEre(matcher.exact)}$`;
|
|
40
|
+
return `^${escapeEre(matcher.prefix ?? "")}`;
|
|
41
|
+
}
|
|
42
|
+
/** POSIX ERE alternation embedded in the deposited deft-core-guard workflow. */
|
|
43
|
+
export function installerManagedGuardEre() {
|
|
44
|
+
return installerManagedMatchers()
|
|
45
|
+
.map((matcher) => matcherToEre(matcher))
|
|
46
|
+
.join("|");
|
|
47
|
+
}
|
|
48
|
+
function matchesInstallerManaged(path, matchers) {
|
|
49
|
+
for (const matcher of matchers) {
|
|
50
|
+
if (matcher.exact && path === matcher.exact)
|
|
51
|
+
return true;
|
|
52
|
+
if (matcher.prefix && path.startsWith(matcher.prefix))
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
export function isInstallerManagedPath(path) {
|
|
58
|
+
return matchesInstallerManaged(path, installerManagedMatchers());
|
|
59
|
+
}
|
|
60
|
+
export function frameworkStagePaths(projectDir, deftDir, options = {}) {
|
|
61
|
+
const paths = [];
|
|
62
|
+
const seen = new Set();
|
|
63
|
+
const add = (rel) => {
|
|
64
|
+
const normalized = rel.replace(/\\/g, "/");
|
|
65
|
+
if (!normalized || normalized === "." || seen.has(normalized))
|
|
66
|
+
return;
|
|
67
|
+
if (!existsSync(join(projectDir, normalized)))
|
|
68
|
+
return;
|
|
69
|
+
seen.add(normalized);
|
|
70
|
+
paths.push(normalized);
|
|
71
|
+
};
|
|
72
|
+
const relDeft = relative(projectDir, deftDir);
|
|
73
|
+
if (relDeft && !relDeft.startsWith("..") && !relDeft.startsWith("/")) {
|
|
74
|
+
add(relDeft);
|
|
75
|
+
}
|
|
76
|
+
for (const matcher of installerManagedMatchers()) {
|
|
77
|
+
if (matcher.exact === "Taskfile.yml" && !options.includeTaskfile)
|
|
78
|
+
continue;
|
|
79
|
+
if (matcher.exact) {
|
|
80
|
+
add(matcher.exact);
|
|
81
|
+
}
|
|
82
|
+
else if (matcher.prefix) {
|
|
83
|
+
add(matcher.prefix.replace(/\/$/, ""));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return paths;
|
|
87
|
+
}
|
|
88
|
+
/** Best-effort scoped `git add` — never fails the install/update (#1453 Layer 2b). */
|
|
89
|
+
export function stageFrameworkPaths(projectDir, paths, seams = {}) {
|
|
90
|
+
if (paths.length === 0)
|
|
91
|
+
return { staged: false, error: null };
|
|
92
|
+
const readPorcelain = seams.gitPorcelain ?? gitPorcelain;
|
|
93
|
+
if (readPorcelain(projectDir) === null)
|
|
94
|
+
return { staged: false, error: null };
|
|
95
|
+
const runGitAdd = seams.runGitAdd ??
|
|
96
|
+
((root, stagePaths) => {
|
|
97
|
+
execFileSync("git", ["add", ...stagePaths], {
|
|
98
|
+
cwd: root,
|
|
99
|
+
encoding: "utf8",
|
|
100
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
try {
|
|
104
|
+
runGitAdd(projectDir, paths);
|
|
105
|
+
return { staged: true, error: null };
|
|
106
|
+
}
|
|
107
|
+
catch (cause) {
|
|
108
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
109
|
+
return { staged: false, error };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export const COMMIT_HYGIENE_BRANCH_NAME = "chore/deft-framework-upgrade";
|
|
113
|
+
export function printCommitGuidance(io, paths, staged) {
|
|
114
|
+
if (paths.length === 0)
|
|
115
|
+
return;
|
|
116
|
+
const addCmd = `git add ${paths.join(" ")}`;
|
|
117
|
+
io.printf("\nCommit hygiene (#1453, #1671): keep the framework deposit in its OWN branch/PR.\n");
|
|
118
|
+
io.printf("Do NOT use `git add -A` -- mixing the payload with your own files trips the\n");
|
|
119
|
+
io.printf("deft-core-guard CI check.\n");
|
|
120
|
+
if (staged) {
|
|
121
|
+
io.printf("The installer already staged ONLY these framework + installer-managed paths:\n");
|
|
122
|
+
io.printf(` ${addCmd}\n`);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
io.printf("Stage ONLY these framework + installer-managed paths:\n");
|
|
126
|
+
io.printf(` ${addCmd}\n`);
|
|
127
|
+
}
|
|
128
|
+
io.printf("Then take the framework deposit through the full PR lifecycle so deft-core-guard\n");
|
|
129
|
+
io.printf("evaluates a clean, standalone PR:\n");
|
|
130
|
+
io.printf(` 1. Branch: git switch -c ${COMMIT_HYGIENE_BRANCH_NAME}\n`);
|
|
131
|
+
io.printf(' 2. Commit: git commit -m "chore(deft): update framework payload"\n');
|
|
132
|
+
io.printf(` 3. Push: git push -u origin ${COMMIT_HYGIENE_BRANCH_NAME}\n`);
|
|
133
|
+
io.printf(' 4. PR: gh pr create --fill --title "chore(deft): update framework payload"\n');
|
|
134
|
+
io.printf(" 5. Merge: gh pr merge --squash --delete-branch # after deft-core-guard passes\n");
|
|
135
|
+
}
|
|
136
|
+
function defaultCachedNames(projectDir) {
|
|
137
|
+
try {
|
|
138
|
+
const out = execFileSync("git", ["diff", "--cached", "--name-only", "-z"], {
|
|
139
|
+
cwd: projectDir,
|
|
140
|
+
encoding: "utf8",
|
|
141
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
142
|
+
});
|
|
143
|
+
return out
|
|
144
|
+
.split("\0")
|
|
145
|
+
.map((entry) => entry.trim().replace(/\\/g, "/"))
|
|
146
|
+
.filter(Boolean);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Narrow the candidate stage paths to those that actually have staged index
|
|
154
|
+
* changes. A candidate file matches when the cached name is identical; a
|
|
155
|
+
* candidate directory (e.g. ``.deft/core``) matches when any cached name lives
|
|
156
|
+
* beneath it. This keeps ``staged_paths`` honest for downstream automation --
|
|
157
|
+
* it reports what git actually staged, not every path passed to ``git add``.
|
|
158
|
+
*/
|
|
159
|
+
function actuallyStagedPaths(stagePaths, cachedNames) {
|
|
160
|
+
return stagePaths.filter((candidate) => cachedNames.some((name) => name === candidate || name.startsWith(`${candidate}/`)));
|
|
161
|
+
}
|
|
162
|
+
export function depositStagePaths(projectDir, options = {}) {
|
|
163
|
+
const deftDir = join(projectDir, CANONICAL_INSTALL_ROOT);
|
|
164
|
+
const stagePaths = frameworkStagePaths(projectDir, deftDir, {
|
|
165
|
+
includeTaskfile: options.includeTaskfile ?? false,
|
|
166
|
+
});
|
|
167
|
+
const { staged } = stageFrameworkPaths(projectDir, stagePaths, options);
|
|
168
|
+
const readCachedNames = options.readCachedNames ?? defaultCachedNames;
|
|
169
|
+
const cachedNames = staged ? readCachedNames(projectDir) : [];
|
|
170
|
+
return {
|
|
171
|
+
stagePaths,
|
|
172
|
+
staged,
|
|
173
|
+
stagedPaths: staged ? actuallyStagedPaths(stagePaths, cachedNames) : [],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=hygiene.js.map
|
|
@@ -19,6 +19,7 @@ export interface InitDepositResult {
|
|
|
19
19
|
readonly taskfileWired: boolean;
|
|
20
20
|
readonly configDir: string;
|
|
21
21
|
readonly legacyLayout: boolean;
|
|
22
|
+
readonly stagedPaths: string[];
|
|
22
23
|
}
|
|
23
24
|
export interface InitDepositSeams {
|
|
24
25
|
resolveContentRoot?: () => Promise<string>;
|
|
@@ -13,6 +13,7 @@ import { prunePythonArtifactsFromDeposit } from "../deposit/python-free.js";
|
|
|
13
13
|
import { resolveInstalledContentRoot } from "../deposit/resolve-content.js";
|
|
14
14
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
15
15
|
import { ensureInitGitignoreLines, reconstituteDepositFromContent } from "./gitignore.js";
|
|
16
|
+
import { depositStagePaths, printCommitGuidance } from "./hygiene.js";
|
|
16
17
|
import { buildLegacyRefusalJson, buildLegacyRefusalMessage, detectLegacyLayout, LEGACY_LAYOUT_REFUSED_EXIT_CODE, LegacyLayoutRefusedError, } from "./legacy-detect.js";
|
|
17
18
|
import { printMigrateNudgeIfNeeded } from "./migrate.js";
|
|
18
19
|
import { CANONICAL_INSTALL_ROOT, depositNeutralization, ensureTaskfile, writeAgentsMd, writeAgentsSkills, writeConsumerGitHooks, writeConsumerVbrief, writeInstallManifest, } from "./scaffold.js";
|
|
@@ -93,7 +94,7 @@ export function buildInstallSummaryJson(result, options) {
|
|
|
93
94
|
strategy: "vendor",
|
|
94
95
|
dirty_tree: false,
|
|
95
96
|
dirty_files: [],
|
|
96
|
-
staged_paths:
|
|
97
|
+
staged_paths: result.stagedPaths,
|
|
97
98
|
backup_path: "",
|
|
98
99
|
previous_version: "",
|
|
99
100
|
};
|
|
@@ -150,6 +151,10 @@ export async function runInitDeposit(args, io, seams = {}) {
|
|
|
150
151
|
taskfileWired = ensureTaskfile(projectDir, io);
|
|
151
152
|
}
|
|
152
153
|
const configDir = createUserConfigDir(io);
|
|
154
|
+
const { stagePaths, staged, stagedPaths } = depositStagePaths(projectDir, {
|
|
155
|
+
includeTaskfile: taskfileWired,
|
|
156
|
+
});
|
|
157
|
+
printCommitGuidance(io, stagePaths, staged);
|
|
153
158
|
return {
|
|
154
159
|
projectDir,
|
|
155
160
|
deftDir,
|
|
@@ -157,6 +162,7 @@ export async function runInitDeposit(args, io, seams = {}) {
|
|
|
157
162
|
taskfileWired,
|
|
158
163
|
configDir,
|
|
159
164
|
legacyLayout: false,
|
|
165
|
+
stagedPaths,
|
|
160
166
|
};
|
|
161
167
|
}
|
|
162
168
|
/** CLI-facing wrapper: runs deposit, emits JSON or wizard UX, returns exit code. */
|
|
@@ -22,6 +22,8 @@ export interface RefreshDepositResult {
|
|
|
22
22
|
readonly agentsMdUpdated: boolean;
|
|
23
23
|
readonly versionSkewNotice: string | null;
|
|
24
24
|
readonly legacyLayout: boolean;
|
|
25
|
+
readonly taskfileWired: boolean;
|
|
26
|
+
readonly stagedPaths: string[];
|
|
25
27
|
}
|
|
26
28
|
export interface RefreshDepositSeams {
|
|
27
29
|
resolveContentRoot?: () => Promise<string>;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Refs #1942, #1430, #1671.
|
|
9
9
|
*/
|
|
10
|
-
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { join, resolve } from "node:path";
|
|
12
12
|
import { copyTree } from "../deposit/copy-tree.js";
|
|
13
13
|
import { prunePythonArtifactsFromDeposit } from "../deposit/python-free.js";
|
|
@@ -17,31 +17,11 @@ import { readCorePackageVersion } from "../engine-version.js";
|
|
|
17
17
|
import { resolveLifecycleRoot } from "../layout/resolve.js";
|
|
18
18
|
import { DEV_FALLBACK } from "../platform/constants.js";
|
|
19
19
|
import { gitPorcelain } from "../story-ready/git.js";
|
|
20
|
+
import { depositStagePaths, isInstallerManagedPath, printCommitGuidance } from "./hygiene.js";
|
|
20
21
|
import { parseInitArgv } from "./init-deposit.js";
|
|
21
22
|
import { buildLegacyRefusalJson, buildLegacyRefusalMessage, detectLegacyLayout, LEGACY_LAYOUT_REFUSED_EXIT_CODE, LegacyLayoutRefusedError, } from "./legacy-detect.js";
|
|
22
23
|
import { printMigrateNudgeIfNeeded } from "./migrate.js";
|
|
23
|
-
import { CANONICAL_INSTALL_ROOT, depositNeutralization, writeAgentsMd, writeInstallManifest, } from "./scaffold.js";
|
|
24
|
-
const INSTALLER_MANAGED_EXACT = new Set([
|
|
25
|
-
"AGENTS.md",
|
|
26
|
-
".gitattributes",
|
|
27
|
-
".gitignore",
|
|
28
|
-
"greptile.json",
|
|
29
|
-
".github/codeql/codeql-config.yml",
|
|
30
|
-
".github/workflows/deft-core-guard.yml",
|
|
31
|
-
"vbrief/.deft-version",
|
|
32
|
-
"vbrief/vbrief.md",
|
|
33
|
-
"vbrief/proposed/.gitkeep",
|
|
34
|
-
"vbrief/pending/.gitkeep",
|
|
35
|
-
"vbrief/active/.gitkeep",
|
|
36
|
-
"vbrief/completed/.gitkeep",
|
|
37
|
-
"vbrief/cancelled/.gitkeep",
|
|
38
|
-
]);
|
|
39
|
-
const INSTALLER_MANAGED_PREFIXES = [
|
|
40
|
-
".agents/",
|
|
41
|
-
".githooks/",
|
|
42
|
-
"vbrief/schemas/",
|
|
43
|
-
"vbrief/migration/",
|
|
44
|
-
];
|
|
24
|
+
import { CANONICAL_INSTALL_ROOT, depositNeutralization, ensureTaskfile, writeAgentsMd, writeInstallManifest, } from "./scaffold.js";
|
|
45
25
|
function normalizeVersion(version) {
|
|
46
26
|
return version.trim().replace(/^v/, "");
|
|
47
27
|
}
|
|
@@ -110,10 +90,34 @@ function syncBareVersionMarker(projectDir, version) {
|
|
|
110
90
|
// best-effort, mirrors install-upgrade marker write
|
|
111
91
|
}
|
|
112
92
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Retire a stale legacy `.deft/VERSION` after the canonical `.deft/core/VERSION`
|
|
95
|
+
* has been (re)written (#2064). Folded in from the former `install-upgrade`
|
|
96
|
+
* path so the shared refresh transaction covers the v0.27.x -> v0.28 manifest
|
|
97
|
+
* transition (#1046 PR-B) that the doctor's `install-manifest-disagreement`
|
|
98
|
+
* check still cites `task upgrade` to repair. Only acts when the canonical
|
|
99
|
+
* manifest lives at `<project>/.deft/core/VERSION` and a legacy
|
|
100
|
+
* `<project>/.deft/VERSION` disagrees; renames the legacy file to
|
|
101
|
+
* `.deft/VERSION.premigrate` (best-effort, never fatal).
|
|
102
|
+
*/
|
|
103
|
+
function migrateLegacyInstallManifest(projectDir, canonicalManifestPath) {
|
|
104
|
+
const canonical = resolve(canonicalManifestPath);
|
|
105
|
+
const expectedParent = resolve(projectDir, ".deft", "core");
|
|
106
|
+
if (resolve(canonical, "..") !== expectedParent)
|
|
107
|
+
return;
|
|
108
|
+
const legacy = join(projectDir, ".deft", "VERSION");
|
|
109
|
+
if (!existsSync(legacy) || !statSync(legacy).isFile())
|
|
110
|
+
return;
|
|
111
|
+
try {
|
|
112
|
+
const legacyVersion = manifestTagToVersion(parseInstallManifest(readFileSync(legacy, "utf8")));
|
|
113
|
+
const canonicalVersion = manifestTagToVersion(parseInstallManifest(readFileSync(canonical, "utf8")));
|
|
114
|
+
if (legacyVersion !== null && legacyVersion === canonicalVersion)
|
|
115
|
+
return;
|
|
116
|
+
renameSync(legacy, join(projectDir, ".deft", "VERSION.premigrate"));
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// best-effort
|
|
120
|
+
}
|
|
117
121
|
}
|
|
118
122
|
function unquoteGitPath(path) {
|
|
119
123
|
if (path.length >= 2 && path.startsWith('"') && path.endsWith('"')) {
|
|
@@ -206,7 +210,7 @@ export function buildUpdateSummaryJson(result, options) {
|
|
|
206
210
|
update: true,
|
|
207
211
|
non_interactive: options.nonInteractive,
|
|
208
212
|
upgrade: options.upgrade,
|
|
209
|
-
taskfile_wired:
|
|
213
|
+
taskfile_wired: result.taskfileWired,
|
|
210
214
|
missing_tools: [],
|
|
211
215
|
maintainer_mode: false,
|
|
212
216
|
maintainer_tools: [],
|
|
@@ -217,7 +221,7 @@ export function buildUpdateSummaryJson(result, options) {
|
|
|
217
221
|
strategy: "file-swap",
|
|
218
222
|
dirty_tree: false,
|
|
219
223
|
dirty_files: [],
|
|
220
|
-
staged_paths:
|
|
224
|
+
staged_paths: result.stagedPaths,
|
|
221
225
|
backup_path: "",
|
|
222
226
|
previous_version: result.previousDepositVersion ?? "",
|
|
223
227
|
content_version: result.contentVersion,
|
|
@@ -268,13 +272,25 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
268
272
|
fetchedBy: "directive-update",
|
|
269
273
|
...(previousManagedBy ? { managedBy: previousManagedBy } : {}),
|
|
270
274
|
};
|
|
271
|
-
writeInstallManifest(projectDir, deftDir, manifestFields);
|
|
275
|
+
const writtenManifestPath = writeInstallManifest(projectDir, deftDir, manifestFields);
|
|
276
|
+
// #2064: retire a stale legacy .deft/VERSION now that the canonical
|
|
277
|
+
// .deft/core/VERSION has been rewritten (folded in from install-upgrade so no
|
|
278
|
+
// manifest behavior is lost by the redirect). Best-effort; never fatal.
|
|
279
|
+
migrateLegacyInstallManifest(projectDir, writtenManifestPath);
|
|
272
280
|
// #2055: regenerate the bare .deft-version derivative so it agrees with the
|
|
273
281
|
// freshly written manifest tag (otherwise doctor's manifest-agreement check
|
|
274
282
|
// fails and the operator must hand-edit the marker).
|
|
275
283
|
syncBareVersionMarker(projectDir, contentVersion);
|
|
276
284
|
const agentsMdUpdated = writeAgentsMd(projectDir, deftDir, io);
|
|
277
285
|
await depositNeutralization(projectDir, io);
|
|
286
|
+
let taskfileWired = false;
|
|
287
|
+
if (args.nonInteractive) {
|
|
288
|
+
taskfileWired = ensureTaskfile(projectDir, io);
|
|
289
|
+
}
|
|
290
|
+
const { stagePaths, staged, stagedPaths } = depositStagePaths(projectDir, {
|
|
291
|
+
includeTaskfile: taskfileWired,
|
|
292
|
+
});
|
|
293
|
+
printCommitGuidance(io, stagePaths, staged);
|
|
278
294
|
const readPorcelain = seams.gitPorcelain ?? gitPorcelain;
|
|
279
295
|
printRefreshSideEffects(io, frameworkRefreshSideEffects(projectDir, readPorcelain));
|
|
280
296
|
if (versionSkewNotice) {
|
|
@@ -289,6 +305,8 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
289
305
|
agentsMdUpdated,
|
|
290
306
|
versionSkewNotice,
|
|
291
307
|
legacyLayout: false,
|
|
308
|
+
taskfileWired,
|
|
309
|
+
stagedPaths,
|
|
292
310
|
};
|
|
293
311
|
}
|
|
294
312
|
/** CLI-facing wrapper: runs refresh, emits JSON or wizard UX, returns exit code. */
|
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Refs #1942, #1430, #1463, #1179.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
export declare const CANONICAL_INSTALL_ROOT = ".deft/core";
|
|
7
|
+
import { CANONICAL_INSTALL_ROOT, type InitDepositIo } from "./constants.js";
|
|
8
|
+
export type { InitDepositIo };
|
|
9
|
+
export { CANONICAL_INSTALL_ROOT };
|
|
11
10
|
export declare const CORE_GLOB = ".deft/core/**";
|
|
12
11
|
export declare const MINIMAL_TASKFILE = "version: '3'\n\n# Taskfile for this project.\n# Installed by deft-install --yes (Epic-4). Add your own tasks below or in\n# additional included files. The deft include makes all framework tasks\n# (task check, task vbrief:*, task doctor, etc.) available from the project root.\n\nincludes:\n deft:\n taskfile: ./.deft/core/Taskfile.yml\n optional: true\n";
|
|
13
12
|
export declare const CANONICAL_TASKFILE_INCLUDE = "taskfile: ./.deft/core/Taskfile.yml";
|
|
@@ -12,7 +12,9 @@ import { dirname, join, relative } from "node:path";
|
|
|
12
12
|
import { copyTree } from "../deposit/copy-tree.js";
|
|
13
13
|
import { resolveLifecycleRoot } from "../layout/resolve.js";
|
|
14
14
|
import { agentsRefreshPlan } from "../platform/agents-md.js";
|
|
15
|
-
|
|
15
|
+
import { CANONICAL_INSTALL_ROOT } from "./constants.js";
|
|
16
|
+
import { installerManagedGuardEre } from "./hygiene.js";
|
|
17
|
+
export { CANONICAL_INSTALL_ROOT };
|
|
16
18
|
export const CORE_GLOB = ".deft/core/**";
|
|
17
19
|
const CODEQL_CONFIG_REL = ".github/codeql/codeql-config.yml";
|
|
18
20
|
const CORE_GUARD_WORKFLOW_REL = ".github/workflows/deft-core-guard.yml";
|
|
@@ -475,29 +477,6 @@ export function writeConsumerGitHooks(projectDir, deftDir, io, seams = {}) {
|
|
|
475
477
|
}
|
|
476
478
|
return filesDeposited || configWired;
|
|
477
479
|
}
|
|
478
|
-
function escapeEre(value) {
|
|
479
|
-
return value.replace(/[.^$*+?()[\]{}|\\]/g, "\\$&");
|
|
480
|
-
}
|
|
481
|
-
function installerManagedGuardEre() {
|
|
482
|
-
const matchers = [
|
|
483
|
-
{ exact: "AGENTS.md" },
|
|
484
|
-
{ prefix: ".agents/" },
|
|
485
|
-
{ prefix: ".githooks/" },
|
|
486
|
-
{ exact: ".gitattributes" },
|
|
487
|
-
{ exact: ".gitignore" },
|
|
488
|
-
{ exact: "greptile.json" },
|
|
489
|
-
{ exact: CODEQL_CONFIG_REL },
|
|
490
|
-
{ exact: CORE_GUARD_WORKFLOW_REL },
|
|
491
|
-
{ exact: "vbrief/.deft-version" },
|
|
492
|
-
{ exact: "vbrief/vbrief.md" },
|
|
493
|
-
{ prefix: "vbrief/schemas/" },
|
|
494
|
-
{ prefix: "vbrief/migration/" },
|
|
495
|
-
...VBRIEF_LIFECYCLE_DIRS.map((sub) => ({ exact: `vbrief/${sub}/.gitkeep` })),
|
|
496
|
-
];
|
|
497
|
-
return matchers
|
|
498
|
-
.map((m) => (m.exact ? `^${escapeEre(m.exact)}$` : `^${escapeEre(m.prefix ?? "")}`))
|
|
499
|
-
.join("|");
|
|
500
|
-
}
|
|
501
480
|
function githubActionsExpr(expression) {
|
|
502
481
|
return ["$", "{{ ", expression, " }}"].join("");
|
|
503
482
|
}
|
|
@@ -48,6 +48,7 @@ export interface GitHubAuthModesCliArgs {
|
|
|
48
48
|
githubAuthMode?: string | null;
|
|
49
49
|
repo?: string;
|
|
50
50
|
json?: boolean;
|
|
51
|
+
runGh?: GhRunner;
|
|
51
52
|
}
|
|
52
53
|
export declare function githubAuthModesMain(args: GitHubAuthModesCliArgs): number;
|
|
53
54
|
export { probeRuntimeCapabilities };
|
|
@@ -257,6 +257,7 @@ export function resultToDict(result) {
|
|
|
257
257
|
export function githubAuthModesMain(args) {
|
|
258
258
|
const result = validateGithubAuthForWorker(args.githubAuthMode ?? null, {
|
|
259
259
|
repo: args.repo ?? DEFAULT_VALIDATION_REPO,
|
|
260
|
+
runGh: args.runGh,
|
|
260
261
|
});
|
|
261
262
|
if (args.json) {
|
|
262
263
|
process.stdout.write(`${JSON.stringify(resultToDict(result), null, 2)}\n`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LEGACY_ARTIFACT_SUFFIX, LEGACY_INFO_ROOT_KEY, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY } from "../xbrief-migrate/constants.js";
|
|
1
2
|
import { type ScmCallFn } from "./reconcile-issues.js";
|
|
2
3
|
export declare const INGEST_STATUSES: readonly ["proposed", "pending", "active"];
|
|
3
4
|
export type IngestStatus = (typeof INGEST_STATUSES)[number];
|
|
@@ -22,8 +23,11 @@ export declare function scanProvenanceRefs(vbriefDir: string): Map<number, strin
|
|
|
22
23
|
export declare function composeOverviewWithComments(body: string, comments: readonly IssueComment[]): string;
|
|
23
24
|
export declare function issueCommentThread(issue: Record<string, unknown>): IssueComment[];
|
|
24
25
|
export declare function issueCommentsAlreadyFetched(issue: Record<string, unknown>): boolean;
|
|
25
|
-
export declare function buildIssueVbrief(issue: Record<string, unknown>, status: IngestStatus, repoUrl: string
|
|
26
|
-
|
|
26
|
+
export declare function buildIssueVbrief(issue: Record<string, unknown>, status: IngestStatus, repoUrl: string, options?: {
|
|
27
|
+
infoRootKey?: typeof LEGACY_INFO_ROOT_KEY | typeof MIGRATED_INFO_ROOT_KEY;
|
|
28
|
+
infoVersion?: string;
|
|
29
|
+
}): [Record<string, unknown>, string];
|
|
30
|
+
export declare function targetFilename(number: number, title: string, artifactSuffix?: typeof LEGACY_ARTIFACT_SUFFIX | typeof MIGRATED_ARTIFACT_SUFFIX): string;
|
|
27
31
|
export interface FetchIssueOptions {
|
|
28
32
|
readonly cwd?: string | null;
|
|
29
33
|
readonly cacheRoot?: string | null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join, resolve } from "node:path";
|
|
2
|
+
import { basename, join, resolve } from "node:path";
|
|
3
3
|
import { cacheGet } from "../cache/operations.js";
|
|
4
4
|
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
5
5
|
import { call } from "../scm/call.js";
|
|
@@ -7,6 +7,7 @@ import { resolveProjectRoot } from "../scope/project-context.js";
|
|
|
7
7
|
import { resolveProjectRepo } from "../slice/project-context.js";
|
|
8
8
|
import { slugify, TODAY } from "../vbrief-build/build.js";
|
|
9
9
|
import { EMITTED_VBRIEF_VERSION } from "../vbrief-build/constants.js";
|
|
10
|
+
import { LEGACY_ARTIFACT_SUFFIX, LEGACY_INFO_ROOT_KEY, LEGACY_VBRIEF_VERSION, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY, VBRIEF_VERSION, } from "../xbrief-migrate/constants.js";
|
|
10
11
|
import { findAcHeading, parseCheckboxItems, parseListItems, sliceAcSection, stripCodeBlocks, stripFencedCodeBlocks, } from "./markdown-scanners.js";
|
|
11
12
|
import { detectRepo, extractReferencesFromVbrief, fetchOpenIssues, GITHUB_ISSUE_REF_TYPES, LIFECYCLE_FOLDERS, parseIssueNumber, } from "./reconcile-issues.js";
|
|
12
13
|
export const INGEST_STATUSES = ["proposed", "pending", "active"];
|
|
@@ -17,6 +18,53 @@ const STATUS_MAP = {
|
|
|
17
18
|
pending: ["pending", "pending"],
|
|
18
19
|
active: ["active", "running"],
|
|
19
20
|
};
|
|
21
|
+
function readDeclaredInfoVersion(vbriefDir, infoRootKey, artifactSuffix, fallbackVersion) {
|
|
22
|
+
const projectDefinitionPath = join(vbriefDir, `PROJECT-DEFINITION${artifactSuffix}`);
|
|
23
|
+
try {
|
|
24
|
+
const parsed = JSON.parse(readFileSync(projectDefinitionPath, "utf8"));
|
|
25
|
+
const info = parsed[infoRootKey];
|
|
26
|
+
if (info !== null && typeof info === "object" && !Array.isArray(info)) {
|
|
27
|
+
const declared = info.version;
|
|
28
|
+
if (typeof declared === "string" && declared.length > 0) {
|
|
29
|
+
return declared;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Fall back to the schema-default version when no declaration is available.
|
|
35
|
+
}
|
|
36
|
+
return fallbackVersion;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Decide the emission format (`.xbrief.json` + `xBRIEFInfo` vs legacy `.vbrief.json`
|
|
40
|
+
* + `vBRIEFInfo`) for an ingested scope artifact.
|
|
41
|
+
*
|
|
42
|
+
* The decision is STRUCTURAL and keyed on the resolved lifecycle root directory
|
|
43
|
+
* (`vbriefDir`), which is itself produced by `resolveLifecycleLayout` / `resolveLifecycleRoot`
|
|
44
|
+
* -- the exact same layout-decision logic `project:render` uses. This guarantees the two
|
|
45
|
+
* surfaces cannot diverge (#2149 finding #1): whichever tree `resolveLifecycleLayout` selected
|
|
46
|
+
* (`xbrief/` when migrated, else `vbrief/`) is the tree ingest writes into, and the emission
|
|
47
|
+
* format always matches that directory.
|
|
48
|
+
*
|
|
49
|
+
* It deliberately does NOT content-scan the tree for legacy markers (`detectLegacyVbriefLayout`).
|
|
50
|
+
* A historical vBRIEF-serialized artifact sitting in `xbrief/completed/` is migrated content,
|
|
51
|
+
* not a legacy layout, and must NOT force legacy emission on a migrated project (#2149 finding #3).
|
|
52
|
+
*/
|
|
53
|
+
function resolveIngestEmissionLayout(vbriefDir) {
|
|
54
|
+
const migrated = basename(vbriefDir) === MIGRATED_ARTIFACT_DIR;
|
|
55
|
+
if (!migrated) {
|
|
56
|
+
return {
|
|
57
|
+
artifactSuffix: LEGACY_ARTIFACT_SUFFIX,
|
|
58
|
+
infoRootKey: LEGACY_INFO_ROOT_KEY,
|
|
59
|
+
infoVersion: LEGACY_VBRIEF_VERSION,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
artifactSuffix: MIGRATED_ARTIFACT_SUFFIX,
|
|
64
|
+
infoRootKey: MIGRATED_INFO_ROOT_KEY,
|
|
65
|
+
infoVersion: readDeclaredInfoVersion(vbriefDir, MIGRATED_INFO_ROOT_KEY, MIGRATED_ARTIFACT_SUFFIX, VBRIEF_VERSION),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
20
68
|
const CONTROL_CHAR_LABELS = {
|
|
21
69
|
"\b": "U+0008 backspace",
|
|
22
70
|
"\t": "U+0009 tab",
|
|
@@ -230,7 +278,7 @@ export function issueCommentThread(issue) {
|
|
|
230
278
|
export function issueCommentsAlreadyFetched(issue) {
|
|
231
279
|
return Object.hasOwn(issue, ISSUE_COMMENT_THREAD_KEY);
|
|
232
280
|
}
|
|
233
|
-
export function buildIssueVbrief(issue, status, repoUrl) {
|
|
281
|
+
export function buildIssueVbrief(issue, status, repoUrl, options = {}) {
|
|
234
282
|
const number = Number(issue.number);
|
|
235
283
|
const title = (typeof issue.title === "string" && issue.title.length > 0
|
|
236
284
|
? issue.title
|
|
@@ -291,20 +339,23 @@ export function buildIssueVbrief(issue, status, repoUrl) {
|
|
|
291
339
|
}
|
|
292
340
|
plan.references = references;
|
|
293
341
|
}
|
|
342
|
+
const infoRootKey = options.infoRootKey ?? LEGACY_INFO_ROOT_KEY;
|
|
343
|
+
const infoVersion = options.infoVersion ?? EMITTED_VBRIEF_VERSION;
|
|
344
|
+
const briefLabel = infoRootKey === MIGRATED_INFO_ROOT_KEY ? "xBRIEF" : "vBRIEF";
|
|
294
345
|
return [
|
|
295
346
|
{
|
|
296
|
-
|
|
297
|
-
version:
|
|
298
|
-
description: `Scope
|
|
347
|
+
[infoRootKey]: {
|
|
348
|
+
version: infoVersion,
|
|
349
|
+
description: `Scope ${briefLabel} ingested from GitHub issue #${number}`,
|
|
299
350
|
},
|
|
300
351
|
plan,
|
|
301
352
|
},
|
|
302
353
|
folder,
|
|
303
354
|
];
|
|
304
355
|
}
|
|
305
|
-
export function targetFilename(number, title) {
|
|
356
|
+
export function targetFilename(number, title, artifactSuffix = LEGACY_ARTIFACT_SUFFIX) {
|
|
306
357
|
const slug = slugify(title) || `issue-${number}`;
|
|
307
|
-
return `${TODAY}-${number}-${slug}
|
|
358
|
+
return `${TODAY}-${number}-${slug}${artifactSuffix}`;
|
|
308
359
|
}
|
|
309
360
|
export function fetchFromCache(repo, number, options = {}) {
|
|
310
361
|
const key = `${repo}/${number}`;
|
|
@@ -423,8 +474,12 @@ export function ingestOne(issue, options) {
|
|
|
423
474
|
cwd: options.cwd,
|
|
424
475
|
cacheRoot: options.cacheRoot,
|
|
425
476
|
});
|
|
426
|
-
const
|
|
427
|
-
const
|
|
477
|
+
const emissionLayout = resolveIngestEmissionLayout(options.vbriefDir);
|
|
478
|
+
const [vbrief, folder] = buildIssueVbrief(enriched, options.status, options.repoUrl, {
|
|
479
|
+
infoRootKey: emissionLayout.infoRootKey,
|
|
480
|
+
infoVersion: emissionLayout.infoVersion,
|
|
481
|
+
});
|
|
482
|
+
const filename = targetFilename(number, String(issue.title ?? ""), emissionLayout.artifactSuffix);
|
|
428
483
|
const target = join(options.vbriefDir, folder, filename);
|
|
429
484
|
if (options.dryRun) {
|
|
430
485
|
return ["dryrun", target, `DRY-RUN would write ${folder}/${filename}`];
|
|
@@ -568,6 +623,7 @@ export function ingestSingleForAccept(n, repo, options = {}) {
|
|
|
568
623
|
vbriefDir,
|
|
569
624
|
status: options.status ?? "proposed",
|
|
570
625
|
repoUrl,
|
|
626
|
+
cwd: root,
|
|
571
627
|
});
|
|
572
628
|
return [result, path];
|
|
573
629
|
}
|
|
@@ -11,7 +11,6 @@ export interface DispatchCall {
|
|
|
11
11
|
projectRoot: string;
|
|
12
12
|
frameworkRoot: string;
|
|
13
13
|
}
|
|
14
|
-
export declare function isFrameworkSourceContext(frameworkRoot: string, projectRoot: string): boolean;
|
|
15
14
|
/** Mirror ``scripts/_project_context.dispatch_task_check`` for consumer-task e2e. */
|
|
16
15
|
export declare function dispatchTaskCheck(frameworkRoot: string, projectRoot: string, runner: (command: string, projectRoot: string, frameworkRoot: string) => {
|
|
17
16
|
code: number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
|
-
import { join
|
|
3
|
+
import { join } from "node:path";
|
|
4
4
|
import { afterEach } from "vitest";
|
|
5
5
|
import { restIssueListPaginated, setPaginatedLister, setSingleIssueFetcher, setSleepFn, } from "../cache/fetch.js";
|
|
6
6
|
import { auditPath } from "../cache/paths.js";
|
|
@@ -108,14 +108,10 @@ export function writeScopeVbrief(projectRoot, folder, filename, status = "propos
|
|
|
108
108
|
}, null, 2)}\n`, "utf8");
|
|
109
109
|
return target;
|
|
110
110
|
}
|
|
111
|
-
|
|
112
|
-
return resolve(frameworkRoot) === resolve(projectRoot);
|
|
113
|
-
}
|
|
111
|
+
import { resolveCheckTarget } from "../check/orchestrator.js";
|
|
114
112
|
/** Mirror ``scripts/_project_context.dispatch_task_check`` for consumer-task e2e. */
|
|
115
113
|
export function dispatchTaskCheck(frameworkRoot, projectRoot, runner) {
|
|
116
|
-
const target =
|
|
117
|
-
? "check:framework-source"
|
|
118
|
-
: "check:consumer";
|
|
114
|
+
const target = resolveCheckTarget(frameworkRoot, projectRoot);
|
|
119
115
|
return runner(target, projectRoot, frameworkRoot).code;
|
|
120
116
|
}
|
|
121
117
|
/** Hermetic mirror of ``tests/integration/test_scm_smoke._probe_rate_limit``. */
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Lifecycle folders searched when a folder-qualified ref dangles (#1926). */
|
|
2
|
+
export declare const LIFECYCLE_SCOPE_FOLDERS: readonly ["proposed", "pending", "active", "completed", "cancelled"];
|
|
3
|
+
export interface ResolveLifecycleArtifactRefOptions {
|
|
4
|
+
/** When false, only resolve the literal path (for rewrite matching). Default true (#1926). */
|
|
5
|
+
readonly allowCrossFolderSearch?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Resolve a lifecycle-relative artifact reference to an absolute path.
|
|
9
|
+
*
|
|
10
|
+
* When the direct path is missing but the ref names a lifecycle artifact
|
|
11
|
+
* (folder-qualified or bare basename), search all lifecycle folders for the
|
|
12
|
+
* same filename (#1926).
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveLifecycleArtifactRef(rel: string, lifecycleRoot: string, options?: ResolveLifecycleArtifactRefOptions): string;
|
|
15
|
+
//# sourceMappingURL=lifecycle-ref.d.ts.map
|