@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.
Files changed (200) hide show
  1. package/README.md +27 -26
  2. package/dist/cli/handlers/auth-login.d.ts +2 -0
  3. package/dist/cli/handlers/auth-login.js +67 -0
  4. package/dist/cli/handlers/auth-logout.d.ts +2 -0
  5. package/dist/cli/handlers/auth-logout.js +20 -0
  6. package/dist/cli/handlers/auth-whoami.d.ts +2 -0
  7. package/dist/cli/handlers/auth-whoami.js +24 -0
  8. package/dist/cli/handlers/close.js +19 -53
  9. package/dist/cli/handlers/config.js +33 -53
  10. package/dist/cli/handlers/destroy.js +34 -79
  11. package/dist/{src/cli/handlers/ship.d.ts → cli/handlers/dev.d.ts} +1 -1
  12. package/dist/cli/handlers/dev.js +19 -0
  13. package/dist/cli/handlers/doctor.js +13 -6
  14. package/dist/cli/handlers/init.js +32 -8
  15. package/dist/cli/handlers/release.js +21 -53
  16. package/dist/cli/handlers/rollback.js +8 -8
  17. package/dist/cli/handlers/save.js +21 -79
  18. package/dist/cli/handlers/stage.d.ts +2 -0
  19. package/dist/cli/handlers/stage.js +28 -0
  20. package/dist/cli/handlers/status.js +35 -26
  21. package/dist/{src/cli/handlers/deploy.d.ts → cli/handlers/switch.d.ts} +1 -1
  22. package/dist/cli/handlers/switch.js +29 -0
  23. package/dist/{src/cli/handlers/next.d.ts → cli/handlers/sync.d.ts} +1 -1
  24. package/dist/cli/handlers/sync.js +26 -0
  25. package/dist/cli/handlers/tasks.d.ts +2 -0
  26. package/dist/cli/handlers/tasks.js +31 -0
  27. package/dist/cli/handlers/template.d.ts +2 -0
  28. package/dist/cli/handlers/template.js +27 -0
  29. package/dist/cli/handlers/workflow.d.ts +6 -0
  30. package/dist/cli/handlers/workflow.js +71 -0
  31. package/dist/{src/cli → cli}/help.d.ts +2 -2
  32. package/dist/cli/help.js +36 -24
  33. package/dist/cli/main.d.ts +6 -0
  34. package/dist/cli/main.js +14 -19
  35. package/dist/cli/operations-help.d.ts +1 -0
  36. package/dist/cli/operations-help.js +1 -0
  37. package/dist/cli/operations-parser.d.ts +1 -0
  38. package/dist/cli/operations-parser.js +1 -0
  39. package/dist/cli/operations-registry.d.ts +5 -0
  40. package/dist/cli/operations-registry.js +260 -0
  41. package/dist/cli/operations-types.d.ts +72 -0
  42. package/dist/cli/parser.d.ts +3 -0
  43. package/dist/cli/parser.js +1 -6
  44. package/dist/cli/registry.d.ts +25 -0
  45. package/dist/cli/registry.js +28 -416
  46. package/dist/cli/repair.js +6 -4
  47. package/dist/cli/runtime.d.ts +31 -0
  48. package/dist/cli/runtime.js +240 -111
  49. package/dist/cli/types.d.ts +1 -0
  50. package/dist/{src/cli → cli}/workflow-state.d.ts +9 -0
  51. package/dist/cli/workflow-state.js +45 -21
  52. package/package.json +13 -13
  53. package/dist/cli/handlers/continue.js +0 -23
  54. package/dist/cli/handlers/deploy.js +0 -139
  55. package/dist/cli/handlers/next.js +0 -27
  56. package/dist/cli/handlers/prepare.js +0 -8
  57. package/dist/cli/handlers/promote.js +0 -8
  58. package/dist/cli/handlers/publish.js +0 -8
  59. package/dist/cli/handlers/setup.js +0 -48
  60. package/dist/cli/handlers/ship.js +0 -49
  61. package/dist/cli/handlers/start.js +0 -97
  62. package/dist/cli/handlers/teardown.js +0 -50
  63. package/dist/cli/handlers/work.js +0 -85
  64. package/dist/scripts/aggregate-book.d.ts +0 -1
  65. package/dist/scripts/aggregate-book.js +0 -121
  66. package/dist/scripts/assert-release-tag-version.d.ts +0 -1
  67. package/dist/scripts/assert-release-tag-version.js +0 -21
  68. package/dist/scripts/build-dist.d.ts +0 -1
  69. package/dist/scripts/build-dist.js +0 -108
  70. package/dist/scripts/build-tenant-worker.d.ts +0 -1
  71. package/dist/scripts/build-tenant-worker.js +0 -36
  72. package/dist/scripts/cleanup-markdown.d.ts +0 -2
  73. package/dist/scripts/cleanup-markdown.js +0 -373
  74. package/dist/scripts/config-runtime-lib.d.ts +0 -122
  75. package/dist/scripts/config-runtime-lib.js +0 -505
  76. package/dist/scripts/config-treeseed.d.ts +0 -2
  77. package/dist/scripts/config-treeseed.js +0 -81
  78. package/dist/scripts/d1-migration-lib.d.ts +0 -6
  79. package/dist/scripts/d1-migration-lib.js +0 -90
  80. package/dist/scripts/deploy-lib.d.ts +0 -127
  81. package/dist/scripts/deploy-lib.js +0 -841
  82. package/dist/scripts/ensure-mailpit.d.ts +0 -1
  83. package/dist/scripts/ensure-mailpit.js +0 -29
  84. package/dist/scripts/git-workflow-lib.d.ts +0 -25
  85. package/dist/scripts/git-workflow-lib.js +0 -136
  86. package/dist/scripts/github-automation-lib.d.ts +0 -156
  87. package/dist/scripts/github-automation-lib.js +0 -242
  88. package/dist/scripts/local-dev-lib.d.ts +0 -9
  89. package/dist/scripts/local-dev-lib.js +0 -84
  90. package/dist/scripts/local-dev.d.ts +0 -1
  91. package/dist/scripts/local-dev.js +0 -129
  92. package/dist/scripts/logs-mailpit.d.ts +0 -1
  93. package/dist/scripts/logs-mailpit.js +0 -2
  94. package/dist/scripts/mailpit-runtime.d.ts +0 -4
  95. package/dist/scripts/mailpit-runtime.js +0 -57
  96. package/dist/scripts/package-tools.d.ts +0 -22
  97. package/dist/scripts/package-tools.js +0 -255
  98. package/dist/scripts/patch-starlight-content-path.d.ts +0 -1
  99. package/dist/scripts/patch-starlight-content-path.js +0 -172
  100. package/dist/scripts/paths.d.ts +0 -17
  101. package/dist/scripts/paths.js +0 -26
  102. package/dist/scripts/publish-package.d.ts +0 -1
  103. package/dist/scripts/publish-package.js +0 -19
  104. package/dist/scripts/release-verify.d.ts +0 -1
  105. package/dist/scripts/release-verify.js +0 -136
  106. package/dist/scripts/run-fixture-astro-command.d.ts +0 -1
  107. package/dist/scripts/run-fixture-astro-command.js +0 -18
  108. package/dist/scripts/save-deploy-preflight-lib.d.ts +0 -34
  109. package/dist/scripts/save-deploy-preflight-lib.js +0 -69
  110. package/dist/scripts/scaffold-site.d.ts +0 -2
  111. package/dist/scripts/scaffold-site.js +0 -92
  112. package/dist/scripts/stop-mailpit.d.ts +0 -1
  113. package/dist/scripts/stop-mailpit.js +0 -5
  114. package/dist/scripts/sync-dev-vars.d.ts +0 -1
  115. package/dist/scripts/sync-dev-vars.js +0 -6
  116. package/dist/scripts/template-registry-lib.d.ts +0 -47
  117. package/dist/scripts/template-registry-lib.js +0 -137
  118. package/dist/scripts/tenant-astro-command.d.ts +0 -1
  119. package/dist/scripts/tenant-astro-command.js +0 -3
  120. package/dist/scripts/tenant-build.d.ts +0 -1
  121. package/dist/scripts/tenant-build.js +0 -16
  122. package/dist/scripts/tenant-check.d.ts +0 -1
  123. package/dist/scripts/tenant-check.js +0 -7
  124. package/dist/scripts/tenant-d1-migrate-local.d.ts +0 -1
  125. package/dist/scripts/tenant-d1-migrate-local.js +0 -11
  126. package/dist/scripts/tenant-deploy.d.ts +0 -2
  127. package/dist/scripts/tenant-deploy.js +0 -180
  128. package/dist/scripts/tenant-destroy.d.ts +0 -2
  129. package/dist/scripts/tenant-destroy.js +0 -104
  130. package/dist/scripts/tenant-dev.d.ts +0 -1
  131. package/dist/scripts/tenant-dev.js +0 -171
  132. package/dist/scripts/tenant-lint.d.ts +0 -1
  133. package/dist/scripts/tenant-lint.js +0 -4
  134. package/dist/scripts/tenant-test.d.ts +0 -1
  135. package/dist/scripts/tenant-test.js +0 -4
  136. package/dist/scripts/test-cloudflare-local.d.ts +0 -1
  137. package/dist/scripts/test-cloudflare-local.js +0 -212
  138. package/dist/scripts/test-scaffold.d.ts +0 -2
  139. package/dist/scripts/test-scaffold.js +0 -297
  140. package/dist/scripts/treeseed.d.ts +0 -2
  141. package/dist/scripts/treeseed.js +0 -4
  142. package/dist/scripts/validate-templates.d.ts +0 -2
  143. package/dist/scripts/validate-templates.js +0 -4
  144. package/dist/scripts/watch-dev-lib.d.ts +0 -21
  145. package/dist/scripts/watch-dev-lib.js +0 -277
  146. package/dist/scripts/workspace-close.d.ts +0 -2
  147. package/dist/scripts/workspace-close.js +0 -24
  148. package/dist/scripts/workspace-command-e2e.d.ts +0 -2
  149. package/dist/scripts/workspace-command-e2e.js +0 -718
  150. package/dist/scripts/workspace-lint.d.ts +0 -1
  151. package/dist/scripts/workspace-lint.js +0 -9
  152. package/dist/scripts/workspace-preflight-lib.d.ts +0 -36
  153. package/dist/scripts/workspace-preflight-lib.js +0 -179
  154. package/dist/scripts/workspace-preflight.d.ts +0 -2
  155. package/dist/scripts/workspace-preflight.js +0 -22
  156. package/dist/scripts/workspace-publish-changed-packages.d.ts +0 -1
  157. package/dist/scripts/workspace-publish-changed-packages.js +0 -16
  158. package/dist/scripts/workspace-release-verify.d.ts +0 -1
  159. package/dist/scripts/workspace-release-verify.js +0 -81
  160. package/dist/scripts/workspace-release.d.ts +0 -2
  161. package/dist/scripts/workspace-release.js +0 -42
  162. package/dist/scripts/workspace-save-lib.d.ts +0 -42
  163. package/dist/scripts/workspace-save-lib.js +0 -220
  164. package/dist/scripts/workspace-save.d.ts +0 -2
  165. package/dist/scripts/workspace-save.js +0 -124
  166. package/dist/scripts/workspace-start-warning.js +0 -3
  167. package/dist/scripts/workspace-start.d.ts +0 -2
  168. package/dist/scripts/workspace-start.js +0 -71
  169. package/dist/scripts/workspace-test-unit.d.ts +0 -1
  170. package/dist/scripts/workspace-test-unit.js +0 -4
  171. package/dist/scripts/workspace-test.d.ts +0 -1
  172. package/dist/scripts/workspace-test.js +0 -11
  173. package/dist/scripts/workspace-tools.d.ts +0 -13
  174. package/dist/scripts/workspace-tools.js +0 -226
  175. package/dist/src/cli/handlers/continue.d.ts +0 -2
  176. package/dist/src/cli/handlers/prepare.d.ts +0 -2
  177. package/dist/src/cli/handlers/promote.d.ts +0 -2
  178. package/dist/src/cli/handlers/publish.d.ts +0 -2
  179. package/dist/src/cli/handlers/setup.d.ts +0 -2
  180. package/dist/src/cli/handlers/start.d.ts +0 -3
  181. package/dist/src/cli/handlers/teardown.d.ts +0 -2
  182. package/dist/src/cli/handlers/work.d.ts +0 -2
  183. package/dist/src/cli/main.d.ts +0 -6
  184. package/dist/src/cli/parser.d.ts +0 -3
  185. package/dist/src/cli/registry.d.ts +0 -27
  186. package/dist/src/cli/runtime.d.ts +0 -4
  187. package/dist/src/cli/types.d.ts +0 -71
  188. /package/dist/{src/cli → cli}/handlers/close.d.ts +0 -0
  189. /package/dist/{src/cli → cli}/handlers/config.d.ts +0 -0
  190. /package/dist/{src/cli → cli}/handlers/destroy.d.ts +0 -0
  191. /package/dist/{src/cli → cli}/handlers/doctor.d.ts +0 -0
  192. /package/dist/{src/cli → cli}/handlers/init.d.ts +0 -0
  193. /package/dist/{src/cli → cli}/handlers/release.d.ts +0 -0
  194. /package/dist/{src/cli → cli}/handlers/rollback.d.ts +0 -0
  195. /package/dist/{src/cli → cli}/handlers/save.d.ts +0 -0
  196. /package/dist/{src/cli → cli}/handlers/status.d.ts +0 -0
  197. /package/dist/{src/cli → cli}/handlers/utils.d.ts +0 -0
  198. /package/dist/{scripts/workspace-start-warning.d.ts → cli/operations-types.js} +0 -0
  199. /package/dist/{src/cli → cli}/repair.d.ts +0 -0
  200. /package/dist/{src/index.d.ts → index.d.ts} +0 -0
