@glrs-dev/cli 2.4.0 → 2.6.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 (49) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/{chunk-HQUCVJ4G.js → chunk-FBXSGZAA.js} +4 -0
  3. package/dist/chunk-J3FXSHMA.js +263 -0
  4. package/dist/{chunk-5ZVUFNCP.js → chunk-S6N5E2GG.js} +8 -1
  5. package/dist/{chunk-2VMFXAJH.js → chunk-UO7WHIKY.js} +18 -5
  6. package/dist/cli.js +10 -3
  7. package/dist/commands/autopilot-tui.d.ts +11 -1
  8. package/dist/commands/autopilot-tui.js +2 -1
  9. package/dist/commands/autopilot.d.ts +2 -0
  10. package/dist/commands/autopilot.js +62 -21
  11. package/dist/commands/debrief.d.ts +2 -0
  12. package/dist/commands/debrief.js +1 -1
  13. package/dist/commands/loop.d.ts +2 -0
  14. package/dist/commands/loop.js +33 -12
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/node_modules/@glrs-dev/adapter-opencode/dist/index.d.ts +270 -0
  18. package/dist/node_modules/@glrs-dev/adapter-opencode/dist/index.js +506 -0
  19. package/dist/node_modules/@glrs-dev/adapter-opencode/package.json +8 -0
  20. package/dist/node_modules/@glrs-dev/autopilot/dist/auto-ship-EVLBKHUZ.js +7 -0
  21. package/dist/node_modules/@glrs-dev/autopilot/dist/changeset-generator-HAHYSSUR.js +15 -0
  22. package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-2X3CWH47.js +3288 -0
  23. package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-2ZQ6SBV3.js +70 -0
  24. package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-6JZQLIRP.js +781 -0
  25. package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-AWRK6S6G.js +91 -0
  26. package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-BLEIZHET.js +101 -0
  27. package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-GXXCEGDD.js +251 -0
  28. package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-S34HOCZ4.js +44 -0
  29. package/dist/node_modules/@glrs-dev/autopilot/dist/index.d.ts +1915 -0
  30. package/dist/node_modules/@glrs-dev/autopilot/dist/index.js +768 -0
  31. package/dist/node_modules/@glrs-dev/autopilot/dist/logger-3XLFMXLN.js +8 -0
  32. package/dist/node_modules/@glrs-dev/autopilot/dist/loop-session-YLCVJGPV.js +9 -0
  33. package/dist/node_modules/@glrs-dev/autopilot/dist/plan-enrichment-4SQYV5FC.js +17 -0
  34. package/dist/node_modules/@glrs-dev/autopilot/package.json +8 -0
  35. package/dist/vendor/harness-opencode/dist/agents/prompts/agents-md-writer.md +1 -1
  36. package/dist/vendor/harness-opencode/dist/agents/prompts/architecture-advisor.md +1 -1
  37. package/dist/vendor/harness-opencode/dist/agents/prompts/code-searcher.md +1 -1
  38. package/dist/vendor/harness-opencode/dist/agents/prompts/docs-maintainer.md +0 -8
  39. package/dist/vendor/harness-opencode/dist/agents/prompts/gap-analyzer.md +1 -3
  40. package/dist/vendor/harness-opencode/dist/agents/prompts/lib-reader.md +1 -1
  41. package/dist/vendor/harness-opencode/dist/agents/prompts/plan-reviewer.md +0 -2
  42. package/dist/vendor/harness-opencode/dist/agents/prompts/plan.md +1 -1
  43. package/dist/vendor/harness-opencode/dist/agents/prompts/prime.md +78 -262
  44. package/dist/vendor/harness-opencode/dist/agents/prompts/research.md +5 -14
  45. package/dist/vendor/harness-opencode/dist/agents/prompts/scoper.md +7 -2
  46. package/dist/vendor/harness-opencode/dist/autopilot/strategies/default.md +29 -0
  47. package/dist/vendor/harness-opencode/dist/index.js +112 -82
  48. package/dist/vendor/harness-opencode/package.json +1 -1
  49. package/package.json +9 -7
