@smithers-orchestrator/cli 0.16.0

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 (110) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +55 -0
  3. package/src/AgentAvailability.ts +13 -0
  4. package/src/AgentAvailabilityStatus.ts +5 -0
  5. package/src/AggregateNodeDetailParams.ts +5 -0
  6. package/src/AskOptions.ts +12 -0
  7. package/src/ChatAttemptMeta.ts +7 -0
  8. package/src/ChatAttemptRow.ts +12 -0
  9. package/src/ChatOutputEvent.ts +6 -0
  10. package/src/DiffBundleLike.ts +6 -0
  11. package/src/DiscoveredWorkflow.ts +9 -0
  12. package/src/EnrichedNodeDetail.ts +60 -0
  13. package/src/EventCategory.ts +18 -0
  14. package/src/FindDbWaitOptions.ts +4 -0
  15. package/src/FormatEventLineOptions.ts +4 -0
  16. package/src/HijackCandidate.ts +11 -0
  17. package/src/HijackLaunchSpec.ts +6 -0
  18. package/src/InitWorkflowPackOptions.ts +4 -0
  19. package/src/InitWorkflowPackResult.ts +6 -0
  20. package/src/NativeHijackEngine.ts +8 -0
  21. package/src/NodeDetailAttempt.ts +22 -0
  22. package/src/NodeDetailTokenUsage.ts +11 -0
  23. package/src/NodeDetailToolCall.ts +12 -0
  24. package/src/ParsedNodeOutputEvent.ts +9 -0
  25. package/src/RenderNodeDetailOptions.ts +4 -0
  26. package/src/RunAutoResumeSkipReason.ts +4 -0
  27. package/src/RunDiffCommandInput.ts +13 -0
  28. package/src/RunDiffCommandResult.ts +3 -0
  29. package/src/RunOutputCommandInput.ts +12 -0
  30. package/src/RunOutputCommandResult.ts +3 -0
  31. package/src/RunRewindCommandInput.ts +14 -0
  32. package/src/RunRewindCommandResult.ts +3 -0
  33. package/src/RunTreeCommandInput.ts +14 -0
  34. package/src/RunTreeCommandResult.ts +3 -0
  35. package/src/SmithersEventType.ts +3 -0
  36. package/src/SupervisorOptions.ts +33 -0
  37. package/src/SupervisorPollSummary.ts +6 -0
  38. package/src/TreeRenderOptions.ts +5 -0
  39. package/src/WatchLoopOptions.ts +9 -0
  40. package/src/WatchLoopResult.ts +8 -0
  41. package/src/WatchRenderContext.ts +4 -0
  42. package/src/WhyBlocker.ts +17 -0
  43. package/src/WhyBlockerKind.ts +9 -0
  44. package/src/WhyDiagnosis.ts +10 -0
  45. package/src/WorkflowCta.ts +4 -0
  46. package/src/WorkflowSourceType.ts +1 -0
  47. package/src/agent-detection.js +257 -0
  48. package/src/ask.js +491 -0
  49. package/src/chat.js +226 -0
  50. package/src/diff.js +221 -0
  51. package/src/event-categories.js +141 -0
  52. package/src/find-db.js +93 -0
  53. package/src/format.js +272 -0
  54. package/src/hijack-session.js +207 -0
  55. package/src/hijack.js +226 -0
  56. package/src/index.d.ts +1 -0
  57. package/src/index.js +4868 -0
  58. package/src/mcp/SemanticMcpServerOptions.ts +4 -0
  59. package/src/mcp/SemanticToolCallResult.ts +14 -0
  60. package/src/mcp/SemanticToolContext.ts +6 -0
  61. package/src/mcp/SemanticToolDefinition.ts +13 -0
  62. package/src/mcp/SemanticToolError.ts +6 -0
  63. package/src/mcp/semantic-server.js +41 -0
  64. package/src/mcp/semantic-tools.js +1242 -0
  65. package/src/node-detail.js +682 -0
  66. package/src/output.js +111 -0
  67. package/src/resume-detached.js +37 -0
  68. package/src/rewind.js +88 -0
  69. package/src/scheduler.js +112 -0
  70. package/src/smithersRuntime.js +63 -0
  71. package/src/supervisor.js +418 -0
  72. package/src/tree.js +307 -0
  73. package/src/tui/app.jsx +139 -0
  74. package/src/tui/app.tsx +5 -0
  75. package/src/tui/components/AskModal.jsx +109 -0
  76. package/src/tui/components/AskModal.tsx +3 -0
  77. package/src/tui/components/AttentionPane.jsx +112 -0
  78. package/src/tui/components/AttentionPane.tsx +6 -0
  79. package/src/tui/components/ChatPane.jsx +57 -0
  80. package/src/tui/components/ChatPane.tsx +7 -0
  81. package/src/tui/components/CronList.jsx +87 -0
  82. package/src/tui/components/CronList.tsx +5 -0
  83. package/src/tui/components/DetailsPane.jsx +96 -0
  84. package/src/tui/components/DetailsPane.tsx +7 -0
  85. package/src/tui/components/FramesPane.jsx +147 -0
  86. package/src/tui/components/FramesPane.tsx +8 -0
  87. package/src/tui/components/LogsPane.jsx +46 -0
  88. package/src/tui/components/LogsPane.tsx +6 -0
  89. package/src/tui/components/MetricsPane.jsx +108 -0
  90. package/src/tui/components/MetricsPane.tsx +5 -0
  91. package/src/tui/components/NodeDetailView.jsx +284 -0
  92. package/src/tui/components/NodeDetailView.tsx +7 -0
  93. package/src/tui/components/NodeInspector.jsx +51 -0
  94. package/src/tui/components/NodeInspector.tsx +7 -0
  95. package/src/tui/components/RunDetailView.jsx +190 -0
  96. package/src/tui/components/RunDetailView.tsx +7 -0
  97. package/src/tui/components/RunsList.jsx +184 -0
  98. package/src/tui/components/RunsList.tsx +7 -0
  99. package/src/tui/components/SqliteBrowser.jsx +131 -0
  100. package/src/tui/components/SqliteBrowser.tsx +5 -0
  101. package/src/tui/components/WorkflowLauncher.jsx +63 -0
  102. package/src/tui/components/WorkflowLauncher.tsx +3 -0
  103. package/src/util/CliErrorMapping.ts +7 -0
  104. package/src/util/CliExitCode.ts +10 -0
  105. package/src/util/errorMessage.js +212 -0
  106. package/src/util/exitCodes.js +18 -0
  107. package/src/watch.js +128 -0
  108. package/src/why-diagnosis.js +1000 -0
  109. package/src/workflow-pack.js +2151 -0
  110. package/src/workflows.js +122 -0
