@deftai/directive-core 0.68.0 → 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/hygiene.js +11 -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,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal adoption dispatcher for `directive init` (#2265 / epic #2203).
|
|
3
|
+
*
|
|
4
|
+
* `directive init` is the ONE entrypoint a first-time adopter runs regardless of
|
|
5
|
+
* their directory state. This module classifies the directory via the shared
|
|
6
|
+
* keystone `classify()`/`plan()` fact-set (#2264, consumed read-only) and
|
|
7
|
+
* dispatches to exactly one of four paths, always printing a concise state
|
|
8
|
+
* summary plus exactly one recommended next action:
|
|
9
|
+
*
|
|
10
|
+
* - `scaffold` — empty greenfield dir; deposit Directive.
|
|
11
|
+
* - `brownfield-install` — app code but no Directive deposit; install support
|
|
12
|
+
* beside the app code without disturbing it.
|
|
13
|
+
* - `delegate-update` — already initialized; DELEGATE to `update` with an
|
|
14
|
+
* explicit disclosure line. init never silently
|
|
15
|
+
* refreshes under its own name (the #2199 anti-pattern)
|
|
16
|
+
* and never re-scaffolds an existing install.
|
|
17
|
+
* - `route-migrate` — legacy / pre-cutover layout; route to `migrate`.
|
|
18
|
+
*
|
|
19
|
+
* Logic is single-sourced: each delegate branch CALLS the existing narrow verb
|
|
20
|
+
* (`runInitDepositCli` / `runRefreshDepositCli` / `runMigrateCli`) — the
|
|
21
|
+
* dispatcher never re-implements deposit, refresh, or migrate behavior.
|
|
22
|
+
*/
|
|
23
|
+
import type { ResolutionFacts, ResolutionPlan } from "@deftai/directive-types";
|
|
24
|
+
import { type ClassifySeams } from "../resolution/index.js";
|
|
25
|
+
import { type RunInitDepositCliOptions } from "./init-deposit.js";
|
|
26
|
+
import { type RunMigrateCliOptions } from "./migrate.js";
|
|
27
|
+
import { type RunRefreshDepositCliOptions } from "./refresh.js";
|
|
28
|
+
import type { InitDepositIo } from "./scaffold.js";
|
|
29
|
+
/** The four adoption paths `directive init` dispatches into. */
|
|
30
|
+
export type InitDispatchDecision = "scaffold" | "brownfield-install" | "delegate-update" | "route-migrate";
|
|
31
|
+
/** Human-readable state label per decision, used in the printed summary. */
|
|
32
|
+
export declare const INIT_DISPATCH_STATE_LABEL: Record<InitDispatchDecision, string>;
|
|
33
|
+
/**
|
|
34
|
+
* Explicit disclosure printed BEFORE delegating an already-initialized project
|
|
35
|
+
* to `update`. Its presence is what keeps `init` from silently masquerading as
|
|
36
|
+
* `update` (the #2199 anti-pattern): the user always sees that init delegated.
|
|
37
|
+
*/
|
|
38
|
+
export declare const UPDATE_DELEGATION_DISCLOSURE = "Existing install detected \u2014 refreshing via `update` (init delegates to update; it does not re-scaffold).";
|
|
39
|
+
/**
|
|
40
|
+
* Collapse the shared resolution {@link ResolutionPlan} + fact-set onto one of
|
|
41
|
+
* the four init dispatch decisions. There is no second classifier: `plan.mode`
|
|
42
|
+
* is the single source of truth, and the empty-vs-brownfield split reads the
|
|
43
|
+
* `hasAppCode`/`hasGit` facts already produced by `classify()`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function decideInitDispatch(facts: ResolutionFacts, resolutionPlan: ResolutionPlan): InitDispatchDecision;
|
|
46
|
+
export interface InitDispatchClassification {
|
|
47
|
+
readonly decision: InitDispatchDecision;
|
|
48
|
+
readonly plan: ResolutionPlan;
|
|
49
|
+
readonly facts: ResolutionFacts;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Run the up-front dispatch classifier. Reuses the keystone `classify()`
|
|
53
|
+
* fact-set + `plan()` precedence table — no second classifier lives here.
|
|
54
|
+
*/
|
|
55
|
+
export declare function classifyInitDispatch(projectDir: string, classifySeams?: ClassifySeams): InitDispatchClassification;
|
|
56
|
+
/** The single recommended next-action line for a decision. */
|
|
57
|
+
export declare function initDispatchNextAction(decision: InitDispatchDecision, resolutionPlan: ResolutionPlan): string;
|
|
58
|
+
/** Print the concise state summary + exactly one recommended next action. */
|
|
59
|
+
export declare function printInitDispatchSummary(io: InitDepositIo, decision: InitDispatchDecision, resolutionPlan: ResolutionPlan): void;
|
|
60
|
+
/** Machine-readable payload emitted for `--dry-run`/`--plan` (no execution). */
|
|
61
|
+
export declare function buildInitDispatchDryRunJson(projectDir: string, classification: InitDispatchClassification): Record<string, unknown>;
|
|
62
|
+
/**
|
|
63
|
+
* Injectable delegate seams. Defaults are the real narrow-verb CLIs; tests
|
|
64
|
+
* inject fakes to assert single-sourced delegation without touching the fs.
|
|
65
|
+
*/
|
|
66
|
+
export interface InitDispatchSeams {
|
|
67
|
+
readonly classifySeams?: ClassifySeams;
|
|
68
|
+
readonly runScaffold?: (options: RunInitDepositCliOptions) => Promise<number>;
|
|
69
|
+
readonly runRefresh?: (options: RunRefreshDepositCliOptions) => Promise<number>;
|
|
70
|
+
readonly runMigrate?: (options: RunMigrateCliOptions) => number;
|
|
71
|
+
}
|
|
72
|
+
export interface RunInitDispatchCliOptions {
|
|
73
|
+
readonly projectDir: string;
|
|
74
|
+
readonly jsonOut: boolean;
|
|
75
|
+
readonly nonInteractive: boolean;
|
|
76
|
+
/** `--dry-run`/`--plan`: print the classified plan without executing. */
|
|
77
|
+
readonly dryRun: boolean;
|
|
78
|
+
readonly writeOut: (text: string) => void;
|
|
79
|
+
readonly writeErr: (text: string) => void;
|
|
80
|
+
readonly seams?: InitDispatchSeams;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* CLI-facing entrypoint for `directive init`. Classifies, prints a state summary
|
|
84
|
+
* + one next action, then either stops (`--dry-run`) or delegates to the narrow
|
|
85
|
+
* verb for the resolved decision. The summary/disclosure use the `printf`
|
|
86
|
+
* channel (stderr under `--json`) so that in JSON mode stdout stays a SINGLE
|
|
87
|
+
* JSON object — the delegate's result, or the dry-run plan.
|
|
88
|
+
*/
|
|
89
|
+
export declare function runInitDispatchCli(options: RunInitDispatchCliOptions): Promise<number>;
|
|
90
|
+
//# sourceMappingURL=init-dispatch.d.ts.map
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal adoption dispatcher for `directive init` (#2265 / epic #2203).
|
|
3
|
+
*
|
|
4
|
+
* `directive init` is the ONE entrypoint a first-time adopter runs regardless of
|
|
5
|
+
* their directory state. This module classifies the directory via the shared
|
|
6
|
+
* keystone `classify()`/`plan()` fact-set (#2264, consumed read-only) and
|
|
7
|
+
* dispatches to exactly one of four paths, always printing a concise state
|
|
8
|
+
* summary plus exactly one recommended next action:
|
|
9
|
+
*
|
|
10
|
+
* - `scaffold` — empty greenfield dir; deposit Directive.
|
|
11
|
+
* - `brownfield-install` — app code but no Directive deposit; install support
|
|
12
|
+
* beside the app code without disturbing it.
|
|
13
|
+
* - `delegate-update` — already initialized; DELEGATE to `update` with an
|
|
14
|
+
* explicit disclosure line. init never silently
|
|
15
|
+
* refreshes under its own name (the #2199 anti-pattern)
|
|
16
|
+
* and never re-scaffolds an existing install.
|
|
17
|
+
* - `route-migrate` — legacy / pre-cutover layout; route to `migrate`.
|
|
18
|
+
*
|
|
19
|
+
* Logic is single-sourced: each delegate branch CALLS the existing narrow verb
|
|
20
|
+
* (`runInitDepositCli` / `runRefreshDepositCli` / `runMigrateCli`) — the
|
|
21
|
+
* dispatcher never re-implements deposit, refresh, or migrate behavior.
|
|
22
|
+
*/
|
|
23
|
+
import { resolve } from "node:path";
|
|
24
|
+
import { classify, plan } from "../resolution/index.js";
|
|
25
|
+
import { runInitDepositCli } from "./init-deposit.js";
|
|
26
|
+
import { runMigrateCli } from "./migrate.js";
|
|
27
|
+
import { runRefreshDepositCli } from "./refresh.js";
|
|
28
|
+
/** Human-readable state label per decision, used in the printed summary. */
|
|
29
|
+
export const INIT_DISPATCH_STATE_LABEL = {
|
|
30
|
+
scaffold: "empty directory (greenfield)",
|
|
31
|
+
"brownfield-install": "brownfield project (app code, no Directive deposit)",
|
|
32
|
+
"delegate-update": "already initialized",
|
|
33
|
+
"route-migrate": "legacy / pre-cutover layout",
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Explicit disclosure printed BEFORE delegating an already-initialized project
|
|
37
|
+
* to `update`. Its presence is what keeps `init` from silently masquerading as
|
|
38
|
+
* `update` (the #2199 anti-pattern): the user always sees that init delegated.
|
|
39
|
+
*/
|
|
40
|
+
export const UPDATE_DELEGATION_DISCLOSURE = "Existing install detected — refreshing via `update` (init delegates to update; it does not re-scaffold).";
|
|
41
|
+
/**
|
|
42
|
+
* Collapse the shared resolution {@link ResolutionPlan} + fact-set onto one of
|
|
43
|
+
* the four init dispatch decisions. There is no second classifier: `plan.mode`
|
|
44
|
+
* is the single source of truth, and the empty-vs-brownfield split reads the
|
|
45
|
+
* `hasAppCode`/`hasGit` facts already produced by `classify()`.
|
|
46
|
+
*/
|
|
47
|
+
export function decideInitDispatch(facts, resolutionPlan) {
|
|
48
|
+
if (resolutionPlan.mode === "migrate")
|
|
49
|
+
return "route-migrate";
|
|
50
|
+
if (resolutionPlan.mode === "init") {
|
|
51
|
+
return facts.hasAppCode || facts.hasGit ? "brownfield-install" : "scaffold";
|
|
52
|
+
}
|
|
53
|
+
// Deposit present (proceed | update | install-* | blocked): the install exists,
|
|
54
|
+
// so `update` owns the engine/content dimension. init delegates.
|
|
55
|
+
return "delegate-update";
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Run the up-front dispatch classifier. Reuses the keystone `classify()`
|
|
59
|
+
* fact-set + `plan()` precedence table — no second classifier lives here.
|
|
60
|
+
*/
|
|
61
|
+
export function classifyInitDispatch(projectDir, classifySeams = {}) {
|
|
62
|
+
const facts = classify(projectDir, classifySeams);
|
|
63
|
+
const resolutionPlan = plan(facts, {});
|
|
64
|
+
return { decision: decideInitDispatch(facts, resolutionPlan), plan: resolutionPlan, facts };
|
|
65
|
+
}
|
|
66
|
+
/** The single recommended next-action line for a decision. */
|
|
67
|
+
export function initDispatchNextAction(decision, resolutionPlan) {
|
|
68
|
+
switch (decision) {
|
|
69
|
+
case "scaffold":
|
|
70
|
+
return "Next: scaffolding a fresh Directive deposit (.deft/core, AGENTS.md managed section, xbrief/ layout).";
|
|
71
|
+
case "brownfield-install":
|
|
72
|
+
return "Next: installing Directive support beside your app code (app source is left untouched); then extract a brownfield spec and set up xbrief/.";
|
|
73
|
+
case "delegate-update":
|
|
74
|
+
return "Next: refreshing the existing install via `update`.";
|
|
75
|
+
case "route-migrate":
|
|
76
|
+
return `Next: routing to \`migrate\`. ${resolutionPlan.nextAction.remediation}`;
|
|
77
|
+
default: {
|
|
78
|
+
const exhaustive = decision;
|
|
79
|
+
throw new Error(`unhandled init dispatch decision: ${String(exhaustive)}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Print the concise state summary + exactly one recommended next action. */
|
|
84
|
+
export function printInitDispatchSummary(io, decision, resolutionPlan) {
|
|
85
|
+
io.printf(`\n[directive init] State: ${INIT_DISPATCH_STATE_LABEL[decision]}\n`);
|
|
86
|
+
io.printf(`[directive init] ${initDispatchNextAction(decision, resolutionPlan)}\n`);
|
|
87
|
+
for (const warning of resolutionPlan.warnings) {
|
|
88
|
+
io.printf(`[directive init] Note: ${warning}\n`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** Machine-readable payload emitted for `--dry-run`/`--plan` (no execution). */
|
|
92
|
+
export function buildInitDispatchDryRunJson(projectDir, classification) {
|
|
93
|
+
return {
|
|
94
|
+
success: true,
|
|
95
|
+
action: "init",
|
|
96
|
+
dry_run: true,
|
|
97
|
+
dispatch: classification.decision,
|
|
98
|
+
mode: classification.plan.mode,
|
|
99
|
+
project_dir: projectDir,
|
|
100
|
+
next_action: classification.plan.nextAction,
|
|
101
|
+
warnings: classification.plan.warnings,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* CLI-facing entrypoint for `directive init`. Classifies, prints a state summary
|
|
106
|
+
* + one next action, then either stops (`--dry-run`) or delegates to the narrow
|
|
107
|
+
* verb for the resolved decision. The summary/disclosure use the `printf`
|
|
108
|
+
* channel (stderr under `--json`) so that in JSON mode stdout stays a SINGLE
|
|
109
|
+
* JSON object — the delegate's result, or the dry-run plan.
|
|
110
|
+
*/
|
|
111
|
+
export async function runInitDispatchCli(options) {
|
|
112
|
+
const projectDir = resolve(options.projectDir);
|
|
113
|
+
const seams = options.seams ?? {};
|
|
114
|
+
const io = {
|
|
115
|
+
printf: (text) => {
|
|
116
|
+
if (options.jsonOut)
|
|
117
|
+
options.writeErr(text);
|
|
118
|
+
else
|
|
119
|
+
options.writeOut(text);
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
const classification = classifyInitDispatch(projectDir, seams.classifySeams ?? {});
|
|
123
|
+
const { decision, plan: resolutionPlan } = classification;
|
|
124
|
+
printInitDispatchSummary(io, decision, resolutionPlan);
|
|
125
|
+
if (options.dryRun) {
|
|
126
|
+
if (options.jsonOut) {
|
|
127
|
+
options.writeOut(`${JSON.stringify(buildInitDispatchDryRunJson(projectDir, classification), null, 2)}\n`);
|
|
128
|
+
}
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
switch (decision) {
|
|
132
|
+
case "scaffold":
|
|
133
|
+
case "brownfield-install": {
|
|
134
|
+
const runScaffold = seams.runScaffold ?? runInitDepositCli;
|
|
135
|
+
return runScaffold({
|
|
136
|
+
projectDir,
|
|
137
|
+
jsonOut: options.jsonOut,
|
|
138
|
+
nonInteractive: options.nonInteractive,
|
|
139
|
+
writeOut: options.writeOut,
|
|
140
|
+
writeErr: options.writeErr,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
case "delegate-update": {
|
|
144
|
+
io.printf(`\n[directive init] ${UPDATE_DELEGATION_DISCLOSURE}\n`);
|
|
145
|
+
const runRefresh = seams.runRefresh ?? runRefreshDepositCli;
|
|
146
|
+
return runRefresh({
|
|
147
|
+
projectDir,
|
|
148
|
+
jsonOut: options.jsonOut,
|
|
149
|
+
nonInteractive: options.nonInteractive,
|
|
150
|
+
upgrade: false,
|
|
151
|
+
writeOut: options.writeOut,
|
|
152
|
+
writeErr: options.writeErr,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
case "route-migrate": {
|
|
156
|
+
const runMigrate = seams.runMigrate ?? runMigrateCli;
|
|
157
|
+
return runMigrate({
|
|
158
|
+
projectDir,
|
|
159
|
+
jsonOut: options.jsonOut,
|
|
160
|
+
writeOut: options.writeOut,
|
|
161
|
+
writeErr: options.writeErr,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
default: {
|
|
165
|
+
const exhaustive = decision;
|
|
166
|
+
throw new Error(`unhandled init dispatch decision: ${String(exhaustive)}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=init-dispatch.js.map
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Refs #1942, #1430, #1671.
|
|
9
9
|
*/
|
|
10
|
+
import type { ResolutionFacts, ResolutionPlan } from "@deftai/directive-types";
|
|
11
|
+
import { type ClassifySeams, type EngineInstallRunner, type EngineResolution, type LadderFacts, type ReprojectRunner } from "../resolution/index.js";
|
|
12
|
+
import { type GitLsFiles } from "./gitignore.js";
|
|
10
13
|
import { type InitDepositArgs } from "./init-deposit.js";
|
|
11
14
|
import { type LegacyLayoutDetection } from "./legacy-detect.js";
|
|
12
15
|
import { type InitDepositIo } from "./scaffold.js";
|
|
@@ -33,18 +36,92 @@ export interface RefreshDepositSeams {
|
|
|
33
36
|
nowIso?: () => string;
|
|
34
37
|
gitPorcelain?: (projectRoot: string) => string | null;
|
|
35
38
|
detectLegacy?: (projectDir: string) => LegacyLayoutDetection;
|
|
39
|
+
/**
|
|
40
|
+
* #2266: `git ls-files` probe threaded into the non-destructive `.gitignore`
|
|
41
|
+
* upkeep so the refresh never invokes a destructive `git rm --cached` path.
|
|
42
|
+
*/
|
|
43
|
+
gitLsFiles?: GitLsFiles;
|
|
36
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The four states `directive update` classifies an EXISTING install into BEFORE
|
|
47
|
+
* any copy (#2266). Derived from the keystone `classify()`/`plan()` spine
|
|
48
|
+
* (#2264) — there is exactly one classifier in the system; this maps the shared
|
|
49
|
+
* {@link ResolutionPlan} onto the narrow update-verb contract.
|
|
50
|
+
*
|
|
51
|
+
* - `not-initialized` — no Directive footprint at all; STOP, hint `init`.
|
|
52
|
+
* - `current` — initialized and already up to date; refresh is a no-op.
|
|
53
|
+
* - `updated` — initialized and the refresh forward-migrates content.
|
|
54
|
+
* - `migration-required` — pre-cutover artifacts; `update` is not enough.
|
|
55
|
+
*/
|
|
56
|
+
export type UpdateState = "not-initialized" | "current" | "updated" | "migration-required";
|
|
57
|
+
export interface UpdateClassification {
|
|
58
|
+
readonly state: UpdateState;
|
|
59
|
+
readonly plan: ResolutionPlan;
|
|
60
|
+
readonly facts: ResolutionFacts;
|
|
61
|
+
}
|
|
62
|
+
/** The exact refusal shown when `update` runs against an un-initialized project. */
|
|
63
|
+
export declare const NOT_INITIALIZED_MESSAGE = "This project is not initialized. Run directive init.";
|
|
64
|
+
/** `update` refused because the project has no install / needs a different verb. */
|
|
65
|
+
export declare const UPDATE_REFUSED_EXIT_CODE = 1;
|
|
66
|
+
/**
|
|
67
|
+
* Collapse the shared resolution {@link ResolutionPlan} onto the four update
|
|
68
|
+
* states. Pre-cutover wins over everything (migrate before any refresh); a
|
|
69
|
+
* project with no footprint is not-initialized; an initialized project that the
|
|
70
|
+
* spine says can `proceed` is already current; everything else (deposit
|
|
71
|
+
* reconstitution, content behind pin, engine self-heal) is an `updated` refresh.
|
|
72
|
+
*/
|
|
73
|
+
export declare function updateStateFromPlan(facts: ResolutionFacts, resolutionPlan: ResolutionPlan): UpdateState;
|
|
74
|
+
/**
|
|
75
|
+
* Run the up-front four-state classifier (#2266). Reuses the keystone
|
|
76
|
+
* `classify()` fact-set + `plan()` precedence table — no second classifier.
|
|
77
|
+
*/
|
|
78
|
+
export declare function classifyUpdateState(projectDir: string, classifySeams?: ClassifySeams): UpdateClassification;
|
|
79
|
+
export interface SelfHealSeams {
|
|
80
|
+
/** Pre-computed ladder facts; when omitted, cheap local probes build them. */
|
|
81
|
+
readonly ladderFacts?: LadderFacts;
|
|
82
|
+
/** Injected side-effecting install runner; when omitted, the install is deferred. */
|
|
83
|
+
readonly engineInstallRunner?: EngineInstallRunner;
|
|
84
|
+
/** Injected content re-projection after a fresh install. */
|
|
85
|
+
readonly reproject?: ReprojectRunner;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Cheap, non-networked ladder facts for the default self-heal path. The global
|
|
89
|
+
* engine version is whatever `classify()` already probed; the local sandbox
|
|
90
|
+
* engine integrity is a filesystem check. `registryUp`/`globalPrefixWritable`
|
|
91
|
+
* default optimistic so the ladder surfaces the canonical `npm i -g` remediation
|
|
92
|
+
* when no install runner is wired.
|
|
93
|
+
*/
|
|
94
|
+
export declare function buildDefaultLadderFacts(projectDir: string, facts: ResolutionFacts): LadderFacts;
|
|
95
|
+
/**
|
|
96
|
+
* Self-heal a mismatched / unreachable engine by DELEGATING to the keystone
|
|
97
|
+
* global-first ladder (`resolveEngine`, #2264). The refresh's content copy comes
|
|
98
|
+
* from the resolved content package and does not itself require the engine, so
|
|
99
|
+
* this is a best-effort heal that prints the ladder trace and remediation; when
|
|
100
|
+
* a caller wires an install runner, the ladder performs the install with zero
|
|
101
|
+
* manual npm/PATH steps (#2266 a3).
|
|
102
|
+
*/
|
|
103
|
+
export declare function selfHealEngine(projectDir: string, facts: ResolutionFacts, io: InitDepositIo, seams?: SelfHealSeams): EngineResolution;
|
|
37
104
|
/** Framework-managed uncommitted paths after refresh (#1671). */
|
|
38
105
|
export declare function frameworkRefreshSideEffects(projectDir: string, readPorcelain?: (root: string) => string | null): string[];
|
|
39
106
|
export declare function printRefreshSideEffects(io: InitDepositIo, files: readonly string[]): void;
|
|
40
107
|
export declare function buildVersionSkewNotice(engineVersion: string, contentVersion: string, previousDepositVersion: string | null): string | null;
|
|
41
|
-
export declare function buildUpdateSummaryJson(result: RefreshDepositResult, options: RefreshDepositArgs): Record<string, unknown>;
|
|
42
|
-
export declare function printUpdateComplete(result: RefreshDepositResult, io: InitDepositIo): void;
|
|
108
|
+
export declare function buildUpdateSummaryJson(result: RefreshDepositResult, options: RefreshDepositArgs, updateState?: UpdateState): Record<string, unknown>;
|
|
109
|
+
export declare function printUpdateComplete(result: RefreshDepositResult, io: InitDepositIo, updateState?: UpdateState): void;
|
|
43
110
|
export declare function runRefreshDeposit(args: RefreshDepositArgs, io: InitDepositIo, seams?: RefreshDepositSeams): Promise<RefreshDepositResult>;
|
|
44
111
|
export interface RunRefreshDepositCliOptions extends RefreshDepositArgs {
|
|
45
112
|
readonly writeOut: (text: string) => void;
|
|
46
113
|
readonly writeErr: (text: string) => void;
|
|
47
114
|
readonly seams?: RefreshDepositSeams;
|
|
115
|
+
/** #2266: print the classified plan without executing the refresh (`--dry-run`/`--plan`). */
|
|
116
|
+
readonly dryRun?: boolean;
|
|
117
|
+
/** #2266: seams for the up-front four-state classifier (default: real fs + engine probe). */
|
|
118
|
+
readonly classifySeams?: ClassifySeams;
|
|
119
|
+
/** #2266: pre-computed engine-ladder facts for the self-heal delegation (default: probed). */
|
|
120
|
+
readonly ladderFacts?: LadderFacts;
|
|
121
|
+
/** #2266: injected engine install runner for the ladder self-heal (default: deferred). */
|
|
122
|
+
readonly engineInstallRunner?: EngineInstallRunner;
|
|
123
|
+
/** #2266: injected content re-projection after a self-heal install. */
|
|
124
|
+
readonly reproject?: ReprojectRunner;
|
|
48
125
|
}
|
|
49
126
|
/** CLI-facing wrapper: runs refresh, emits JSON or wizard UX, returns exit code. */
|
|
50
127
|
export declare function runRefreshDepositCli(options: RunRefreshDepositCliOptions): Promise<number>;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* Refs #1942, #1430, #1671.
|
|
9
9
|
*/
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { platform as osPlatform } from "node:os";
|
|
11
12
|
import { join, resolve } from "node:path";
|
|
12
13
|
import { copyTree } from "../deposit/copy-tree.js";
|
|
13
14
|
import { prunePythonArtifactsFromDeposit } from "../deposit/python-free.js";
|
|
@@ -16,12 +17,92 @@ import { manifestTagToVersion, parseInstallManifest } from "../doctor/manifest.j
|
|
|
16
17
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
17
18
|
import { resolveLifecycleRoot } from "../layout/resolve.js";
|
|
18
19
|
import { DEV_FALLBACK } from "../platform/constants.js";
|
|
20
|
+
import { checkLocalEngineIntegrity, classify, ENGINE_PACKAGE, plan, renderGlobalInstall, resolveEngine, } from "../resolution/index.js";
|
|
19
21
|
import { gitPorcelain } from "../story-ready/git.js";
|
|
22
|
+
import { ensureInitGitignoreLines } from "./gitignore.js";
|
|
20
23
|
import { depositStagePaths, isInstallerManagedPath, printCommitGuidance } from "./hygiene.js";
|
|
21
24
|
import { parseInitArgv } from "./init-deposit.js";
|
|
22
25
|
import { buildLegacyRefusalJson, buildLegacyRefusalMessage, detectLegacyLayout, LEGACY_LAYOUT_REFUSED_EXIT_CODE, LegacyLayoutRefusedError, } from "./legacy-detect.js";
|
|
23
26
|
import { printMigrateNudgeIfNeeded } from "./migrate.js";
|
|
24
27
|
import { CANONICAL_INSTALL_ROOT, depositNeutralization, ensureTaskfile, writeAgentsMd, writeInstallManifest, } from "./scaffold.js";
|
|
28
|
+
/** The exact refusal shown when `update` runs against an un-initialized project. */
|
|
29
|
+
export const NOT_INITIALIZED_MESSAGE = "This project is not initialized. Run directive init.";
|
|
30
|
+
/** `update` refused because the project has no install / needs a different verb. */
|
|
31
|
+
export const UPDATE_REFUSED_EXIT_CODE = 1;
|
|
32
|
+
/** A project is initialized when it carries ANY Directive footprint. */
|
|
33
|
+
function isInitialized(facts) {
|
|
34
|
+
return facts.hasDeftCore || facts.hasManagedSection || facts.pinVersion !== null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Collapse the shared resolution {@link ResolutionPlan} onto the four update
|
|
38
|
+
* states. Pre-cutover wins over everything (migrate before any refresh); a
|
|
39
|
+
* project with no footprint is not-initialized; an initialized project that the
|
|
40
|
+
* spine says can `proceed` is already current; everything else (deposit
|
|
41
|
+
* reconstitution, content behind pin, engine self-heal) is an `updated` refresh.
|
|
42
|
+
*/
|
|
43
|
+
export function updateStateFromPlan(facts, resolutionPlan) {
|
|
44
|
+
if (resolutionPlan.mode === "migrate")
|
|
45
|
+
return "migration-required";
|
|
46
|
+
if (!isInitialized(facts))
|
|
47
|
+
return "not-initialized";
|
|
48
|
+
if (resolutionPlan.mode === "proceed")
|
|
49
|
+
return "current";
|
|
50
|
+
return "updated";
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Run the up-front four-state classifier (#2266). Reuses the keystone
|
|
54
|
+
* `classify()` fact-set + `plan()` precedence table — no second classifier.
|
|
55
|
+
*/
|
|
56
|
+
export function classifyUpdateState(projectDir, classifySeams = {}) {
|
|
57
|
+
const facts = classify(projectDir, classifySeams);
|
|
58
|
+
const resolutionPlan = plan(facts, {});
|
|
59
|
+
return { state: updateStateFromPlan(facts, resolutionPlan), plan: resolutionPlan, facts };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Cheap, non-networked ladder facts for the default self-heal path. The global
|
|
63
|
+
* engine version is whatever `classify()` already probed; the local sandbox
|
|
64
|
+
* engine integrity is a filesystem check. `registryUp`/`globalPrefixWritable`
|
|
65
|
+
* default optimistic so the ladder surfaces the canonical `npm i -g` remediation
|
|
66
|
+
* when no install runner is wired.
|
|
67
|
+
*/
|
|
68
|
+
export function buildDefaultLadderFacts(projectDir, facts) {
|
|
69
|
+
const integrity = checkLocalEngineIntegrity(projectDir);
|
|
70
|
+
return {
|
|
71
|
+
pinVersion: facts.pinVersion,
|
|
72
|
+
globalEngineVersion: facts.engineReachable ? facts.engineVersion : null,
|
|
73
|
+
localEngine: integrity.present ? { version: null, integrity } : null,
|
|
74
|
+
registryUp: true,
|
|
75
|
+
globalPrefixWritable: true,
|
|
76
|
+
stagedTarballAvailable: false,
|
|
77
|
+
platform: osPlatform(),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Self-heal a mismatched / unreachable engine by DELEGATING to the keystone
|
|
82
|
+
* global-first ladder (`resolveEngine`, #2264). The refresh's content copy comes
|
|
83
|
+
* from the resolved content package and does not itself require the engine, so
|
|
84
|
+
* this is a best-effort heal that prints the ladder trace and remediation; when
|
|
85
|
+
* a caller wires an install runner, the ladder performs the install with zero
|
|
86
|
+
* manual npm/PATH steps (#2266 a3).
|
|
87
|
+
*/
|
|
88
|
+
export function selfHealEngine(projectDir, facts, io, seams = {}) {
|
|
89
|
+
const ladderFacts = seams.ladderFacts ?? buildDefaultLadderFacts(projectDir, facts);
|
|
90
|
+
const resolution = resolveEngine(ladderFacts, {
|
|
91
|
+
installRunner: seams.engineInstallRunner,
|
|
92
|
+
reproject: seams.reproject,
|
|
93
|
+
});
|
|
94
|
+
io.printf(`\n[deft update] engine self-heal (global-first ladder): ${resolution.trace}\n`);
|
|
95
|
+
if (!resolution.selfHealed && !resolution.decision.usable) {
|
|
96
|
+
io.printf(`[deft update] ${resolution.decision.reason}\n`);
|
|
97
|
+
if (resolution.decision.rung === "install-global") {
|
|
98
|
+
const suffix = facts.pinVersion ? `@${facts.pinVersion}` : "";
|
|
99
|
+
const spec = `${ENGINE_PACKAGE}${suffix}`;
|
|
100
|
+
io.printf(` Remediation: ${renderGlobalInstall("npm", spec)}\n`);
|
|
101
|
+
io.printf(` (pnpm: ${renderGlobalInstall("pnpm", spec)})\n`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return resolution;
|
|
105
|
+
}
|
|
25
106
|
function normalizeVersion(version) {
|
|
26
107
|
return version.trim().replace(/^v/, "");
|
|
27
108
|
}
|
|
@@ -199,10 +280,11 @@ export function buildVersionSkewNotice(engineVersion, contentVersion, previousDe
|
|
|
199
280
|
}
|
|
200
281
|
return null;
|
|
201
282
|
}
|
|
202
|
-
export function buildUpdateSummaryJson(result, options) {
|
|
283
|
+
export function buildUpdateSummaryJson(result, options, updateState) {
|
|
203
284
|
return {
|
|
204
285
|
success: true,
|
|
205
286
|
action: "upgrade",
|
|
287
|
+
...(updateState ? { update_state: updateState } : {}),
|
|
206
288
|
version: result.engineVersion,
|
|
207
289
|
project_dir: result.projectDir,
|
|
208
290
|
deft_dir: result.deftDir,
|
|
@@ -229,10 +311,13 @@ export function buildUpdateSummaryJson(result, options) {
|
|
|
229
311
|
agents_md_updated: result.agentsMdUpdated,
|
|
230
312
|
};
|
|
231
313
|
}
|
|
232
|
-
export function printUpdateComplete(result, io) {
|
|
314
|
+
export function printUpdateComplete(result, io, updateState) {
|
|
233
315
|
io.printf("\n✓ Deft framework payload refreshed.\n\n");
|
|
234
316
|
io.printf(` Location : ${result.deftDir}\n`);
|
|
235
317
|
io.printf(` Content : v${normalizeVersion(result.contentVersion)}\n`);
|
|
318
|
+
if (updateState) {
|
|
319
|
+
io.printf(` State : ${updateState}\n`);
|
|
320
|
+
}
|
|
236
321
|
io.printf(` AGENTS.md : ${result.agentsMdUpdated ? "updated" : "already current"}\n`);
|
|
237
322
|
if (result.versionSkewNotice) {
|
|
238
323
|
io.printf(`\n${result.versionSkewNotice}\n`);
|
|
@@ -283,6 +368,12 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
283
368
|
syncBareVersionMarker(projectDir, contentVersion);
|
|
284
369
|
const agentsMdUpdated = writeAgentsMd(projectDir, deftDir, io);
|
|
285
370
|
await depositNeutralization(projectDir, io);
|
|
371
|
+
// #2266: non-destructive `.gitignore` upkeep for framework-owned paths. This
|
|
372
|
+
// NEVER un-tracks a committed deposit -- `ensureInitGitignoreLines` only writes
|
|
373
|
+
// `.gitignore` and leaves an already-tracked `.deft/core` tracked. The
|
|
374
|
+
// destructive `git rm --cached .deft/core` un-track is the deliberate
|
|
375
|
+
// `migrate --untrack-core` step (#2269), not `update`.
|
|
376
|
+
ensureInitGitignoreLines(projectDir, io, { gitLsFiles: seams.gitLsFiles });
|
|
286
377
|
let taskfileWired = false;
|
|
287
378
|
if (args.nonInteractive) {
|
|
288
379
|
taskfileWired = ensureTaskfile(projectDir, io);
|
|
@@ -309,6 +400,59 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
309
400
|
stagedPaths,
|
|
310
401
|
};
|
|
311
402
|
}
|
|
403
|
+
function buildRefusalJson(state, projectDir, message, command) {
|
|
404
|
+
return {
|
|
405
|
+
success: false,
|
|
406
|
+
action: "update",
|
|
407
|
+
update_state: state,
|
|
408
|
+
project_dir: projectDir,
|
|
409
|
+
message,
|
|
410
|
+
next_action: { command },
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
/** Emit the `not-initialized` refusal: STOP, hint `init`, never write a partial install. */
|
|
414
|
+
function emitNotInitialized(options, io, projectDir) {
|
|
415
|
+
io.printf(`${NOT_INITIALIZED_MESSAGE}\n`);
|
|
416
|
+
if (options.jsonOut) {
|
|
417
|
+
options.writeOut(`${JSON.stringify(buildRefusalJson("not-initialized", projectDir, NOT_INITIALIZED_MESSAGE, "directive init"), null, 2)}\n`);
|
|
418
|
+
}
|
|
419
|
+
return UPDATE_REFUSED_EXIT_CODE;
|
|
420
|
+
}
|
|
421
|
+
/** Emit the `migration-required` refusal: `update` is not enough; point at init/migrate. */
|
|
422
|
+
function emitMigrationRequired(options, io, projectDir, classification) {
|
|
423
|
+
const remediation = classification.plan.nextAction.remediation;
|
|
424
|
+
const message = `directive update: this project requires migration before it can be refreshed. ${remediation}`;
|
|
425
|
+
io.printf(`${message}\n`);
|
|
426
|
+
if (options.jsonOut) {
|
|
427
|
+
options.writeOut(`${JSON.stringify(buildRefusalJson("migration-required", projectDir, message, classification.plan.nextAction.command ?? "directive init"), null, 2)}\n`);
|
|
428
|
+
}
|
|
429
|
+
return UPDATE_REFUSED_EXIT_CODE;
|
|
430
|
+
}
|
|
431
|
+
/** Emit the classified plan for `--dry-run`/`--plan` without executing the refresh. */
|
|
432
|
+
function emitDryRunPlan(options, io, projectDir, classification) {
|
|
433
|
+
const { state, plan: resolutionPlan } = classification;
|
|
434
|
+
io.printf(`\n[deft update] dry-run -- classified plan (no changes written):\n`);
|
|
435
|
+
io.printf(` State : ${state}\n`);
|
|
436
|
+
io.printf(` Mode : ${resolutionPlan.mode}\n`);
|
|
437
|
+
io.printf(` Root cause : ${resolutionPlan.nextAction.rootCause}\n`);
|
|
438
|
+
io.printf(` Remediation : ${resolutionPlan.nextAction.remediation}\n`);
|
|
439
|
+
for (const warning of resolutionPlan.warnings) {
|
|
440
|
+
io.printf(` Warning : ${warning}\n`);
|
|
441
|
+
}
|
|
442
|
+
if (options.jsonOut) {
|
|
443
|
+
options.writeOut(`${JSON.stringify({
|
|
444
|
+
success: true,
|
|
445
|
+
action: "update",
|
|
446
|
+
dry_run: true,
|
|
447
|
+
update_state: state,
|
|
448
|
+
mode: resolutionPlan.mode,
|
|
449
|
+
project_dir: projectDir,
|
|
450
|
+
next_action: resolutionPlan.nextAction,
|
|
451
|
+
warnings: resolutionPlan.warnings,
|
|
452
|
+
}, null, 2)}\n`);
|
|
453
|
+
}
|
|
454
|
+
return 0;
|
|
455
|
+
}
|
|
312
456
|
/** CLI-facing wrapper: runs refresh, emits JSON or wizard UX, returns exit code. */
|
|
313
457
|
export async function runRefreshDepositCli(options) {
|
|
314
458
|
const io = {
|
|
@@ -321,14 +465,42 @@ export async function runRefreshDepositCli(options) {
|
|
|
321
465
|
}
|
|
322
466
|
},
|
|
323
467
|
};
|
|
468
|
+
// #2266: classify up front (BEFORE any copy) and gate on the four states.
|
|
469
|
+
// Legacy layouts keep their existing refusal path (thrown by runRefreshDeposit
|
|
470
|
+
// and handled below), so classification is skipped for them.
|
|
471
|
+
const projectDir = resolve(options.projectDir);
|
|
472
|
+
const detectLegacy = options.seams?.detectLegacy ?? detectLegacyLayout;
|
|
473
|
+
let classification = null;
|
|
474
|
+
if (!detectLegacy(projectDir).legacy) {
|
|
475
|
+
classification = classifyUpdateState(projectDir, options.classifySeams ?? {});
|
|
476
|
+
if (classification.state === "not-initialized") {
|
|
477
|
+
return emitNotInitialized(options, io, projectDir);
|
|
478
|
+
}
|
|
479
|
+
if (classification.state === "migration-required") {
|
|
480
|
+
return emitMigrationRequired(options, io, projectDir, classification);
|
|
481
|
+
}
|
|
482
|
+
if (options.dryRun) {
|
|
483
|
+
return emitDryRunPlan(options, io, projectDir, classification);
|
|
484
|
+
}
|
|
485
|
+
// #2266 a3: self-heal a mismatched / unreachable engine via the keystone
|
|
486
|
+
// global-first ladder before the refresh proceeds.
|
|
487
|
+
if (!classification.facts.engineReachable) {
|
|
488
|
+
selfHealEngine(projectDir, classification.facts, io, {
|
|
489
|
+
ladderFacts: options.ladderFacts,
|
|
490
|
+
engineInstallRunner: options.engineInstallRunner,
|
|
491
|
+
reproject: options.reproject,
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
324
495
|
try {
|
|
325
496
|
const result = await runRefreshDeposit(options, io, options.seams);
|
|
497
|
+
const state = classification?.state;
|
|
326
498
|
if (options.jsonOut) {
|
|
327
|
-
options.writeOut(`${JSON.stringify(buildUpdateSummaryJson(result, options), null, 2)}\n`);
|
|
328
|
-
printUpdateComplete(result, { printf: options.writeErr });
|
|
499
|
+
options.writeOut(`${JSON.stringify(buildUpdateSummaryJson(result, options, state), null, 2)}\n`);
|
|
500
|
+
printUpdateComplete(result, { printf: options.writeErr }, state);
|
|
329
501
|
}
|
|
330
502
|
else {
|
|
331
|
-
printUpdateComplete(result, io);
|
|
503
|
+
printUpdateComplete(result, io, state);
|
|
332
504
|
}
|
|
333
505
|
return 0;
|
|
334
506
|
}
|
|
@@ -27,6 +27,31 @@ export interface InstallManifestFields {
|
|
|
27
27
|
}
|
|
28
28
|
export declare function buildInstallManifestText(fields: InstallManifestFields): string;
|
|
29
29
|
export declare function writeInstallManifest(projectDir: string, deftDir: string, fields: InstallManifestFields): string;
|
|
30
|
+
/** Canonical committed pin dependency name (mirrors resolution/pin.ts). */
|
|
31
|
+
export declare const PIN_DEPENDENCY_NAME = "@deftai/directive";
|
|
32
|
+
export interface EnsurePackageJsonPinResult {
|
|
33
|
+
readonly changed: boolean;
|
|
34
|
+
/** Exact version written to the devDependency pin. */
|
|
35
|
+
readonly pinVersion: string;
|
|
36
|
+
/** A fresh package.json was created (vs. an existing one updated). */
|
|
37
|
+
readonly created: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Write the committed `package.json` version pin on `@deftai/directive`
|
|
41
|
+
* (exact devDependency; `"private": true` preserved) so the resolution spine
|
|
42
|
+
* has a canonical, npm-native pin to read before directive runs. This unblocks
|
|
43
|
+
* #2269 (which is gated on the pin existing).
|
|
44
|
+
*
|
|
45
|
+
* Behavior:
|
|
46
|
+
* - existing `package.json` -> the exact devDependency pin is set/updated;
|
|
47
|
+
* an existing `"private"` value is preserved verbatim (never clobbered).
|
|
48
|
+
* - absent `package.json` -> a minimal `{ "private": true, ... }` is created.
|
|
49
|
+
* - idempotent -> re-running with the same pin makes no change.
|
|
50
|
+
*
|
|
51
|
+
* This is a deposit primitive; wiring it into the `init` verb flow is owned by
|
|
52
|
+
* #2265 (the init-consumes-plan child), per the #2264 scope guard.
|
|
53
|
+
*/
|
|
54
|
+
export declare function ensurePackageJsonPin(projectDir: string, version: string, io: InitDepositIo): EnsurePackageJsonPinResult;
|
|
30
55
|
export declare function writeAgentsMd(projectDir: string, deftDir: string, io: InitDepositIo): boolean;
|
|
31
56
|
export declare function writeConsumerVbrief(projectDir: string, deftDir: string, io: InitDepositIo): Promise<boolean>;
|
|
32
57
|
export declare function writeAgentsSkills(projectDir: string, io: InitDepositIo): boolean;
|