@deftai/directive-core 0.68.1 → 0.69.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/doctor/constants.d.ts +9 -0
- package/dist/doctor/constants.js +11 -0
- package/dist/doctor/main.d.ts +47 -1
- package/dist/doctor/main.js +352 -2
- package/dist/doctor/payload-staleness.d.ts +8 -0
- package/dist/doctor/payload-staleness.js +11 -8
- package/dist/doctor/types.d.ts +48 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/init-deposit/gitignore.d.ts +18 -0
- package/dist/init-deposit/gitignore.js +94 -15
- package/dist/init-deposit/headless-manifest.d.ts +87 -0
- package/dist/init-deposit/headless-manifest.js +261 -0
- package/dist/init-deposit/index.d.ts +3 -0
- package/dist/init-deposit/index.js +3 -0
- package/dist/init-deposit/init-dispatch.d.ts +90 -0
- package/dist/init-deposit/init-dispatch.js +170 -0
- package/dist/init-deposit/refresh.d.ts +79 -2
- package/dist/init-deposit/refresh.js +177 -5
- package/dist/init-deposit/scaffold.d.ts +25 -0
- package/dist/init-deposit/scaffold.js +57 -0
- package/dist/init-deposit/untrack-core.d.ts +84 -0
- package/dist/init-deposit/untrack-core.js +150 -0
- package/dist/resolution/classify.d.ts +41 -0
- package/dist/resolution/classify.js +138 -0
- package/dist/resolution/engine-ladder.d.ts +98 -0
- package/dist/resolution/engine-ladder.js +185 -0
- package/dist/resolution/index.d.ts +19 -0
- package/dist/resolution/index.js +19 -0
- package/dist/resolution/integrity.d.ts +48 -0
- package/dist/resolution/integrity.js +80 -0
- package/dist/resolution/package-manager.d.ts +77 -0
- package/dist/resolution/package-manager.js +103 -0
- package/dist/resolution/pin.d.ts +73 -0
- package/dist/resolution/pin.js +169 -0
- package/dist/resolution/plan.d.ts +66 -0
- package/dist/resolution/plan.js +219 -0
- package/dist/resolution/skew-policy.d.ts +46 -0
- package/dist/resolution/skew-policy.js +120 -0
- package/dist/session/session-start.d.ts +2 -0
- package/dist/session/session-start.js +28 -1
- package/dist/user-config/index.d.ts +2 -0
- package/dist/user-config/index.js +2 -0
- package/dist/user-config/resolve-user-md.d.ts +76 -0
- package/dist/user-config/resolve-user-md.js +120 -0
- package/dist/xbrief-migrate/constants.d.ts +10 -0
- package/dist/xbrief-migrate/constants.js +19 -0
- package/dist/xbrief-migrate/detect.d.ts +34 -0
- package/dist/xbrief-migrate/detect.js +33 -0
- package/dist/xbrief-migrate/fs-helpers.d.ts +13 -0
- package/dist/xbrief-migrate/fs-helpers.js +46 -1
- package/dist/xbrief-migrate/index.d.ts +4 -2
- package/dist/xbrief-migrate/index.js +4 -2
- package/dist/xbrief-migrate/migrate-project.d.ts +23 -1
- package/dist/xbrief-migrate/migrate-project.js +97 -8
- package/dist/xbrief-migrate/signpost.d.ts +7 -1
- package/dist/xbrief-migrate/signpost.js +18 -3
- package/package.json +7 -3
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global-first engine-resolution ladder for the resolution spine (#2264, from #2124).
|
|
3
|
+
*
|
|
4
|
+
* The defect this closes is `execution-env != install-env`: an agent can read
|
|
5
|
+
* every rule in `.deft/core/` yet execute no gate because the engine that is
|
|
6
|
+
* reachable in the environment it is ACTUALLY running in is absent or stale.
|
|
7
|
+
* The ladder detects that mismatch and self-heals only then:
|
|
8
|
+
*
|
|
9
|
+
* 1. global `deft` reachable AND >= pin -> use it
|
|
10
|
+
* 2. local `.deft/.cli/<platform>` reachable, intact, >= pin -> use it
|
|
11
|
+
* 3. must install:
|
|
12
|
+
* a. registry up + global prefix writable -> npm i -g @deftai/directive@<pin>
|
|
13
|
+
* b. registry up + global prefix NOT writable (sandbox) -> npm install --prefix .deft/.cli/<platform>
|
|
14
|
+
* c. registry down + staged tarball -> install from staged payload
|
|
15
|
+
* d. registry down + no tarball -> hard-fail ("stage this")
|
|
16
|
+
*
|
|
17
|
+
* `decideEngineLadder` is a PURE decision function (no I/O). The side-effecting
|
|
18
|
+
* install is factored behind an injected runner in `resolveEngine` so the whole
|
|
19
|
+
* self-heal path is unit-testable without touching the network.
|
|
20
|
+
*/
|
|
21
|
+
import { semverGte } from "./pin.js";
|
|
22
|
+
function fmt(version) {
|
|
23
|
+
return version ?? "absent";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Pure global-first ladder decision. Emits a structured trace describing each
|
|
27
|
+
* rung it evaluated and why it was skipped or chosen.
|
|
28
|
+
*/
|
|
29
|
+
export function decideEngineLadder(facts) {
|
|
30
|
+
const { pinVersion } = facts;
|
|
31
|
+
const steps = [];
|
|
32
|
+
// Rung 1: global engine.
|
|
33
|
+
if (facts.globalEngineVersion !== null && semverGte(facts.globalEngineVersion, pinVersion)) {
|
|
34
|
+
steps.push(`global: ${fmt(facts.globalEngineVersion)} >= pin ${fmt(pinVersion)} -> use`);
|
|
35
|
+
return {
|
|
36
|
+
rung: "global",
|
|
37
|
+
usable: true,
|
|
38
|
+
resolvedVersion: facts.globalEngineVersion,
|
|
39
|
+
trace: steps.join("; "),
|
|
40
|
+
reason: `global engine ${facts.globalEngineVersion} satisfies pin ${fmt(pinVersion)}`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (facts.globalEngineVersion === null) {
|
|
44
|
+
steps.push("global: absent");
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
steps.push(`global: ${facts.globalEngineVersion} < pin ${fmt(pinVersion)}`);
|
|
48
|
+
}
|
|
49
|
+
// Rung 2: local sandbox engine (must be intact AND >= pin).
|
|
50
|
+
const local = facts.localEngine;
|
|
51
|
+
if (local !== null) {
|
|
52
|
+
if (!local.integrity.usable) {
|
|
53
|
+
steps.push(local.integrity.partial
|
|
54
|
+
? `local: partial install -> not-usable`
|
|
55
|
+
: `local: ${local.integrity.reason}`);
|
|
56
|
+
}
|
|
57
|
+
else if (!semverGte(local.version, pinVersion)) {
|
|
58
|
+
steps.push(`local: ${fmt(local.version)} < pin ${fmt(pinVersion)}`);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
steps.push(`local: ${fmt(local.version)} >= pin ${fmt(pinVersion)} -> use`);
|
|
62
|
+
return {
|
|
63
|
+
rung: "local",
|
|
64
|
+
usable: true,
|
|
65
|
+
resolvedVersion: local.version,
|
|
66
|
+
trace: steps.join("; "),
|
|
67
|
+
reason: `local engine ${local.version} satisfies pin ${fmt(pinVersion)}`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
steps.push("local: absent");
|
|
73
|
+
}
|
|
74
|
+
// Rung 3: must install.
|
|
75
|
+
if (facts.registryUp && facts.globalPrefixWritable) {
|
|
76
|
+
steps.push(`install: npm i -g @deftai/directive@${fmt(pinVersion)}`);
|
|
77
|
+
return {
|
|
78
|
+
rung: "install-global",
|
|
79
|
+
usable: false,
|
|
80
|
+
resolvedVersion: null,
|
|
81
|
+
trace: steps.join("; "),
|
|
82
|
+
reason: "registry up and global prefix writable -> global install",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (facts.registryUp && !facts.globalPrefixWritable) {
|
|
86
|
+
steps.push(`install: npm install --prefix .deft/.cli/${facts.platform} @deftai/directive@${fmt(pinVersion)}`);
|
|
87
|
+
return {
|
|
88
|
+
rung: "install-sandbox",
|
|
89
|
+
usable: false,
|
|
90
|
+
resolvedVersion: null,
|
|
91
|
+
trace: steps.join("; "),
|
|
92
|
+
reason: "registry up but global prefix not writable (sandbox) -> --prefix install",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (!facts.registryUp && facts.stagedTarballAvailable) {
|
|
96
|
+
steps.push("install: staged tarball / vendored payload");
|
|
97
|
+
return {
|
|
98
|
+
rung: "install-staged",
|
|
99
|
+
usable: false,
|
|
100
|
+
resolvedVersion: null,
|
|
101
|
+
trace: steps.join("; "),
|
|
102
|
+
reason: "registry down but staged tarball available -> offline install",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
steps.push("hard-fail: registry down and no staged tarball");
|
|
106
|
+
return {
|
|
107
|
+
rung: "hard-fail",
|
|
108
|
+
usable: false,
|
|
109
|
+
resolvedVersion: null,
|
|
110
|
+
trace: steps.join("; "),
|
|
111
|
+
reason: "registry down and no staged tarball -- stage a payload to proceed",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const INSTALL_RUNGS = new Set([
|
|
115
|
+
"install-global",
|
|
116
|
+
"install-sandbox",
|
|
117
|
+
"install-staged",
|
|
118
|
+
]);
|
|
119
|
+
/**
|
|
120
|
+
* Resolve the engine by composing the pure ladder decision with an injected
|
|
121
|
+
* install runner. When a rung requires an install, the runner performs it and
|
|
122
|
+
* (on success) the re-projection runner forward-migrates content — yielding the
|
|
123
|
+
* "self-heals with zero manual npm/PATH steps" behavior with a structured trace.
|
|
124
|
+
*/
|
|
125
|
+
export function resolveEngine(facts, options = {}) {
|
|
126
|
+
const decision = decideEngineLadder(facts);
|
|
127
|
+
const steps = [decision.trace];
|
|
128
|
+
if (decision.usable) {
|
|
129
|
+
return {
|
|
130
|
+
decision,
|
|
131
|
+
installOutcome: null,
|
|
132
|
+
resolvedVersion: decision.resolvedVersion,
|
|
133
|
+
selfHealed: false,
|
|
134
|
+
trace: steps.join("; "),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (decision.rung === "hard-fail" || !INSTALL_RUNGS.has(decision.rung)) {
|
|
138
|
+
return {
|
|
139
|
+
decision,
|
|
140
|
+
installOutcome: null,
|
|
141
|
+
resolvedVersion: null,
|
|
142
|
+
selfHealed: false,
|
|
143
|
+
trace: steps.join("; "),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const runner = options.installRunner;
|
|
147
|
+
if (!runner) {
|
|
148
|
+
steps.push("install: deferred (no install runner supplied)");
|
|
149
|
+
return {
|
|
150
|
+
decision,
|
|
151
|
+
installOutcome: null,
|
|
152
|
+
resolvedVersion: null,
|
|
153
|
+
selfHealed: false,
|
|
154
|
+
trace: steps.join("; "),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const outcome = runner({
|
|
158
|
+
rung: decision.rung,
|
|
159
|
+
pinVersion: facts.pinVersion,
|
|
160
|
+
platform: facts.platform,
|
|
161
|
+
});
|
|
162
|
+
if (!outcome.installed) {
|
|
163
|
+
steps.push(`install failed: ${outcome.detail}`);
|
|
164
|
+
return {
|
|
165
|
+
decision,
|
|
166
|
+
installOutcome: outcome,
|
|
167
|
+
resolvedVersion: null,
|
|
168
|
+
selfHealed: false,
|
|
169
|
+
trace: steps.join("; "),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
steps.push(`installed ${decision.rung} -> ${fmt(outcome.version)}`);
|
|
173
|
+
if (options.reproject) {
|
|
174
|
+
options.reproject(outcome.version);
|
|
175
|
+
steps.push(`re-projected content ${fmt(outcome.version)}`);
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
decision,
|
|
179
|
+
installOutcome: outcome,
|
|
180
|
+
resolvedVersion: outcome.version,
|
|
181
|
+
selfHealed: true,
|
|
182
|
+
trace: steps.join("; "),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=engine-ladder.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolution spine (keystone #2264 / epic #2203) — shared library primitives.
|
|
3
|
+
*
|
|
4
|
+
* The classifier (`classify`) produces the orthogonal fact-set; `plan()` applies
|
|
5
|
+
* one ordered precedence table and emits the versioned public resolution-plan-v1
|
|
6
|
+
* schema (the single source of truth). The engine ladder, local-engine integrity
|
|
7
|
+
* check, three-band skew policy, and pin/VERSION/sha reconciliation are the
|
|
8
|
+
* supporting primitives that init / update / doctor / headless (children B–E)
|
|
9
|
+
* consume — this module ships them; it does NOT rewire verb behavior.
|
|
10
|
+
*/
|
|
11
|
+
export { RESOLUTION_ENCODINGS, RESOLUTION_MODES, RESOLUTION_PLAN_SCHEMA_VERSION, type ResolutionEncoding, type ResolutionFacts, type ResolutionFile, type ResolutionMode, type ResolutionNextAction, type ResolutionPlan, } from "@deftai/directive-types";
|
|
12
|
+
export { type ClassifySeams, classify, defaultEngineProbe, type EngineProbeResult, } from "./classify.js";
|
|
13
|
+
export { decideEngineLadder, type EngineInstallOutcome, type EngineInstallRunner, type EngineResolution, type LadderDecision, type LadderFacts, type LadderRung, type LocalEngineFacts, type ReprojectRunner, type ResolveEngineOptions, resolveEngine, } from "./engine-ladder.js";
|
|
14
|
+
export { checkLocalEngineIntegrity, type IntegrityResult, type IntegritySeams, LOCAL_ENGINE_MARKERS, LOCAL_ENGINE_ROOT, localEnginePlatformDir, } from "./integrity.js";
|
|
15
|
+
export { DEFAULT_PACKAGE_MANAGER, type DetectPackageManagerInput, detectPackageManager, ENGINE_PACKAGE, PACKAGE_MANAGERS, type PackageManager, type PackageManagerCommands, renderEphemeral, renderGlobalInstall, renderPackageManagerCommands, renderProjectInstall, } from "./package-manager.js";
|
|
16
|
+
export { compareSemver, isExactPin, PIN_DEPENDENCY_NAME, type PinReadResult, type PinReadSeams, parseSemver, type ReconcileInputs, type ReconcileResult, readPin, reconcileVersions, type SemverTriple, semverGte, } from "./pin.js";
|
|
17
|
+
export { type PlanOptions, plan } from "./plan.js";
|
|
18
|
+
export { ACCEPT_ENGINE_SKEW_ENV, DEFAULT_ENGINE_SKEW_WINDOW, evaluateSkew, type SkewBand, type SkewDecision, type SkewOptions, type SkewResult, } from "./skew-policy.js";
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolution spine (keystone #2264 / epic #2203) — shared library primitives.
|
|
3
|
+
*
|
|
4
|
+
* The classifier (`classify`) produces the orthogonal fact-set; `plan()` applies
|
|
5
|
+
* one ordered precedence table and emits the versioned public resolution-plan-v1
|
|
6
|
+
* schema (the single source of truth). The engine ladder, local-engine integrity
|
|
7
|
+
* check, three-band skew policy, and pin/VERSION/sha reconciliation are the
|
|
8
|
+
* supporting primitives that init / update / doctor / headless (children B–E)
|
|
9
|
+
* consume — this module ships them; it does NOT rewire verb behavior.
|
|
10
|
+
*/
|
|
11
|
+
export { RESOLUTION_ENCODINGS, RESOLUTION_MODES, RESOLUTION_PLAN_SCHEMA_VERSION, } from "@deftai/directive-types";
|
|
12
|
+
export { classify, defaultEngineProbe, } from "./classify.js";
|
|
13
|
+
export { decideEngineLadder, resolveEngine, } from "./engine-ladder.js";
|
|
14
|
+
export { checkLocalEngineIntegrity, LOCAL_ENGINE_MARKERS, LOCAL_ENGINE_ROOT, localEnginePlatformDir, } from "./integrity.js";
|
|
15
|
+
export { DEFAULT_PACKAGE_MANAGER, detectPackageManager, ENGINE_PACKAGE, PACKAGE_MANAGERS, renderEphemeral, renderGlobalInstall, renderPackageManagerCommands, renderProjectInstall, } from "./package-manager.js";
|
|
16
|
+
export { compareSemver, isExactPin, PIN_DEPENDENCY_NAME, parseSemver, readPin, reconcileVersions, semverGte, } from "./pin.js";
|
|
17
|
+
export { plan } from "./plan.js";
|
|
18
|
+
export { ACCEPT_ENGINE_SKEW_ENV, DEFAULT_ENGINE_SKEW_WINDOW, evaluateSkew, } from "./skew-policy.js";
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local-engine integrity check for the resolution spine (#2264, from #2124).
|
|
3
|
+
*
|
|
4
|
+
* A locally-installed engine at `.deft/.cli/<platform>` is only usable when the
|
|
5
|
+
* install completed. A present-but-partial directory (an interrupted
|
|
6
|
+
* `npm install --prefix`) must be treated as NOT-USABLE — otherwise the ladder
|
|
7
|
+
* would select a half-installed engine and fail at execution time instead of
|
|
8
|
+
* falling through to a clean re-install.
|
|
9
|
+
*/
|
|
10
|
+
/** Root of the sandbox-local engine install, relative to the project root. */
|
|
11
|
+
export declare const LOCAL_ENGINE_ROOT = ".deft/.cli";
|
|
12
|
+
/**
|
|
13
|
+
* Marker paths (relative to the platform install dir) that MUST all exist for a
|
|
14
|
+
* local engine to count as a completed, usable install. `package.json` marks the
|
|
15
|
+
* install root; the `directive` bin under `node_modules/.bin` marks that npm
|
|
16
|
+
* finished linking the executable.
|
|
17
|
+
*/
|
|
18
|
+
export declare const LOCAL_ENGINE_MARKERS: readonly string[];
|
|
19
|
+
export interface IntegritySeams {
|
|
20
|
+
readonly isFile?: (p: string) => boolean;
|
|
21
|
+
readonly isDir?: (p: string) => boolean;
|
|
22
|
+
readonly platform?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface IntegrityResult {
|
|
25
|
+
/** The install is complete and the engine may be selected. */
|
|
26
|
+
readonly usable: boolean;
|
|
27
|
+
/** The platform install directory exists at all (vs. wholly absent). */
|
|
28
|
+
readonly present: boolean;
|
|
29
|
+
/** Present but missing required markers (interrupted install). */
|
|
30
|
+
readonly partial: boolean;
|
|
31
|
+
/** Absolute-relative platform install directory that was probed. */
|
|
32
|
+
readonly platformDir: string;
|
|
33
|
+
/** Marker paths that were missing. */
|
|
34
|
+
readonly missingMarkers: readonly string[];
|
|
35
|
+
/** Human-facing reason. */
|
|
36
|
+
readonly reason: string;
|
|
37
|
+
}
|
|
38
|
+
/** Resolve the platform-specific local engine directory (e.g. `.deft/.cli/linux`). */
|
|
39
|
+
export declare function localEnginePlatformDir(projectRoot: string, platform?: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Classify the local engine install at `.deft/.cli/<platform>`.
|
|
42
|
+
*
|
|
43
|
+
* - wholly absent -> `{ usable: false, present: false, partial: false }`
|
|
44
|
+
* - present, all markers -> `{ usable: true, present: true, partial: false }`
|
|
45
|
+
* - present, missing markers -> `{ usable: false, present: true, partial: true }`
|
|
46
|
+
*/
|
|
47
|
+
export declare function checkLocalEngineIntegrity(projectRoot: string, seams?: IntegritySeams): IntegrityResult;
|
|
48
|
+
//# sourceMappingURL=integrity.d.ts.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local-engine integrity check for the resolution spine (#2264, from #2124).
|
|
3
|
+
*
|
|
4
|
+
* A locally-installed engine at `.deft/.cli/<platform>` is only usable when the
|
|
5
|
+
* install completed. A present-but-partial directory (an interrupted
|
|
6
|
+
* `npm install --prefix`) must be treated as NOT-USABLE — otherwise the ladder
|
|
7
|
+
* would select a half-installed engine and fail at execution time instead of
|
|
8
|
+
* falling through to a clean re-install.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
11
|
+
import { platform as osPlatform } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
/** Root of the sandbox-local engine install, relative to the project root. */
|
|
14
|
+
export const LOCAL_ENGINE_ROOT = ".deft/.cli";
|
|
15
|
+
/**
|
|
16
|
+
* Marker paths (relative to the platform install dir) that MUST all exist for a
|
|
17
|
+
* local engine to count as a completed, usable install. `package.json` marks the
|
|
18
|
+
* install root; the `directive` bin under `node_modules/.bin` marks that npm
|
|
19
|
+
* finished linking the executable.
|
|
20
|
+
*/
|
|
21
|
+
export const LOCAL_ENGINE_MARKERS = [
|
|
22
|
+
"package.json",
|
|
23
|
+
join("node_modules", ".bin", "directive"),
|
|
24
|
+
];
|
|
25
|
+
function defaultIsFile(p) {
|
|
26
|
+
try {
|
|
27
|
+
return existsSync(p);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Resolve the platform-specific local engine directory (e.g. `.deft/.cli/linux`). */
|
|
34
|
+
export function localEnginePlatformDir(projectRoot, platform) {
|
|
35
|
+
const plat = platform ?? osPlatform();
|
|
36
|
+
return join(projectRoot, LOCAL_ENGINE_ROOT, plat);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Classify the local engine install at `.deft/.cli/<platform>`.
|
|
40
|
+
*
|
|
41
|
+
* - wholly absent -> `{ usable: false, present: false, partial: false }`
|
|
42
|
+
* - present, all markers -> `{ usable: true, present: true, partial: false }`
|
|
43
|
+
* - present, missing markers -> `{ usable: false, present: true, partial: true }`
|
|
44
|
+
*/
|
|
45
|
+
export function checkLocalEngineIntegrity(projectRoot, seams = {}) {
|
|
46
|
+
const isFile = seams.isFile ?? defaultIsFile;
|
|
47
|
+
const isDir = seams.isDir ?? seams.isFile ?? defaultIsFile;
|
|
48
|
+
const platformDir = localEnginePlatformDir(projectRoot, seams.platform);
|
|
49
|
+
const rootPresent = isDir(platformDir);
|
|
50
|
+
const missingMarkers = LOCAL_ENGINE_MARKERS.filter((marker) => !isFile(join(platformDir, marker)));
|
|
51
|
+
if (!rootPresent && missingMarkers.length === LOCAL_ENGINE_MARKERS.length) {
|
|
52
|
+
return {
|
|
53
|
+
usable: false,
|
|
54
|
+
present: false,
|
|
55
|
+
partial: false,
|
|
56
|
+
platformDir,
|
|
57
|
+
missingMarkers,
|
|
58
|
+
reason: `no local engine at ${platformDir}`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (missingMarkers.length > 0) {
|
|
62
|
+
return {
|
|
63
|
+
usable: false,
|
|
64
|
+
present: true,
|
|
65
|
+
partial: true,
|
|
66
|
+
platformDir,
|
|
67
|
+
missingMarkers,
|
|
68
|
+
reason: `partial local engine at ${platformDir} (missing: ${missingMarkers.join(", ")}) -- treated as not-usable`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
usable: true,
|
|
73
|
+
present: true,
|
|
74
|
+
partial: false,
|
|
75
|
+
platformDir,
|
|
76
|
+
missingMarkers: [],
|
|
77
|
+
reason: `intact local engine at ${platformDir}`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=integrity.js.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-manager abstraction for the resolution spine (#2197, from #2264).
|
|
3
|
+
*
|
|
4
|
+
* The documented install/upgrade path historically hard-coded npm
|
|
5
|
+
* (`npm i -g @deftai/directive`). pnpm-managed setups have no first-class
|
|
6
|
+
* flow, and mixing an npm global into a pnpm environment breaks PATH/shim/store
|
|
7
|
+
* consistency. This module makes the *command rendering* package-manager aware
|
|
8
|
+
* so the resolution plan and doctor emit the right form for the active manager.
|
|
9
|
+
*
|
|
10
|
+
* Two load-bearing facts keep this small (locked on issue #2197):
|
|
11
|
+
* 1. NO additional registry. pnpm resolves from the same npm registry
|
|
12
|
+
* (`registry.npmjs.org`) by default; the published `@deftai/directive`
|
|
13
|
+
* tarball is unchanged. No renderer here ever emits a `--registry` flag.
|
|
14
|
+
* 2. The internal `.deft/.cli/<platform>` sandbox vendoring STAYS on npm
|
|
15
|
+
* (its `node_modules/.bin` layout is validated by `integrity.ts` and is
|
|
16
|
+
* gitignored / package-manager-invisible). Only the global, project-local,
|
|
17
|
+
* ephemeral, and upgrade command forms vary by package manager.
|
|
18
|
+
*
|
|
19
|
+
* Every function here is PURE (no I/O): detection consumes an injected fact-set
|
|
20
|
+
* so the whole surface is unit-testable without touching the filesystem or env.
|
|
21
|
+
*/
|
|
22
|
+
export type PackageManager = "npm" | "pnpm";
|
|
23
|
+
export declare const PACKAGE_MANAGERS: readonly PackageManager[];
|
|
24
|
+
export declare const DEFAULT_PACKAGE_MANAGER: PackageManager;
|
|
25
|
+
/** Canonical published engine package name. */
|
|
26
|
+
export declare const ENGINE_PACKAGE = "@deftai/directive";
|
|
27
|
+
export interface DetectPackageManagerInput {
|
|
28
|
+
/** Environment map (reads `DEFT_PACKAGE_MANAGER` and `npm_config_user_agent`). */
|
|
29
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
30
|
+
/** The `packageManager` field from the project package.json (Corepack), if any. */
|
|
31
|
+
readonly packageManagerField?: string | null;
|
|
32
|
+
/** Whether a `pnpm-lock.yaml` is present at the project root. */
|
|
33
|
+
readonly pnpmLockPresent?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Detect the active package manager. Precedence (first match wins):
|
|
37
|
+
* 1. `DEFT_PACKAGE_MANAGER` env override
|
|
38
|
+
* 2. `packageManager` field / Corepack shim
|
|
39
|
+
* 3. `pnpm-lock.yaml` present
|
|
40
|
+
* 4. `npm_config_user_agent` (set by the manager that spawned the process)
|
|
41
|
+
* 5. default: npm
|
|
42
|
+
*/
|
|
43
|
+
export declare function detectPackageManager(input?: DetectPackageManagerInput): PackageManager;
|
|
44
|
+
/**
|
|
45
|
+
* Render a global install command, e.g. `npm i -g @deftai/directive@0.65.0`
|
|
46
|
+
* (npm) or `pnpm add -g @deftai/directive@0.65.0` (pnpm). `spec` is the full
|
|
47
|
+
* package spec including any `@version` suffix.
|
|
48
|
+
*/
|
|
49
|
+
export declare function renderGlobalInstall(pm: PackageManager, spec?: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Render a project-local (dev-dependency) install command. pnpm-managed repos
|
|
52
|
+
* that prefer not to install globally use this.
|
|
53
|
+
*/
|
|
54
|
+
export declare function renderProjectInstall(pm: PackageManager, spec?: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* Render an ephemeral (no-install) invocation, e.g. `npx @deftai/directive update`
|
|
57
|
+
* (npm) or `pnpm dlx @deftai/directive update` (pnpm).
|
|
58
|
+
*/
|
|
59
|
+
export declare function renderEphemeral(pm: PackageManager, subcommand: string, pkg?: string): string;
|
|
60
|
+
export interface PackageManagerCommands {
|
|
61
|
+
readonly packageManager: PackageManager;
|
|
62
|
+
/** Global install of the engine at the given spec. */
|
|
63
|
+
readonly globalInstall: string;
|
|
64
|
+
/** Project-local (dev-dependency) install of the engine. */
|
|
65
|
+
readonly projectInstall: string;
|
|
66
|
+
/** Ephemeral `update` invocation. */
|
|
67
|
+
readonly ephemeralUpdate: string;
|
|
68
|
+
/** The canonical upgrade one-liner (global install of `@latest`). */
|
|
69
|
+
readonly upgradeOneLiner: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Render the full command matrix for a package manager at a given spec
|
|
73
|
+
* (defaults to `@latest`). Single-sourced so docs, doctor, and the plan all
|
|
74
|
+
* derive from the same renderers.
|
|
75
|
+
*/
|
|
76
|
+
export declare function renderPackageManagerCommands(pm: PackageManager, spec?: string): PackageManagerCommands;
|
|
77
|
+
//# sourceMappingURL=package-manager.d.ts.map
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-manager abstraction for the resolution spine (#2197, from #2264).
|
|
3
|
+
*
|
|
4
|
+
* The documented install/upgrade path historically hard-coded npm
|
|
5
|
+
* (`npm i -g @deftai/directive`). pnpm-managed setups have no first-class
|
|
6
|
+
* flow, and mixing an npm global into a pnpm environment breaks PATH/shim/store
|
|
7
|
+
* consistency. This module makes the *command rendering* package-manager aware
|
|
8
|
+
* so the resolution plan and doctor emit the right form for the active manager.
|
|
9
|
+
*
|
|
10
|
+
* Two load-bearing facts keep this small (locked on issue #2197):
|
|
11
|
+
* 1. NO additional registry. pnpm resolves from the same npm registry
|
|
12
|
+
* (`registry.npmjs.org`) by default; the published `@deftai/directive`
|
|
13
|
+
* tarball is unchanged. No renderer here ever emits a `--registry` flag.
|
|
14
|
+
* 2. The internal `.deft/.cli/<platform>` sandbox vendoring STAYS on npm
|
|
15
|
+
* (its `node_modules/.bin` layout is validated by `integrity.ts` and is
|
|
16
|
+
* gitignored / package-manager-invisible). Only the global, project-local,
|
|
17
|
+
* ephemeral, and upgrade command forms vary by package manager.
|
|
18
|
+
*
|
|
19
|
+
* Every function here is PURE (no I/O): detection consumes an injected fact-set
|
|
20
|
+
* so the whole surface is unit-testable without touching the filesystem or env.
|
|
21
|
+
*/
|
|
22
|
+
export const PACKAGE_MANAGERS = ["npm", "pnpm"];
|
|
23
|
+
export const DEFAULT_PACKAGE_MANAGER = "npm";
|
|
24
|
+
/** Canonical published engine package name. */
|
|
25
|
+
export const ENGINE_PACKAGE = "@deftai/directive";
|
|
26
|
+
function normalizePackageManager(value) {
|
|
27
|
+
const v = value.trim().toLowerCase();
|
|
28
|
+
if (v.startsWith("pnpm"))
|
|
29
|
+
return "pnpm";
|
|
30
|
+
if (v.startsWith("npm"))
|
|
31
|
+
return "npm";
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Detect the active package manager. Precedence (first match wins):
|
|
36
|
+
* 1. `DEFT_PACKAGE_MANAGER` env override
|
|
37
|
+
* 2. `packageManager` field / Corepack shim
|
|
38
|
+
* 3. `pnpm-lock.yaml` present
|
|
39
|
+
* 4. `npm_config_user_agent` (set by the manager that spawned the process)
|
|
40
|
+
* 5. default: npm
|
|
41
|
+
*/
|
|
42
|
+
export function detectPackageManager(input = {}) {
|
|
43
|
+
const env = input.env ?? {};
|
|
44
|
+
const override = env.DEFT_PACKAGE_MANAGER;
|
|
45
|
+
if (typeof override === "string" && override.trim() !== "") {
|
|
46
|
+
const pm = normalizePackageManager(override);
|
|
47
|
+
if (pm)
|
|
48
|
+
return pm;
|
|
49
|
+
}
|
|
50
|
+
if (input.packageManagerField != null && input.packageManagerField.trim() !== "") {
|
|
51
|
+
const pm = normalizePackageManager(input.packageManagerField);
|
|
52
|
+
if (pm)
|
|
53
|
+
return pm;
|
|
54
|
+
}
|
|
55
|
+
if (input.pnpmLockPresent)
|
|
56
|
+
return "pnpm";
|
|
57
|
+
const ua = env.npm_config_user_agent;
|
|
58
|
+
if (typeof ua === "string" && ua.trim() !== "") {
|
|
59
|
+
const pm = normalizePackageManager(ua);
|
|
60
|
+
if (pm)
|
|
61
|
+
return pm;
|
|
62
|
+
}
|
|
63
|
+
return DEFAULT_PACKAGE_MANAGER;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Render a global install command, e.g. `npm i -g @deftai/directive@0.65.0`
|
|
67
|
+
* (npm) or `pnpm add -g @deftai/directive@0.65.0` (pnpm). `spec` is the full
|
|
68
|
+
* package spec including any `@version` suffix.
|
|
69
|
+
*/
|
|
70
|
+
export function renderGlobalInstall(pm, spec = ENGINE_PACKAGE) {
|
|
71
|
+
return pm === "pnpm" ? `pnpm add -g ${spec}` : `npm i -g ${spec}`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Render a project-local (dev-dependency) install command. pnpm-managed repos
|
|
75
|
+
* that prefer not to install globally use this.
|
|
76
|
+
*/
|
|
77
|
+
export function renderProjectInstall(pm, spec = ENGINE_PACKAGE) {
|
|
78
|
+
return pm === "pnpm" ? `pnpm add -D ${spec}` : `npm install --save-dev ${spec}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Render an ephemeral (no-install) invocation, e.g. `npx @deftai/directive update`
|
|
82
|
+
* (npm) or `pnpm dlx @deftai/directive update` (pnpm).
|
|
83
|
+
*/
|
|
84
|
+
export function renderEphemeral(pm, subcommand, pkg = ENGINE_PACKAGE) {
|
|
85
|
+
const runner = pm === "pnpm" ? "pnpm dlx" : "npx";
|
|
86
|
+
const tail = subcommand.trim() === "" ? "" : ` ${subcommand.trim()}`;
|
|
87
|
+
return `${runner} ${pkg}${tail}`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Render the full command matrix for a package manager at a given spec
|
|
91
|
+
* (defaults to `@latest`). Single-sourced so docs, doctor, and the plan all
|
|
92
|
+
* derive from the same renderers.
|
|
93
|
+
*/
|
|
94
|
+
export function renderPackageManagerCommands(pm, spec = `${ENGINE_PACKAGE}@latest`) {
|
|
95
|
+
return {
|
|
96
|
+
packageManager: pm,
|
|
97
|
+
globalInstall: renderGlobalInstall(pm, spec),
|
|
98
|
+
projectInstall: renderProjectInstall(pm, spec),
|
|
99
|
+
ephemeralUpdate: renderEphemeral(pm, "update"),
|
|
100
|
+
upgradeOneLiner: renderGlobalInstall(pm, `${ENGINE_PACKAGE}@latest`),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=package-manager.js.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version pin + reconciliation for the resolution spine (#2264, absorbs #2199).
|
|
3
|
+
*
|
|
4
|
+
* The committed `package.json` devDependency on `@deftai/directive` (exact,
|
|
5
|
+
* `"private": true`) is the CANONICAL pin — read before directive runs, npm-native
|
|
6
|
+
* so the engine ladder stays plain npm. This module reads that pin and reconciles
|
|
7
|
+
* it against the reachable engine version, the `.deft/core/VERSION` content marker,
|
|
8
|
+
* and the AGENTS.md managed-section `sha=`.
|
|
9
|
+
*
|
|
10
|
+
* Semver helpers here are the single comparison primitive reused by the skew
|
|
11
|
+
* policy and the engine ladder so version ordering is defined in exactly one place.
|
|
12
|
+
*/
|
|
13
|
+
/** The canonical committed dependency the pin lives on. */
|
|
14
|
+
export declare const PIN_DEPENDENCY_NAME = "@deftai/directive";
|
|
15
|
+
/** A parsed semver triple (prerelease / build metadata dropped). */
|
|
16
|
+
export type SemverTriple = readonly [number, number, number];
|
|
17
|
+
/** Parse a semver prefix (`major.minor.patch`); null when not exact semver. */
|
|
18
|
+
export declare function parseSemver(version: string | null | undefined): SemverTriple | null;
|
|
19
|
+
/** True when `version` is an EXACT pin (no range operators like `^` / `~` / `*`). */
|
|
20
|
+
export declare function isExactPin(version: string | null | undefined): boolean;
|
|
21
|
+
/** Compare two versions: -1 (a<b), 0 (a==b), 1 (a>b); null when either is unparseable. */
|
|
22
|
+
export declare function compareSemver(a: string | null, b: string | null): -1 | 0 | 1 | null;
|
|
23
|
+
/** True when `a >= b` under semver ordering; false when either is unparseable. */
|
|
24
|
+
export declare function semverGte(a: string | null, b: string | null): boolean;
|
|
25
|
+
export interface PinReadSeams {
|
|
26
|
+
readonly isFile?: (p: string) => boolean;
|
|
27
|
+
readonly readText?: (p: string) => string | null;
|
|
28
|
+
}
|
|
29
|
+
export interface PinReadResult {
|
|
30
|
+
/** Exact pinned version (no `v` prefix), or null when absent / non-exact. */
|
|
31
|
+
readonly pinVersion: string | null;
|
|
32
|
+
/** Raw devDependency spec string as written, or null. */
|
|
33
|
+
readonly rawSpec: string | null;
|
|
34
|
+
/** `package.json` declares `"private": true`. */
|
|
35
|
+
readonly isPrivate: boolean;
|
|
36
|
+
/** The pin spec is present but NOT an exact version (range operator used). */
|
|
37
|
+
readonly nonExact: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Read the committed `package.json` pin on `@deftai/directive`. The pin is
|
|
41
|
+
* canonical only when it is an EXACT version; a range spec is reported via
|
|
42
|
+
* `nonExact` so callers can warn rather than silently trusting a floating range.
|
|
43
|
+
*/
|
|
44
|
+
export declare function readPin(projectRoot: string, seams?: PinReadSeams): PinReadResult;
|
|
45
|
+
export interface ReconcileInputs {
|
|
46
|
+
/** Canonical pin from `package.json`. */
|
|
47
|
+
readonly pinVersion: string | null;
|
|
48
|
+
/** Reachable engine version, or null when unreachable. */
|
|
49
|
+
readonly engineVersion: string | null;
|
|
50
|
+
/** `.deft/core/VERSION` content marker, or null. */
|
|
51
|
+
readonly contentVersion: string | null;
|
|
52
|
+
/** AGENTS.md managed-section `sha=`, or null. */
|
|
53
|
+
readonly managedSectionSha: string | null;
|
|
54
|
+
/** The sha the engine would stamp for the current payload, when known. */
|
|
55
|
+
readonly expectedManagedSectionSha?: string | null;
|
|
56
|
+
}
|
|
57
|
+
export interface ReconcileResult {
|
|
58
|
+
/** No mismatches detected against the pin. */
|
|
59
|
+
readonly consistent: boolean;
|
|
60
|
+
/** Deposited content (`VERSION`) is behind the pin (needs forward-migration). */
|
|
61
|
+
readonly contentBehindPin: boolean;
|
|
62
|
+
/** The managed-section sha is present but disagrees with the expected sha. */
|
|
63
|
+
readonly managedShaMismatch: boolean;
|
|
64
|
+
/** Human-facing mismatch reasons (empty when consistent). */
|
|
65
|
+
readonly mismatches: readonly string[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Reconcile the pin against the engine, the deposited content marker, and the
|
|
69
|
+
* AGENTS managed-section sha. The managed-section `sha=` is part of the
|
|
70
|
+
* reconciliation, not just `VERSION` (per the #2264 acceptance).
|
|
71
|
+
*/
|
|
72
|
+
export declare function reconcileVersions(inputs: ReconcileInputs): ReconcileResult;
|
|
73
|
+
//# sourceMappingURL=pin.d.ts.map
|