@treeseed/cli 0.6.15 → 0.6.17

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.
@@ -0,0 +1,2 @@
1
+ import type { TreeseedCommandHandler } from '../types.js';
2
+ export declare const handleCi: TreeseedCommandHandler;
@@ -0,0 +1,96 @@
1
+ import { guidedResult } from "./utils.js";
2
+ import { createWorkflowSdk, workflowErrorResult } from "./workflow.js";
3
+ function asStringArray(value) {
4
+ return Array.isArray(value) ? value.map(String) : typeof value === "string" ? [value] : [];
5
+ }
6
+ function workflowLabel(repo, workflow) {
7
+ const status = workflow.conclusion ?? workflow.state ?? "unknown";
8
+ return `- ${repo.name ?? repo.repository ?? "repo"} ${workflow.workflow ?? "workflow"}: ${status}${workflow.url ? ` (${workflow.url})` : ""}${workflow.inspectCommand ? `
9
+ Inspect: ${workflow.inspectCommand}` : ""}`;
10
+ }
11
+ function failureLines(payload) {
12
+ return payload.failures.flatMap((failure) => {
13
+ const target = failure.jobName ? `${failure.repoName} ${failure.workflow ?? "workflow"} job ${failure.jobName}` : failure.workflow ? `${failure.repoName} ${failure.workflow}` : failure.repoName;
14
+ const lines = [
15
+ `- ${target}: ${failure.message}`,
16
+ ...failure.url ? [` URL: ${failure.url}`] : [],
17
+ ...failure.inspectCommand ? [` Inspect: ${failure.inspectCommand}`] : [],
18
+ ...failure.failedSteps.length > 0 ? [` Failed steps: ${failure.failedSteps.map((step) => step.name).join(", ")}`] : []
19
+ ];
20
+ if (failure.logExcerpt) {
21
+ lines.push(" Log excerpt:");
22
+ lines.push(...failure.logExcerpt.split(/\r?\n/u).map((line) => ` ${line}`));
23
+ }
24
+ return lines;
25
+ });
26
+ }
27
+ function pendingLines(payload) {
28
+ return payload.repositories.flatMap((repo) => repo.workflows.filter((workflow) => workflow.state === "pending").map((workflow) => workflowLabel(repo, workflow)));
29
+ }
30
+ function missingOrNotPushedLines(payload) {
31
+ const lines = [];
32
+ for (const repo of payload.repositories) {
33
+ if (repo.state === "not_pushed" || repo.state === "error") {
34
+ lines.push(`- ${repo.name}: ${repo.message ?? repo.state}`);
35
+ continue;
36
+ }
37
+ for (const workflow of repo.workflows.filter((entry) => entry.state === "missing" || entry.state === "error")) {
38
+ lines.push(workflowLabel(repo, workflow));
39
+ }
40
+ }
41
+ return lines;
42
+ }
43
+ function passingLines(payload) {
44
+ return payload.repositories.flatMap((repo) => repo.workflows.filter((workflow) => workflow.state === "success").map((workflow) => workflowLabel(repo, workflow)));
45
+ }
46
+ function ciSections(payload, failedOnly) {
47
+ const sections = [
48
+ { title: "Failures", lines: failureLines(payload) },
49
+ { title: "Pending", lines: pendingLines(payload) },
50
+ { title: "Missing / Not Pushed", lines: missingOrNotPushedLines(payload) }
51
+ ];
52
+ if (!failedOnly) {
53
+ sections.push({ title: "Passing", lines: passingLines(payload) });
54
+ }
55
+ return sections;
56
+ }
57
+ const handleCi = async (invocation, context) => {
58
+ try {
59
+ const result = await createWorkflowSdk(context).ci({
60
+ failed: invocation.args.failed === true,
61
+ logs: invocation.args.logs === true,
62
+ logLines: typeof invocation.args.logLines === "string" ? invocation.args.logLines : void 0,
63
+ scope: typeof invocation.args.scope === "string" ? invocation.args.scope : void 0,
64
+ workflows: asStringArray(invocation.args.workflow),
65
+ branch: typeof invocation.args.branch === "string" ? invocation.args.branch : void 0,
66
+ strict: invocation.args.strict === true
67
+ });
68
+ const payload = result.payload;
69
+ return guidedResult({
70
+ command: invocation.commandName || "ci",
71
+ summary: payload.hasFailures ? "Treeseed CI found remote GitHub Actions failures." : payload.strict && payload.hasPending ? "Treeseed CI found pending remote GitHub Actions runs." : "Treeseed CI status is clear.",
72
+ facts: [
73
+ { label: "Mode", value: payload.mode },
74
+ { label: "Scope", value: payload.scope },
75
+ { label: "Branch", value: payload.branch ?? "(mixed)" },
76
+ { label: "Repositories", value: payload.summary.repositories },
77
+ { label: "Workflows", value: payload.summary.workflows },
78
+ { label: "Passing", value: payload.summary.success },
79
+ { label: "Failing", value: payload.summary.failure },
80
+ { label: "Pending", value: payload.summary.pending },
81
+ { label: "Missing", value: payload.summary.missing },
82
+ { label: "Not pushed", value: payload.summary.notPushed },
83
+ { label: "Errors", value: payload.summary.error },
84
+ { label: "Checked at", value: payload.checkedAt }
85
+ ],
86
+ sections: ciSections(payload, invocation.args.failed === true),
87
+ report: result,
88
+ exitCode: payload.exitCode
89
+ });
90
+ } catch (error) {
91
+ return workflowErrorResult(error);
92
+ }
93
+ };
94
+ export {
95
+ handleCi
96
+ };
@@ -336,7 +336,9 @@ function buildStartupDetailLines(step, draftValue) {
336
336
  `Pending value: ${formatDisplayValue(step, draftValue, "(unset)")}`,
337
337
  "",
338
338
  step.entry.description || "Treeseed needs this value to complete setup.",
339
- `How to get it: ${step.entry.howToGet || "Use the suggested/default value if it matches your setup."}`
339
+ "",
340
+ "How to get it:",
341
+ ...(step.entry.howToGet || "Use the suggested/default value if it matches your setup.").split("\n")
340
342
  ];