@@ -1,9 +1,10 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import { isWorkspaceRoot, packageScriptPath } from "../scripts/package-tools.js";
3
- import { writeResult } from "./handlers/utils.js";
4
- import { renderTreeseedHelp, renderUsage, suggestTreeseedCommands } from "./help.js";
5
- import { parseTreeseedInvocation, validateTreeseedInvocation } from "./parser.js";
6
- import { COMMAND_HANDLERS, findCommandSpec } from "./registry.js";
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 resolveAdapter(spec, cwd) {
35
- const adapter = spec.adapter;
36
- if (!adapter) return { error: `Treeseed command \`${spec.name}\` is missing adapter metadata.` };
37
- if (adapter.requireWorkspaceRoot && !isWorkspaceRoot(cwd)) {
38
- return {
39
- error: [
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
- const scriptName = adapter.workspaceScript || adapter.directScript ? isWorkspaceRoot(cwd) ? adapter.workspaceScript ?? adapter.script : adapter.directScript ?? adapter.script : adapter.script;
47
- return {
48
- scriptPath: packageScriptPath(scriptName),
49
- extraArgs: adapter.extraArgs ?? [],
50
- rewriteArgs: adapter.rewriteArgs
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
- async function executeHandler(spec, argv, context) {
54
- try {
55
- const invocation = parseTreeseedInvocation(spec, argv);
56
- const errors = validateTreeseedInvocation(spec, invocation);
57
- const handlerContext = {
58
- ...context,
59
- outputFormat: invocation.args.json === true ? "json" : context.outputFormat ?? "human"
60
- };
61
- if (errors.length > 0) {
62
- return writeResult({
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: [formatValidationError(spec, errors)],
116
+ stderr: [message, `Run \`treeseed help ${spec.name}\` for details.`],
65
117
  report: {
66
118
  command: spec.name,
67
119
  ok: false,
68
- error: errors.join(" "),
69
- errors,
70
- usage: renderUsage(spec)
120
+ error: message,
121
+ hint: `treeseed help ${spec.name}`
71
122
  }
72
- }, handlerContext);
123
+ }, { ...context, outputFormat: wantsJson ? "json" : context.outputFormat ?? "human" });
73
124
  }
74
- const handlerName = spec.handlerName;
75
- if (!handlerName) {
76
- return writeResult({ exitCode: 1, stderr: [`Treeseed command \`${spec.name}\` is missing a handler binding.`] }, handlerContext);
77
- }
78
- const handler = COMMAND_HANDLERS[handlerName];
79
- const result = await handler(invocation, handlerContext);
80
- return writeResult(result, handlerContext);
81
- } catch (error) {
82
- const message = error instanceof Error ? error.message : String(error);
83
- const wantsJson = argv.includes("--json");
84
- return writeResult({
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: [message, `Run \`treeseed help ${spec.name}\` for details.`],
87
- report: {
88
- command: spec.name,
89
- ok: false,
90
- error: message,
91
- hint: `treeseed help ${spec.name}`
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
- }, { ...context, outputFormat: wantsJson ? "json" : context.outputFormat ?? "human" });
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 executeAdapter(spec, argv, context) {
97
- const resolved = resolveAdapter(spec, context.cwd);
98
- if ("error" in resolved) {
99
- return writeResult({ exitCode: 1, stderr: [resolved.error] }, context);
100
- }
101
- const rewritten = resolved.rewriteArgs ? resolved.rewriteArgs(argv) : argv;
102
- const result = context.spawn(process.execPath, [resolved.scriptPath, ...resolved.extraArgs, ...rewritten], {
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 executeAgents(argv, context) {
110
- if (argv.some(isHelpFlag)) {
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
- async function executeTreeseedCommand(commandName, argv, context) {
131
- if (commandName === "agents") {
132
- return executeAgents(argv, context);
216
+ function resolveTreeseedCommandCwd(spec, cwd) {
217
+ if (!commandNeedsProjectRoot(spec)) {
218
+ return {
219
+ cwd,
220
+ resolvedProjectRoot: null,
221
+ resolvedWorkspaceRoot: null
222
+ };
133
223
  }
134
- const spec = findCommandSpec(commandName);
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
- const suggestions = suggestTreeseedCommands(commandName);
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
- context.write(renderTreeseedHelp(spec.name), "stdout");
146
- return 0;
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 spec.executionMode === "adapter" ? executeAdapter(spec, argv, context) : executeHandler(spec, argv, context);
256
+ return cliOperationsSdk.executeOperation({ commandName, argv }, { ...context, cwd: resolved.cwd });
149
257
  }
150
258
  async function runTreeseedCli(argv, overrides = {}) {
151
- const context = createTreeseedCommandContext(overrides);
152
- const [firstArg, ...restArgs] = argv;
153
- if (!firstArg || isHelpFlag(firstArg) || firstArg === "help") {
154
- const commandName = firstArg === "help" ? restArgs[0] ?? null : null;
155
- const helpText = renderTreeseedHelp(commandName);
156
- context.write(helpText, "stdout");
157
- return commandName && helpText.startsWith("Unknown treeseed command:") ? 1 : 0;
158
- }
159
- return executeTreeseedCommand(firstArg, restArgs, context);
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
- runTreeseedCli
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 "../scripts/config-runtime-lib.js";
3
+ import { getTreeseedMachineConfigPaths, resolveTreeseedRemoteSession } from "@treeseed/sdk/workflow-support";
4
4
  import {
5
5
  createBranchPreviewDeployTarget,
6
6
  createPersistentDeployTarget,
7
7
  loadDeployState
8
- } from "../scripts/deploy-lib.js";
9
- import { PRODUCTION_BRANCH, STAGING_BRANCH } from "../scripts/git-workflow-lib.js";
10
- import { loadCliDeployConfig } from "../scripts/package-tools.js";
11
- import { collectCliPreflight } from "../scripts/workspace-preflight-lib.js";
12
- import { currentBranch, gitStatusPorcelain, repoRoot } from "../scripts/workspace-save-lib.js";
13
- import { isWorkspaceRoot } from "../scripts/workspace-tools.js";
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
- copilot: preflight.checks.auth.copilot?.configured === true
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: "cd docs && treeseed doctor", reason: "Switch to the Treeseed workspace root before using the guided workflow." }];
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 doctor", reason: "Validate tooling, auth, and repository readiness first." });
128
- recommendations.push({ command: "treeseed setup", reason: "Bootstrap the local machine config and local environment files." });
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 ship "describe your change"', reason: "Persist and push the current feature branch before closing or releasing." });
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: "treeseed teardown", reason: "Merge this feature branch into staging and clean up branch artifacts." });
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: `treeseed publish --target-branch ${state.branchName}`, reason: "Refresh the branch preview deployment when you need a live Cloudflare preview." });
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 prepare --environment staging", reason: "Initialize the staging environment before publishing or promoting." });
168
+ recommendations.push({ command: "treeseed config --environment staging", reason: "Initialize the staging environment before releasing." });
147
169
  } else {
148
- recommendations.push({ command: "treeseed publish --environment staging", reason: "Publish the current staging branch to the initialized staging environment." });
149
- recommendations.push({ command: "treeseed promote --patch", reason: "Promote staging into main when the integration branch is ready for production." });
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 ship --hotfix "describe the hotfix"', reason: "Only explicit hotfix saves are allowed on main." });
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 prepare --environment prod", reason: "Initialize production before attempting a manual production publish." });
181
+ recommendations.push({ command: "treeseed config --environment prod", reason: "Initialize production before a release requires it." });
158
182
  } else {
159
- recommendations.push({ command: "treeseed publish --environment prod", reason: "Run a manual production publish only when you intentionally need to bypass CI." });
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 work feature/my-change", reason: "Create a feature branch from the latest staging commit." });
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.1.1",
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": ">=20"
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
- "test": "npm run test:templates && npm run build:dist && node --test ./scripts/*.test.mjs",
33
- "test:templates": "node ./scripts/run-ts.mjs ./scripts/validate-templates.ts",
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
- "test:scaffold": "node ./scripts/run-ts.mjs ./scripts/test-scaffold.ts",
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.2",
45
- "@treeseed/core": "^0.1.2",
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
- };