@treeseed/cli 0.10.20 → 0.10.21

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 CHANGED
@@ -45,6 +45,7 @@ The main workflow commands exposed by the current CLI are:
45
45
  - `treeseed tasks [--json]`
46
46
  - `treeseed switch <branch-name> [--preview]`
47
47
  - `treeseed dev`
48
+ - `treeseed dev start|status|logs|stop|restart`
48
49
  - `treeseed save [--preview] [--plan] "<commit message>"`
49
50
  - `treeseed stage "<resolution message>"`
50
51
  - `treeseed close "<close reason>"`
@@ -65,6 +66,7 @@ treeseed config
65
66
  treeseed switch feature/search-improvements --plan
66
67
  treeseed switch feature/search-improvements --preview
67
68
  treeseed dev
69
+ treeseed dev start --web-runtime local
68
70
  treeseed save --preview "feat: add search filters"
69
71
  treeseed stage "feat: add search filters"
70
72
  treeseed release --patch
@@ -72,6 +74,27 @@ treeseed recover
72
74
  treeseed status --json
73
75
  ```
74
76
 
77
+ ## Development Server Instances
78
+
79
+ `treeseed dev` remains the foreground local runtime supervisor. It delegates to `@treeseed/core`, starts the Market web/API/control-plane development surface, streams output in the active terminal, and exits when the shell-owned process is stopped.
80
+
81
+ Managed dev instances use subcommands:
82
+
83
+ ```bash
84
+ treeseed dev start --web-runtime local --json
85
+ treeseed dev status --json
86
+ treeseed dev status --all --json
87
+ treeseed dev logs --follow
88
+ treeseed dev stop --json
89
+ treeseed dev restart --web-runtime local --json
90
+ ```
91
+
92
+ Managed instances are scoped to the current physical worktree. The core runtime writes `.treeseed/dev/instances/<scope>.json`, `.treeseed/dev/pids/<scope>.pid`, and `.treeseed/logs/dev-<scope>.jsonl` in that worktree. A repository-family index under the git common dir makes sibling worktree instances discoverable to humans and AI agents.
93
+
94
+ `--force` replaces only the current worktree instance. `--force-conflicts` is the explicit cross-worktree port-owner escape hatch. Additional worktrees receive stable alternate port blocks and worktree-specific local PostgreSQL/Mailpit names, so many agents can run development sessions in the same repository family.
95
+
96
+ For the complete architecture, see the root workspace document `docs/local-dev-instances.md`.
97
+
75
98
  ## Agent-Safe Workflow
76
99
 
77
100
  Use planning mode before any destructive or multi-repo mutation:
@@ -52,6 +52,11 @@ const handleDev = async (invocation, context) => {
52
52
  }
53
53
  const feedback = typeof invocation.args.feedback === "string" ? invocation.args.feedback : void 0;
54
54
  const watch = feedback !== "off";
55
+ const subcommand = typeof invocation.positionals[0] === "string" ? invocation.positionals[0] : "";
56
+ const managedSubcommands = /* @__PURE__ */ new Set(["start", "status", "logs", "stop", "restart"]);
57
+ if (subcommand && !managedSubcommands.has(subcommand)) {
58
+ return fail(`Unknown dev subcommand "${subcommand}". Use start, status, logs, stop, or restart.`);
59
+ }
55
60
  const passthroughArgs = ["--surfaces", "web,api"];
56
61
  const forwardStringOption = (name, flag) => {
57
62
  const value = invocation.args[name];
@@ -75,6 +80,9 @@ const handleDev = async (invocation, context) => {
75
80
  forwardBooleanOption("plan", "--plan");
76
81
  forwardBooleanOption("reset", "--reset");
77
82
  forwardBooleanOption("force", "--force");
83
+ forwardBooleanOption("forceConflicts", "--force-conflicts");
84
+ forwardBooleanOption("all", "--all");
85
+ forwardBooleanOption("follow", "--follow");
78
86
  forwardBooleanOption("json", "--json");
79
87
  const workspaceRoot = findNearestTreeseedWorkspaceRoot(context.cwd);
80
88
  const workspaceLinksMode = typeof invocation.args.workspaceLinks === "string" ? invocation.args.workspaceLinks : void 0;
@@ -83,7 +91,12 @@ const handleDev = async (invocation, context) => {
83
91
  context.write(`[workspace][link] Linked ${workspaceLinks.created.length} local workspace package paths.`, "stdout");
84
92
  }
85
93
  const resolved = resolveCoreDevEntrypoint(context.cwd);
86
- const args = watch ? [...resolved.args, ...passthroughArgs, "--watch"] : [...resolved.args, ...passthroughArgs];
94
+ const args = [
95
+ ...resolved.args,
96
+ ...subcommand ? [subcommand] : [],
97
+ ...passthroughArgs,
98
+ ...watch && !subcommand ? ["--watch"] : []
99
+ ];
87
100
  const result = context.spawn(resolved.command, args, {
88
101
  cwd: context.cwd,
89
102
  env: resolveTreeseedLaunchEnvironment({
@@ -32,10 +32,53 @@ const DEV_RUNTIME_OPTIONS = [
32
32
  { name: "open", flags: "--open <mode>", description: "Control whether dev opens the browser after readiness. Defaults to off; use --open on to launch it.", kind: "enum", values: ["auto", "on", "off"] },
33
33
  { name: "plan", flags: "--plan", description: "Print the dev runtime plan and exit without starting services.", kind: "boolean" },
34
34
  { name: "reset", flags: "--reset", description: "Clear local dev runtime state before setup, migrations, and service startup.", kind: "boolean" },
35
- { name: "force", flags: "--force", description: "Terminate overlapping Treeseed dev runtimes and listeners on required local dev ports before startup.", kind: "boolean" },
35
+ { name: "force", flags: "--force", description: "Replace the current worktree dev instance before startup.", kind: "boolean" },
36
+ { name: "forceConflicts", flags: "--force-conflicts", description: "Allow managed dev start to stop sibling worktree port owners when explicit ports conflict.", kind: "boolean" },
37
+ { name: "all", flags: "--all", description: "Apply managed dev status or stop to all worktrees in the repository family.", kind: "boolean" },
38
+ { name: "follow", flags: "--follow", description: "Follow managed dev logs when supported.", kind: "boolean" },
36
39
  { name: "json", flags: "--json", description: "Emit structured JSON or newline-delimited dev events.", kind: "boolean" },
37
40
  { name: "workspaceLinks", flags: "--workspace-links <mode>", description: "Control local workspace package links.", kind: "enum", values: ["auto", "off"] }
38
41
  ];
42
+ const DEV_STATUS_OPTIONS = DEV_RUNTIME_OPTIONS.filter((option) => ["all", "json"].includes(option.name));
43
+ const DEV_LOGS_OPTIONS = DEV_RUNTIME_OPTIONS.filter((option) => ["follow", "json"].includes(option.name));
44
+ const DEV_STOP_OPTIONS = DEV_RUNTIME_OPTIONS.filter((option) => ["all", "json"].includes(option.name));
45
+ const DEV_START_OPTIONS = DEV_RUNTIME_OPTIONS.filter((option) => !["all", "follow"].includes(option.name));
46
+ function devManagedHelpCommand(subcommand, spec) {
47
+ return {
48
+ id: `dev.${subcommand}`,
49
+ name: `dev ${subcommand}`,
50
+ aliases: [],
51
+ group: "Local Development",
52
+ summary: spec.summary,
53
+ description: spec.description,
54
+ provider: "default",
55
+ related: ["dev"],
56
+ usage: spec.usage,
57
+ options: spec.options,
58
+ examples: spec.examples,
59
+ helpVisible: false,
60
+ helpFeatured: false,
61
+ executionMode: "handler",
62
+ handlerName: "dev",
63
+ help: {
64
+ workflowPosition: "managed dev instance",
65
+ longSummary: [spec.description],
66
+ whenToUse: spec.whenToUse,
67
+ beforeYouRun: spec.beforeYouRun,
68
+ outcomes: spec.outcomes,
69
+ examples: spec.examples,
70
+ automationNotes: [
71
+ "These managed dev subcommands use the same `dev` handler and core supervisor as foreground `treeseed dev`.",
72
+ "Use `--json` when another process needs stable instance records, ports, URLs, PIDs, ready checks, or log paths."
73
+ ],
74
+ warnings: spec.warnings ?? [],
75
+ relatedDetails: [
76
+ related("dev", "Use `dev` without a subcommand for the foreground supervisor.")
77
+ ],
78
+ seeAlso: ["dev"]
79
+ }
80
+ };
81
+ }
39
82
  function genericWorkflowPosition(spec) {
40
83
  if (spec.group === "Workflow") {
41
84
  if (spec.name === "switch") return "start work";
@@ -1173,15 +1216,18 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
1173
1216
  })],
1174
1217
  ["dev", command({
1175
1218
  options: DEV_RUNTIME_OPTIONS,
1176
- examples: ["treeseed dev", "treeseed dev --reset", "treeseed dev --reset --plan --json", "treeseed dev --web-runtime local --plan --json", "treeseed dev --port 4322"],
1219
+ arguments: [{ name: "subcommand", description: "Optional managed instance action: start, status, logs, stop, or restart.", required: false }],
1220
+ examples: ["treeseed dev", "treeseed dev start", "treeseed dev status --all --json", "treeseed dev logs", "treeseed dev stop", "treeseed dev --web-runtime local --plan --json"],
1177
1221
  help: {
1178
1222
  longSummary: [
1179
- "Dev starts the local Treeseed Market web/API/runtime services as a foreground supervisor.",
1223
+ "Dev starts or manages the local Treeseed Market web/API/runtime services.",
1224
+ "Without a subcommand, dev runs as the existing foreground supervisor.",
1180
1225
  "Capacity provider lifecycle is package-owned and runs through `treeseed capacity ...`, not through `treeseed dev`."
1181
1226
  ],
1182
1227
  beforeYouRun: [
1183
1228
  "Run from the tenant or workspace root you want to develop.",
1184
1229
  "From the Market repo root, dev automatically starts the local Market API, managed local PostgreSQL, and Market operations runner alongside the web UI.",
1230
+ "Use `dev start` for worktree-scoped background instance management that AI agents can discover and stop later.",
1185
1231
  "Use `--plan --json` when you want to inspect fixed web/API/runner commands, setup steps, readiness checks, watched paths, and restart policy without starting services.",
1186
1232
  "Use `--reset` when you want a fresh local D1 database, Market PostgreSQL state, Mailpit inbox, generated worker bundle, and Wrangler temp output without deleting configuration.",
1187
1233
  "Dev prints the local web URL after readiness; it does not open a browser unless you pass `--open on` or `--open auto`.",
@@ -1189,6 +1235,10 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
1189
1235
  ],
1190
1236
  examples: [
1191
1237
  example("treeseed dev", "Start local Market development", "Run web, API, managed PostgreSQL setup, and the Market operations runner locally."),
1238
+ example("treeseed dev start", "Start a managed worktree instance", "Launch the same dev runtime detached, write worktree-local PID/log/state files, and return after readiness."),
1239
+ example("treeseed dev status --all --json", "Inspect all worktree instances", "List managed dev instances discovered through the repository-family index."),
1240
+ example("treeseed dev logs", "Show managed dev logs", "Print the current worktree managed dev log file."),
1241
+ example("treeseed dev stop", "Stop managed dev", "Stop only the current worktree managed dev instance."),
1192
1242
  example("treeseed dev --reset", "Start from a fresh local runtime", "Clear disposable local dev state, rerun setup and database migrations, then start the dev supervisor."),
1193
1243
  example("treeseed dev --reset --plan --json", "Inspect reset actions", "Emit the reset, setup, readiness, command, and watch plan without deleting local state or starting services."),
1194
1244
  example("treeseed dev --plan --json", "Inspect the runtime plan", "Emit a structured plan with setup steps, commands, ports, URLs, readiness checks, and watch entries."),
@@ -1257,7 +1307,129 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
1257
1307
  })],
1258
1308
  ["starlight:patch", command({ examples: ["treeseed starlight:patch"], executionMode: "adapter" })]
1259
1309
  ]);
1310
+ const DEV_MANAGED_OPERATION_SPECS = [
1311
+ devManagedHelpCommand("start", {
1312
+ summary: "Start a detached worktree-scoped dev instance.",
1313
+ description: "Start launches the same Market web/API/runner runtime as foreground `dev`, but detaches it, writes worktree-local instance state, captures logs, waits for readiness, and returns a summary.",
1314
+ usage: "treeseed dev start [--web-runtime local|provider|auto] [--port <port>] [--api-port <port>] [--force] [--force-conflicts] [--json]",
1315
+ options: DEV_START_OPTIONS,
1316
+ whenToUse: [
1317
+ "Use this when you want the local Market runtime to keep running after the shell command exits.",
1318
+ "Use it for AI-agent workflows where later commands need discoverable PID, port, URL, and log state."
1319
+ ],
1320
+ beforeYouRun: [
1321
+ "Run from the worktree that should own the managed instance.",
1322
+ "Use `--web-runtime local` for fast Astro hot reload.",
1323
+ "Use `--force` to replace only the current worktree instance. Use `--force-conflicts` only when you intentionally want to stop sibling worktree port owners."
1324
+ ],
1325
+ outcomes: [
1326
+ "Writes `.treeseed/dev/instances/web-api.json`, `.treeseed/dev/pids/web-api.pid`, and `.treeseed/logs/dev-web-api.jsonl` in the current worktree.",
1327
+ "Returns after readiness with URLs, ports, PID, process group, log path, and ready-check state."
1328
+ ],
1329
+ examples: [
1330
+ example("treeseed dev start --web-runtime local --force", "Start the current worktree instance", "Launch the managed background runtime with local web hot reload and replace any stale current-worktree owner."),
1331
+ example("treeseed dev start --port 4322 --api-port 3002 --json", "Start on explicit ports", "Pin web and API ports and emit a structured instance summary."),
1332
+ example("trsd dev start --web-runtime local", "Use the short alias", "Start the same managed runtime through the shorter entrypoint.")
1333
+ ]
1334
+ }),
1335
+ devManagedHelpCommand("status", {
1336
+ summary: "Inspect managed dev instance state.",
1337
+ description: "Status reads worktree-local managed dev state, checks process and readiness health, repairs stale records opportunistically, and can discover sibling worktree instances through the repository-family index.",
1338
+ usage: "treeseed dev status [--all] [--json]",
1339
+ options: DEV_STATUS_OPTIONS,
1340
+ whenToUse: [
1341
+ "Use this before starting a new managed instance to see whether one is already running.",
1342
+ "Use `--all` when multiple worktrees or AI agents may be running sibling instances for the same repository family."
1343
+ ],
1344
+ beforeYouRun: [
1345
+ "Run from the worktree you want to inspect.",
1346
+ "Use `--json` for automation that needs the exact status, PID, ports, URLs, stale reason, or sibling conflicts."
1347
+ ],
1348
+ outcomes: [
1349
+ "Prints ready, starting, degraded, stopped, or stale state for the current worktree instance.",
1350
+ "With `--all`, includes instances discovered from sibling worktrees through the shared repository-family index."
1351
+ ],
1352
+ examples: [
1353
+ example("treeseed dev status", "Inspect current worktree", "Show the managed instance state for the current worktree."),
1354
+ example("treeseed dev status --all", "Inspect sibling worktrees", "List managed instances discoverable across the repository family."),
1355
+ example("treeseed dev status --json", "Read status programmatically", "Emit machine-readable instance state for agents or scripts.")
1356
+ ]
1357
+ }),
1358
+ devManagedHelpCommand("logs", {
1359
+ summary: "Read managed dev logs.",
1360
+ description: "Logs prints the current worktree managed dev log in human-readable form, rendering structured dev events as concise text and optionally following the stable log file.",
1361
+ usage: "treeseed dev logs [--follow] [--json]",
1362
+ options: DEV_LOGS_OPTIONS,
1363
+ whenToUse: [
1364
+ "Use this when `dev status` reports stale, degraded, or stopped and you need to see what happened.",
1365
+ "Use `--follow` while iterating locally to watch the background runtime without reattaching to the supervisor process."
1366
+ ],
1367
+ beforeYouRun: [
1368
+ "Run from the worktree that owns the managed instance.",
1369
+ "Default human output shows a recent, readable tail. Use the reported log path if you need the full historical file."
1370
+ ],
1371
+ outcomes: [
1372
+ "Prints the current managed log tail from `.treeseed/logs/dev-web-api.jsonl`.",
1373
+ "With `--follow`, continues streaming appended log lines until interrupted."
1374
+ ],
1375
+ examples: [
1376
+ example("treeseed dev logs", "Show recent managed logs", "Print the current worktree managed dev log tail in human-readable form."),
1377
+ example("treeseed dev logs --follow", "Follow background runtime logs", "Stream appended log entries while the managed instance runs."),
1378
+ example("treeseed dev status --json", "Find the log path", "Use status when automation needs the authoritative log file location.")
1379
+ ]
1380
+ }),
1381
+ devManagedHelpCommand("stop", {
1382
+ summary: "Stop managed dev instances.",
1383
+ description: "Stop terminates the current worktree managed dev process group and leaves sibling worktree instances alone unless `--all` is explicitly provided.",
1384
+ usage: "treeseed dev stop [--all] [--json]",
1385
+ options: DEV_STOP_OPTIONS,
1386
+ whenToUse: [
1387
+ "Use this when you are done with the current worktree background runtime.",
1388
+ "Use `--all` only when intentionally cleaning up every discoverable managed instance in the repository family."
1389
+ ],
1390
+ beforeYouRun: [
1391
+ "Run from the worktree whose instance should be stopped.",
1392
+ "Prefer `dev status --all` first when several agents may be working in sibling worktrees."
1393
+ ],
1394
+ outcomes: [
1395
+ "Stops only the owned process group for the current worktree by default.",
1396
+ "Marks stopped or stale records so later `dev start` and `dev status` calls see accurate state."
1397
+ ],
1398
+ warnings: [
1399
+ "`--all` can interrupt sibling worktree sessions owned by other humans or agents."
1400
+ ],
1401
+ examples: [
1402
+ example("treeseed dev stop", "Stop current worktree instance", "Terminate only the current managed dev process group."),
1403
+ example("treeseed dev stop --all", "Stop repository-family instances", "Stop all discoverable managed instances for the repository family."),
1404
+ example("treeseed dev stop --json", "Stop from automation", "Emit a structured stop summary.")
1405
+ ]
1406
+ }),
1407
+ devManagedHelpCommand("restart", {
1408
+ summary: "Restart a managed dev instance.",
1409
+ description: "Restart stops the current worktree managed dev instance, then starts it again with the same managed-start semantics, state files, log path, readiness checks, and port ownership rules.",
1410
+ usage: "treeseed dev restart [--web-runtime local|provider|auto] [--port <port>] [--api-port <port>] [--force] [--force-conflicts] [--json]",
1411
+ options: DEV_START_OPTIONS,
1412
+ whenToUse: [
1413
+ "Use this after changing runtime configuration, package links, or local service state that needs a fresh supervisor.",
1414
+ "Use it when the managed instance is degraded and a clean stop/start is preferable to inspecting every child process manually."
1415
+ ],
1416
+ beforeYouRun: [
1417
+ "Run from the worktree that owns the managed instance.",
1418
+ "Pass the same runtime options you would use with `dev start` when you want to change ports or web runtime mode during restart."
1419
+ ],
1420
+ outcomes: [
1421
+ "Stops the current worktree managed instance and starts a new one.",
1422
+ "Returns the new PID, process group, ports, URLs, log path, and readiness result."
1423
+ ],
1424
+ examples: [
1425
+ example("treeseed dev restart --web-runtime local --force", "Restart local hot-reload runtime", "Replace the current worktree managed instance and wait for readiness."),
1426
+ example("treeseed dev restart --port 4322 --api-port 3002 --json", "Restart on new ports", "Restart with explicit web/API ports and emit a structured summary."),
1427
+ example("trsd dev restart", "Use the short alias", "Restart through the shorter entrypoint.")
1428
+ ]
1429
+ })
1430
+ ];
1260
1431
  const CLI_ONLY_OPERATION_SPECS = [
1432
+ ...DEV_MANAGED_OPERATION_SPECS,
1261
1433
  {
1262
1434
  id: "seed.plan",
1263
1435
  name: "seed",
@@ -22,6 +22,28 @@ function shouldRenderCommandHelp(spec, argv) {
22
22
  const treeseedArgs = separatorIndex >= 0 ? argv.slice(0, separatorIndex) : argv;
23
23
  return treeseedArgs.some(isHelpFlag);
24
24
  }
25
+ function resolveNestedDevHelpTarget(argv) {
26
+ const managedSubcommands = /* @__PURE__ */ new Set(["start", "status", "logs", "stop", "restart"]);
27
+ const subcommand = argv.find((arg) => !arg.startsWith("-") && managedSubcommands.has(arg));
28
+ return subcommand ? `dev ${subcommand}` : null;
29
+ }
30
+ function resolveCommandHelpTarget(spec, argv) {
31
+ if (spec.name === "dev") {
32
+ return resolveNestedDevHelpTarget(argv) ?? spec.name;
33
+ }
34
+ return spec.name;
35
+ }
36
+ function resolveExplicitHelpTarget(args) {
37
+ const helpArgs = args.filter((arg) => !arg.startsWith("-"));
38
+ if (helpArgs.length === 0) return null;
39
+ for (let length = Math.min(3, helpArgs.length); length > 0; length -= 1) {
40
+ const candidate = helpArgs.slice(0, length).join(" ");
41
+ if (findTreeseedOperation(candidate)) {
42
+ return candidate;
43
+ }
44
+ }
45
+ return helpArgs[0] ?? null;
46
+ }
25
47
  function isNoColorFlag(value) {
26
48
  return value === "--no-color";
27
49
  }
@@ -303,13 +325,14 @@ class TreeseedOperationsSdk {
303
325
  return writeTreeseedResult({ exitCode: 1, stderr: [lines.join("\n")] }, context);
304
326
  }
305
327
  if (shouldRenderCommandHelp(spec, argv)) {
328
+ const helpTarget = resolveCommandHelpTarget(spec, argv);
306
329
  if (shouldUseInkHelp(context)) {
307
- const helpExitCode = await renderTreeseedHelpInk(spec.name, context);
330
+ const helpExitCode = await renderTreeseedHelpInk(helpTarget, context);
308
331
  if (typeof helpExitCode === "number") {
309
332
  return helpExitCode;
310
333
  }
311
334
  }
312
- context.write(renderTreeseedHelp(spec.name), "stdout");
335
+ context.write(renderTreeseedHelp(helpTarget), "stdout");
313
336
  return 0;
314
337
  }
315
338
  return spec.executionMode === "adapter" ? this.executeAdapter(spec, argv, context) : spec.executionMode === "delegate" ? this.executeAgents(argv, context) : this.executeHandler(spec, argv, context);
@@ -318,7 +341,7 @@ class TreeseedOperationsSdk {
318
341
  const context = createTreeseedCommandContext(overrides);
319
342
  const [firstArg, ...restArgs] = argv;
320
343
  if (!firstArg || isHelpFlag(firstArg) || firstArg === "help") {
321
- const commandName = firstArg === "help" ? restArgs[0] ?? null : null;
344
+ const commandName = firstArg === "help" ? resolveExplicitHelpTarget(restArgs) : null;
322
345
  if (shouldUseInkHelp(context)) {
323
346
  const helpExitCode = await renderTreeseedHelpInk(commandName, context);
324
347
  if (typeof helpExitCode === "number") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/cli",
3
- "version": "0.10.20",
3
+ "version": "0.10.21",
4
4
  "description": "Operator-facing Treeseed CLI package.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -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": "github:treeseed-ai/sdk#0.10.26",
48
+ "@treeseed/sdk": "github:treeseed-ai/sdk#0.10.27",
49
49
  "ink": "^7.0.0",
50
50
  "react": "^19.2.5"
51
51
  },