@deftai/directive-core 0.67.0 → 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/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/doctor/checks.js +11 -11
- package/dist/doctor/constants.d.ts +2 -0
- package/dist/doctor/constants.js +2 -0
- package/dist/doctor/payload-staleness.js +15 -6
- 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/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-md.d.ts +28 -0
- package/dist/platform/agents-md.js +34 -0
- package/dist/policy/resolve.js +56 -46
- package/dist/pr-merge-readiness/compute.d.ts +10 -0
- package/dist/pr-merge-readiness/compute.js +53 -17
- package/dist/pr-merge-readiness/index.d.ts +2 -1
- package/dist/pr-merge-readiness/index.js +1 -0
- package/dist/pr-merge-readiness/mergeability.d.ts +43 -0
- package/dist/pr-merge-readiness/mergeability.js +105 -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/render/project-render.js +114 -89
- 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 +7 -0
- package/dist/swarm/launch.js +17 -5
- 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/package.json +7 -7
- package/dist/install-upgrade/index.d.ts +0 -13
- package/dist/install-upgrade/index.js +0 -251
|
@@ -2,6 +2,7 @@ import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, renam
|
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
3
|
import { hasArtifactSuffix, resolveLifecycleFolder, resolveProjectDefinitionPath, } from "../../layout/resolve.js";
|
|
4
4
|
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY } from "../../policy/plan-extensions.js";
|
|
5
|
+
import { projectDefinitionMutationLock } from "../../vbrief-build/project-definition-io.js";
|
|
5
6
|
import { AUDIT_LOG_REL_PATH, DEFAULT_RELIEF_AGE_DAYS, DEFAULT_WIP_CAP, SUBSCRIPTION_PRESETS, WELCOME_AUDIT_TAG, } from "./constants.js";
|
|
6
7
|
function projectDefinitionPath(projectRoot) {
|
|
7
8
|
return resolveProjectDefinitionPath(projectRoot);
|
|
@@ -27,77 +28,91 @@ function atomicWrite(path, data) {
|
|
|
27
28
|
renameSync(tmp, path);
|
|
28
29
|
}
|
|
29
30
|
export function writeTriageScope(projectRoot, rules, options = { presetLabel: "custom" }) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
31
|
+
// Serialise read-modify-write + audit append under the shared lock (#1260).
|
|
32
|
+
return projectDefinitionMutationLock(projectRoot, () => {
|
|
33
|
+
const path = projectDefinitionPath(projectRoot);
|
|
34
|
+
if (!existsSync(path))
|
|
35
|
+
throw new Error(`PROJECT-DEFINITION not found at ${path}`);
|
|
36
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
37
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
38
|
+
throw new Error(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
|
|
39
|
+
}
|
|
40
|
+
const data = parsed;
|
|
41
|
+
const plan = data.plan;
|
|
42
|
+
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
43
|
+
throw new Error("PROJECT-DEFINITION 'plan' is not an object");
|
|
44
|
+
}
|
|
45
|
+
const planRec = plan;
|
|
46
|
+
migrateLegacyPolicyKey(planRec);
|
|
47
|
+
let policy = planRec[PLAN_POLICY_KEY];
|
|
48
|
+
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
49
|
+
policy = {};
|
|
50
|
+
planRec[PLAN_POLICY_KEY] = policy;
|
|
51
|
+
}
|
|
52
|
+
const policyRec = policy;
|
|
53
|
+
const previous = policyRec.triageScope;
|
|
54
|
+
policyRec.triageScope = rules;
|
|
55
|
+
atomicWrite(path, data);
|
|
56
|
+
const changed = JSON.stringify(previous) !== JSON.stringify(rules);
|
|
57
|
+
const actor = options.actor ?? WELCOME_AUDIT_TAG;
|
|
58
|
+
const auditEntry = [
|
|
59
|
+
`actor=${actor}`,
|
|
60
|
+
"field=plan.policy.triageScope",
|
|
61
|
+
`preset=${options.presetLabel}`,
|
|
62
|
+
`rule_count=${rules.length}`,
|
|
63
|
+
`changed=${changed ? "true" : "false"}`,
|
|
64
|
+
].join(" ");
|
|
65
|
+
appendAuditEntry(projectRoot, auditEntry);
|
|
66
|
+
return [changed, auditEntry];
|
|
67
|
+
});
|
|
60
68
|
}
|
|
61
69
|
export function writeWipCap(projectRoot, wipCap, options = {}) {
|
|
62
70
|
if (!Number.isInteger(wipCap) || wipCap < 1) {
|
|
63
71
|
throw new Error(`wipCap must be a positive int, got ${JSON.stringify(wipCap)}`);
|
|
64
72
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
73
|
+
// Serialise read-modify-write + audit append under the shared lock (#1260).
|
|
74
|
+
return projectDefinitionMutationLock(projectRoot, () => {
|
|
75
|
+
const path = projectDefinitionPath(projectRoot);
|
|
76
|
+
if (!existsSync(path))
|
|
77
|
+
throw new Error(`PROJECT-DEFINITION not found at ${path}`);
|
|
78
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
79
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
80
|
+
throw new Error(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
|
|
81
|
+
}
|
|
82
|
+
const data = parsed;
|
|
83
|
+
const plan = data.plan;
|
|
84
|
+
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
85
|
+
throw new Error("PROJECT-DEFINITION 'plan' is not an object");
|
|
86
|
+
}
|
|
87
|
+
const planRec = plan;
|
|
88
|
+
migrateLegacyPolicyKey(planRec);
|
|
89
|
+
let policy = planRec[PLAN_POLICY_KEY];
|
|
90
|
+
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
91
|
+
policy = {};
|
|
92
|
+
planRec[PLAN_POLICY_KEY] = policy;
|
|
93
|
+
}
|
|
94
|
+
const policyRec = policy;
|
|
95
|
+
const previous = policyRec.wipCap;
|
|
96
|
+
const actor = options.actor ?? WELCOME_AUDIT_TAG;
|
|
97
|
+
if (previous === undefined && wipCap === DEFAULT_WIP_CAP) {
|
|
98
|
+
return [false, ""];
|
|
99
|
+
}
|
|
100
|
+
if (previous !== undefined && wipCap === DEFAULT_WIP_CAP) {
|
|
101
|
+
delete policyRec.wipCap;
|
|
102
|
+
atomicWrite(path, data);
|
|
103
|
+
const auditEntry = `actor=${actor} field=plan.policy.wipCap action=cleared-to-default value=${wipCap} ` +
|
|
104
|
+
`previous=${JSON.stringify(previous)} changed=true`;
|
|
105
|
+
appendAuditEntry(projectRoot, auditEntry);
|
|
106
|
+
return [true, auditEntry];
|
|
107
|
+
}
|
|
108
|
+
policyRec.wipCap = wipCap;
|
|
88
109
|
atomicWrite(path, data);
|
|
89
|
-
const
|
|
90
|
-
|
|
110
|
+
const changed = previous !== wipCap;
|
|
111
|
+
const auditEntry = `actor=${actor} field=plan.policy.wipCap value=${wipCap} previous=${JSON.stringify(previous)} ` +
|
|
112
|
+
`changed=${changed ? "true" : "false"}`;
|
|
91
113
|
appendAuditEntry(projectRoot, auditEntry);
|
|
92
|
-
return [
|
|
93
|
-
}
|
|
94
|
-
policyRec.wipCap = wipCap;
|
|
95
|
-
atomicWrite(path, data);
|
|
96
|
-
const changed = previous !== wipCap;
|
|
97
|
-
const auditEntry = `actor=${actor} field=plan.policy.wipCap value=${wipCap} previous=${JSON.stringify(previous)} ` +
|
|
98
|
-
`changed=${changed ? "true" : "false"}`;
|
|
99
|
-
appendAuditEntry(projectRoot, auditEntry);
|
|
100
|
-
return [changed, auditEntry];
|
|
114
|
+
return [changed, auditEntry];
|
|
115
|
+
});
|
|
101
116
|
}
|
|
102
117
|
function daysInPending(path, now) {
|
|
103
118
|
try {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync, writeSync, } from "node:fs";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { resolveProjectDefinitionPath } from "../layout/resolve.js";
|
|
4
5
|
import { PROJECT_DEFINITION_REL_PATH } from "./constants.js";
|
|
5
6
|
import { pythonJsonPretty } from "./json.js";
|
|
6
7
|
import { ProjectDefinitionIOError } from "./types.js";
|
|
@@ -18,8 +19,12 @@ function defaultSleep(ms) {
|
|
|
18
19
|
export function projectDefinitionMutationLock(projectRoot, fn, deps = {}) {
|
|
19
20
|
const sleepMs = deps.sleepMs ?? defaultSleep;
|
|
20
21
|
const now = deps.now ?? Date.now;
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
// Derive the sidecar lock path from the layout-aware resolved PROJECT-DEFINITION
|
|
23
|
+
// path (xbrief/ when migrated, else vbrief/) so the lock lives next to the real
|
|
24
|
+
// artifact and every mutator sharing a project root contends on the same lock,
|
|
25
|
+
// instead of the constant vbrief/ path which would strand a stray lock (#1260).
|
|
26
|
+
const path = resolveProjectDefinitionPath(resolve(projectRoot));
|
|
27
|
+
const lockPath = `${path}.lock`;
|
|
23
28
|
mkdirSync(dirname(lockPath), { recursive: true });
|
|
24
29
|
if (mutationThreadLock.held) {
|
|
25
30
|
throw new Error("project definition mutation lock is not reentrant");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { basename, relative, resolve, sep } from "node:path";
|
|
2
|
+
import { resolveLifecycleArtifactRef } from "../layout/lifecycle-ref.js";
|
|
2
3
|
import { hasArtifactSuffix, stripArtifactSuffix } from "../layout/resolve.js";
|
|
3
4
|
/** Return true when ``child`` resolves under ``parent``. */
|
|
4
5
|
export function isRelativeTo(child, parent) {
|
|
@@ -15,14 +16,17 @@ export function resolveRefPath(uri, vbriefDir) {
|
|
|
15
16
|
if (typeof uri !== "string") {
|
|
16
17
|
return null;
|
|
17
18
|
}
|
|
19
|
+
let rel;
|
|
18
20
|
if (uri.startsWith("file://")) {
|
|
19
|
-
|
|
20
|
-
return resolve(vbriefDir, rel);
|
|
21
|
+
rel = uri.slice("file://".length);
|
|
21
22
|
}
|
|
22
|
-
if (uri.startsWith("http://") || uri.startsWith("https://") || uri.startsWith("#")) {
|
|
23
|
+
else if (uri.startsWith("http://") || uri.startsWith("https://") || uri.startsWith("#")) {
|
|
23
24
|
return null;
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
+
else {
|
|
27
|
+
rel = uri;
|
|
28
|
+
}
|
|
29
|
+
return resolveLifecycleArtifactRef(rel, vbriefDir);
|
|
26
30
|
}
|
|
27
31
|
/** Split like Python ``str.split(sep, maxsplit)`` (maxsplit splits, not result length). */
|
|
28
32
|
function splitMax(value, sep, maxsplit) {
|
|
@@ -4,7 +4,7 @@ export { DEPRECATION_SENTINEL as DEPRECATED_REDIRECT_SENTINEL };
|
|
|
4
4
|
export declare function missingLifecycleFolders(projectRoot: string): string[];
|
|
5
5
|
/** Return true when markdown content is a migration redirect stub. */
|
|
6
6
|
export declare function isDeprecationRedirect(content: string): boolean;
|
|
7
|
-
/** Full-spec generated export (specification
|
|
7
|
+
/** Full-spec generated export (layout-resolved specification artifact source line). */
|
|
8
8
|
export declare function isGeneratedSpecificationExport(projectRoot: string, content: string): boolean;
|
|
9
9
|
/** Return true for a fully current generated spec export (full-spec or greenfield). */
|
|
10
10
|
export declare function isCurrentGeneratedSpecification(projectRoot: string, content: string): boolean;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveLifecycleLayout, resolveLifecycleRoot, resolveSpecArtifactPath, } from "../layout/resolve.js";
|
|
4
|
+
import { contentHasGeneratedSpecSource, GENERATED_SPEC_PURPOSE, } from "../spec-authority/constants.js";
|
|
4
5
|
import { isFullSpecState, isGreenfieldSpecExport } from "../spec-authority/resolver.js";
|
|
5
6
|
import { DEPRECATION_SENTINEL } from "../vbrief-build/constants.js";
|
|
6
7
|
export { DEPRECATION_SENTINEL as DEPRECATED_REDIRECT_SENTINEL };
|
|
7
8
|
const DEPRECATION_REDIRECT_PURPOSE = "<!-- Purpose: deprecation redirect -->";
|
|
8
|
-
const SPEC_SOURCE_RELPATH = join("vbrief", "specification.vbrief.json");
|
|
9
9
|
const LIFECYCLE_FOLDERS = ["proposed", "pending", "active", "completed", "cancelled"];
|
|
10
10
|
export function missingLifecycleFolders(projectRoot) {
|
|
11
|
-
const
|
|
12
|
-
return LIFECYCLE_FOLDERS.filter((folder) => !existsSync(join(
|
|
11
|
+
const lifecycleRoot = resolveLifecycleRoot(projectRoot);
|
|
12
|
+
return LIFECYCLE_FOLDERS.filter((folder) => !existsSync(join(lifecycleRoot, folder)));
|
|
13
13
|
}
|
|
14
14
|
function hasCompleteLifecycle(projectRoot) {
|
|
15
15
|
return missingLifecycleFolders(projectRoot).length === 0;
|
|
@@ -18,11 +18,11 @@ function hasCompleteLifecycle(projectRoot) {
|
|
|
18
18
|
export function isDeprecationRedirect(content) {
|
|
19
19
|
return content.includes(DEPRECATION_SENTINEL) || content.includes(DEPRECATION_REDIRECT_PURPOSE);
|
|
20
20
|
}
|
|
21
|
-
/** Full-spec generated export (specification
|
|
21
|
+
/** Full-spec generated export (layout-resolved specification artifact source line). */
|
|
22
22
|
export function isGeneratedSpecificationExport(projectRoot, content) {
|
|
23
23
|
return (content.includes(GENERATED_SPEC_PURPOSE) &&
|
|
24
|
-
content
|
|
25
|
-
existsSync(
|
|
24
|
+
contentHasGeneratedSpecSource(content) &&
|
|
25
|
+
existsSync(resolveSpecArtifactPath(projectRoot)));
|
|
26
26
|
}
|
|
27
27
|
/** Return true for a fully current generated spec export (full-spec or greenfield). */
|
|
28
28
|
export function isCurrentGeneratedSpecification(projectRoot, content) {
|
|
@@ -56,6 +56,8 @@ function rootMarkdownIsLegacy(projectRoot, filename, content) {
|
|
|
56
56
|
return false;
|
|
57
57
|
if (isGreenfieldSpecExport(projectRoot))
|
|
58
58
|
return false;
|
|
59
|
+
if (isFullSpecState(projectRoot))
|
|
60
|
+
return false;
|
|
59
61
|
}
|
|
60
62
|
return filename === "SPECIFICATION.md" || filename === "PROJECT.md";
|
|
61
63
|
}
|
|
@@ -89,11 +91,11 @@ export function detectPreCutover(projectRoot) {
|
|
|
89
91
|
reasons.push("PROJECT.md is a pre-v0.20 hand-authored doc (not a deprecation redirect)");
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
|
-
const
|
|
93
|
-
if (existsSync(
|
|
94
|
+
const layout = resolveLifecycleLayout(projectRoot);
|
|
95
|
+
if (existsSync(layout.root)) {
|
|
94
96
|
const missing = missingLifecycleFolders(projectRoot);
|
|
95
97
|
if (missing.length > 0) {
|
|
96
|
-
reasons.push(
|
|
98
|
+
reasons.push(`${layout.artifactDir}/ is missing lifecycle folder(s): ${missing.join(", ")}`);
|
|
97
99
|
}
|
|
98
100
|
}
|
|
99
101
|
return { preCutover: reasons.length > 0, reasons };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.68.0",
|
|
4
4
|
"description": "TypeScript engine core for the Directive framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -110,6 +110,10 @@
|
|
|
110
110
|
"types": "./dist/pr-wait-mergeable/index.d.ts",
|
|
111
111
|
"default": "./dist/pr-wait-mergeable/index.js"
|
|
112
112
|
},
|
|
113
|
+
"./pr-watch": {
|
|
114
|
+
"types": "./dist/pr-watch/index.d.ts",
|
|
115
|
+
"default": "./dist/pr-watch/index.js"
|
|
116
|
+
},
|
|
113
117
|
"./vbrief-build": {
|
|
114
118
|
"types": "./dist/vbrief-build/index.d.ts",
|
|
115
119
|
"default": "./dist/vbrief-build/index.js"
|
|
@@ -218,10 +222,6 @@
|
|
|
218
222
|
"types": "./dist/umbrella-current-shape/index.d.ts",
|
|
219
223
|
"default": "./dist/umbrella-current-shape/index.js"
|
|
220
224
|
},
|
|
221
|
-
"./install-upgrade": {
|
|
222
|
-
"types": "./dist/install-upgrade/index.d.ts",
|
|
223
|
-
"default": "./dist/install-upgrade/index.js"
|
|
224
|
-
},
|
|
225
225
|
"./task-surface": {
|
|
226
226
|
"types": "./dist/task-surface/index.d.ts",
|
|
227
227
|
"default": "./dist/task-surface/index.js"
|
|
@@ -245,8 +245,8 @@
|
|
|
245
245
|
"provenance": true
|
|
246
246
|
},
|
|
247
247
|
"dependencies": {
|
|
248
|
-
"@deftai/directive-content": "^0.
|
|
249
|
-
"@deftai/directive-types": "^0.
|
|
248
|
+
"@deftai/directive-content": "^0.68.0",
|
|
249
|
+
"@deftai/directive-types": "^0.68.0",
|
|
250
250
|
"archiver": "^8.0.0"
|
|
251
251
|
},
|
|
252
252
|
"scripts": {
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export interface InstallUpgradeArgs {
|
|
2
|
-
readonly projectRoot: string;
|
|
3
|
-
readonly frameworkRoot: string;
|
|
4
|
-
readonly migrate?: boolean;
|
|
5
|
-
readonly force?: boolean;
|
|
6
|
-
}
|
|
7
|
-
export interface InstallUpgradeIo {
|
|
8
|
-
writeOut: (text: string) => void;
|
|
9
|
-
writeErr: (text: string) => void;
|
|
10
|
-
}
|
|
11
|
-
/** Port of ``run upgrade`` / ``task install:upgrade`` for the consumer task surface (#1061 / #2022). */
|
|
12
|
-
export declare function runInstallUpgrade(args: InstallUpgradeArgs, io: InstallUpgradeIo): number;
|
|
13
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join, relative, resolve } from "node:path";
|
|
3
|
-
import { locateManifest, manifestTagToVersion, parseInstallManifest } from "../doctor/manifest.js";
|
|
4
|
-
import { readCorePackageVersion } from "../engine-version.js";
|
|
5
|
-
import { buildInstallManifestText, CANONICAL_INSTALL_ROOT, } from "../init-deposit/scaffold.js";
|
|
6
|
-
import { resolveLifecycleRoot } from "../layout/resolve.js";
|
|
7
|
-
import { agentsRefreshPlan } from "../platform/agents-md.js";
|
|
8
|
-
import { DEV_FALLBACK } from "../platform/constants.js";
|
|
9
|
-
import { resolveVersion } from "../platform/resolve-version.js";
|
|
10
|
-
import { detectPreCutoverLegacy, frozenPreCutoverMigrationGuidance, } from "../vbrief-validate/precutover.js";
|
|
11
|
-
import { detectLegacyVbriefLayout, emitXbriefMigration, isPatchOnlyUpgrade, renderXbriefMigrationLine, runXbriefMigration, } from "../xbrief-migrate/index.js";
|
|
12
|
-
const ENGINE_PACKAGE_FALLBACK = "0.0.0";
|
|
13
|
-
/**
|
|
14
|
-
* Resolve the framework version for an upgrade (#2053).
|
|
15
|
-
*
|
|
16
|
-
* `resolveVersion({ frameworkRoot })` reads env / install-manifest / .deft-version
|
|
17
|
-
* / git against the *engine* framework root. When `deft install-upgrade` runs from
|
|
18
|
-
* a global npm install, that root is the npm package directory — which carries no
|
|
19
|
-
* install manifest, no bare marker, and is not its own git repo — so the chain
|
|
20
|
-
* dead-ends at `0.0.0-dev`. Recover by auto-detecting the *consumer project's*
|
|
21
|
-
* deposited manifest, then fall back to the engine package.json version, before
|
|
22
|
-
* surrendering to the dev fallback.
|
|
23
|
-
*/
|
|
24
|
-
function resolveUpgradeVersion(frameworkRoot, projectRoot) {
|
|
25
|
-
const primary = resolveVersion({ frameworkRoot });
|
|
26
|
-
if (primary !== DEV_FALLBACK)
|
|
27
|
-
return primary;
|
|
28
|
-
const manifestPath = locateManifest(projectRoot, null);
|
|
29
|
-
if (manifestPath) {
|
|
30
|
-
try {
|
|
31
|
-
const tag = manifestTagToVersion(parseInstallManifest(readFileSync(manifestPath, "utf8")));
|
|
32
|
-
if (tag)
|
|
33
|
-
return tag;
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
// fall through to package.json fallback
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
const pkgVersion = readCorePackageVersion();
|
|
40
|
-
if (pkgVersion && pkgVersion !== ENGINE_PACKAGE_FALLBACK)
|
|
41
|
-
return pkgVersion;
|
|
42
|
-
return DEV_FALLBACK;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* The consumer's deposited framework install root (`.deft/core`, then legacy
|
|
46
|
-
* `deft/`), if present. Used as the AGENTS.md render root and manifest target so
|
|
47
|
-
* the refresh renders from the *deposited* templates that match the installed
|
|
48
|
-
* payload -- not the (possibly newer) templates bundled in a global npm engine.
|
|
49
|
-
*/
|
|
50
|
-
function resolveInstallRoot(projectRoot) {
|
|
51
|
-
for (const candidate of [join(projectRoot, ".deft", "core"), join(projectRoot, "deft")]) {
|
|
52
|
-
try {
|
|
53
|
-
if (statSync(candidate).isDirectory())
|
|
54
|
-
return candidate;
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
// try next candidate
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
/** Prior `managed_by` provenance sentinel from an install manifest, if any (#2056). */
|
|
63
|
-
function readManagedByAt(installRoot) {
|
|
64
|
-
const manifestPath = join(installRoot, "VERSION");
|
|
65
|
-
if (!existsSync(manifestPath) || !statSync(manifestPath).isFile())
|
|
66
|
-
return null;
|
|
67
|
-
try {
|
|
68
|
-
const value = (parseInstallManifest(readFileSync(manifestPath, "utf8")).managed_by ?? "").trim();
|
|
69
|
-
return value || null;
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
function versionMarkerPaths(projectRoot) {
|
|
76
|
-
return [
|
|
77
|
-
join(resolveLifecycleRoot(projectRoot), ".deft-version"),
|
|
78
|
-
join(projectRoot, ".deft-version"),
|
|
79
|
-
];
|
|
80
|
-
}
|
|
81
|
-
function readVersionMarker(projectRoot) {
|
|
82
|
-
for (const candidate of versionMarkerPaths(projectRoot)) {
|
|
83
|
-
if (!existsSync(candidate) || !statSync(candidate).isFile())
|
|
84
|
-
continue;
|
|
85
|
-
try {
|
|
86
|
-
const value = readFileSync(candidate, "utf8").trim();
|
|
87
|
-
if (value)
|
|
88
|
-
return value;
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
// try next candidate
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
function writeVersionMarker(targetDir, version) {
|
|
97
|
-
if (version === DEV_FALLBACK)
|
|
98
|
-
return;
|
|
99
|
-
try {
|
|
100
|
-
mkdirSync(targetDir, { recursive: true });
|
|
101
|
-
writeFileSync(join(targetDir, ".deft-version"), `${version}\n`, "utf8");
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
// best-effort, mirrors Python
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
function deriveInstallRootString(installRoot, projectRoot) {
|
|
108
|
-
try {
|
|
109
|
-
return relative(projectRoot, installRoot).split("\\").join("/") || CANONICAL_INSTALL_ROOT;
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
return resolve(installRoot).split("\\").join("/");
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function writeInstallManifestAt(installRoot, projectRoot, version) {
|
|
116
|
-
if (version.replace(/^v/, "") === DEV_FALLBACK)
|
|
117
|
-
return null;
|
|
118
|
-
const priorManagedBy = readManagedByAt(installRoot);
|
|
119
|
-
const fields = {
|
|
120
|
-
ref: version.startsWith("v") ? version : `v${version}`,
|
|
121
|
-
sha: "content-package",
|
|
122
|
-
tag: version.startsWith("v") ? version : `v${version}`,
|
|
123
|
-
installRoot: deriveInstallRootString(installRoot, projectRoot),
|
|
124
|
-
fetchedAt: new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
125
|
-
fetchedBy: "deft-upgrade",
|
|
126
|
-
...(priorManagedBy ? { managedBy: priorManagedBy } : {}),
|
|
127
|
-
};
|
|
128
|
-
try {
|
|
129
|
-
mkdirSync(installRoot, { recursive: true });
|
|
130
|
-
const body = buildInstallManifestText(fields);
|
|
131
|
-
const path = join(installRoot, "VERSION");
|
|
132
|
-
writeFileSync(path, body, "utf8");
|
|
133
|
-
return path;
|
|
134
|
-
}
|
|
135
|
-
catch {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
function migrateLegacyInstallManifest(projectRoot, canonicalManifestPath) {
|
|
140
|
-
if (canonicalManifestPath === null)
|
|
141
|
-
return;
|
|
142
|
-
const canonical = resolve(canonicalManifestPath);
|
|
143
|
-
const expectedParent = resolve(projectRoot, ".deft", "core");
|
|
144
|
-
if (resolve(canonical, "..") !== expectedParent)
|
|
145
|
-
return;
|
|
146
|
-
const legacy = join(projectRoot, ".deft", "VERSION");
|
|
147
|
-
if (!existsSync(legacy) || !statSync(legacy).isFile())
|
|
148
|
-
return;
|
|
149
|
-
try {
|
|
150
|
-
const legacyVersion = manifestTagToVersion(parseInstallManifest(readFileSync(legacy, "utf8")));
|
|
151
|
-
const canonicalVersion = manifestTagToVersion(parseInstallManifest(readFileSync(canonical, "utf8")));
|
|
152
|
-
if (legacyVersion !== null && legacyVersion === canonicalVersion)
|
|
153
|
-
return;
|
|
154
|
-
renameSync(legacy, join(projectRoot, ".deft", "VERSION.premigrate"));
|
|
155
|
-
}
|
|
156
|
-
catch {
|
|
157
|
-
// best-effort
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
function runAgentsRefresh(projectRoot, frameworkRoot, io) {
|
|
161
|
-
const plan = agentsRefreshPlan(projectRoot, { frameworkRoot });
|
|
162
|
-
const state = String(plan.state ?? "unknown");
|
|
163
|
-
if (state === "current") {
|
|
164
|
-
io.writeOut("AGENTS.md managed section is current — no changes.\n");
|
|
165
|
-
return 0;
|
|
166
|
-
}
|
|
167
|
-
if (state === "template-missing" || state === "template-malformed" || state === "unreadable") {
|
|
168
|
-
io.writeErr(`agents:refresh failed: ${state}\n`);
|
|
169
|
-
return 2;
|
|
170
|
-
}
|
|
171
|
-
const newContent = plan.new_content;
|
|
172
|
-
if (typeof newContent !== "string") {
|
|
173
|
-
io.writeErr("agents:refresh failed: plan produced no new_content\n");
|
|
174
|
-
return 2;
|
|
175
|
-
}
|
|
176
|
-
const path = String(plan.path ?? join(projectRoot, "AGENTS.md"));
|
|
177
|
-
writeFileSync(path, newContent, "utf8");
|
|
178
|
-
io.writeOut(`AGENTS.md updated (state=${state}).\n`);
|
|
179
|
-
return 0;
|
|
180
|
-
}
|
|
181
|
-
function handleLegacyXbriefLayout(args, io, recorded, normalizedVersion) {
|
|
182
|
-
const detection = detectLegacyVbriefLayout(args.projectRoot);
|
|
183
|
-
if (!detection.legacyLayout) {
|
|
184
|
-
return 0;
|
|
185
|
-
}
|
|
186
|
-
io.writeOut(`${renderXbriefMigrationLine(args.projectRoot)}\n`);
|
|
187
|
-
const patchInert = isPatchOnlyUpgrade(recorded, normalizedVersion);
|
|
188
|
-
if (!args.migrate || patchInert) {
|
|
189
|
-
return 0;
|
|
190
|
-
}
|
|
191
|
-
const outcome = runXbriefMigration({
|
|
192
|
-
projectRoot: args.projectRoot,
|
|
193
|
-
frameworkRoot: args.frameworkRoot,
|
|
194
|
-
force: args.force,
|
|
195
|
-
}, io);
|
|
196
|
-
const code = emitXbriefMigration(outcome, io, { projectRoot: args.projectRoot });
|
|
197
|
-
return code;
|
|
198
|
-
}
|
|
199
|
-
/** Port of ``run upgrade`` / ``task install:upgrade`` for the consumer task surface (#1061 / #2022). */
|
|
200
|
-
export function runInstallUpgrade(args, io) {
|
|
201
|
-
const projectRoot = resolve(args.projectRoot);
|
|
202
|
-
const frameworkRoot = resolve(args.frameworkRoot);
|
|
203
|
-
const version = resolveUpgradeVersion(frameworkRoot, projectRoot);
|
|
204
|
-
const normalizedVersion = version.startsWith("v") ? version.slice(1) : version;
|
|
205
|
-
// Render AGENTS.md from the deposited install root so the managed section
|
|
206
|
-
// matches the installed payload, not the engine's bundled templates (which a
|
|
207
|
-
// global npm `deft` may carry at a different version). Falls back to the engine
|
|
208
|
-
// framework root when no deposit is present.
|
|
209
|
-
const installRoot = resolveInstallRoot(projectRoot);
|
|
210
|
-
const agentsRoot = installRoot ?? frameworkRoot;
|
|
211
|
-
io.writeOut(`Deft CLI v${normalizedVersion} - Upgrade\n\n`);
|
|
212
|
-
const recorded = readVersionMarker(projectRoot);
|
|
213
|
-
if (recorded === normalizedVersion) {
|
|
214
|
-
io.writeOut(`Project already at ${normalizedVersion}. Nothing to do.\n`);
|
|
215
|
-
const xbriefCode = handleLegacyXbriefLayout(args, io, recorded, normalizedVersion);
|
|
216
|
-
if (xbriefCode !== 0)
|
|
217
|
-
return xbriefCode;
|
|
218
|
-
return runAgentsRefresh(projectRoot, agentsRoot, io);
|
|
219
|
-
}
|
|
220
|
-
const legacy = detectPreCutoverLegacy(projectRoot);
|
|
221
|
-
if (legacy.length > 0) {
|
|
222
|
-
io.writeOut(`Pre-v0.20 document model detected (${legacy.join(", ")}). ${frozenPreCutoverMigrationGuidance()}\n`);
|
|
223
|
-
}
|
|
224
|
-
const vbriefDir = resolveLifecycleRoot(projectRoot);
|
|
225
|
-
const targetDir = existsSync(vbriefDir) && statSync(vbriefDir).isDirectory() ? vbriefDir : projectRoot;
|
|
226
|
-
writeVersionMarker(targetDir, normalizedVersion);
|
|
227
|
-
const writtenManifestPath = installRoot !== null
|
|
228
|
-
? writeInstallManifestAt(installRoot, projectRoot, normalizedVersion)
|
|
229
|
-
: null;
|
|
230
|
-
migrateLegacyInstallManifest(projectRoot, writtenManifestPath);
|
|
231
|
-
if (normalizedVersion === DEV_FALLBACK) {
|
|
232
|
-
// #2053: the marker + manifest writers above no-op on the dev fallback, so do
|
|
233
|
-
// not claim a marker update that never happened.
|
|
234
|
-
io.writeOut("Could not resolve a published framework version (resolved 0.0.0-dev); " +
|
|
235
|
-
".deft-version marker and install manifest were left unchanged. On a consumer " +
|
|
236
|
-
"install, ensure <project>/.deft/core/VERSION carries a real tag, or upgrade the " +
|
|
237
|
-
"engine with `npm i -g @deftai/directive@latest`.\n");
|
|
238
|
-
}
|
|
239
|
-
else if (recorded === null) {
|
|
240
|
-
io.writeOut(`Recorded framework version ${normalizedVersion} in .deft-version.\n`);
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
io.writeOut(`Updated .deft-version from ${recorded} to ${normalizedVersion}.\n`);
|
|
244
|
-
}
|
|
245
|
-
io.writeOut(`If legacy SPECIFICATION.md or PROJECT.md content remains, see UPGRADING.md § Frozen pre-v0.20 document-model migration (#2068).\n`);
|
|
246
|
-
const xbriefCode = handleLegacyXbriefLayout(args, io, recorded, normalizedVersion);
|
|
247
|
-
if (xbriefCode !== 0)
|
|
248
|
-
return xbriefCode;
|
|
249
|
-
return runAgentsRefresh(projectRoot, agentsRoot, io);
|
|
250
|
-
}
|
|
251
|
-
//# sourceMappingURL=index.js.map
|