@deftai/directive-core 0.61.0 → 0.61.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.
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Refs #1942, #1430, #1671.
|
|
9
9
|
*/
|
|
10
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, 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";
|
|
14
14
|
import { resolveInstalledContentRoot } from "../deposit/resolve-content.js";
|
|
15
15
|
import { manifestTagToVersion, parseInstallManifest } from "../doctor/manifest.js";
|
|
16
16
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
17
|
+
import { DEV_FALLBACK } from "../platform/constants.js";
|
|
17
18
|
import { gitPorcelain } from "../story-ready/git.js";
|
|
18
19
|
import { parseInitArgv } from "./init-deposit.js";
|
|
19
20
|
import { buildLegacyRefusalJson, buildLegacyRefusalMessage, detectLegacyLayout, LEGACY_LAYOUT_REFUSED_EXIT_CODE, LegacyLayoutRefusedError, } from "./legacy-detect.js";
|
|
@@ -67,6 +68,46 @@ function readRecordedDepositVersion(deftDir) {
|
|
|
67
68
|
return null;
|
|
68
69
|
}
|
|
69
70
|
}
|
|
71
|
+
/** Prior `managed_by` provenance sentinel from the deposit manifest, if any (#2056). */
|
|
72
|
+
function readRecordedManagedBy(deftDir) {
|
|
73
|
+
const manifestPath = join(deftDir, "VERSION");
|
|
74
|
+
if (!existsSync(manifestPath))
|
|
75
|
+
return null;
|
|
76
|
+
try {
|
|
77
|
+
const value = (parseInstallManifest(readFileSync(manifestPath, "utf8")).managed_by ?? "").trim();
|
|
78
|
+
return value || null;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Regenerate the bare `.deft-version` derivative from the deposited content
|
|
86
|
+
* version in the same transaction as the payload swap (#2055). The canonical
|
|
87
|
+
* marker lives at `vbrief/.deft-version`; fall back to the project root only
|
|
88
|
+
* when `vbrief/` is absent. Never persist the dev fallback.
|
|
89
|
+
*/
|
|
90
|
+
function syncBareVersionMarker(projectDir, version) {
|
|
91
|
+
const normalized = normalizeVersion(version);
|
|
92
|
+
if (!normalized || normalized === DEV_FALLBACK)
|
|
93
|
+
return;
|
|
94
|
+
const vbriefDir = join(projectDir, "vbrief");
|
|
95
|
+
let targetDir = projectDir;
|
|
96
|
+
try {
|
|
97
|
+
if (statSync(vbriefDir).isDirectory())
|
|
98
|
+
targetDir = vbriefDir;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// vbrief/ absent — write the root-level derivative instead
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
mkdirSync(targetDir, { recursive: true });
|
|
105
|
+
writeFileSync(join(targetDir, ".deft-version"), `${normalized}\n`, "utf8");
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// best-effort, mirrors install-upgrade marker write
|
|
109
|
+
}
|
|
110
|
+
}
|
|
70
111
|
function isInstallerManagedPath(path) {
|
|
71
112
|
if (INSTALLER_MANAGED_EXACT.has(path))
|
|
72
113
|
return true;
|
|
@@ -208,6 +249,7 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
208
249
|
const readPackageVersion = seams.readPackageVersion ?? readCorePackageVersion;
|
|
209
250
|
const contentRoot = await resolveContent();
|
|
210
251
|
const previousDepositVersion = readRecordedDepositVersion(deftDir);
|
|
252
|
+
const previousManagedBy = readRecordedManagedBy(deftDir);
|
|
211
253
|
const engineVersion = readEngine();
|
|
212
254
|
const contentVersion = readContentPackageVersion(contentRoot, readPackageVersion);
|
|
213
255
|
const versionSkewNotice = buildVersionSkewNotice(engineVersion, contentVersion, previousDepositVersion);
|
|
@@ -221,8 +263,13 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
221
263
|
installRoot: CANONICAL_INSTALL_ROOT,
|
|
222
264
|
fetchedAt: nowIso(),
|
|
223
265
|
fetchedBy: "directive-update",
|
|
266
|
+
...(previousManagedBy ? { managedBy: previousManagedBy } : {}),
|
|
224
267
|
};
|
|
225
268
|
writeInstallManifest(projectDir, deftDir, manifestFields);
|
|
269
|
+
// #2055: regenerate the bare .deft-version derivative so it agrees with the
|
|
270
|
+
// freshly written manifest tag (otherwise doctor's manifest-agreement check
|
|
271
|
+
// fails and the operator must hand-edit the marker).
|
|
272
|
+
syncBareVersionMarker(projectDir, contentVersion);
|
|
226
273
|
const agentsMdUpdated = writeAgentsMd(projectDir, deftDir, io);
|
|
227
274
|
await depositNeutralization(projectDir, io);
|
|
228
275
|
const readPorcelain = seams.gitPorcelain ?? gitPorcelain;
|
|
@@ -18,6 +18,13 @@ export interface InstallManifestFields {
|
|
|
18
18
|
installRoot: string;
|
|
19
19
|
fetchedAt: string;
|
|
20
20
|
fetchedBy: string;
|
|
21
|
+
/**
|
|
22
|
+
* Provenance sentinel (#2056). When the prior manifest was stamped
|
|
23
|
+
* `managed_by: 'npm'` by the npm-migration path, callers thread it through a
|
|
24
|
+
* manifest rebuild so a routine `directive update` / `install-upgrade` does not
|
|
25
|
+
* regress provenance and re-arm the doctor signpost.
|
|
26
|
+
*/
|
|
27
|
+
managedBy?: string;
|
|
21
28
|
}
|
|
22
29
|
export declare function buildInstallManifestText(fields: InstallManifestFields): string;
|
|
23
30
|
export declare function writeInstallManifest(projectDir: string, deftDir: string, fields: InstallManifestFields): string;
|
|
@@ -187,12 +187,17 @@ export function buildInstallManifestText(fields) {
|
|
|
187
187
|
effectiveTag = `v${effectiveTag}`;
|
|
188
188
|
}
|
|
189
189
|
const effectiveRef = fields.ref || effectiveTag;
|
|
190
|
-
|
|
190
|
+
let body = `ref: '${effectiveRef}'\n` +
|
|
191
191
|
`sha: '${fields.sha}'\n` +
|
|
192
192
|
`tag: '${effectiveTag}'\n` +
|
|
193
193
|
`install_root: '${fields.installRoot}'\n` +
|
|
194
194
|
`fetched_at: '${fields.fetchedAt}'\n` +
|
|
195
|
-
`fetched_by: '${fields.fetchedBy}'\n
|
|
195
|
+
`fetched_by: '${fields.fetchedBy}'\n`;
|
|
196
|
+
const managedBy = fields.managedBy?.trim();
|
|
197
|
+
if (managedBy) {
|
|
198
|
+
body += `managed_by: '${managedBy}'\n`;
|
|
199
|
+
}
|
|
200
|
+
return body;
|
|
196
201
|
}
|
|
197
202
|
export function writeInstallManifest(projectDir, deftDir, fields) {
|
|
198
203
|
const installRoot = fields.installRoot ||
|
|
@@ -1,11 +1,75 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join, relative, resolve } from "node:path";
|
|
3
|
-
import { manifestTagToVersion, parseInstallManifest } from "../doctor/manifest.js";
|
|
3
|
+
import { locateManifest, manifestTagToVersion, parseInstallManifest } from "../doctor/manifest.js";
|
|
4
|
+
import { readCorePackageVersion } from "../engine-version.js";
|
|
4
5
|
import { buildInstallManifestText, CANONICAL_INSTALL_ROOT, } from "../init-deposit/scaffold.js";
|
|
5
6
|
import { agentsRefreshPlan } from "../platform/agents-md.js";
|
|
6
7
|
import { DEV_FALLBACK } from "../platform/constants.js";
|
|
7
8
|
import { resolveVersion } from "../platform/resolve-version.js";
|
|
8
9
|
import { detectPreCutoverLegacy } from "../vbrief-validate/precutover.js";
|
|
10
|
+
const ENGINE_PACKAGE_FALLBACK = "0.0.0";
|
|
11
|
+
/**
|
|
12
|
+
* Resolve the framework version for an upgrade (#2053).
|
|
13
|
+
*
|
|
14
|
+
* `resolveVersion({ frameworkRoot })` reads env / install-manifest / .deft-version
|
|
15
|
+
* / git against the *engine* framework root. When `deft install-upgrade` runs from
|
|
16
|
+
* a global npm install, that root is the npm package directory — which carries no
|
|
17
|
+
* install manifest, no bare marker, and is not its own git repo — so the chain
|
|
18
|
+
* dead-ends at `0.0.0-dev`. Recover by auto-detecting the *consumer project's*
|
|
19
|
+
* deposited manifest, then fall back to the engine package.json version, before
|
|
20
|
+
* surrendering to the dev fallback.
|
|
21
|
+
*/
|
|
22
|
+
function resolveUpgradeVersion(frameworkRoot, projectRoot) {
|
|
23
|
+
const primary = resolveVersion({ frameworkRoot });
|
|
24
|
+
if (primary !== DEV_FALLBACK)
|
|
25
|
+
return primary;
|
|
26
|
+
const manifestPath = locateManifest(projectRoot, null);
|
|
27
|
+
if (manifestPath) {
|
|
28
|
+
try {
|
|
29
|
+
const tag = manifestTagToVersion(parseInstallManifest(readFileSync(manifestPath, "utf8")));
|
|
30
|
+
if (tag)
|
|
31
|
+
return tag;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// fall through to package.json fallback
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const pkgVersion = readCorePackageVersion();
|
|
38
|
+
if (pkgVersion && pkgVersion !== ENGINE_PACKAGE_FALLBACK)
|
|
39
|
+
return pkgVersion;
|
|
40
|
+
return DEV_FALLBACK;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The consumer's deposited framework install root (`.deft/core`, then legacy
|
|
44
|
+
* `deft/`), if present. Used as the AGENTS.md render root and manifest target so
|
|
45
|
+
* the refresh renders from the *deposited* templates that match the installed
|
|
46
|
+
* payload -- not the (possibly newer) templates bundled in a global npm engine.
|
|
47
|
+
*/
|
|
48
|
+
function resolveInstallRoot(projectRoot) {
|
|
49
|
+
for (const candidate of [join(projectRoot, ".deft", "core"), join(projectRoot, "deft")]) {
|
|
50
|
+
try {
|
|
51
|
+
if (statSync(candidate).isDirectory())
|
|
52
|
+
return candidate;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// try next candidate
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
/** Prior `managed_by` provenance sentinel from an install manifest, if any (#2056). */
|
|
61
|
+
function readManagedByAt(installRoot) {
|
|
62
|
+
const manifestPath = join(installRoot, "VERSION");
|
|
63
|
+
if (!existsSync(manifestPath) || !statSync(manifestPath).isFile())
|
|
64
|
+
return null;
|
|
65
|
+
try {
|
|
66
|
+
const value = (parseInstallManifest(readFileSync(manifestPath, "utf8")).managed_by ?? "").trim();
|
|
67
|
+
return value || null;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
9
73
|
function versionMarkerPaths(projectRoot) {
|
|
10
74
|
return [join(projectRoot, "vbrief", ".deft-version"), join(projectRoot, ".deft-version")];
|
|
11
75
|
}
|
|
@@ -46,6 +110,7 @@ function deriveInstallRootString(installRoot, projectRoot) {
|
|
|
46
110
|
function writeInstallManifestAt(installRoot, projectRoot, version) {
|
|
47
111
|
if (version.replace(/^v/, "") === DEV_FALLBACK)
|
|
48
112
|
return null;
|
|
113
|
+
const priorManagedBy = readManagedByAt(installRoot);
|
|
49
114
|
const fields = {
|
|
50
115
|
ref: version.startsWith("v") ? version : `v${version}`,
|
|
51
116
|
sha: "content-package",
|
|
@@ -53,6 +118,7 @@ function writeInstallManifestAt(installRoot, projectRoot, version) {
|
|
|
53
118
|
installRoot: deriveInstallRootString(installRoot, projectRoot),
|
|
54
119
|
fetchedAt: new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
55
120
|
fetchedBy: "deft-upgrade",
|
|
121
|
+
...(priorManagedBy ? { managedBy: priorManagedBy } : {}),
|
|
56
122
|
};
|
|
57
123
|
try {
|
|
58
124
|
mkdirSync(installRoot, { recursive: true });
|
|
@@ -111,13 +177,19 @@ function runAgentsRefresh(projectRoot, frameworkRoot, io) {
|
|
|
111
177
|
export function runInstallUpgrade(args, io) {
|
|
112
178
|
const projectRoot = resolve(args.projectRoot);
|
|
113
179
|
const frameworkRoot = resolve(args.frameworkRoot);
|
|
114
|
-
const version =
|
|
180
|
+
const version = resolveUpgradeVersion(frameworkRoot, projectRoot);
|
|
115
181
|
const normalizedVersion = version.startsWith("v") ? version.slice(1) : version;
|
|
182
|
+
// Render AGENTS.md from the deposited install root so the managed section
|
|
183
|
+
// matches the installed payload, not the engine's bundled templates (which a
|
|
184
|
+
// global npm `deft` may carry at a different version). Falls back to the engine
|
|
185
|
+
// framework root when no deposit is present.
|
|
186
|
+
const installRoot = resolveInstallRoot(projectRoot);
|
|
187
|
+
const agentsRoot = installRoot ?? frameworkRoot;
|
|
116
188
|
io.writeOut(`Deft CLI v${normalizedVersion} - Upgrade\n\n`);
|
|
117
189
|
const recorded = readVersionMarker(projectRoot);
|
|
118
190
|
if (recorded === normalizedVersion) {
|
|
119
191
|
io.writeOut(`Project already at ${normalizedVersion}. Nothing to do.\n`);
|
|
120
|
-
return runAgentsRefresh(projectRoot,
|
|
192
|
+
return runAgentsRefresh(projectRoot, agentsRoot, io);
|
|
121
193
|
}
|
|
122
194
|
const legacy = detectPreCutoverLegacy(projectRoot);
|
|
123
195
|
if (legacy.length > 0) {
|
|
@@ -126,21 +198,25 @@ export function runInstallUpgrade(args, io) {
|
|
|
126
198
|
const vbriefDir = join(projectRoot, "vbrief");
|
|
127
199
|
const targetDir = existsSync(vbriefDir) && statSync(vbriefDir).isDirectory() ? vbriefDir : projectRoot;
|
|
128
200
|
writeVersionMarker(targetDir, normalizedVersion);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
writtenManifestPath = writeInstallManifestAt(installCandidate, projectRoot, normalizedVersion);
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
201
|
+
const writtenManifestPath = installRoot !== null
|
|
202
|
+
? writeInstallManifestAt(installRoot, projectRoot, normalizedVersion)
|
|
203
|
+
: null;
|
|
136
204
|
migrateLegacyInstallManifest(projectRoot, writtenManifestPath);
|
|
137
|
-
if (
|
|
205
|
+
if (normalizedVersion === DEV_FALLBACK) {
|
|
206
|
+
// #2053: the marker + manifest writers above no-op on the dev fallback, so do
|
|
207
|
+
// not claim a marker update that never happened.
|
|
208
|
+
io.writeOut("Could not resolve a published framework version (resolved 0.0.0-dev); " +
|
|
209
|
+
".deft-version marker and install manifest were left unchanged. On a consumer " +
|
|
210
|
+
"install, ensure <project>/.deft/core/VERSION carries a real tag, or upgrade the " +
|
|
211
|
+
"engine with `npm i -g @deftai/directive@latest`.\n");
|
|
212
|
+
}
|
|
213
|
+
else if (recorded === null) {
|
|
138
214
|
io.writeOut(`Recorded framework version ${normalizedVersion} in .deft-version.\n`);
|
|
139
215
|
}
|
|
140
216
|
else {
|
|
141
217
|
io.writeOut(`Updated .deft-version from ${recorded} to ${normalizedVersion}.\n`);
|
|
142
218
|
}
|
|
143
219
|
io.writeOut("If legacy SPECIFICATION.md or PROJECT.md content remains, run `task migrate:vbrief` to complete the upgrade.\n");
|
|
144
|
-
return runAgentsRefresh(projectRoot,
|
|
220
|
+
return runAgentsRefresh(projectRoot, agentsRoot, io);
|
|
145
221
|
}
|
|
146
222
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive-core",
|
|
3
|
-
"version": "0.61.
|
|
3
|
+
"version": "0.61.1",
|
|
4
4
|
"description": "TypeScript engine core for the Directive framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -221,8 +221,8 @@
|
|
|
221
221
|
"provenance": true
|
|
222
222
|
},
|
|
223
223
|
"dependencies": {
|
|
224
|
-
"@deftai/directive-content": "^0.61.
|
|
225
|
-
"@deftai/directive-types": "^0.61.
|
|
224
|
+
"@deftai/directive-content": "^0.61.1",
|
|
225
|
+
"@deftai/directive-types": "^0.61.1"
|
|
226
226
|
},
|
|
227
227
|
"scripts": {
|
|
228
228
|
"build": "tsc -b",
|