@@ -1,12 +1,18 @@
1
1
  import {
2
2
  runDebrief,
3
3
  shouldRunDebrief
4
- } from "../chunk-5ZVUFNCP.js";
4
+ } from "../chunk-S6N5E2GG.js";
5
+ import {
6
+ ADAPTER_NAMES,
7
+ DEFAULT_ADAPTER,
8
+ createAdapter,
9
+ resolveConfig
10
+ } from "../chunk-J3FXSHMA.js";
5
11
  import "../chunk-3RG5ZIWI.js";
6
12
 
7
13
  // src/commands/loop.ts
8
- import { command, option, positional, string as stringType, optional, number as numberType, flag } from "cmd-ts";
9
- import { runRalphLoop, MAX_ITERATIONS, TIMEOUT_MS } from "@glrs-dev/autopilot";
14
+ import { command, option, positional, string as stringType, optional, number as numberType, flag, oneOf } from "cmd-ts";
15
+ import { runRalphLoop, MAX_ITERATIONS, TIMEOUT_MS, applyCLIOverrides } from "@glrs-dev/autopilot";
10
16
  var loopCmd = command({
11
17
  name: "loop",
12
18
  description: "Run the Ralph loop: send a prompt to PRIME repeatedly until it emits <autopilot-done> or a budget is exhausted.",
@@ -43,9 +49,15 @@ var loopCmd = command({
43
49
  debriefOnly: flag({
44
50
  long: "debrief-only",
45
51
  description: "Run the debrief against the most recent log file without re-executing the loop. (Not yet implemented \u2014 requires log-directory discovery.)"
52
+ }),
53
+ adapter: option({
54
+ long: "adapter",
55
+ short: "a",
56
+ type: optional(oneOf(ADAPTER_NAMES)),
57
+ description: `Agent adapter to use (default: ${DEFAULT_ADAPTER}). Available: ${ADAPTER_NAMES.join(", ")}`
46
58
  })
47
59
  },
48
- handler: async ({ prompt, maxIterations, timeout, stallTimeout, noDebrief, notify, debriefOnly }) => {
60
+ handler: async ({ prompt, maxIterations, timeout, stallTimeout, noDebrief, notify, debriefOnly, adapter: adapterName }) => {
49
61
  const cwd = process.cwd();
50
62
  let loopStarted = false;
51
63
  const earlyExit = (signal) => {
@@ -66,17 +78,24 @@ ${signal} received before loop start \u2014 exiting
66
78
  process.exit(1);
67
79
  }
68
80
  const willDebrief = shouldRunDebrief({ noDebrief, env: process.env });
69
- const { OpenCodeAdapter } = await import("@glrs-dev/adapter-opencode");
70
- const adapter = new OpenCodeAdapter();
81
+ const resolvedConfig = resolveConfig(cwd);
82
+ const config = applyCLIOverrides(resolvedConfig, {
83
+ adapter: adapterName,
84
+ stallTimeout,
85
+ notify
86
+ });
87
+ const finalAdapterName = config.adapter ?? DEFAULT_ADAPTER;
88
+ const adapter = await createAdapter(finalAdapterName, config);
71
89
  const result = await runRalphLoop({
72
90
  prompt,
73
91
  cwd,
74
92
  maxIterations: maxIterations ?? void 0,
75
93
  timeoutMs: timeout ?? void 0,
76
- stallMs: stallTimeout ?? void 0,
77
- notifyUrl: notify ?? void 0,
94
+ stallMs: config.stall_timeout ?? void 0,
95
+ notifyUrl: config.notify_url ?? void 0,
78
96
  keepAlive: willDebrief,
79
- adapter
97
+ adapter,
98
+ config
80
99
  });
81
100
  loopStarted = true;
82
101
  process.removeListener("SIGINT", earlySigint);
@@ -96,7 +115,8 @@ ${icon} ${result.message}
96
115
  agentHandle: loopHandle,
97
116
  loopResult: result,
98
117
  prompt,
99
- cwd
118
+ cwd,
119
+ config
100
120
  });
101
121
  } catch {
102
122
  process.stderr.write("\x1B[33m\u26A0 Debrief failed (non-fatal)\x1B[0m\n");
@@ -105,14 +125,15 @@ ${icon} ${result.message}
105
125
  });
106
126
  }
