@treeseed/cli 0.1.1 → 0.4.2
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/README.md +27 -26
- package/dist/cli/handlers/auth-login.d.ts +2 -0
- package/dist/cli/handlers/auth-login.js +67 -0
- package/dist/cli/handlers/auth-logout.d.ts +2 -0
- package/dist/cli/handlers/auth-logout.js +20 -0
- package/dist/cli/handlers/auth-whoami.d.ts +2 -0
- package/dist/cli/handlers/auth-whoami.js +24 -0
- package/dist/cli/handlers/close.js +19 -53
- package/dist/cli/handlers/config.js +33 -53
- package/dist/cli/handlers/destroy.js +34 -79
- package/dist/{src/cli/handlers/ship.d.ts → cli/handlers/dev.d.ts} +1 -1
- package/dist/cli/handlers/dev.js +19 -0
- package/dist/cli/handlers/doctor.js +13 -6
- package/dist/cli/handlers/init.js +32 -8
- package/dist/cli/handlers/release.js +21 -53
- package/dist/cli/handlers/rollback.js +8 -8
- package/dist/cli/handlers/save.js +21 -79
- package/dist/cli/handlers/stage.d.ts +2 -0
- package/dist/cli/handlers/stage.js +28 -0
- package/dist/cli/handlers/status.js +35 -26
- package/dist/{src/cli/handlers/deploy.d.ts → cli/handlers/switch.d.ts} +1 -1
- package/dist/cli/handlers/switch.js +29 -0
- package/dist/{src/cli/handlers/next.d.ts → cli/handlers/sync.d.ts} +1 -1
- package/dist/cli/handlers/sync.js +26 -0
- package/dist/cli/handlers/tasks.d.ts +2 -0
- package/dist/cli/handlers/tasks.js +31 -0
- package/dist/cli/handlers/template.d.ts +2 -0
- package/dist/cli/handlers/template.js +27 -0
- package/dist/cli/handlers/workflow.d.ts +6 -0
- package/dist/cli/handlers/workflow.js +71 -0
- package/dist/{src/cli → cli}/help.d.ts +2 -2
- package/dist/cli/help.js +36 -24
- package/dist/cli/main.d.ts +6 -0
- package/dist/cli/main.js +14 -19
- package/dist/cli/operations-help.d.ts +1 -0
- package/dist/cli/operations-help.js +1 -0
- package/dist/cli/operations-parser.d.ts +1 -0
- package/dist/cli/operations-parser.js +1 -0
- package/dist/cli/operations-registry.d.ts +5 -0
- package/dist/cli/operations-registry.js +260 -0
- package/dist/cli/operations-types.d.ts +72 -0
- package/dist/cli/parser.d.ts +3 -0
- package/dist/cli/parser.js +1 -6
- package/dist/cli/registry.d.ts +25 -0
- package/dist/cli/registry.js +28 -416
- package/dist/cli/repair.js +6 -4
- package/dist/cli/runtime.d.ts +31 -0
- package/dist/cli/runtime.js +240 -111
- package/dist/cli/types.d.ts +1 -0
- package/dist/{src/cli → cli}/workflow-state.d.ts +9 -0
- package/dist/cli/workflow-state.js +45 -21
- package/package.json +13 -13
- package/dist/cli/handlers/continue.js +0 -23
- package/dist/cli/handlers/deploy.js +0 -139
- package/dist/cli/handlers/next.js +0 -27
- package/dist/cli/handlers/prepare.js +0 -8
- package/dist/cli/handlers/promote.js +0 -8
- package/dist/cli/handlers/publish.js +0 -8
- package/dist/cli/handlers/setup.js +0 -48
- package/dist/cli/handlers/ship.js +0 -49
- package/dist/cli/handlers/start.js +0 -97
- package/dist/cli/handlers/teardown.js +0 -50
- package/dist/cli/handlers/work.js +0 -85
- package/dist/scripts/aggregate-book.d.ts +0 -1
- package/dist/scripts/aggregate-book.js +0 -121
- package/dist/scripts/assert-release-tag-version.d.ts +0 -1
- package/dist/scripts/assert-release-tag-version.js +0 -21
- package/dist/scripts/build-dist.d.ts +0 -1
- package/dist/scripts/build-dist.js +0 -108
- package/dist/scripts/build-tenant-worker.d.ts +0 -1
- package/dist/scripts/build-tenant-worker.js +0 -36
- package/dist/scripts/cleanup-markdown.d.ts +0 -2
- package/dist/scripts/cleanup-markdown.js +0 -373
- package/dist/scripts/config-runtime-lib.d.ts +0 -122
- package/dist/scripts/config-runtime-lib.js +0 -505
- package/dist/scripts/config-treeseed.d.ts +0 -2
- package/dist/scripts/config-treeseed.js +0 -81
- package/dist/scripts/d1-migration-lib.d.ts +0 -6
- package/dist/scripts/d1-migration-lib.js +0 -90
- package/dist/scripts/deploy-lib.d.ts +0 -127
- package/dist/scripts/deploy-lib.js +0 -841
- package/dist/scripts/ensure-mailpit.d.ts +0 -1
- package/dist/scripts/ensure-mailpit.js +0 -29
- package/dist/scripts/git-workflow-lib.d.ts +0 -25
- package/dist/scripts/git-workflow-lib.js +0 -136
- package/dist/scripts/github-automation-lib.d.ts +0 -156
- package/dist/scripts/github-automation-lib.js +0 -242
- package/dist/scripts/local-dev-lib.d.ts +0 -9
- package/dist/scripts/local-dev-lib.js +0 -84
- package/dist/scripts/local-dev.d.ts +0 -1
- package/dist/scripts/local-dev.js +0 -129
- package/dist/scripts/logs-mailpit.d.ts +0 -1
- package/dist/scripts/logs-mailpit.js +0 -2
- package/dist/scripts/mailpit-runtime.d.ts +0 -4
- package/dist/scripts/mailpit-runtime.js +0 -57
- package/dist/scripts/package-tools.d.ts +0 -22
- package/dist/scripts/package-tools.js +0 -255
- package/dist/scripts/patch-starlight-content-path.d.ts +0 -1
- package/dist/scripts/patch-starlight-content-path.js +0 -172
- package/dist/scripts/paths.d.ts +0 -17
- package/dist/scripts/paths.js +0 -26
- package/dist/scripts/publish-package.d.ts +0 -1
- package/dist/scripts/publish-package.js +0 -19
- package/dist/scripts/release-verify.d.ts +0 -1
- package/dist/scripts/release-verify.js +0 -136
- package/dist/scripts/run-fixture-astro-command.d.ts +0 -1
- package/dist/scripts/run-fixture-astro-command.js +0 -18
- package/dist/scripts/save-deploy-preflight-lib.d.ts +0 -34
- package/dist/scripts/save-deploy-preflight-lib.js +0 -69
- package/dist/scripts/scaffold-site.d.ts +0 -2
- package/dist/scripts/scaffold-site.js +0 -92
- package/dist/scripts/stop-mailpit.d.ts +0 -1
- package/dist/scripts/stop-mailpit.js +0 -5
- package/dist/scripts/sync-dev-vars.d.ts +0 -1
- package/dist/scripts/sync-dev-vars.js +0 -6
- package/dist/scripts/template-registry-lib.d.ts +0 -47
- package/dist/scripts/template-registry-lib.js +0 -137
- package/dist/scripts/tenant-astro-command.d.ts +0 -1
- package/dist/scripts/tenant-astro-command.js +0 -3
- package/dist/scripts/tenant-build.d.ts +0 -1
- package/dist/scripts/tenant-build.js +0 -16
- package/dist/scripts/tenant-check.d.ts +0 -1
- package/dist/scripts/tenant-check.js +0 -7
- package/dist/scripts/tenant-d1-migrate-local.d.ts +0 -1
- package/dist/scripts/tenant-d1-migrate-local.js +0 -11
- package/dist/scripts/tenant-deploy.d.ts +0 -2
- package/dist/scripts/tenant-deploy.js +0 -180
- package/dist/scripts/tenant-destroy.d.ts +0 -2
- package/dist/scripts/tenant-destroy.js +0 -104
- package/dist/scripts/tenant-dev.d.ts +0 -1
- package/dist/scripts/tenant-dev.js +0 -171
- package/dist/scripts/tenant-lint.d.ts +0 -1
- package/dist/scripts/tenant-lint.js +0 -4
- package/dist/scripts/tenant-test.d.ts +0 -1
- package/dist/scripts/tenant-test.js +0 -4
- package/dist/scripts/test-cloudflare-local.d.ts +0 -1
- package/dist/scripts/test-cloudflare-local.js +0 -212
- package/dist/scripts/test-scaffold.d.ts +0 -2
- package/dist/scripts/test-scaffold.js +0 -297
- package/dist/scripts/treeseed.d.ts +0 -2
- package/dist/scripts/treeseed.js +0 -4
- package/dist/scripts/validate-templates.d.ts +0 -2
- package/dist/scripts/validate-templates.js +0 -4
- package/dist/scripts/watch-dev-lib.d.ts +0 -21
- package/dist/scripts/watch-dev-lib.js +0 -277
- package/dist/scripts/workspace-close.d.ts +0 -2
- package/dist/scripts/workspace-close.js +0 -24
- package/dist/scripts/workspace-command-e2e.d.ts +0 -2
- package/dist/scripts/workspace-command-e2e.js +0 -718
- package/dist/scripts/workspace-lint.d.ts +0 -1
- package/dist/scripts/workspace-lint.js +0 -9
- package/dist/scripts/workspace-preflight-lib.d.ts +0 -36
- package/dist/scripts/workspace-preflight-lib.js +0 -179
- package/dist/scripts/workspace-preflight.d.ts +0 -2
- package/dist/scripts/workspace-preflight.js +0 -22
- package/dist/scripts/workspace-publish-changed-packages.d.ts +0 -1
- package/dist/scripts/workspace-publish-changed-packages.js +0 -16
- package/dist/scripts/workspace-release-verify.d.ts +0 -1
- package/dist/scripts/workspace-release-verify.js +0 -81
- package/dist/scripts/workspace-release.d.ts +0 -2
- package/dist/scripts/workspace-release.js +0 -42
- package/dist/scripts/workspace-save-lib.d.ts +0 -42
- package/dist/scripts/workspace-save-lib.js +0 -220
- package/dist/scripts/workspace-save.d.ts +0 -2
- package/dist/scripts/workspace-save.js +0 -124
- package/dist/scripts/workspace-start-warning.js +0 -3
- package/dist/scripts/workspace-start.d.ts +0 -2
- package/dist/scripts/workspace-start.js +0 -71
- package/dist/scripts/workspace-test-unit.d.ts +0 -1
- package/dist/scripts/workspace-test-unit.js +0 -4
- package/dist/scripts/workspace-test.d.ts +0 -1
- package/dist/scripts/workspace-test.js +0 -11
- package/dist/scripts/workspace-tools.d.ts +0 -13
- package/dist/scripts/workspace-tools.js +0 -226
- package/dist/src/cli/handlers/continue.d.ts +0 -2
- package/dist/src/cli/handlers/prepare.d.ts +0 -2
- package/dist/src/cli/handlers/promote.d.ts +0 -2
- package/dist/src/cli/handlers/publish.d.ts +0 -2
- package/dist/src/cli/handlers/setup.d.ts +0 -2
- package/dist/src/cli/handlers/start.d.ts +0 -3
- package/dist/src/cli/handlers/teardown.d.ts +0 -2
- package/dist/src/cli/handlers/work.d.ts +0 -2
- package/dist/src/cli/main.d.ts +0 -6
- package/dist/src/cli/parser.d.ts +0 -3
- package/dist/src/cli/registry.d.ts +0 -27
- package/dist/src/cli/runtime.d.ts +0 -4
- package/dist/src/cli/types.d.ts +0 -71
- /package/dist/{src/cli → cli}/handlers/close.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/config.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/destroy.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/doctor.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/init.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/release.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/rollback.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/save.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/status.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/utils.d.ts +0 -0
- /package/dist/{scripts/workspace-start-warning.d.ts → cli/operations-types.js} +0 -0
- /package/dist/{src/cli → cli}/repair.d.ts +0 -0
- /package/dist/{src/index.d.ts → index.d.ts} +0 -0
package/dist/cli/runtime.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { findNearestTreeseedRoot, findNearestTreeseedWorkspaceRoot } from "@treeseed/sdk/workflow-support";
|
|
3
|
+
import { TreeseedOperationsSdk as SdkOperationsRuntime } from "@treeseed/sdk/operations";
|
|
4
|
+
import { COMMAND_HANDLERS } from "./registry.js";
|
|
5
|
+
import { renderTreeseedHelp, renderUsage, suggestTreeseedCommands } from "./operations-help.js";
|
|
6
|
+
import { parseTreeseedInvocation, validateTreeseedInvocation } from "./operations-parser.js";
|
|
7
|
+
import { findTreeseedOperation, TRESEED_OPERATION_SPECS } from "./operations-registry.js";
|
|
7
8
|
function isHelpFlag(value) {
|
|
8
9
|
return value === "--help" || value === "-h";
|
|
9
10
|
}
|
|
@@ -15,6 +16,7 @@ function defaultWrite(output, stream = "stdout") {
|
|
|
15
16
|
function defaultSpawn(command, args, options) {
|
|
16
17
|
return spawnSync(command, args, options);
|
|
17
18
|
}
|
|
19
|
+
const sdkOperationsRuntime = new SdkOperationsRuntime();
|
|
18
20
|
function formatValidationError(spec, errors) {
|
|
19
21
|
return [
|
|
20
22
|
...errors,
|
|
@@ -28,138 +30,265 @@ function createTreeseedCommandContext(overrides = {}) {
|
|
|
28
30
|
env: overrides.env ?? process.env,
|
|
29
31
|
write: overrides.write ?? defaultWrite,
|
|
30
32
|
spawn: overrides.spawn ?? defaultSpawn,
|
|
31
|
-
outputFormat: overrides.outputFormat ?? "human"
|
|
33
|
+
outputFormat: overrides.outputFormat ?? "human",
|
|
34
|
+
prompt: overrides.prompt,
|
|
35
|
+
confirm: overrides.confirm
|
|
32
36
|
};
|
|
33
37
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
`Treeseed command \`${spec.name}\` must be run from a workspace root.`,
|
|
41
|
-
`Usage: ${renderUsage(spec)}`,
|
|
42
|
-
`Run \`treeseed help ${spec.name}\` for details.`
|
|
43
|
-
].join("\n")
|
|
38
|
+
function writeTreeseedResult(result, context) {
|
|
39
|
+
if (context.outputFormat === "json") {
|
|
40
|
+
const payload = result.report ?? {
|
|
41
|
+
ok: (result.exitCode ?? 0) === 0,
|
|
42
|
+
stdout: result.stdout ?? [],
|
|
43
|
+
stderr: result.stderr ?? []
|
|
44
44
|
};
|
|
45
|
+
context.write(JSON.stringify(payload, null, 2), (result.exitCode ?? 0) === 0 ? "stdout" : "stderr");
|
|
46
|
+
return result.exitCode ?? 0;
|
|
45
47
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
48
|
+
for (const line of result.stdout ?? []) {
|
|
49
|
+
context.write(line, "stdout");
|
|
50
|
+
}
|
|
51
|
+
for (const line of result.stderr ?? []) {
|
|
52
|
+
context.write(line, "stderr");
|
|
53
|
+
}
|
|
54
|
+
return result.exitCode ?? 0;
|
|
52
55
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
class TreeseedOperationsSdk {
|
|
57
|
+
constructor(options = {}) {
|
|
58
|
+
this.options = options;
|
|
59
|
+
}
|
|
60
|
+
listOperations() {
|
|
61
|
+
return [...TRESEED_OPERATION_SPECS];
|
|
62
|
+
}
|
|
63
|
+
findOperation(name) {
|
|
64
|
+
return findTreeseedOperation(name);
|
|
65
|
+
}
|
|
66
|
+
parseInvocation(spec, argv) {
|
|
67
|
+
return parseTreeseedInvocation(spec, argv);
|
|
68
|
+
}
|
|
69
|
+
validateInvocation(spec, argv) {
|
|
70
|
+
return validateTreeseedInvocation(spec, parseTreeseedInvocation(spec, argv));
|
|
71
|
+
}
|
|
72
|
+
async executeHandler(spec, argv, context) {
|
|
73
|
+
try {
|
|
74
|
+
const invocation = parseTreeseedInvocation(spec, argv);
|
|
75
|
+
const errors = validateTreeseedInvocation(spec, invocation);
|
|
76
|
+
const handlerContext = {
|
|
77
|
+
...context,
|
|
78
|
+
outputFormat: invocation.args.json === true ? "json" : context.outputFormat ?? "human"
|
|
79
|
+
};
|
|
80
|
+
if (errors.length > 0) {
|
|
81
|
+
return writeTreeseedResult({
|
|
82
|
+
exitCode: 1,
|
|
83
|
+
stderr: [formatValidationError(spec, errors)],
|
|
84
|
+
report: {
|
|
85
|
+
command: spec.name,
|
|
86
|
+
ok: false,
|
|
87
|
+
error: errors.join(" "),
|
|
88
|
+
errors,
|
|
89
|
+
usage: renderUsage(spec)
|
|
90
|
+
}
|
|
91
|
+
}, handlerContext);
|
|
92
|
+
}
|
|
93
|
+
const handlerName = spec.handlerName;
|
|
94
|
+
if (!handlerName) {
|
|
95
|
+
return writeTreeseedResult({ exitCode: 1, stderr: [`Treeseed command \`${spec.name}\` is missing a handler binding.`] }, handlerContext);
|
|
96
|
+
}
|
|
97
|
+
const handler = this.options.resolveHandler?.(handlerName) ?? null;
|
|
98
|
+
if (!handler) {
|
|
99
|
+
return writeTreeseedResult({
|
|
100
|
+
exitCode: 1,
|
|
101
|
+
stderr: [`Treeseed command \`${spec.name}\` is not executable in this runtime.`],
|
|
102
|
+
report: {
|
|
103
|
+
command: spec.name,
|
|
104
|
+
ok: false,
|
|
105
|
+
error: `No handler registered for ${handlerName}.`
|
|
106
|
+
}
|
|
107
|
+
}, handlerContext);
|
|
108
|
+
}
|
|
109
|
+
const result = await handler(invocation, handlerContext);
|
|
110
|
+
return writeTreeseedResult(result, handlerContext);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
113
|
+
const wantsJson = argv.includes("--json");
|
|
114
|
+
return writeTreeseedResult({
|
|
63
115
|
exitCode: 1,
|
|
64
|
-
stderr: [
|
|
116
|
+
stderr: [message, `Run \`treeseed help ${spec.name}\` for details.`],
|
|
65
117
|
report: {
|
|
66
118
|
command: spec.name,
|
|
67
119
|
ok: false,
|
|
68
|
-
error:
|
|
69
|
-
|
|
70
|
-
usage: renderUsage(spec)
|
|
120
|
+
error: message,
|
|
121
|
+
hint: `treeseed help ${spec.name}`
|
|
71
122
|
}
|
|
72
|
-
},
|
|
123
|
+
}, { ...context, outputFormat: wantsJson ? "json" : context.outputFormat ?? "human" });
|
|
73
124
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
125
|
+
}
|
|
126
|
+
executeAdapter(spec, argv, context) {
|
|
127
|
+
const invocation = spec.options?.length || spec.arguments?.length ? parseTreeseedInvocation(spec, argv) : {
|
|
128
|
+
commandName: spec.name,
|
|
129
|
+
args: {},
|
|
130
|
+
positionals: argv.filter((value) => value !== "--"),
|
|
131
|
+
rawArgs: argv
|
|
132
|
+
};
|
|
133
|
+
const input = spec.buildAdapterInput?.(invocation, context) ?? {};
|
|
134
|
+
return sdkOperationsRuntime.execute(
|
|
135
|
+
{ operationName: spec.name, input },
|
|
136
|
+
{
|
|
137
|
+
cwd: context.cwd,
|
|
138
|
+
env: context.env,
|
|
139
|
+
write: context.write,
|
|
140
|
+
spawn: context.spawn,
|
|
141
|
+
outputFormat: context.outputFormat,
|
|
142
|
+
transport: "cli"
|
|
143
|
+
}
|
|
144
|
+
).then((result) => writeTreeseedResult(result, context)).catch((error) => writeTreeseedResult({
|
|
85
145
|
exitCode: 1,
|
|
86
|
-
stderr: [
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
146
|
+
stderr: [error instanceof Error ? error.message : String(error)]
|
|
147
|
+
}, context));
|
|
148
|
+
}
|
|
149
|
+
async executeAgents(argv, context) {
|
|
150
|
+
try {
|
|
151
|
+
const { runTreeseedAgentCli } = await import("@treeseed/agent/cli");
|
|
152
|
+
return await runTreeseedAgentCli(argv, {
|
|
153
|
+
cwd: context.cwd,
|
|
154
|
+
env: context.env,
|
|
155
|
+
outputFormat: context.outputFormat,
|
|
156
|
+
write: context.write
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
return writeTreeseedResult({
|
|
160
|
+
exitCode: 1,
|
|
161
|
+
stderr: [error instanceof Error ? error.message : String(error)],
|
|
162
|
+
report: {
|
|
163
|
+
command: "agents",
|
|
164
|
+
ok: false,
|
|
165
|
+
error: error instanceof Error ? error.message : String(error)
|
|
166
|
+
}
|
|
167
|
+
}, context);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async executeOperation(request, overrides = {}) {
|
|
171
|
+
const context = createTreeseedCommandContext(overrides);
|
|
172
|
+
const argv = request.argv ?? [];
|
|
173
|
+
const commandName = request.commandName;
|
|
174
|
+
if (commandName === "agents") {
|
|
175
|
+
return this.executeAgents(argv, context);
|
|
176
|
+
}
|
|
177
|
+
const spec = findTreeseedOperation(commandName);
|
|
178
|
+
if (!spec) {
|
|
179
|
+
const suggestions = suggestTreeseedCommands(commandName);
|
|
180
|
+
const lines = [`Unknown treeseed command: ${commandName}`];
|
|
181
|
+
if (suggestions.length > 0) {
|
|
182
|
+
lines.push(`Did you mean: ${suggestions.map((value) => `\`${value}\``).join(", ")}?`);
|
|
92
183
|
}
|
|
93
|
-
|
|
184
|
+
lines.push("Run `treeseed help` to see the full command list.");
|
|
185
|
+
return writeTreeseedResult({ exitCode: 1, stderr: [lines.join("\n")] }, context);
|
|
186
|
+
}
|
|
187
|
+
if (argv.some(isHelpFlag)) {
|
|
188
|
+
context.write(renderTreeseedHelp(spec.name), "stdout");
|
|
189
|
+
return 0;
|
|
190
|
+
}
|
|
191
|
+
return spec.executionMode === "adapter" ? this.executeAdapter(spec, argv, context) : this.executeHandler(spec, argv, context);
|
|
192
|
+
}
|
|
193
|
+
async run(argv, overrides = {}) {
|
|
194
|
+
const context = createTreeseedCommandContext(overrides);
|
|
195
|
+
const [firstArg, ...restArgs] = argv;
|
|
196
|
+
if (!firstArg || isHelpFlag(firstArg) || firstArg === "help") {
|
|
197
|
+
const commandName = firstArg === "help" ? restArgs[0] ?? null : null;
|
|
198
|
+
const helpText = renderTreeseedHelp(commandName);
|
|
199
|
+
context.write(helpText, "stdout");
|
|
200
|
+
return commandName && helpText.startsWith("Unknown treeseed command:") ? 1 : 0;
|
|
201
|
+
}
|
|
202
|
+
return this.executeOperation({ commandName: firstArg, argv: restArgs }, context);
|
|
94
203
|
}
|
|
95
204
|
}
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
cwd: context.cwd,
|
|
104
|
-
env: { ...context.env },
|
|
105
|
-
stdio: "inherit"
|
|
106
|
-
});
|
|
107
|
-
return result.status ?? 1;
|
|
205
|
+
function formatProjectError(spec) {
|
|
206
|
+
return [
|
|
207
|
+
`Treeseed command \`${spec.name}\` must be run inside a Treeseed project.`,
|
|
208
|
+
"No ancestor directory containing `treeseed.site.yaml` was found.",
|
|
209
|
+
`Usage: ${renderUsage(spec)}`,
|
|
210
|
+
`Run \`treeseed help ${spec.name}\` for details.`
|
|
211
|
+
].join("\n");
|
|
108
212
|
}
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
context.write([
|
|
112
|
-
"agents Run the Treeseed agents entrypoint.",
|
|
113
|
-
"",
|
|
114
|
-
"Usage",
|
|
115
|
-
" treeseed agents [args...]",
|
|
116
|
-
"",
|
|
117
|
-
"Notes",
|
|
118
|
-
" - Delegates directly to the installed treeseed-agents command."
|
|
119
|
-
].join("\n"), "stdout");
|
|
120
|
-
return 0;
|
|
121
|
-
}
|
|
122
|
-
const command = process.platform === "win32" ? "treeseed-agents.cmd" : "treeseed-agents";
|
|
123
|
-
const result = context.spawn(command, argv, {
|
|
124
|
-
cwd: context.cwd,
|
|
125
|
-
env: { ...context.env },
|
|
126
|
-
stdio: "inherit"
|
|
127
|
-
});
|
|
128
|
-
return result.status ?? 1;
|
|
213
|
+
function commandNeedsProjectRoot(spec) {
|
|
214
|
+
return spec.name !== "init";
|
|
129
215
|
}
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
return
|
|
216
|
+
function resolveTreeseedCommandCwd(spec, cwd) {
|
|
217
|
+
if (!commandNeedsProjectRoot(spec)) {
|
|
218
|
+
return {
|
|
219
|
+
cwd,
|
|
220
|
+
resolvedProjectRoot: null,
|
|
221
|
+
resolvedWorkspaceRoot: null
|
|
222
|
+
};
|
|
133
223
|
}
|
|
134
|
-
const
|
|
224
|
+
const resolvedProjectRoot = findNearestTreeseedRoot(cwd);
|
|
225
|
+
const resolvedWorkspaceRoot = resolvedProjectRoot ? findNearestTreeseedWorkspaceRoot(resolvedProjectRoot) : null;
|
|
226
|
+
return {
|
|
227
|
+
cwd: resolvedProjectRoot ?? cwd,
|
|
228
|
+
resolvedProjectRoot,
|
|
229
|
+
resolvedWorkspaceRoot
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
const cliOperationsSdk = new TreeseedOperationsSdk({
|
|
233
|
+
resolveHandler: (handlerName) => COMMAND_HANDLERS[handlerName] ?? null
|
|
234
|
+
});
|
|
235
|
+
async function executeTreeseedCommand(commandName, argv, context) {
|
|
236
|
+
const spec = cliOperationsSdk.findOperation(commandName);
|
|
135
237
|
if (!spec) {
|
|
136
|
-
|
|
137
|
-
const lines = [`Unknown treeseed command: ${commandName}`];
|
|
138
|
-
if (suggestions.length > 0) {
|
|
139
|
-
lines.push(`Did you mean: ${suggestions.map((value) => `\`${value}\``).join(", ")}?`);
|
|
140
|
-
}
|
|
141
|
-
lines.push("Run `treeseed help` to see the full command list.");
|
|
142
|
-
return writeResult({ exitCode: 1, stderr: [lines.join("\n")] }, context);
|
|
238
|
+
return cliOperationsSdk.executeOperation({ commandName, argv }, context);
|
|
143
239
|
}
|
|
144
240
|
if (argv.some(isHelpFlag)) {
|
|
145
|
-
|
|
146
|
-
|
|
241
|
+
return cliOperationsSdk.executeOperation({ commandName, argv }, context);
|
|
242
|
+
}
|
|
243
|
+
const resolved = resolveTreeseedCommandCwd(spec, context.cwd);
|
|
244
|
+
if (commandNeedsProjectRoot(spec) && !resolved.resolvedProjectRoot) {
|
|
245
|
+
return writeTreeseedResult({
|
|
246
|
+
exitCode: 1,
|
|
247
|
+
stderr: [formatProjectError(spec)],
|
|
248
|
+
report: {
|
|
249
|
+
command: spec.name,
|
|
250
|
+
ok: false,
|
|
251
|
+
error: `No ancestor containing treeseed.site.yaml was found from ${context.cwd}.`,
|
|
252
|
+
hint: `treeseed help ${spec.name}`
|
|
253
|
+
}
|
|
254
|
+
}, { ...context, outputFormat: argv.includes("--json") ? "json" : context.outputFormat ?? "human" });
|
|
147
255
|
}
|
|
148
|
-
return
|
|
256
|
+
return cliOperationsSdk.executeOperation({ commandName, argv }, { ...context, cwd: resolved.cwd });
|
|
149
257
|
}
|
|
150
258
|
async function runTreeseedCli(argv, overrides = {}) {
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
if (!
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return
|
|
158
|
-
}
|
|
159
|
-
|
|
259
|
+
const [firstArg] = argv;
|
|
260
|
+
const spec = firstArg ? cliOperationsSdk.findOperation(firstArg) : null;
|
|
261
|
+
if (!spec) {
|
|
262
|
+
return cliOperationsSdk.run(argv, overrides);
|
|
263
|
+
}
|
|
264
|
+
if (argv.slice(1).some(isHelpFlag)) {
|
|
265
|
+
return cliOperationsSdk.run(argv, overrides);
|
|
266
|
+
}
|
|
267
|
+
const baseCwd = overrides.cwd ?? process.cwd();
|
|
268
|
+
const resolved = resolveTreeseedCommandCwd(spec, baseCwd);
|
|
269
|
+
if (commandNeedsProjectRoot(spec) && !resolved.resolvedProjectRoot) {
|
|
270
|
+
return writeTreeseedResult({
|
|
271
|
+
exitCode: 1,
|
|
272
|
+
stderr: [formatProjectError(spec)],
|
|
273
|
+
report: {
|
|
274
|
+
command: spec.name,
|
|
275
|
+
ok: false,
|
|
276
|
+
error: `No ancestor containing treeseed.site.yaml was found from ${baseCwd}.`,
|
|
277
|
+
hint: `treeseed help ${spec.name}`
|
|
278
|
+
}
|
|
279
|
+
}, createTreeseedCommandContext({
|
|
280
|
+
...overrides,
|
|
281
|
+
outputFormat: argv.includes("--json") ? "json" : overrides.outputFormat ?? "human"
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
const contextOverrides = commandNeedsProjectRoot(spec) && resolved.resolvedProjectRoot ? { ...overrides, cwd: resolved.cwd } : overrides;
|
|
285
|
+
return cliOperationsSdk.run(argv, contextOverrides);
|
|
160
286
|
}
|
|
161
287
|
export {
|
|
288
|
+
TreeseedOperationsSdk,
|
|
162
289
|
createTreeseedCommandContext,
|
|
163
290
|
executeTreeseedCommand,
|
|
164
|
-
|
|
291
|
+
resolveTreeseedCommandCwd,
|
|
292
|
+
runTreeseedCli,
|
|
293
|
+
writeTreeseedResult
|
|
165
294
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { TreeseedCommandArgumentSpec, TreeseedCommandContext, TreeseedCommandExample, TreeseedCommandGroup, TreeseedCommandHandler, TreeseedCommandOptionSpec, TreeseedCommandResult, TreeseedCommandSpec, TreeseedExecutionMode, TreeseedParsedInvocation, TreeseedSpawner, TreeseedWriter, } from './operations-types.js';
|
|
@@ -27,8 +27,17 @@ export type TreeseedWorkflowState = {
|
|
|
27
27
|
auth: {
|
|
28
28
|
gh: boolean;
|
|
29
29
|
wrangler: boolean;
|
|
30
|
+
railway: boolean;
|
|
30
31
|
copilot: boolean;
|
|
32
|
+
remoteApi: boolean;
|
|
31
33
|
};
|
|
34
|
+
managedServices: Record<string, {
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
initialized: boolean;
|
|
37
|
+
lastDeploymentTimestamp: string | null;
|
|
38
|
+
lastDeployedUrl: string | null;
|
|
39
|
+
provider: string | null;
|
|
40
|
+
}>;
|
|
32
41
|
files: {
|
|
33
42
|
treeseedConfig: boolean;
|
|
34
43
|
machineConfig: boolean;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import { getTreeseedMachineConfigPaths } from "
|
|
3
|
+
import { getTreeseedMachineConfigPaths, resolveTreeseedRemoteSession } from "@treeseed/sdk/workflow-support";
|
|
4
4
|
import {
|
|
5
5
|
createBranchPreviewDeployTarget,
|
|
6
6
|
createPersistentDeployTarget,
|
|
7
7
|
loadDeployState
|
|
8
|
-
} from "
|
|
9
|
-
import { PRODUCTION_BRANCH, STAGING_BRANCH } from "
|
|
10
|
-
import { loadCliDeployConfig } from "
|
|
11
|
-
import { collectCliPreflight } from "
|
|
12
|
-
import { currentBranch, gitStatusPorcelain, repoRoot } from "
|
|
13
|
-
import { isWorkspaceRoot } from "
|
|
8
|
+
} from "@treeseed/sdk/workflow-support";
|
|
9
|
+
import { PRODUCTION_BRANCH, STAGING_BRANCH } from "@treeseed/sdk/workflow-support";
|
|
10
|
+
import { loadCliDeployConfig } from "@treeseed/sdk/workflow-support";
|
|
11
|
+
import { collectCliPreflight } from "@treeseed/sdk/workflow-support";
|
|
12
|
+
import { currentBranch, gitStatusPorcelain, repoRoot } from "@treeseed/sdk/workflow-support";
|
|
13
|
+
import { isWorkspaceRoot } from "@treeseed/sdk/workflow-support";
|
|
14
14
|
function safeResolveRepoRoot(cwd) {
|
|
15
15
|
try {
|
|
16
16
|
return repoRoot(cwd);
|
|
@@ -66,7 +66,17 @@ function resolveTreeseedWorkflowState(cwd) {
|
|
|
66
66
|
auth: {
|
|
67
67
|
gh: preflight.checks.auth.gh?.authenticated === true,
|
|
68
68
|
wrangler: preflight.checks.auth.wrangler?.authenticated === true,
|
|
69
|
-
|
|
69
|
+
railway: preflight.checks.auth.railway?.authenticated === true,
|
|
70
|
+
copilot: preflight.checks.auth.copilot?.configured === true,
|
|
71
|
+
remoteApi: Boolean(resolveTreeseedRemoteSession(cwd))
|
|
72
|
+
},
|
|
73
|
+
managedServices: {
|
|
74
|
+
api: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null },
|
|
75
|
+
agents: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null },
|
|
76
|
+
manager: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null },
|
|
77
|
+
worker: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null },
|
|
78
|
+
workdayStart: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null },
|
|
79
|
+
workdayReport: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null }
|
|
70
80
|
},
|
|
71
81
|
files: {
|
|
72
82
|
treeseedConfig: tenantRoot,
|
|
@@ -100,6 +110,17 @@ function resolveTreeseedWorkflowState(cwd) {
|
|
|
100
110
|
url: typeof latestHistory?.url === "string" ? latestHistory.url : deployState.lastDeployedUrl ?? null
|
|
101
111
|
});
|
|
102
112
|
}
|
|
113
|
+
for (const serviceKey of ["api", "agents", "manager", "worker", "workdayStart", "workdayReport"]) {
|
|
114
|
+
const service = deployState.services?.[serviceKey];
|
|
115
|
+
if (!service) continue;
|
|
116
|
+
state.managedServices[serviceKey] = {
|
|
117
|
+
enabled: service.enabled === true,
|
|
118
|
+
initialized: service.initialized === true,
|
|
119
|
+
lastDeploymentTimestamp: service.lastDeploymentTimestamp ?? null,
|
|
120
|
+
lastDeployedUrl: service.lastDeployedUrl ?? service.publicBaseUrl ?? null,
|
|
121
|
+
provider: service.provider ?? null
|
|
122
|
+
};
|
|
123
|
+
}
|
|
103
124
|
}
|
|
104
125
|
if (branchRole === "feature" && branchName) {
|
|
105
126
|
const previewState = loadDeployState(cwd, deployConfig, { target: createBranchPreviewDeployTarget(branchName) });
|
|
@@ -118,51 +139,54 @@ function resolveTreeseedWorkflowState(cwd) {
|
|
|
118
139
|
function recommendTreeseedNextSteps(state) {
|
|
119
140
|
const recommendations = [];
|
|
120
141
|
if (!state.workspaceRoot) {
|
|
121
|
-
return [{ command: "
|
|
142
|
+
return [{ command: "treeseed status", reason: "Run this command from inside a Treeseed workspace so the CLI can resolve the project root." }];
|
|
122
143
|
}
|
|
123
144
|
if (!state.deployConfigPresent) {
|
|
124
145
|
return [{ command: "treeseed init <directory>", reason: "Create a new Treeseed tenant before configuring or releasing anything." }];
|
|
125
146
|
}
|
|
126
147
|
if (!state.files.machineConfig) {
|
|
127
|
-
recommendations.push({ command: "treeseed
|
|
128
|
-
recommendations.push({ command: "treeseed
|
|
148
|
+
recommendations.push({ command: "treeseed status", reason: "Validate tooling, auth, and repository readiness first." });
|
|
149
|
+
recommendations.push({ command: "treeseed config", reason: "Bootstrap the local machine config and local environment files." });
|
|
129
150
|
return recommendations;
|
|
130
151
|
}
|
|
131
152
|
if (state.branchRole === "feature") {
|
|
132
153
|
if (state.dirtyWorktree) {
|
|
133
|
-
recommendations.push({ command: 'treeseed
|
|
154
|
+
recommendations.push({ command: 'treeseed save "describe your change"', reason: "Persist, verify, and push the current task branch before staging or closing it." });
|
|
134
155
|
} else {
|
|
135
|
-
recommendations.push({ command:
|
|
156
|
+
recommendations.push({ command: 'treeseed stage "describe the resolution"', reason: "Merge this task branch into staging and clean up branch artifacts." });
|
|
136
157
|
}
|
|
137
158
|
if (state.preview.enabled && state.branchName) {
|
|
138
|
-
recommendations.push({ command:
|
|
159
|
+
recommendations.push({ command: 'treeseed save "describe your change"', reason: "Save refreshes the branch preview deployment when one is enabled." });
|
|
139
160
|
} else {
|
|
140
161
|
recommendations.push({ command: "treeseed dev", reason: "Use the local environment for iterative work on this feature branch." });
|
|
141
162
|
}
|
|
163
|
+
recommendations.push({ command: 'treeseed close "reason"', reason: "Archive this task without merging if it should be abandoned." });
|
|
142
164
|
return recommendations.slice(0, 3);
|
|
143
165
|
}
|
|
144
166
|
if (state.branchRole === "staging") {
|
|
145
167
|
if (!state.persistentEnvironments.staging.initialized) {
|
|
146
|
-
recommendations.push({ command: "treeseed
|
|
168
|
+
recommendations.push({ command: "treeseed config --environment staging", reason: "Initialize the staging environment before releasing." });
|
|
147
169
|
} else {
|
|
148
|
-
recommendations.push({ command: "treeseed
|
|
149
|
-
|
|
170
|
+
recommendations.push({ command: "treeseed release --patch", reason: "Promote staging into main when the integration branch is ready for production." });
|
|
171
|
+
if (state.managedServices.api.enabled || state.managedServices.agents.enabled) {
|
|
172
|
+
recommendations.push({ command: "treeseed auth:login", reason: "Keep the local CLI authenticated to the remote API used by managed services." });
|
|
173
|
+
}
|
|
150
174
|
}
|
|
151
175
|
return recommendations.slice(0, 3);
|
|
152
176
|
}
|
|
153
177
|
if (state.branchRole === "main") {
|
|
154
178
|
if (state.dirtyWorktree) {
|
|
155
|
-
recommendations.push({ command: 'treeseed
|
|
179
|
+
recommendations.push({ command: 'treeseed save --hotfix "describe the hotfix"', reason: "Only explicit hotfix saves are allowed on main." });
|
|
156
180
|
} else if (!state.persistentEnvironments.prod.initialized) {
|
|
157
|
-
recommendations.push({ command: "treeseed
|
|
181
|
+
recommendations.push({ command: "treeseed config --environment prod", reason: "Initialize production before a release requires it." });
|
|
158
182
|
} else {
|
|
159
|
-
recommendations.push({ command: "treeseed
|
|
183
|
+
recommendations.push({ command: "treeseed status", reason: "Inspect production state and release readiness." });
|
|
160
184
|
recommendations.push({ command: "treeseed rollback prod", reason: "Roll back production to the previous recorded deployment if needed." });
|
|
161
185
|
}
|
|
162
186
|
return recommendations.slice(0, 3);
|
|
163
187
|
}
|
|
164
188
|
recommendations.push({ command: "treeseed dev", reason: "Start the local Treeseed development environment." });
|
|
165
|
-
recommendations.push({ command: "treeseed
|
|
189
|
+
recommendations.push({ command: "treeseed switch feature/my-change", reason: "Create a task branch from the latest staging commit." });
|
|
166
190
|
return recommendations.slice(0, 3);
|
|
167
191
|
}
|
|
168
192
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treeseed/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Operator-facing Treeseed CLI package.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"packageManager": "npm@11.7.0",
|
|
16
16
|
"type": "module",
|
|
17
17
|
"engines": {
|
|
18
|
-
"node": ">=
|
|
18
|
+
"node": ">=22"
|
|
19
19
|
},
|
|
20
20
|
"types": "./dist/index.d.ts",
|
|
21
21
|
"files": [
|
|
@@ -29,27 +29,27 @@
|
|
|
29
29
|
"setup": "npm install",
|
|
30
30
|
"setup:ci": "npm ci",
|
|
31
31
|
"build": "npm run build:dist",
|
|
32
|
-
"
|
|
33
|
-
"test:
|
|
32
|
+
"lint": "npm run build:dist",
|
|
33
|
+
"test": "npm run build:dist && node --test ./scripts/treeseed-help.test.mjs ./scripts/wrapper-package.test.mjs",
|
|
34
34
|
"build:dist": "node ./scripts/run-ts.mjs ./scripts/build-dist.ts",
|
|
35
|
-
"prepare": "npm run build:dist",
|
|
36
35
|
"prepack": "npm run build:dist",
|
|
37
|
-
"
|
|
36
|
+
"verify:direct": "npm run release:verify",
|
|
37
|
+
"verify:local": "node --input-type=module -e \"process.env.TREESEED_VERIFY_DRIVER='direct'; await import('@treeseed/sdk/scripts/verify-driver')\"",
|
|
38
|
+
"verify:action": "node --input-type=module -e \"process.env.TREESEED_VERIFY_DRIVER='act'; await import('@treeseed/sdk/scripts/verify-driver')\"",
|
|
39
|
+
"verify": "node --input-type=module -e \"await import('@treeseed/sdk/scripts/verify-driver')\"",
|
|
38
40
|
"release:setup": "npm run setup:ci",
|
|
39
41
|
"release:check-tag": "node ./scripts/run-ts.mjs ./scripts/assert-release-tag-version.ts",
|
|
40
42
|
"release:verify": "node ./scripts/run-ts.mjs ./scripts/release-verify.ts",
|
|
41
43
|
"release:publish": "node ./scripts/run-ts.mjs ./scripts/publish-package.ts"
|
|
42
44
|
},
|
|
43
45
|
"dependencies": {
|
|
44
|
-
"@treeseed/agent": "^0.1
|
|
45
|
-
"@treeseed/
|
|
46
|
-
"@treeseed/sdk": "^0.1.2",
|
|
47
|
-
"esbuild": "^0.25.10",
|
|
48
|
-
"typescript": "^5.9.3",
|
|
49
|
-
"yaml": "^2.8.1"
|
|
46
|
+
"@treeseed/agent": "^0.4.1",
|
|
47
|
+
"@treeseed/sdk": "^0.4.2"
|
|
50
48
|
},
|
|
51
49
|
"devDependencies": {
|
|
52
|
-
"@types/node": "^24.6.0"
|
|
50
|
+
"@types/node": "^24.6.0",
|
|
51
|
+
"esbuild": "^0.25.10",
|
|
52
|
+
"typescript": "^5.9.3"
|
|
53
53
|
},
|
|
54
54
|
"bin": {
|
|
55
55
|
"treeseed": "./dist/cli/main.js"
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { guidedResult } from "./utils.js";
|
|
2
|
-
import { resolveTreeseedWorkflowState } from "../workflow-state.js";
|
|
3
|
-
const handleContinue = (_invocation, context) => {
|
|
4
|
-
const state = resolveTreeseedWorkflowState(context.cwd);
|
|
5
|
-
const next = state.recommendations[0] ?? null;
|
|
6
|
-
return guidedResult({
|
|
7
|
-
command: "continue",
|
|
8
|
-
summary: next ? "Treeseed selected the safest next workflow step." : "Treeseed could not infer a next workflow step.",
|
|
9
|
-
facts: [
|
|
10
|
-
{ label: "Branch", value: state.branchName ?? "(none)" },
|
|
11
|
-
{ label: "Suggested command", value: next?.command ?? "(none)" }
|
|
12
|
-
],
|
|
13
|
-
nextSteps: next ? [`${next.command} # ${next.reason}`] : ["treeseed doctor"],
|
|
14
|
-
report: {
|
|
15
|
-
state,
|
|
16
|
-
selected: next
|
|
17
|
-
},
|
|
18
|
-
exitCode: next ? 0 : 1
|
|
19
|
-
});
|
|
20
|
-
};
|
|
21
|
-
export {
|
|
22
|
-
handleContinue
|
|
23
|
-
};
|