341
343
  }
342
344
  function buildFullDetailLines(page, draftValue) {
@@ -354,7 +356,9 @@ function buildFullDetailLines(page, draftValue) {
354
356
  `Pending: ${formatDisplayValue(page, draftValue, "(unset)")}`,
355
357
  "",
356
358
  page.entry.description || "(no description)",
357
- `Get it: ${page.entry.howToGet || "(no extra setup guidance)"}`
359
+ "",
360
+ "How to get it:",
361
+ ...(page.entry.howToGet || "(no extra setup guidance)").split("\n")
358
362
  ];
359
363
  }
360
364
  function detailViewportLines(lines, width, height, offset) {
@@ -65,6 +65,7 @@ const handleDev = async (invocation, context) => {
65
65
  forwardStringOption("feedback", "--feedback");
66
66
  forwardStringOption("open", "--open");
67
67
  forwardBooleanOption("plan", "--plan");
68
+ forwardBooleanOption("reset", "--reset");
68
69
  forwardBooleanOption("json", "--json");
69
70
  const workspaceRoot = findNearestTreeseedWorkspaceRoot(context.cwd);
70
71
  const workspaceLinksMode = typeof invocation.args.workspaceLinks === "string" ? invocation.args.workspaceLinks : void 0;
@@ -31,6 +31,7 @@ const DEV_RUNTIME_OPTIONS = [
31
31
  { name: "feedback", flags: "--feedback <mode>", description: "Control live feedback, service restarts, and browser reload stamps.", kind: "enum", values: ["live", "restart", "off"] },
32
32
  { name: "open", flags: "--open <mode>", description: "Control whether dev opens the browser after readiness.", 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
+ { name: "reset", flags: "--reset", description: "Clear local dev runtime state before setup, migrations, and service startup.", kind: "boolean" },
34
35
  { name: "json", flags: "--json", description: "Emit structured JSON or newline-delimited dev events.", kind: "boolean" },
35
36
  { name: "watch", flags: "--watch", description: "Enable live watch behavior. `dev` defaults to live feedback; this remains for compatibility.", kind: "boolean" },
36
37
  { name: "workspaceLinks", flags: "--workspace-links <mode>", description: "Control local workspace package links.", kind: "enum", values: ["auto", "off"] }
@@ -191,6 +192,56 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
191
192
  executionMode: "handler",
192
193
  handlerName: "status"
193
194
  })],
195
+ ["ci", command({
196
+ options: [
197
+ { name: "failed", flags: "--failed", description: "Focus human output on failures and attention-needed workflows.", kind: "boolean" },
198
+ { name: "logs", flags: "--logs", description: "Fetch capped log excerpts for failed jobs.", kind: "boolean" },
199
+ { name: "logLines", flags: "--log-lines <n>", description: "Maximum failed-job log lines to include with --logs.", kind: "string" },
200
+ { name: "scope", flags: "--scope <scope>", description: "Select workspace, root, or package repositories.", kind: "enum", values: ["workspace", "root", "packages"] },
201
+ { name: "workflow", flags: "--workflow <file>", description: "Inspect a specific workflow file. May be repeated.", kind: "string", repeatable: true },
202
+ { name: "branch", flags: "--branch <name>", description: "Inspect this branch name in all selected repositories.", kind: "string" },
203
+ { name: "strict", flags: "--strict", description: "Return nonzero for pending workflows as well as failures.", kind: "boolean" },
204
+ { name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }
205
+ ],
206
+ examples: ["treeseed ci", "treeseed ci --failed", "treeseed ci --logs --log-lines 50", "treeseed ci --scope packages --workflow verify.yml --json"],
207
+ help: {
208
+ workflowPosition: "inspect",
209
+ longSummary: [
210
+ "CI inspects the remote GitHub Actions runs for the current branch heads in market and checked-out package repositories.",
211
+ "It is read-only and is designed for quickly finding failed hosted verification without digging through GitHub UI pages."
212
+ ],
213
+ whenToUse: [
214
+ "Use this after `save`, `stage`, or a pushed package change when you need the latest remote verification state.",
215
+ "Use `--failed` when you only want attention items, or `--logs` when you want failed-job excerpts inline."
216
+ ],
217
+ beforeYouRun: [
218
+ "Run from the Treeseed workspace you want to inspect.",
219
+ "Make sure the branch heads you care about have been pushed; unpushed heads are reported as not pushed.",
220
+ "Use `--json` for agent or script consumption."
221
+ ],
222
+ outcomes: [
223
+ "Lists passing, pending, missing, not-pushed, and failing GitHub Actions workflows.",
224
+ "Shows failed jobs, failed steps when GitHub provides them, workflow URLs, and inspect commands."
225
+ ],
226
+ examples: [
227
+ example("treeseed ci", "Inspect workspace CI", "Check market and checked-out package workflows for the active branch heads."),
228
+ example("treeseed ci --failed", "Focus on failures", "Show only failed and attention-needed workflows in human output."),
229
+ example("treeseed ci --logs --log-lines 50", "Include failed logs", "Fetch compact failed-job log excerpts while keeping output bounded."),
230
+ example("treeseed ci --scope packages --json", "Automate package CI checks", "Emit structured package workflow status for agents and scripts.")
231
+ ],
232
+ automationNotes: [
233
+ "`--json` includes the full repository list plus a flattened `failures` array.",
234
+ "The command is read-only; it does not wait, rerun, or mutate GitHub Actions runs."
235
+ ],
236
+ relatedDetails: [
237
+ related("status", "Use `status` for local workspace health before inspecting hosted CI."),
238
+ related("save", "Use `save` before CI inspection when local changes need to be pushed."),
239
+ related("stage", "Use `stage` after hosted CI is healthy and the task is ready for staging.")
240
+ ]
241
+ },
242
+ executionMode: "handler",
243
+ handlerName: "ci"
244
+ })],
194
245
  ["tasks", command({
195
246
  options: [{ name: "json", flags: "--json", description: "Emit machine-readable JSON instead of human-readable text.", kind: "boolean" }],
196
247
  examples: ["treeseed tasks", "treeseed tasks --json"],
@@ -1052,7 +1103,7 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
1052
1103
  })],
1053
1104
  ["dev", command({
1054
1105
  options: DEV_RUNTIME_OPTIONS,
1055
- examples: ["treeseed dev", "treeseed dev --plan --json", "treeseed dev --surface web --port 4322 --open off"],
1106
+ examples: ["treeseed dev", "treeseed dev --reset", "treeseed dev --reset --plan --json", "treeseed dev --surface web --port 4322 --open off"],
1056
1107
  help: {
1057
1108
  longSummary: [
1058
1109
  "Dev starts the unified local Treeseed runtime as a foreground supervisor so you can work against the integrated web, API, and supporting local surfaces.",
@@ -1061,10 +1112,13 @@ const CLI_COMMAND_OVERLAYS = /* @__PURE__ */ new Map([
1061
1112
  beforeYouRun: [
1062
1113
  "Run from the tenant or workspace root you want to develop.",
1063
1114
  "Use `--plan --json` when you want to inspect commands, setup steps, readiness checks, and watched paths without starting services.",
1115
+ "Use `--reset` when you want a fresh local D1 database, Mailpit inbox, generated worker bundle, and Wrangler temp output without deleting configuration.",
1064
1116
  "Keep the foreground process running while you test. Press Ctrl+C to stop the supervised stack and free the local ports."
1065
1117
  ],
1066
1118
  examples: [
1067
1119
  example("treeseed dev", "Start integrated local development", "Run the default integrated local runtime and keep supervising it in the foreground."),
1120
+ example("treeseed dev --reset", "Start from a fresh local runtime", "Clear disposable local dev state, rerun setup and D1 migrations, then start the dev supervisor."),
1121
+ 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."),
1068
1122
  example("treeseed dev --plan --json", "Inspect the runtime plan", "Emit a structured plan with setup steps, commands, ports, URLs, readiness checks, and watch entries."),
1069
1123
  example("treeseed dev --surface web --port 4322 --open off", "Run only the web surface", "Start the Astro UI on a specific port without opening a browser."),
1070
1124
  example("trsd dev", "Use the short alias", "Start the same local runtime through the shorter entrypoint."),
@@ -8,6 +8,7 @@ export declare const COMMAND_HANDLERS: {
8
8
  readonly release: import("./operations-types.js").TreeseedCommandHandler;
9
9
  readonly destroy: import("./operations-types.js").TreeseedCommandHandler;
10
10
  readonly status: import("./operations-types.js").TreeseedCommandHandler;
11
+ readonly ci: import("./operations-types.js").TreeseedCommandHandler;
11
12
  readonly dev: import("./operations-types.js").TreeseedCommandHandler;
12
13
  readonly 'dev:watch': import("./operations-types.js").TreeseedCommandHandler;
13
14
  readonly doctor: import("./operations-types.js").TreeseedCommandHandler;
@@ -10,6 +10,7 @@ import { handleSave } from "./handlers/save.js";
10
10
  import { handleRelease } from "./handlers/release.js";
11
11
  import { handleDestroy } from "./handlers/destroy.js";
12
12
  import { handleStatus } from "./handlers/status.js";
13
+ import { handleCi } from "./handlers/ci.js";
13
14
  import { handleDev } from "./handlers/dev.js";
14
15
  import { handleDoctor } from "./handlers/doctor.js";
15
16
  import { handleRollback } from "./handlers/rollback.js";
@@ -42,6 +43,7 @@ const COMMAND_HANDLERS = {
42
43
  release: handleRelease,
43
44
  destroy: handleDestroy,
44
45
  status: handleStatus,
46
+ ci: handleCi,
45
47
  dev: handleDev,
46
48
  "dev:watch": handleDev,
47
49
  doctor: handleDoctor,
@@ -70,7 +70,7 @@ type AppFrameProps = {
70
70
  body: React.ReactNode;
71
71
  footer: React.ReactNode;
72
72
  };
73
- export declare function AppFrame(props: AppFrameProps): any;
73
+ export declare function AppFrame(props: AppFrameProps): React.ReactElement;
74
74
  type TopTabsProps = {
75
75
  title?: string;
76
76
  items: TabItem[];
@@ -79,7 +79,7 @@ type TopTabsProps = {
79
79
  width: number;
80
80
  prefix?: string;
81
81
  };
82
- export declare function TopTabs(props: TopTabsProps): any;
82
+ export declare function TopTabs(props: TopTabsProps): React.ReactElement;
83
83
  type SidebarListProps = {
84
84
  width: number;
85
85
  height: number;
@@ -93,7 +93,7 @@ type SidebarListProps = {
93
93
  scrollState?: ScrollRegionState;
94
94
  title?: string;
95
95
  };
96
- export declare function SidebarList(props: SidebarListProps): any;
96
+ export declare function SidebarList(props: SidebarListProps): React.ReactElement;
97
97
  type ScrollPanelProps = {
98
98
  width: number;
99
99
  height: number;
@@ -103,7 +103,7 @@ type ScrollPanelProps = {
103
103
  tone?: 'normal' | 'accent';
104
104
  scrollState?: ScrollRegionState;
105
105
  };
106
- export declare function ScrollPanel(props: ScrollPanelProps): any;
106
+ export declare function ScrollPanel(props: ScrollPanelProps): React.ReactElement;
107
107
  type FieldCardProps = {
108
108
  width: number;
109
109
  height: number;
@@ -111,7 +111,7 @@ type FieldCardProps = {
111
111
  lines: string[];
112
112
  focused?: boolean;
113
113
  };
114
- export declare function FieldCard(props: FieldCardProps): any;
114
+ export declare function FieldCard(props: FieldCardProps): React.ReactElement;
115
115
  type TextInputFieldProps = {
116
116
  label: string;
117
117
  value: string;
@@ -123,7 +123,7 @@ type TextInputFieldProps = {
123
123
  cursorPosition?: number;
124
124
  helperText?: string;
125
125
  };
126
- export declare function TextInputField(props: TextInputFieldProps): any;
126
+ export declare function TextInputField(props: TextInputFieldProps): React.ReactElement;
127
127
  type TextAreaFieldProps = {
128
128
  label: string;
129
129
  value: string;
@@ -131,28 +131,28 @@ type TextAreaFieldProps = {
131
131
  height: number;
132
132
  focused?: boolean;
133
133
  };
134
- export declare function TextAreaField(props: TextAreaFieldProps): any;
134
+ export declare function TextAreaField(props: TextAreaFieldProps): React.ReactElement;
135
135
  type ButtonProps = {
136
136
  label: string;
137
137
  focused?: boolean;
138
138
  active?: boolean;
139
139
  width?: number;
140
140
  };
141
- export declare function PrimaryButton(props: ButtonProps): any;
142
- export declare function SecondaryButton(props: ButtonProps): any;
141
+ export declare function PrimaryButton(props: ButtonProps): React.ReactElement;
142
+ export declare function SecondaryButton(props: ButtonProps): React.ReactElement;
143
143
  type StatusBarProps = {
144
144
  width: number;
145
145
  primary: string;
146
146
  secondary?: string;
147
147
  accent?: boolean;
148
148
  };
149
- export declare function StatusBar(props: StatusBarProps): any;
149
+ export declare function StatusBar(props: StatusBarProps): React.ReactElement;
150
150
  export declare function EmptyState(props: {
151
151
  width: number;
152
152
  height: number;
153
153
  title: string;
154
154
  message: string;
155
- }): any;
155
+ }): React.ReactElement;
156
156
  export declare function ConfirmDialog(props: {
157
157
  width: number;
158
158
  title: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/cli",
3
- "version": "0.6.15",
3
+ "version": "0.6.17",
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": "0.6.15",
48
+ "@treeseed/sdk": "0.6.17",
49
49
  "ink": "^7.0.0",
50
50
  "react": "^19.2.5"
51
51
  },