107
127
  } else {
108
- const debriefAdapter = new OpenCodeAdapter();
128
+ const debriefAdapter = await createAdapter(config.adapter ?? DEFAULT_ADAPTER, config);
109
129
  const debriefHandle = await debriefAdapter.start({ cwd });
110
130
  try {
111
131
  await runDebrief({
112
132
  agentHandle: { adapter: debriefAdapter, handle: debriefHandle },
113
133
  loopResult: result,
114
134
  prompt,
115
- cwd
135
+ cwd,
136
+ config
116
137
  });
117
138
  } catch {
118
139
  process.stderr.write("\x1B[33m\u26A0 Debrief agent failed to start (non-fatal)\x1B[0m\n");
package/dist/index.d.ts CHANGED
@@ -24,7 +24,7 @@ interface ResolvedBin {
24
24
  }
25
25
  declare function resolveSubcommand(sub: Subcommand): ResolvedBin;
26
26
  declare const SUBCOMMANDS: Subcommand[];
27
- declare const HELP_TEXT = "glrs \u2014 unified CLI for the @glrs-dev ecosystem\n\nUSAGE\n glrs <subcommand> [args...]\n\nSUBCOMMANDS\n oc OpenCode agent harness (install, pilot, etc.)\n wt Worktree management (create, list, switch, delete, cleanup)\n dashboard Live TUI dashboard for all running autopilot sessions\n\nRun 'glrs <subcommand> --help' for per-command help.\n\nEXAMPLES\n glrs oc install\n glrs wt new\n glrs wt list\n glrs wt switch\n glrs dashboard\n\nREQUIREMENTS\n Bun >= 1.2.0 on PATH (install: https://bun.sh)\n\nDOCS https://glrs.dev\nISSUES https://github.com/iceglober/glrs/issues\n";
27
+ declare const HELP_TEXT = "glrs \u2014 unified CLI for the @glrs-dev ecosystem\n\nUSAGE\n glrs <subcommand> [args...]\n\nSUBCOMMANDS\n oc OpenCode agent harness (install, pilot, etc.)\n wt Worktree management (create, list, switch, delete, cleanup)\n autopilot Run the autopilot orchestrator (scope \u2192 plan \u2192 execute)\n loop Run the Ralph loop with a raw prompt\n dashboard Live TUI dashboard for all running autopilot sessions\n\nRun 'glrs <subcommand> --help' for per-command help.\n\nEXAMPLES\n glrs oc install\n glrs wt new\n glrs wt list\n glrs wt switch\n glrs autopilot --plan docs/plans/my-plan/\n glrs loop \"implement the auth middleware\"\n glrs dashboard\n\nREQUIREMENTS\n Bun >= 1.2.0 on PATH (install: https://bun.sh)\n\nDOCS https://glrs.dev\nISSUES https://github.com/iceglober/glrs/issues\n";
28
28
  declare const WORKTREE_HELP_TEXT = "glrs wt \u2014 worktree management\n\nUSAGE\n glrs wt <command> [args...]\n\nCOMMANDS\n new Create a new worktree (auto-named from origin/default)\n list, ls List all worktrees across repos\n switch, sw Interactively select and switch to a worktree\n delete, rm Remove worktrees (interactive or by name)\n cleanup Delete merged/stale worktrees\n\nEXAMPLES\n glrs wt new # Create worktree in current repo\n glrs wt new myrepo # Create worktree for named repo\n glrs wt list # Show all worktrees\n glrs wt list -i # Interactive picker\n glrs wt switch # Interactive switcher\n glrs wt delete my-branch # Delete specific worktree\n glrs wt cleanup # Clean up merged worktrees\n\nWorktrees are stored in ~/.glrs/worktrees/<repo>/<name>/\n";
29
29
 
30
30
  export { HELP_TEXT, type ResolvedBin, SUBCOMMANDS, type Subcommand, WORKTREE_HELP_TEXT, resolveSubcommand };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  SUBCOMMANDS,
4
4
  WORKTREE_HELP_TEXT,
5
5
  resolveSubcommand
6
- } from "./chunk-HQUCVJ4G.js";
6
+ } from "./chunk-FBXSGZAA.js";
7
7
  import "./chunk-3RG5ZIWI.js";
