@treeseed/cli 0.8.13 → 0.8.14
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/cli/handlers/dev.js
CHANGED
|
@@ -42,6 +42,7 @@ function resolveCoreDevEntrypoint(cwd) {
|
|
|
42
42
|
}
|
|
43
43
|
const handleDev = async (invocation, context) => {
|
|
44
44
|
try {
|
|
45
|
+
const managerMode = invocation.commandName === "dev:manager";
|
|
45
46
|
const feedback = typeof invocation.args.feedback === "string" ? invocation.args.feedback : void 0;
|
|
46
47
|
const watch = feedback !== "off";
|
|
47
48
|
const passthroughArgs = [];
|
|
@@ -56,8 +57,14 @@ const handleDev = async (invocation, context) => {
|
|
|
56
57
|
passthroughArgs.push(flag);
|
|
57
58
|
}
|
|
58
59
|
};
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
if (managerMode) {
|
|
61
|
+
const explicitSurfaces = typeof invocation.args.surfaces === "string" && invocation.args.surfaces.trim() ? invocation.args.surfaces.trim() : typeof invocation.args.surface === "string" && invocation.args.surface.trim() ? invocation.args.surface.trim() : null;
|
|
62
|
+
const surfaces = explicitSurfaces ?? (invocation.args.withWorker === true ? "manager,worker" : "manager");
|
|
63
|
+
passthroughArgs.push("--surfaces", surfaces);
|
|
64
|
+
} else {
|
|
65
|
+
forwardStringOption("surface", "--surface");
|
|
66
|
+
forwardStringOption("surfaces", "--surfaces");
|
|
67
|
+
}
|
|
61
68
|
forwardStringOption("host", "--host");
|
|
62
69
|
forwardStringOption("port", "--port");
|
|
63
70
|
forwardStringOption("apiHost", "--api-host");
|
|
@@ -69,6 +76,19 @@ const handleDev = async (invocation, context) => {
|
|
|
69
76
|
forwardBooleanOption("plan", "--plan");
|
|
70
77
|
forwardBooleanOption("reset", "--reset");
|
|
71
78
|
forwardBooleanOption("json", "--json");
|
|
79
|
+
const docsAutomationMode = typeof invocation.args.docsAutomation === "string" ? invocation.args.docsAutomation.trim() : "";
|
|
80
|
+
const workdayId = typeof invocation.args.workdayId === "string" ? invocation.args.workdayId.trim() : "";
|
|
81
|
+
const capacityBudget = typeof invocation.args.capacityBudget === "string" ? invocation.args.capacityBudget.trim() : "";
|
|
82
|
+
const approvalPolicy = typeof invocation.args.approvalPolicy === "string" ? invocation.args.approvalPolicy.trim() : "";
|
|
83
|
+
const devManagerEnv = managerMode ? {
|
|
84
|
+
TREESEED_DOCS_AUTOMATION_MODE: docsAutomationMode || "on",
|
|
85
|
+
...workdayId ? { TREESEED_WORKDAY_ID: workdayId } : {},
|
|
86
|
+
...capacityBudget ? {
|
|
87
|
+
TREESEED_CAPACITY_BUDGET: capacityBudget,
|
|
88
|
+
TREESEED_WORKDAY_TASK_CREDIT_BUDGET: capacityBudget
|
|
89
|
+
} : {},
|
|
90
|
+
TREESEED_APPROVAL_POLICY: approvalPolicy || "manual"
|
|
91
|
+
} : {};
|
|
72
92
|
const workspaceRoot = findNearestTreeseedWorkspaceRoot(context.cwd);
|
|
73
93
|
const workspaceLinksMode = typeof invocation.args.workspaceLinks === "string" ? invocation.args.workspaceLinks : void 0;
|
|
74
94
|
const workspaceLinks = workspaceRoot ? ensureLocalWorkspaceLinks(workspaceRoot, { env: context.env, mode: workspaceLinksMode }) : null;
|
|
@@ -82,7 +102,7 @@ const handleDev = async (invocation, context) => {
|
|
|
82
102
|
env: resolveTreeseedLaunchEnvironment({
|
|
83
103
|
tenantRoot: context.cwd,
|
|
84
104
|
scope: "local",
|
|
85
|
-
baseEnv: { ...process.env, ...context.env ?? {} }
|
|
105
|
+
baseEnv: { ...process.env, ...context.env ?? {}, ...devManagerEnv }
|
|
86
106
|
}),
|
|
87
107
|
stdio: "inherit"
|
|
88
108
|
});
|
|
@@ -91,10 +111,18 @@ const handleDev = async (invocation, context) => {
|
|
|
91
111
|
suppressJsonResult: invocation.args.json === true,
|
|
92
112
|
report: {
|
|
93
113
|
command: "dev",
|
|
114
|
+
alias: managerMode ? "dev:manager" : invocation.commandName,
|
|
94
115
|
ok: (result.status ?? 1) === 0,
|
|
95
116
|
watch,
|
|
96
117
|
executable: resolved.command,
|
|
97
118
|
args,
|
|
119
|
+
docsAutomation: managerMode ? {
|
|
120
|
+
mode: docsAutomationMode || "on",
|
|
121
|
+
workdayId: workdayId || null,
|
|
122
|
+
capacityBudget: capacityBudget || null,
|
|
123
|
+
approvalPolicy: approvalPolicy || "manual",
|
|
124
|
+
withWorker: invocation.args.withWorker === true
|
|
125
|
+
} : void 0,
|
|
98
126
|
workspaceLinks
|
|
99
127
|
}
|
|
100
128
|
};
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { formatSeedDiagnostics, formatSeedPlan, loadAndPlanSeed } from "@treeseed/sdk/seeds";
|
|
5
|
+
import { MarketApiError } from "@treeseed/sdk/market-client";
|
|
6
|
+
import { createMarketClientForInvocation } from "./market-utils.js";
|
|
7
|
+
async function loadLocalSeedModule(projectRoot) {
|
|
8
|
+
const moduleUrl = pathToFileURL(resolve(projectRoot, "src", "lib", "market", "seeds", "apply.js")).href;
|
|
9
|
+
return await import(moduleUrl);
|
|
10
|
+
}
|
|
11
|
+
function seedRequestBody(invocation) {
|
|
12
|
+
return {
|
|
13
|
+
...typeof invocation.args.environments === "string" ? { environments: invocation.args.environments.split(",").map((entry) => entry.trim()).filter(Boolean) } : {},
|
|
14
|
+
...typeof invocation.args.approvalRequest === "string" ? { approvalRequestId: invocation.args.approvalRequest } : {}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function planFromRemotePayload(payload) {
|
|
18
|
+
return {
|
|
19
|
+
ok: payload.ok !== false,
|
|
20
|
+
seed: String(payload.seed ?? ""),
|
|
21
|
+
version: 1,
|
|
22
|
+
mode: payload.mode === "apply" ? "apply" : "plan",
|
|
23
|
+
environments: Array.isArray(payload.environments) ? payload.environments : [],
|
|
24
|
+
summary: payload.summary,
|
|
25
|
+
actions: Array.isArray(payload.actions) ? payload.actions : [],
|
|
26
|
+
diagnostics: Array.isArray(payload.diagnostics) ? payload.diagnostics : [],
|
|
27
|
+
manifestPath: ""
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function remoteSeedResult(payload, command, exitCode = 0) {
|
|
31
|
+
const plan = planFromRemotePayload(payload);
|
|
32
|
+
const result = payload.result && typeof payload.result === "object" ? payload.result : null;
|
|
33
|
+
return {
|
|
34
|
+
exitCode,
|
|
35
|
+
stdout: exitCode === 0 ? [
|
|
36
|
+
...formatSeedPlan(plan),
|
|
37
|
+
...result ? [
|
|
38
|
+
"",
|
|
39
|
+
"Apply:",
|
|
40
|
+
` created: ${plan.summary.create}`,
|
|
41
|
+
` updated: ${plan.summary.update}`,
|
|
42
|
+
` unchanged: ${plan.summary.unchanged}`,
|
|
43
|
+
` skipped: ${plan.summary.skip}`,
|
|
44
|
+
` failed: ${plan.summary.error}`,
|
|
45
|
+
...formatCapacityProviderKeyNotes(result)
|
|
46
|
+
] : []
|
|
47
|
+
] : [],
|
|
48
|
+
stderr: exitCode === 0 ? [] : [typeof payload.error === "string" ? payload.error : "Seed operation failed."],
|
|
49
|
+
report: {
|
|
50
|
+
...payload,
|
|
51
|
+
command,
|
|
52
|
+
ok: exitCode === 0
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function formatCapacityProviderKeyNotes(result) {
|
|
57
|
+
const capacityProviderKeys = result.capacityProviderKeys && typeof result.capacityProviderKeys === "object" ? result.capacityProviderKeys : null;
|
|
58
|
+
const created = Array.isArray(capacityProviderKeys?.created) ? capacityProviderKeys.created : [];
|
|
59
|
+
if (created.length === 0) return [];
|
|
60
|
+
return [
|
|
61
|
+
"",
|
|
62
|
+
"Provider security codes:",
|
|
63
|
+
...created.map((entry) => ` ${String(entry.providerName ?? entry.providerKey ?? entry.providerId)} (${String(entry.keyPrefix ?? "new key")}): ${String(entry.plaintextKey ?? "created")}`),
|
|
64
|
+
" Copy these now. TreeSeed will not show the plaintext codes again."
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
function remoteSeedError(error, command) {
|
|
68
|
+
if (error instanceof MarketApiError) {
|
|
69
|
+
const payload = error.payload && typeof error.payload === "object" ? error.payload : { error: error.message };
|
|
70
|
+
const blocked = error.status === 409 || payload.result?.blocked === true;
|
|
71
|
+
const auth = error.status === 401 || error.status === 403;
|
|
72
|
+
return remoteSeedResult(payload, command, blocked ? 2 : auth ? 4 : 3);
|
|
73
|
+
}
|
|
74
|
+
if (error instanceof Error && /not logged in|authentication|permission denied/iu.test(error.message)) {
|
|
75
|
+
return {
|
|
76
|
+
exitCode: 4,
|
|
77
|
+
stderr: [error.message],
|
|
78
|
+
report: {
|
|
79
|
+
command,
|
|
80
|
+
ok: false,
|
|
81
|
+
error: error.message
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
function writeSeedExportOutput(context, outputPath, yaml) {
|
|
88
|
+
const destination = resolve(context.cwd, outputPath);
|
|
89
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
90
|
+
writeFileSync(destination, yaml, "utf8");
|
|
91
|
+
return destination;
|
|
92
|
+
}
|
|
93
|
+
async function handleSeedExport(invocation, context) {
|
|
94
|
+
const seedName = invocation.positionals[1];
|
|
95
|
+
const team = typeof invocation.args.team === "string" ? invocation.args.team.trim() : "";
|
|
96
|
+
if (!seedName || !team) {
|
|
97
|
+
return {
|
|
98
|
+
exitCode: 1,
|
|
99
|
+
stderr: ["Usage: treeseed seed export <name> --team <team> [--output seeds/exported.yaml]"],
|
|
100
|
+
report: {
|
|
101
|
+
command: "seed export",
|
|
102
|
+
ok: false,
|
|
103
|
+
error: !seedName ? "Missing required export seed name." : "Missing required --team."
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const body = {
|
|
108
|
+
name: seedName,
|
|
109
|
+
...typeof invocation.args.environments === "string" ? { environments: invocation.args.environments.split(",").map((entry) => entry.trim()).filter(Boolean) } : {},
|
|
110
|
+
...invocation.args.includePrivate === true ? { includePrivate: true } : {},
|
|
111
|
+
...invocation.args.includeArtifacts === true ? { includeArtifacts: true } : {}
|
|
112
|
+
};
|
|
113
|
+
let payload;
|
|
114
|
+
try {
|
|
115
|
+
if (typeof invocation.args.market === "string" || typeof invocation.args.host === "string") {
|
|
116
|
+
const { client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
|
|
117
|
+
payload = await client.exportSeed(team, body);
|
|
118
|
+
} else {
|
|
119
|
+
const localModule = await loadLocalSeedModule(context.cwd);
|
|
120
|
+
if (typeof localModule.exportSeedFromCli !== "function") {
|
|
121
|
+
throw new Error("Local seed export service is not available in this market project.");
|
|
122
|
+
}
|
|
123
|
+
payload = await localModule.exportSeedFromCli({
|
|
124
|
+
projectRoot: context.cwd,
|
|
125
|
+
seedName,
|
|
126
|
+
team,
|
|
127
|
+
environments: typeof invocation.args.environments === "string" ? invocation.args.environments : void 0,
|
|
128
|
+
includePrivate: invocation.args.includePrivate === true,
|
|
129
|
+
includeArtifacts: invocation.args.includeArtifacts === true,
|
|
130
|
+
env: context.env
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return remoteSeedError(error, "seed export");
|
|
135
|
+
}
|
|
136
|
+
const yaml = typeof payload.yaml === "string" ? payload.yaml : "";
|
|
137
|
+
const outputPath = typeof invocation.args.output === "string" && invocation.args.output.trim() ? invocation.args.output.trim() : null;
|
|
138
|
+
const writtenPath = outputPath ? writeSeedExportOutput(context, outputPath, yaml) : null;
|
|
139
|
+
const ok = payload.ok !== false;
|
|
140
|
+
return {
|
|
141
|
+
exitCode: ok ? 0 : 1,
|
|
142
|
+
stdout: context.outputFormat === "json" ? [] : writtenPath ? [`Exported seed ${String(payload.seed ?? seedName)} to ${writtenPath}.`] : yaml.trimEnd().split("\n"),
|
|
143
|
+
stderr: ok ? [] : ["Seed export failed."],
|
|
144
|
+
report: {
|
|
145
|
+
...payload,
|
|
146
|
+
command: "seed export",
|
|
147
|
+
outputPath: writtenPath
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const handleSeed = async (invocation, context) => {
|
|
152
|
+
if (invocation.positionals[0] === "export") {
|
|
153
|
+
return handleSeedExport(invocation, context);
|
|
154
|
+
}
|
|
155
|
+
const seedName = invocation.positionals[0];
|
|
156
|
+
if (!seedName) {
|
|
157
|
+
return {
|
|
158
|
+
exitCode: 1,
|
|
159
|
+
stderr: ["Usage: treeseed seed <name> [--environments local,staging,prod] [--plan|--validate]"],
|
|
160
|
+
report: {
|
|
161
|
+
command: "seed",
|
|
162
|
+
ok: false,
|
|
163
|
+
error: "Missing required seed name."
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const wantsApply = invocation.args.apply === true;
|
|
168
|
+
const wantsValidate = invocation.args.validate === true;
|
|
169
|
+
const mode = wantsApply ? "apply" : wantsValidate ? "validate" : "plan";
|
|
170
|
+
const planned = loadAndPlanSeed({
|
|
171
|
+
projectRoot: context.cwd,
|
|
172
|
+
seedName,
|
|
173
|
+
environments: typeof invocation.args.environments === "string" ? invocation.args.environments : void 0,
|
|
174
|
+
mode
|
|
175
|
+
});
|
|
176
|
+
if (!planned.plan) {
|
|
177
|
+
return {
|
|
178
|
+
exitCode: 1,
|
|
179
|
+
stderr: formatSeedDiagnostics(planned.diagnostics),
|
|
180
|
+
report: {
|
|
181
|
+
command: "seed",
|
|
182
|
+
ok: false,
|
|
183
|
+
seed: seedName,
|
|
184
|
+
mode,
|
|
185
|
+
manifestPath: planned.manifestPath,
|
|
186
|
+
diagnostics: planned.diagnostics
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (!wantsApply && !wantsValidate && planned.plan.environments.length === 1 && planned.plan.environments[0] === "local") {
|
|
191
|
+
try {
|
|
192
|
+
const localModule = await loadLocalSeedModule(context.cwd);
|
|
193
|
+
if (typeof localModule.planLocalSeedFromCli === "function") {
|
|
194
|
+
const localPlanned = await localModule.planLocalSeedFromCli({
|
|
195
|
+
projectRoot: context.cwd,
|
|
196
|
+
seedName,
|
|
197
|
+
environments: typeof invocation.args.environments === "string" ? invocation.args.environments : void 0,
|
|
198
|
+
mode,
|
|
199
|
+
env: context.env
|
|
200
|
+
});
|
|
201
|
+
if (localPlanned.plan) {
|
|
202
|
+
planned.plan = localPlanned.plan;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
} catch {
|
|
206
|
+
planned.plan.diagnostics.push({
|
|
207
|
+
severity: "warning",
|
|
208
|
+
code: "seed.local_state_unavailable",
|
|
209
|
+
message: "Local current state could not be loaded; falling back to manifest-only planning.",
|
|
210
|
+
path: "local"
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (wantsApply) {
|
|
215
|
+
if (planned.plan.environments.some((environment) => environment !== "local")) {
|
|
216
|
+
try {
|
|
217
|
+
const { client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
|
|
218
|
+
const payload = await client.applySeed(seedName, seedRequestBody(invocation));
|
|
219
|
+
return remoteSeedResult(payload, "seed");
|
|
220
|
+
} catch (error) {
|
|
221
|
+
return remoteSeedError(error, "seed");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const localModule = await loadLocalSeedModule(context.cwd);
|
|
225
|
+
if (typeof localModule.applyLocalSeedFromCli !== "function") {
|
|
226
|
+
throw new Error("Local seed apply service is not available in this market project.");
|
|
227
|
+
}
|
|
228
|
+
const runner = localModule.applyLocalSeedFromCli;
|
|
229
|
+
const applied = await runner({
|
|
230
|
+
projectRoot: context.cwd,
|
|
231
|
+
seedName,
|
|
232
|
+
environments: typeof invocation.args.environments === "string" ? invocation.args.environments : void 0,
|
|
233
|
+
plan: planned.plan,
|
|
234
|
+
env: context.env
|
|
235
|
+
});
|
|
236
|
+
const message = "Local seed apply completed.";
|
|
237
|
+
return {
|
|
238
|
+
exitCode: 0,
|
|
239
|
+
stdout: context.outputFormat === "json" ? [] : [
|
|
240
|
+
...formatSeedPlan(applied.plan),
|
|
241
|
+
"",
|
|
242
|
+
"Apply:",
|
|
243
|
+
` created: ${applied.plan.summary.create}`,
|
|
244
|
+
` updated: ${applied.plan.summary.update}`,
|
|
245
|
+
` unchanged: ${applied.plan.summary.unchanged}`,
|
|
246
|
+
` skipped: ${applied.plan.summary.skip}`,
|
|
247
|
+
` failed: ${applied.plan.summary.error}`,
|
|
248
|
+
...formatCapacityProviderKeyNotes(applied.result)
|
|
249
|
+
],
|
|
250
|
+
report: {
|
|
251
|
+
...applied.plan,
|
|
252
|
+
ok: true,
|
|
253
|
+
command: "seed",
|
|
254
|
+
result: {
|
|
255
|
+
message,
|
|
256
|
+
...applied.result
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
if (wantsValidate) {
|
|
262
|
+
return {
|
|
263
|
+
exitCode: 0,
|
|
264
|
+
stdout: [`Seed ${planned.plan.seed} is valid for environments: ${planned.plan.environments.join(", ")}.`],
|
|
265
|
+
report: {
|
|
266
|
+
...planned.plan,
|
|
267
|
+
command: "seed"
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
if (planned.plan.environments.some((environment) => environment !== "local")) {
|
|
272
|
+
try {
|
|
273
|
+
const { client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
|
|
274
|
+
const payload = await client.planSeed(seedName, seedRequestBody(invocation));
|
|
275
|
+
return remoteSeedResult(payload, "seed");
|
|
276
|
+
} catch (error) {
|
|
277
|
+
return remoteSeedError(error, "seed");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
exitCode: 0,
|
|
282
|
+
stdout: formatSeedPlan(planned.plan),
|
|
283
|
+
report: {
|
|
284
|
+
...planned.plan,
|
|
285
|
+
command: "seed"
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
};
|
|
289
|
+
export {
|
|
290
|
+
handleSeed
|
|
291
|
+
};
|
|
@@ -37,6 +37,14 @@ const DEV_RUNTIME_OPTIONS = [
|
|
|
37
37
|
{ name: "watch", flags: "--watch", description: "Enable live watch behavior. `dev` defaults to live feedback; this remains for compatibility.", kind: "boolean" },
|
|
38
38
|
{ name: "workspaceLinks", flags: "--workspace-links <mode>", description: "Control local workspace package links.", kind: "enum", values: ["auto", "off"] }
|
|
39
39
|
];
|
|
40
|
+
const DEV_MANAGER_OPTIONS = [
|
|
41
|
+
...DEV_RUNTIME_OPTIONS,
|
|
42
|
+
{ name: "docsAutomation", flags: "--docs-automation <mode>", description: "Control governed documentation automation for the local manager.", kind: "enum", values: ["on", "dry-run", "off"] },
|
|
43
|
+
{ name: "withWorker", flags: "--with-worker", description: "Run the local worker alongside the manager in the same dev supervisor.", kind: "boolean" },
|
|
44
|
+
{ name: "workdayId", flags: "--workday-id <id>", description: "Start or resume a specific local documentation automation workday.", kind: "string" },
|
|
45
|
+
{ name: "capacityBudget", flags: "--capacity-budget <credits>", description: "Override the local workday task credit budget.", kind: "string" },
|
|
46
|
+
{ name: "approvalPolicy", flags: "--approval-policy <policy>", description: "Set the local docs automation approval policy.", kind: "enum", values: ["manual", "low-risk-auto"] }
|
|
47
|
+
];
|
|
40
48
|
function genericWorkflowPosition(spec) {
|
|
41
49
|
if (spec.group === "Workflow") {
|
|
42
50
|
if (spec.name === "switch") return "start work";
|
|
@@ -1196,6 +1204,27 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
1196
1204
|
executionMode: "handler",
|
|
1197
1205
|
handlerName: "dev"
|
|
1198
1206
|
})],
|
|
1207
|
+
["dev:manager", command({
|
|
1208
|
+
options: DEV_MANAGER_OPTIONS,
|
|
1209
|
+
examples: ["treeseed dev:manager", "treeseed dev:manager --with-worker", "treeseed dev:manager --docs-automation dry-run --plan --json"],
|
|
1210
|
+
help: {
|
|
1211
|
+
longSummary: [
|
|
1212
|
+
"Dev:manager starts the governed local documentation automation manager through the same integrated dev supervisor used by `treeseed dev`.",
|
|
1213
|
+
"It selects the manager surface by default, can supervise a local worker with `--with-worker`, and passes docs automation policy into the existing manager and worker runtime."
|
|
1214
|
+
],
|
|
1215
|
+
examples: [
|
|
1216
|
+
example("treeseed dev:manager", "Start the local manager", "Run the governed workday manager in the foreground."),
|
|
1217
|
+
example("treeseed dev:manager --with-worker", "Start manager plus worker", "Run scheduling and task execution in the same local supervision session."),
|
|
1218
|
+
example("treeseed dev:manager --docs-automation dry-run --plan --json", "Inspect dry-run manager mode", "Show the manager plan and docs automation settings without starting the supervisor.")
|
|
1219
|
+
],
|
|
1220
|
+
outcomes: [
|
|
1221
|
+
"Starts or plans the existing manager surface, with optional worker supervision.",
|
|
1222
|
+
"Keeps manual approval as the local default and forwards docs automation mode, workday id, capacity budget, and approval policy through runtime environment variables."
|
|
1223
|
+
]
|
|
1224
|
+
},
|
|
1225
|
+
executionMode: "handler",
|
|
1226
|
+
handlerName: "dev"
|
|
1227
|
+
})],
|
|
1199
1228
|
["dev:watch", command({
|
|
1200
1229
|
options: DEV_RUNTIME_OPTIONS,
|
|
1201
1230
|
examples: ["treeseed dev:watch", "treeseed dev:watch --json"],
|
|
@@ -1265,6 +1294,75 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
|
|
|
1265
1294
|
["starlight:patch", command({ examples: ["treeseed starlight:patch"], executionMode: "adapter" })]
|
|
1266
1295
|
]);
|
|
1267
1296
|
const CLI_ONLY_OPERATION_SPECS = [
|
|
1297
|
+
{
|
|
1298
|
+
id: "seed.plan",
|
|
1299
|
+
name: "seed",
|
|
1300
|
+
aliases: [],
|
|
1301
|
+
group: "Validation",
|
|
1302
|
+
summary: "Validate and plan declarative Treeseed environment seeds.",
|
|
1303
|
+
description: "Load a seed manifest from seeds/<name>.yaml, validate references and environment targeting, produce a deterministic plan, apply governed seeds through the market store/API, or export a team portfolio to YAML.",
|
|
1304
|
+
provider: "default",
|
|
1305
|
+
related: ["status", "config", "capacity", "projects"],
|
|
1306
|
+
usage: "treeseed seed <name> [--environments local,staging,prod] [--plan|--validate|--apply] [--json]\n treeseed seed export <name> --team <team> [--output <path>] [--json]",
|
|
1307
|
+
arguments: [{ name: "name", description: "Seed manifest name under the project seeds directory, or `export <name>` for portfolio export.", required: true }],
|
|
1308
|
+
options: [
|
|
1309
|
+
{ name: "environments", flags: "--environments <list>", description: "Comma-separated environments to select from the manifest.", kind: "string" },
|
|
1310
|
+
{ name: "plan", flags: "--plan", description: "Generate a deterministic plan without applying it. This is the Phase 1 default.", kind: "boolean" },
|
|
1311
|
+
{ name: "validate", flags: "--validate", description: "Validate the manifest and selected environments without printing plan actions.", kind: "boolean" },
|
|
1312
|
+
{ name: "apply", flags: "--apply", description: "Apply local seeds directly or governed staging/production seeds through the market API.", kind: "boolean" },
|
|
1313
|
+
{ name: "market", flags: "--market <market>", description: "Market profile or URL for staging and production seed operations.", kind: "string" },
|
|
1314
|
+
{ name: "host", flags: "--host <host>", description: "Compatibility alias for --market.", kind: "string" },
|
|
1315
|
+
{ name: "approvalRequest", flags: "--approval-request <id>", description: "Approved production seed apply request id.", kind: "string" },
|
|
1316
|
+
{ name: "team", flags: "--team <team>", description: "Team slug, name, or id for seed export.", kind: "string" },
|
|
1317
|
+
{ name: "output", flags: "--output <path>", description: "Write exported seed YAML to this path.", kind: "string" },
|
|
1318
|
+
{ name: "includePrivate", flags: "--include-private", description: "Include private catalog products in seed export when authorized.", kind: "boolean" },
|
|
1319
|
+
{ name: "includeArtifacts", flags: "--include-artifacts", description: "Include catalog artifact version references in seed export.", kind: "boolean" },
|
|
1320
|
+
{ name: "yes", flags: "--yes", description: "Future-compatible non-interactive confirmation flag for local seed apply.", kind: "boolean" },
|
|
1321
|
+
{ name: "strict", flags: "--strict", description: "Reserved for stricter future diagnostics; Phase 1 validation is already strict.", kind: "boolean" },
|
|
1322
|
+
{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
|
|
1323
|
+
],
|
|
1324
|
+
examples: [
|
|
1325
|
+
"treeseed seed treeseed --validate",
|
|
1326
|
+
"treeseed seed treeseed --environments local --plan",
|
|
1327
|
+
"trsd seed treeseed --environments prod --plan --json",
|
|
1328
|
+
"trsd seed export treeseed --team treeseed --include-artifacts --json"
|
|
1329
|
+
],
|
|
1330
|
+
help: {
|
|
1331
|
+
workflowPosition: "validate",
|
|
1332
|
+
longSummary: [
|
|
1333
|
+
"Seed validates a declarative market portfolio manifest and produces a deterministic reconciliation plan.",
|
|
1334
|
+
"Phase 5 also exports an existing team portfolio into a reusable YAML seed bundle without embedding secrets or artifact bytes."
|
|
1335
|
+
],
|
|
1336
|
+
whenToUse: [
|
|
1337
|
+
"Use this when a TreeSeed workspace needs a repeatable description of teams, projects, repositories, capacity providers, grants, and work policies.",
|
|
1338
|
+
"Use `--json` when an agent or CI check needs the same plan in a stable machine-readable shape."
|
|
1339
|
+
],
|
|
1340
|
+
beforeYouRun: [
|
|
1341
|
+
"Run from a Treeseed project containing the requested `seeds/<name>.yaml` manifest.",
|
|
1342
|
+
"Choose the target environments explicitly when reviewing staging or production resources."
|
|
1343
|
+
],
|
|
1344
|
+
outcomes: [
|
|
1345
|
+
"Prints validation diagnostics, a deterministic plan, an apply summary, or an exported seed manifest.",
|
|
1346
|
+
"Mutates the local market store for local applies, or the selected authenticated market for staging applies and approved production applies."
|
|
1347
|
+
],
|
|
1348
|
+
automationNotes: [
|
|
1349
|
+
"Agents may run validation and planning safely. Production apply requires an approved seed approval request.",
|
|
1350
|
+
"Skipped resources are omitted from human plan output but included in JSON actions for review."
|
|
1351
|
+
],
|
|
1352
|
+
warnings: [
|
|
1353
|
+
"Do not put raw secrets in seed manifests; validation rejects secret-looking fields and values.",
|
|
1354
|
+
"`--apply --environments prod` is blocked until a matching approval request is approved."
|
|
1355
|
+
],
|
|
1356
|
+
relatedDetails: [
|
|
1357
|
+
related("projects", "Use `projects` after Phase 2 apply work lands to inspect created projects through the market API."),
|
|
1358
|
+
related("capacity", "Use `capacity` to inspect existing provider and grant state outside the seed planner.")
|
|
1359
|
+
]
|
|
1360
|
+
},
|
|
1361
|
+
helpVisible: true,
|
|
1362
|
+
helpFeatured: true,
|
|
1363
|
+
executionMode: "handler",
|
|
1364
|
+
handlerName: "seed"
|
|
1365
|
+
},
|
|
1268
1366
|
{
|
|
1269
1367
|
id: "audit.hosting",
|
|
1270
1368
|
name: "audit",
|
package/dist/cli/registry.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare const COMMAND_HANDLERS: {
|
|
|
10
10
|
readonly status: import("./operations-types.js").TreeseedCommandHandler;
|
|
11
11
|
readonly ci: import("./operations-types.js").TreeseedCommandHandler;
|
|
12
12
|
readonly dev: import("./operations-types.js").TreeseedCommandHandler;
|
|
13
|
+
readonly 'dev:manager': import("./operations-types.js").TreeseedCommandHandler;
|
|
13
14
|
readonly 'dev:watch': import("./operations-types.js").TreeseedCommandHandler;
|
|
14
15
|
readonly doctor: import("./operations-types.js").TreeseedCommandHandler;
|
|
15
16
|
readonly rollback: import("./operations-types.js").TreeseedCommandHandler;
|
|
@@ -40,6 +41,7 @@ export declare const COMMAND_HANDLERS: {
|
|
|
40
41
|
readonly 'secrets:rotate-passphrase': import("./operations-types.js").TreeseedCommandHandler;
|
|
41
42
|
readonly 'secrets:rotate-machine-key': import("./operations-types.js").TreeseedCommandHandler;
|
|
42
43
|
readonly audit: import("./operations-types.js").TreeseedCommandHandler;
|
|
44
|
+
readonly seed: import("./operations-types.js").TreeseedCommandHandler;
|
|
43
45
|
};
|
|
44
46
|
export declare const TRESEED_COMMAND_SPECS: TreeseedCommandSpec[];
|
|
45
47
|
export declare function findCommandSpec(name: string | null | undefined): import("./operations-types.js").TreeseedOperationSpec | null;
|
package/dist/cli/registry.js
CHANGED
|
@@ -42,6 +42,7 @@ import { handleResume } from "./handlers/resume.js";
|
|
|
42
42
|
import { handleRecover } from "./handlers/recover.js";
|
|
43
43
|
import { handleWorkspace } from "./handlers/workspace.js";
|
|
44
44
|
import { handleAudit } from "./handlers/audit.js";
|
|
45
|
+
import { handleSeed } from "./handlers/seed.js";
|
|
45
46
|
const workspaceCommand = (name) => `workspace${":"}${name}`;
|
|
46
47
|
const COMMAND_HANDLERS = {
|
|
47
48
|
init: handleInit,
|
|
@@ -53,6 +54,7 @@ const COMMAND_HANDLERS = {
|
|
|
53
54
|
status: handleStatus,
|
|
54
55
|
ci: handleCi,
|
|
55
56
|
dev: handleDev,
|
|
57
|
+
"dev:manager": handleDev,
|
|
56
58
|
"dev:watch": handleDev,
|
|
57
59
|
doctor: handleDoctor,
|
|
58
60
|
rollback: handleRollback,
|
|
@@ -85,7 +87,8 @@ const COMMAND_HANDLERS = {
|
|
|
85
87
|
"secrets:migrate-key": handleSecretsMigrateKey,
|
|
86
88
|
"secrets:rotate-passphrase": handleSecretsRotatePassphrase,
|
|
87
89
|
"secrets:rotate-machine-key": handleSecretsRotateMachineKey,
|
|
88
|
-
audit: handleAudit
|
|
90
|
+
audit: handleAudit,
|
|
91
|
+
seed: handleSeed
|
|
89
92
|
};
|
|
90
93
|
const TRESEED_COMMAND_SPECS = TRESEED_OPERATION_SPECS;
|
|
91
94
|
function findCommandSpec(name) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treeseed/cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.14",
|
|
4
4
|
"description": "Operator-facing Treeseed CLI package.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"setup:ci": "npm ci",
|
|
32
32
|
"build": "npm run build:dist",
|
|
33
33
|
"lint": "npm run build:dist",
|
|
34
|
-
"test": "npm run build:dist && node --test --test-concurrency=1 ./scripts/treeseed-help.test.mjs ./scripts/wrapper-package.test.mjs",
|
|
34
|
+
"test": "npm run build:dist && node --test --test-concurrency=1 ./scripts/treeseed-help.test.mjs ./scripts/seed.test.mjs ./scripts/wrapper-package.test.mjs",
|
|
35
35
|
"build:dist": "node ./scripts/run-ts.mjs ./scripts/build-dist.ts",
|
|
36
36
|
"prepare": "node ./scripts/prepare.mjs",
|
|
37
37
|
"prepack": "npm run build:dist",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"release:publish": "node ./scripts/run-ts.mjs ./scripts/publish-package.ts"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@treeseed/sdk": "0.8.
|
|
48
|
+
"@treeseed/sdk": "0.8.14",
|
|
49
49
|
"ink": "^7.0.0",
|
|
50
50
|
"react": "^19.2.5"
|
|
51
51
|
},
|