package/src/hijack.js ADDED
@@ -0,0 +1,226 @@
1
+ import { spawn } from "node:child_process";
2
+ import { SmithersError } from "@smithers-orchestrator/errors";
3
+ /** @typedef {import("./HijackCandidate.ts").HijackCandidate} HijackCandidate */
4
+ /** @typedef {import("./HijackLaunchSpec.ts").HijackLaunchSpec} HijackLaunchSpec */
5
+ /** @typedef {import("./NativeHijackEngine.ts").NativeHijackEngine} NativeHijackEngine */
6
+ /** @typedef {import("@smithers-orchestrator/db/adapter").SmithersDb} SmithersDb */
7
+
8
+ /**
9
+ * @param {string | null} [metaJson]
10
+ * @returns {Record<string, unknown>}
11
+ */
12
+ function parseAttemptMeta(metaJson) {
13
+ if (!metaJson)
14
+ return {};
15
+ try {
16
+ const parsed = JSON.parse(metaJson);
17
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
18
+ ? parsed
19
+ : {};
20
+ }
21
+ catch {
22
+ return {};
23
+ }
24
+ }
25
+ /**
26
+ * @param {unknown} value
27
+ * @returns {NativeHijackEngine | undefined}
28
+ */
29
+ function asNativeHijackEngine(value) {
30
+ return value === "claude-code" ||
31
+ value === "codex" ||
32
+ value === "gemini" ||
33
+ value === "pi" ||
34
+ value === "kimi" ||
35
+ value === "forge" ||
36
+ value === "amp"
37
+ ? value
38
+ : undefined;
39
+ }
40
+ /**
41
+ * @param {unknown} value
42
+ * @returns {unknown[] | undefined}
43
+ */
44
+ function asConversationMessages(value) {
45
+ return Array.isArray(value) ? value : undefined;
46
+ }
47
+ /**
48
+ * @param {Record<string, unknown>} meta
49
+ * @returns {| { engine: string; mode: "native-cli"; resume: string } | { engine: string; mode: "conversation"; messages: unknown[] } | null}
50
+ */
51
+ function extractContinuationFromMeta(meta) {
52
+ const handoff = meta.hijackHandoff;
53
+ if (handoff && typeof handoff === "object" && !Array.isArray(handoff)) {
54
+ const engine = typeof handoff.engine === "string"
55
+ ? handoff.engine
56
+ : undefined;
57
+ const mode = handoff.mode === "conversation" ? "conversation" : "native-cli";
58
+ const resume = typeof handoff.resume === "string" ? handoff.resume : undefined;
59
+ const messages = asConversationMessages(handoff.messages);
60
+ if (engine && mode === "native-cli" && resume) {
61
+ return { engine, mode: "native-cli", resume };
62
+ }
63
+ if (engine && mode === "conversation" && messages?.length) {
64
+ return { engine, mode: "conversation", messages };
65
+ }
66
+ }
67
+ const engine = typeof meta.agentEngine === "string" ? meta.agentEngine : undefined;
68
+ const resume = typeof meta.agentResume === "string" ? meta.agentResume : undefined;
69
+ if (engine && resume) {
70
+ return { engine, mode: "native-cli", resume };
71
+ }
72
+ const messages = asConversationMessages(meta.agentConversation);
73
+ if (engine && messages?.length) {
74
+ return { engine, mode: "conversation", messages };
75
+ }
76
+ return null;
77
+ }
78
+ /**
79
+ * @param {SmithersDb} adapter
80
+ * @param {string} runId
81
+ * @param {string} [target]
82
+ * @returns {Promise<HijackCandidate | null>}
83
+ */
84
+ export async function resolveHijackCandidate(adapter, runId, target) {
85
+ const attempts = await adapter.listAttemptsForRun(runId);
86
+ const sortedAttempts = [...attempts].sort((a, b) => {
87
+ const aMs = a.startedAtMs ?? 0;
88
+ const bMs = b.startedAtMs ?? 0;
89
+ if (aMs !== bMs)
90
+ return bMs - aMs;
91
+ if ((a.iteration ?? 0) !== (b.iteration ?? 0))
92
+ return (b.iteration ?? 0) - (a.iteration ?? 0);
93
+ return (b.attempt ?? 0) - (a.attempt ?? 0);
94
+ });
95
+ for (const attempt of sortedAttempts) {
96
+ const meta = parseAttemptMeta(attempt.metaJson);
97
+ const extracted = extractContinuationFromMeta(meta);
98
+ if (!extracted)
99
+ continue;
100
+ if (target && target !== extracted.engine && target !== attempt.nodeId)
101
+ continue;
102
+ return {
103
+ runId,
104
+ nodeId: attempt.nodeId,
105
+ iteration: attempt.iteration ?? 0,
106
+ attempt: attempt.attempt,
107
+ engine: extracted.engine,
108
+ mode: extracted.mode,
109
+ resume: extracted.mode === "native-cli" ? extracted.resume : undefined,
110
+ messages: extracted.mode === "conversation" ? extracted.messages : undefined,
111
+ cwd: attempt.jjCwd ?? process.cwd(),
112
+ };
113
+ }
114
+ return null;
115
+ }
116
+ /**
117
+ * @param {SmithersDb} adapter
118
+ * @param {string} runId
119
+ * @param {{ target?: string; timeoutMs?: number }} [options]
120
+ * @returns {Promise<HijackCandidate>}
121
+ */
122
+ export async function waitForHijackCandidate(adapter, runId, options = {}) {
123
+ const timeoutMs = options.timeoutMs ?? 30_000;
124
+ const deadline = Date.now() + timeoutMs;
125
+ while (Date.now() <= deadline) {
126
+ const run = await adapter.getRun(runId);
127
+ const candidate = await resolveHijackCandidate(adapter, runId, options.target);
128
+ if (run && run.status !== "running" && candidate) {
129
+ return candidate;
130
+ }
131
+ await Bun.sleep(200);
132
+ }
133
+ throw new SmithersError("HIJACK_TIMEOUT", `Timed out waiting for Smithers to hand off run ${runId}`, { runId, timeoutMs });
134
+ }
135
+ /**
136
+ * @param {HijackCandidate} candidate
137
+ * @returns {HijackLaunchSpec}
138
+ */
139
+ export function buildHijackLaunchSpec(candidate) {
140
+ if (candidate.mode !== "native-cli" || !candidate.resume) {
141
+ throw new SmithersError("HIJACK_LAUNCH_MODE", `Candidate ${candidate.engine} requires the Smithers conversation hijack flow, not a native CLI launch`, candidate);
142
+ }
143
+ const env = { ...process.env };
144
+ if (candidate.engine === "claude-code") {
145
+ if (env.CLAUDE_CODE_ENTRYPOINT)
146
+ env.CLAUDE_CODE_ENTRYPOINT = "";
147
+ if (env.CLAUDECODE)
148
+ env.CLAUDECODE = "";
149
+ return {
150
+ command: "claude",
151
+ args: ["--resume", candidate.resume],
152
+ cwd: candidate.cwd,
153
+ env,
154
+ };
155
+ }
156
+ if (candidate.engine === "gemini") {
157
+ return {
158
+ command: "gemini",
159
+ args: ["--resume", candidate.resume],
160
+ cwd: candidate.cwd,
161
+ env,
162
+ };
163
+ }
164
+ if (candidate.engine === "pi") {
165
+ return {
166
+ command: "pi",
167
+ args: ["--session", candidate.resume],
168
+ cwd: candidate.cwd,
169
+ env,
170
+ };
171
+ }
172
+ if (candidate.engine === "kimi") {
173
+ return {
174
+ command: "kimi",
175
+ args: ["--session", candidate.resume, "--work-dir", candidate.cwd],
176
+ cwd: candidate.cwd,
177
+ env,
178
+ };
179
+ }
180
+ if (candidate.engine === "forge") {
181
+ return {
182
+ command: "forge",
183
+ args: ["--conversation-id", candidate.resume, "-C", candidate.cwd],
184
+ cwd: candidate.cwd,
185
+ env,
186
+ };
187
+ }
188
+ if (candidate.engine === "amp") {
189
+ return {
190
+ command: "amp",
191
+ args: ["threads", "continue", candidate.resume],
192
+ cwd: candidate.cwd,
193
+ env,
194
+ };
195
+ }
196
+ return {
197
+ command: "codex",
198
+ args: ["resume", candidate.resume, "-C", candidate.cwd],
199
+ cwd: candidate.cwd,
200
+ env,
201
+ };
202
+ }
203
+ /**
204
+ * @param {HijackCandidate} candidate
205
+ * @returns {candidate is HijackCandidate & { mode: "native-cli"; engine: NativeHijackEngine; resume: string }}
206
+ */
207
+ export function isNativeHijackCandidate(candidate) {
208
+ return candidate.mode === "native-cli" &&
209
+ typeof candidate.resume === "string" &&
210
+ Boolean(asNativeHijackEngine(candidate.engine));
211
+ }
212
+ /**
213
+ * @param {HijackLaunchSpec} spec
214
+ * @returns {Promise<number>}
215
+ */
216
+ export function launchHijackSession(spec) {
217
+ return new Promise((resolve, reject) => {
218
+ const child = spawn(spec.command, spec.args, {
219
+ cwd: spec.cwd,
220
+ env: spec.env,
221
+ stdio: "inherit",
222
+ });
223
+ child.on("error", reject);
224
+ child.on("close", (code) => resolve(code ?? 0));
225
+ });
226
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env bun