8
8
  export {
9
9
  HELP_TEXT,
@@ -0,0 +1,270 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { OpencodeClient } from '@opencode-ai/sdk';
3
+ import { AgentAdapter, AgentHandle, AdapterSessionResult } from '@glrs-dev/autopilot';
4
+
5
+ /**
6
+ * OpenCode server lifecycle helpers.
7
+ *
8
+ * General-purpose utilities for starting an OpenCode server, creating
9
+ * sessions, sending messages, and waiting for idle. Used by the Ralph
10
+ * autopilot loop and any future feature that needs to drive OpenCode
11
+ * programmatically.
12
+ *
13
+ * Extracted from src/pilot/server.ts so these helpers are not coupled
14
+ * to the pilot subsystem.
15
+ */
16
+
17
+ declare const execFileP: typeof execFile.__promisify__;
18
+ type StartedServer = {
19
+ url: string;
20
+ client: OpencodeClient;
21
+ shutdown: () => Promise<void>;
22
+ };
23
+ type SessionResult = {
24
+ kind: "idle";
25
+ } | {
26
+ kind: "stall";
27
+ stallMs: number;
28
+ } | {
29
+ kind: "abort";
30
+ } | {
31
+ kind: "error";
32
+ message: string;
33
+ } | {
34
+ kind: "question_rejected";
35
+ title: string;
36
+ };
37
+ declare const DEFAULT_STARTUP_TIMEOUT_MS = 30000;
38
+ /**
39
+ * Start an OpenCode server for the given working directory.
40
+ * Returns the server URL, a bound client, and a shutdown function.
41
+ *
42
+ * Uses createOpencode() which spawns the server process and creates
43
+ * a connected client. The server inherits process.cwd() as its project
44
+ * directory.
45
+ *
46
+ * When `agentOverrides` is provided, sets GLRS_AGENT_OVERRIDES in the
47
+ * server environment before startup, and restores the prior value on
48
+ * shutdown to keep the parent process clean.
49
+ */
50
+ declare function startServer(opts: {
51
+ cwd: string;
52
+ port?: number;
53
+ timeoutMs?: number;
54
+ agentOverrides?: Record<string, {
55
+ model?: string;
56
+ prompt?: string;
57
+ }>;
58
+ }): Promise<StartedServer>;
59
+ /**
60
+ * Verify the server is responsive by listing sessions.
61
+ * Fails fast with a diagnostic if the server isn't working.
62
+ */
63
+ declare function selfTest(client: OpencodeClient): Promise<void>;
64
+ /**
65
+ * Create a new OpenCode session for the given directory.
66
+ *
67
+ * Note: the SDK's `session.create` body takes only `parentID` and `title`
68
+ * — agent selection is a per-message concern in OpenCode's model and lives
69
+ * on `session.prompt`'s body, not on session creation. The working
70
+ * directory is passed via the `query.directory` param.
71
+ *
72
+ * Returns the session ID.
73
+ */
74
+ declare function createSession(client: OpencodeClient, opts: {
75
+ cwd: string;
76
+ agentName?: string;
77
+ }): Promise<string>;
78
+ /**
79
+ * Send a message to a session and wait for the agent to go idle.
80
+ *
81
+ * Uses `session.prompt` (the correct SDK method for sending a message).
82
+ * `session.chat` does not exist on the SDK — an earlier version of this
83
+ * module used that name and silently failed at runtime.
84
+ *
85
+ * The optional `onToolCall` callback fires once per tool invocation that
86
+ * reaches the `completed` state. Used by the autopilot loop to emit
87
+ * debug-level log events for each tool call.
88
+ *
89
+ * The optional `onTextDelta` callback fires for each assistant text
90
+ * chunk the agent streams. Used by the autopilot loop's stream-liveness
91
+ * indicator — between tool calls, the agent may stream text for minutes
92
+ * while reasoning, and without this signal the user can't distinguish
93
+ * "still working" from "hung."
94
+ *
95
+ * When `autoRejectPermissions: true`, any `permission.updated` event
96
+ * fired for this session is immediately answered with `response: "reject"`.
97
+ * Used by autopilot to prevent the `question` tool (or any tool-approval
98
+ * prompt) from deadlocking the session — no human is available to answer.
99
+ * The `onPermissionRejected` callback (if provided) fires each time a
100
+ * permission is auto-rejected so the caller can log it.
101
+ *
102
+ * Returns the result kind.
103
+ */
104
+ declare function sendAndWait(client: OpencodeClient, opts: {
105
+ sessionId: string;
106
+ message: string;
107
+ agentName?: string;
108
+ stallMs?: number;
109
+ abortSignal?: AbortSignal;
110
+ onToolCall?: (toolName: string, firstArg?: string) => void;
111
+ onTextDelta?: (charCount: number) => void;
112
+ /** Fires on `message.updated` events with cost/token data. Used for
113
+ * real-time cost visibility during long iterations. */
114
+ onCostUpdate?: (cost: number, tokens: {
115
+ input: number;
116
+ output: number;
117
+ }) => void;
118
+ autoRejectPermissions?: boolean;
119
+ /** Server URL for raw HTTP calls (question-reject endpoint). */
120
+ serverUrl?: string;
121
+ onPermissionRejected?: (permission: {
122
+ id: string;
123
+ type: string;
124
+ title: string;
125
+ }) => void;
126
+ }): Promise<SessionResult>;
127
+ /**
128
+ * Wait for a session to go idle (agent done), stall, abort, or error.
129
+ *
130
+ * `client.event.subscribe()` is async and returns `{ stream }` (a
131
+ * `ServerSentEventsResult`). The stream is an AsyncGenerator of events.
132
+ *
133
+ * When `onToolCall` is provided, fires once per tool invocation that
134
+ * reaches the `completed` state. Filters out pending/running transitions
135
+ * to avoid double-counting.
136
+ *
137
+ * When `onTextDelta` is provided, fires for each non-empty delta string
138
+ * the server emits (charCount = delta.length). Used by autopilot to
139
+ * signal liveness during long reasoning streams between tool calls.
140
+ *
141
+ * When `autoRejectPermissions: true`, any `permission.updated` event
142
+ * for this session is answered with `response: "reject"` via
143
+ * `POST /session/{id}/permissions/{permissionID}`. This prevents the
144
+ * `question` tool (and any tool-approval prompts) from deadlocking the
145
+ * session when no human is present to answer. `onPermissionRejected`
146
+ * fires synchronously with the permission payload so callers can log.
147
+ */
148
+ declare function waitForIdle(client: OpencodeClient, opts: {
149
+ sessionId: string;
150
+ stallMs?: number;
151
+ abortSignal?: AbortSignal;
152
+ onToolCall?: (toolName: string, firstArg?: string) => void;
153
+ onTextDelta?: (charCount: number) => void;
154
+ /** Fires on `message.updated` events with cost/token data. Used for
155
+ * real-time cost visibility during long iterations. */
156
+ onCostUpdate?: (cost: number, tokens: {
157
+ input: number;
158
+ output: number;
159
+ }) => void;
160
+ autoRejectPermissions?: boolean;
161
+ /** Server URL for raw HTTP calls (question-reject endpoint). */
162
+ serverUrl?: string;
163
+ onPermissionRejected?: (permission: {
164
+ id: string;
165
+ type: string;
166
+ title: string;
167
+ }) => void;
168
+ }): Promise<SessionResult>;
169
+ /**
170
+ * Get the cumulative cost of a session in USD.
171
+ *
172
+ * Cost is tracked per-message on the server (AssistantMessage.cost),
173
+ * not aggregated on the Session object. We sum across all assistant
174
+ * messages to get a session total.
175
+ */
176
+ declare function getSessionCost(client: OpencodeClient, sessionId: string): Promise<number>;
177
+ /**
178
+ * Get cumulative cost + token totals for a session.
179
+ *
180
+ * Sums cost, input tokens, and output tokens across all assistant
181
+ * messages. Returns zeros on any error (non-fatal).
182
+ */
183
+ declare function getSessionStats(client: OpencodeClient, sessionId: string): Promise<{
184
+ cost: number;
185
+ tokensIn: number;
186
+ tokensOut: number;
187
+ }>;
188
+ /**
189
+ * Fetch the last assistant message text from a session.
190
+ *
191
+ * Calls `client.session.messages()`, filters to assistant-role messages,
192
+ * takes the last one, and concatenates all text parts.
193
+ *
194
+ * Returns an empty string if there are no assistant messages or if the
195
+ * API call fails.
196
+ */
197
+ declare function getLastAssistantMessage(client: OpencodeClient, sessionId: string): Promise<string>;
198
+
199
+ /**
200
+ * Extract actionable error details from OpenCode server logs.
201
+ *
202
+ * When the SSE `session.error` event carries only a generic "session error"
203
+ * message, the real error (credential failure, model not found, etc.) is
204
+ * only in the OpenCode server's own log at ~/.local/share/opencode/log/.
205
+ *
206
+ * This module reads the most recent log file and extracts the last
207
+ * session.processor error line — which contains the actual provider error.
208
+ */
209
+ /**
210
+ * Read the most recent OpenCode server log and extract the last
211
+ * session.processor error message. Returns null if no log found
212
+ * or no error extracted.
213
+ *
214
+ * This is best-effort — the log directory may not exist, the log
215
+ * format may change, or the error may not be present. Callers
216
+ * should fall back to the generic message when this returns null.
217
+ */
218
+ declare function extractServerError(): string | null;
219
+ /**
220
+ * Enhance a generic "session error" message with details from the
221
+ * OpenCode server log. If the log contains a more specific error,
222
+ * returns that. Otherwise returns the original message unchanged.
223
+ */
224
+ declare function enhanceSessionError(genericMessage: string): string;
225
+
226
+ /**
227
+ * OpenCodeAdapter — implements AgentAdapter using the OpenCode SDK.
228
+ *
229
+ * Wraps the low-level opencode-server.ts functions into the AgentAdapter
230
+ * interface so the autopilot loop engine can drive OpenCode without
231
+ * importing the SDK directly.
232
+ */
233
+
234
+ /**
235
+ * OpenCode implementation of AgentAdapter.
236
+ * Creates and manages OpenCode server instances.
237
+ */
238
+ declare class OpenCodeAdapter implements AgentAdapter {
239
+ readonly name = "opencode";
240
+ start(opts: {
241
+ cwd: string;
242
+ agents?: Record<string, unknown>;
243
+ }): Promise<AgentHandle>;
244
+ createSession(handle: AgentHandle, opts: {
245
+ agentName?: string;
246
+ }): Promise<string>;
247
+ sendAndWait(handle: AgentHandle, opts: {
248
+ sessionId: string;
249
+ message: string;
250
+ stallMs?: number;
251
+ abortSignal?: AbortSignal;
252
+ onToolCall?: (name: string, arg?: string) => void;
253
+ onTextDelta?: (chars: number) => void;
254
+ onCostUpdate?: (cost: number, tokens: {
255
+ input: number;
256
+ output: number;
257
+ }) => void;
258
+ }): Promise<AdapterSessionResult>;
259
+ getLastResponse(handle: AgentHandle, sessionId: string): Promise<string>;
260
+ getSessionCost(handle: AgentHandle, sessionId: string): Promise<number>;
261
+ getSessionStats(handle: AgentHandle, sessionId: string): Promise<{
262
+ cost: number;
263
+ tokensIn: number;
264
+ tokensOut: number;
265
+ }>;
266
+ shutdown(handle: AgentHandle): Promise<void>;
267
+ enhanceError(message: string): Promise<string>;
268
+ }
269
+
270
+ export { DEFAULT_STARTUP_TIMEOUT_MS, OpenCodeAdapter, type SessionResult as OpenCodeSessionResult, type StartedServer, createSession, enhanceSessionError, execFileP, extractServerError, getLastAssistantMessage, getSessionCost, getSessionStats, selfTest, sendAndWait, startServer, waitForIdle };