@link-assistant/agent 0.0.9 → 0.0.12

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 (104) hide show
  1. package/EXAMPLES.md +36 -0
  2. package/MODELS.md +72 -24
  3. package/README.md +59 -2
  4. package/TOOLS.md +20 -0
  5. package/package.json +35 -2
  6. package/src/agent/agent.ts +68 -54
  7. package/src/auth/claude-oauth.ts +426 -0
  8. package/src/auth/index.ts +28 -26
  9. package/src/auth/plugins.ts +876 -0
  10. package/src/bun/index.ts +53 -43
  11. package/src/bus/global.ts +5 -5
  12. package/src/bus/index.ts +59 -53
  13. package/src/cli/bootstrap.js +12 -12
  14. package/src/cli/bootstrap.ts +6 -6
  15. package/src/cli/cmd/agent.ts +97 -92
  16. package/src/cli/cmd/auth.ts +469 -0
  17. package/src/cli/cmd/cmd.ts +2 -2
  18. package/src/cli/cmd/export.ts +41 -41
  19. package/src/cli/cmd/mcp.ts +144 -119
  20. package/src/cli/cmd/models.ts +30 -29
  21. package/src/cli/cmd/run.ts +269 -213
  22. package/src/cli/cmd/stats.ts +185 -146
  23. package/src/cli/error.ts +17 -13
  24. package/src/cli/ui.ts +39 -24
  25. package/src/command/index.ts +26 -26
  26. package/src/config/config.ts +528 -288
  27. package/src/config/markdown.ts +15 -15
  28. package/src/file/ripgrep.ts +201 -169
  29. package/src/file/time.ts +21 -18
  30. package/src/file/watcher.ts +51 -42
  31. package/src/file.ts +1 -1
  32. package/src/flag/flag.ts +26 -11
  33. package/src/format/formatter.ts +206 -162
  34. package/src/format/index.ts +61 -61
  35. package/src/global/index.ts +21 -21
  36. package/src/id/id.ts +47 -33
  37. package/src/index.js +346 -199
  38. package/src/json-standard/index.ts +67 -51
  39. package/src/mcp/index.ts +135 -128
  40. package/src/patch/index.ts +336 -267
  41. package/src/project/bootstrap.ts +15 -15
  42. package/src/project/instance.ts +43 -36
  43. package/src/project/project.ts +47 -47
  44. package/src/project/state.ts +37 -33
  45. package/src/provider/models-macro.ts +5 -5
  46. package/src/provider/models.ts +32 -32
  47. package/src/provider/opencode.js +19 -19
  48. package/src/provider/provider.ts +518 -277
  49. package/src/provider/transform.ts +143 -102
  50. package/src/server/project.ts +21 -21
  51. package/src/server/server.ts +111 -105
  52. package/src/session/agent.js +66 -60
  53. package/src/session/compaction.ts +136 -111
  54. package/src/session/index.ts +189 -156
  55. package/src/session/message-v2.ts +312 -268
  56. package/src/session/message.ts +73 -57
  57. package/src/session/processor.ts +180 -166
  58. package/src/session/prompt.ts +678 -533
  59. package/src/session/retry.ts +26 -23
  60. package/src/session/revert.ts +76 -62
  61. package/src/session/status.ts +26 -26
  62. package/src/session/summary.ts +97 -76
  63. package/src/session/system.ts +77 -63
  64. package/src/session/todo.ts +22 -16
  65. package/src/snapshot/index.ts +92 -76
  66. package/src/storage/storage.ts +157 -120
  67. package/src/tool/bash.ts +116 -106
  68. package/src/tool/batch.ts +73 -59
  69. package/src/tool/codesearch.ts +60 -53
  70. package/src/tool/edit.ts +319 -263
  71. package/src/tool/glob.ts +32 -28
  72. package/src/tool/grep.ts +72 -53
  73. package/src/tool/invalid.ts +7 -7
  74. package/src/tool/ls.ts +77 -64
  75. package/src/tool/multiedit.ts +30 -21
  76. package/src/tool/patch.ts +121 -94
  77. package/src/tool/read.ts +140 -122
  78. package/src/tool/registry.ts +38 -38
  79. package/src/tool/task.ts +93 -60
  80. package/src/tool/todo.ts +16 -16
  81. package/src/tool/tool.ts +45 -36
  82. package/src/tool/webfetch.ts +97 -74
  83. package/src/tool/websearch.ts +78 -64
  84. package/src/tool/write.ts +21 -15
  85. package/src/util/binary.ts +27 -19
  86. package/src/util/context.ts +8 -8
  87. package/src/util/defer.ts +7 -5
  88. package/src/util/error.ts +24 -19
  89. package/src/util/eventloop.ts +16 -10
  90. package/src/util/filesystem.ts +37 -33
  91. package/src/util/fn.ts +11 -8
  92. package/src/util/iife.ts +1 -1
  93. package/src/util/keybind.ts +44 -44
  94. package/src/util/lazy.ts +7 -7
  95. package/src/util/locale.ts +20 -16
  96. package/src/util/lock.ts +43 -38
  97. package/src/util/log.ts +95 -85
  98. package/src/util/queue.ts +8 -8
  99. package/src/util/rpc.ts +35 -23
  100. package/src/util/scrap.ts +4 -4
  101. package/src/util/signal.ts +5 -5
  102. package/src/util/timeout.ts +6 -6
  103. package/src/util/token.ts +2 -2
  104. package/src/util/wildcard.ts +38 -27
package/src/index.js CHANGED
@@ -1,125 +1,200 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { Server } from './server/server.ts'
4
- import { Instance } from './project/instance.ts'
5
- import { Log } from './util/log.ts'
6
- import { Bus } from './bus/index.ts'
7
- import { Session } from './session/index.ts'
8
- import { SessionPrompt } from './session/prompt.ts'
9
- import { EOL } from 'os'
10
- import yargs from 'yargs'
11
- import { hideBin } from 'yargs/helpers'
12
- import { createEventHandler, isValidJsonStandard } from './json-standard/index.ts'
13
- import { McpCommand } from './cli/cmd/mcp.ts'
3
+ import { Server } from './server/server.ts';
4
+ import { Instance } from './project/instance.ts';
5
+ import { Log } from './util/log.ts';
6
+ import { Bus } from './bus/index.ts';
7
+ import { Session } from './session/index.ts';
8
+ import { SessionPrompt } from './session/prompt.ts';
9
+ // EOL is reserved for future use
10
+ import yargs from 'yargs';
11
+ import { hideBin } from 'yargs/helpers';
12
+ import {
13
+ createEventHandler,
14
+ isValidJsonStandard,
15
+ } from './json-standard/index.ts';
16
+ import { McpCommand } from './cli/cmd/mcp.ts';
17
+ import { AuthCommand } from './cli/cmd/auth.ts';
18
+ import { Flag } from './flag/flag.ts';
14
19
 
15
20
  // Track if any errors occurred during execution
16
- let hasError = false
21
+ let hasError = false;
17
22
 
18
23
  // Install global error handlers to ensure non-zero exit codes
19
24
  process.on('uncaughtException', (error) => {
20
- hasError = true
21
- console.error(JSON.stringify({
22
- type: 'error',
23
- errorType: error.name || 'UncaughtException',
24
- message: error.message,
25
- stack: error.stack
26
- }, null, 2))
27
- process.exit(1)
28
- })
29
-
30
- process.on('unhandledRejection', (reason, promise) => {
31
- hasError = true
32
- console.error(JSON.stringify({
33
- type: 'error',
34
- errorType: 'UnhandledRejection',
35
- message: reason?.message || String(reason),
36
- stack: reason?.stack
37
- }, null, 2))
38
- process.exit(1)
39
- })
40
-
41
- async function readStdin() {
25
+ hasError = true;
26
+ console.error(
27
+ JSON.stringify(
28
+ {
29
+ type: 'error',
30
+ errorType: error.name || 'UncaughtException',
31
+ message: error.message,
32
+ stack: error.stack,
33
+ },
34
+ null,
35
+ 2
36
+ )
37
+ );
38
+ process.exit(1);
39
+ });
40
+
41
+ process.on('unhandledRejection', (reason, _promise) => {
42
+ hasError = true;
43
+ console.error(
44
+ JSON.stringify(
45
+ {
46
+ type: 'error',
47
+ errorType: 'UnhandledRejection',
48
+ message: reason?.message || String(reason),
49
+ stack: reason?.stack,
50
+ },
51
+ null,
52
+ 2
53
+ )
54
+ );
55
+ process.exit(1);
56
+ });
57
+
58
+ function readStdin() {
42
59
  return new Promise((resolve, reject) => {
43
- let data = ''
44
- const onData = chunk => {
45
- data += chunk
46
- }
60
+ let data = '';
61
+ const onData = (chunk) => {
62
+ data += chunk;
63
+ };
47
64
  const onEnd = () => {
48
- cleanup()
49
- resolve(data)
50
- }
51
- const onError = err => {
52
- cleanup()
53
- reject(err)
54
- }
65
+ cleanup();
66
+ resolve(data);
67
+ };
68
+ const onError = (err) => {
69
+ cleanup();
70
+ reject(err);
71
+ };
55
72
  const cleanup = () => {
56
- process.stdin.removeListener('data', onData)
57
- process.stdin.removeListener('end', onEnd)
58
- process.stdin.removeListener('error', onError)
59
- }
60
- process.stdin.on('data', onData)
61
- process.stdin.on('end', onEnd)
62
- process.stdin.on('error', onError)
63
- })
73
+ process.stdin.removeListener('data', onData);
74
+ process.stdin.removeListener('end', onEnd);
75
+ process.stdin.removeListener('error', onError);
76
+ };
77
+ process.stdin.on('data', onData);
78
+ process.stdin.on('end', onEnd);
79
+ process.stdin.on('error', onError);
80
+ });
64
81
  }
65
82
 
66
83
  async function runAgentMode(argv) {
67
- // Parse model argument
68
- const modelParts = argv.model.split('/')
69
- const providerID = modelParts[0] || 'opencode'
70
- const modelID = modelParts[1] || 'grok-code'
84
+ // Set verbose mode if requested via CLI flag
85
+ if (argv.verbose) {
86
+ Flag.setVerbose(true);
87
+ }
88
+
89
+ // Parse model argument (handle model IDs with slashes like groq/qwen/qwen3-32b)
90
+ const modelParts = argv.model.split('/');
91
+ let providerID = modelParts[0] || 'opencode';
92
+ let modelID = modelParts.slice(1).join('/') || 'grok-code';
93
+
94
+ // Handle --use-existing-claude-oauth option
95
+ // This reads OAuth credentials from ~/.claude/.credentials.json (Claude Code CLI)
96
+ // For new authentication, use: agent auth login (select Anthropic > Claude Pro/Max)
97
+ if (argv['use-existing-claude-oauth']) {
98
+ // Import ClaudeOAuth to check for credentials from Claude Code CLI
99
+ const { ClaudeOAuth } = await import('./auth/claude-oauth.ts');
100
+ const creds = await ClaudeOAuth.getCredentials();
101
+
102
+ if (!creds?.accessToken) {
103
+ console.error(
104
+ JSON.stringify({
105
+ type: 'error',
106
+ errorType: 'AuthenticationError',
107
+ message:
108
+ 'No Claude OAuth credentials found in ~/.claude/.credentials.json. Either authenticate with Claude Code CLI first, or use: agent auth login (select Anthropic > Claude Pro/Max)',
109
+ })
110
+ );
111
+ process.exit(1);
112
+ }
113
+
114
+ // Set environment variable for the provider to use
115
+ process.env.CLAUDE_CODE_OAUTH_TOKEN = creds.accessToken;
116
+
117
+ // If user specified a model, use it with claude-oauth provider
118
+ // If not, use claude-oauth/claude-sonnet-4-5 as default
119
+ if (providerID === 'opencode' && modelID === 'grok-code') {
120
+ providerID = 'claude-oauth';
121
+ modelID = 'claude-sonnet-4-5';
122
+ } else if (!['claude-oauth', 'anthropic'].includes(providerID)) {
123
+ // If user specified a different provider, warn them
124
+ console.error(
125
+ JSON.stringify({
126
+ type: 'warning',
127
+ message: `--use-existing-claude-oauth is set but model uses provider "${providerID}". Using OAuth credentials anyway.`,
128
+ })
129
+ );
130
+ providerID = 'claude-oauth';
131
+ }
132
+ }
71
133
 
72
134
  // Validate and get JSON standard
73
- const jsonStandard = argv['json-standard']
135
+ const jsonStandard = argv['json-standard'];
74
136
  if (!isValidJsonStandard(jsonStandard)) {
75
- console.error(`Invalid JSON standard: ${jsonStandard}. Use "opencode" or "claude".`)
76
- process.exit(1)
137
+ console.error(
138
+ `Invalid JSON standard: ${jsonStandard}. Use "opencode" or "claude".`
139
+ );
140
+ process.exit(1);
77
141
  }
78
142
 
79
143
  // Read system message files
80
- let systemMessage = argv['system-message']
81
- let appendSystemMessage = argv['append-system-message']
144
+ let systemMessage = argv['system-message'];
145
+ let appendSystemMessage = argv['append-system-message'];
82
146
 
83
147
  if (argv['system-message-file']) {
84
- const resolvedPath = require('path').resolve(process.cwd(), argv['system-message-file'])
85
- const file = Bun.file(resolvedPath)
148
+ const resolvedPath = require('path').resolve(
149
+ process.cwd(),
150
+ argv['system-message-file']
151
+ );
152
+ const file = Bun.file(resolvedPath);
86
153
  if (!(await file.exists())) {
87
- console.error(`System message file not found: ${argv['system-message-file']}`)
88
- process.exit(1)
154
+ console.error(
155
+ `System message file not found: ${argv['system-message-file']}`
156
+ );
157
+ process.exit(1);
89
158
  }
90
- systemMessage = await file.text()
159
+ systemMessage = await file.text();
91
160
  }
92
161
 
93
162
  if (argv['append-system-message-file']) {
94
- const resolvedPath = require('path').resolve(process.cwd(), argv['append-system-message-file'])
95
- const file = Bun.file(resolvedPath)
163
+ const resolvedPath = require('path').resolve(
164
+ process.cwd(),
165
+ argv['append-system-message-file']
166
+ );
167
+ const file = Bun.file(resolvedPath);
96
168
  if (!(await file.exists())) {
97
- console.error(`Append system message file not found: ${argv['append-system-message-file']}`)
98
- process.exit(1)
169
+ console.error(
170
+ `Append system message file not found: ${argv['append-system-message-file']}`
171
+ );
172
+ process.exit(1);
99
173
  }
100
- appendSystemMessage = await file.text()
174
+ appendSystemMessage = await file.text();
101
175
  }
102
176
 
103
177
  // Initialize logging to redirect to log file instead of stderr
104
178
  // This prevents log messages from mixing with JSON output
179
+ // In verbose mode, print to stderr for debugging
105
180
  await Log.init({
106
- print: false, // Don't print to stderr
107
- level: 'INFO'
108
- })
181
+ print: Flag.OPENCODE_VERBOSE, // Print to stderr only in verbose mode
182
+ level: Flag.OPENCODE_VERBOSE ? 'DEBUG' : 'INFO',
183
+ });
109
184
 
110
185
  // Read input from stdin
111
- const input = await readStdin()
112
- const trimmedInput = input.trim()
186
+ const input = await readStdin();
187
+ const trimmedInput = input.trim();
113
188
 
114
189
  // Try to parse as JSON, if it fails treat it as plain text message
115
- let request
190
+ let request;
116
191
  try {
117
- request = JSON.parse(trimmedInput)
118
- } catch (e) {
192
+ request = JSON.parse(trimmedInput);
193
+ } catch (_e) {
119
194
  // Not JSON, treat as plain text message
120
195
  request = {
121
- message: trimmedInput
122
- }
196
+ message: trimmedInput,
197
+ };
123
198
  }
124
199
 
125
200
  // Wrap in Instance.provide for OpenCode infrastructure
@@ -128,47 +203,73 @@ async function runAgentMode(argv) {
128
203
  fn: async () => {
129
204
  if (argv.server) {
130
205
  // SERVER MODE: Start server and communicate via HTTP
131
- await runServerMode(request, providerID, modelID, systemMessage, appendSystemMessage, jsonStandard)
206
+ await runServerMode(
207
+ request,
208
+ providerID,
209
+ modelID,
210
+ systemMessage,
211
+ appendSystemMessage,
212
+ jsonStandard
213
+ );
132
214
  } else {
133
215
  // DIRECT MODE: Run everything in single process
134
- await runDirectMode(request, providerID, modelID, systemMessage, appendSystemMessage, jsonStandard)
216
+ await runDirectMode(
217
+ request,
218
+ providerID,
219
+ modelID,
220
+ systemMessage,
221
+ appendSystemMessage,
222
+ jsonStandard
223
+ );
135
224
  }
136
- }
137
- })
225
+ },
226
+ });
138
227
 
139
228
  // Explicitly exit to ensure process terminates
140
- process.exit(hasError ? 1 : 0)
229
+ process.exit(hasError ? 1 : 0);
141
230
  }
142
231
 
143
- async function runServerMode(request, providerID, modelID, systemMessage, appendSystemMessage, jsonStandard) {
232
+ async function runServerMode(
233
+ request,
234
+ providerID,
235
+ modelID,
236
+ systemMessage,
237
+ appendSystemMessage,
238
+ jsonStandard
239
+ ) {
144
240
  // Start server like OpenCode does
145
- const server = Server.listen({ port: 0, hostname: "127.0.0.1" })
146
- let unsub = null
241
+ const server = Server.listen({ port: 0, hostname: '127.0.0.1' });
242
+ let unsub = null;
147
243
 
148
244
  try {
149
245
  // Create a session
150
- const createRes = await fetch(`http://${server.hostname}:${server.port}/session`, {
151
- method: 'POST',
152
- headers: { 'Content-Type': 'application/json' },
153
- body: JSON.stringify({})
154
- })
155
- const session = await createRes.json()
156
- const sessionID = session.id
246
+ const createRes = await fetch(
247
+ `http://${server.hostname}:${server.port}/session`,
248
+ {
249
+ method: 'POST',
250
+ headers: { 'Content-Type': 'application/json' },
251
+ body: JSON.stringify({}),
252
+ }
253
+ );
254
+ const session = await createRes.json();
255
+ const sessionID = session.id;
157
256
 
158
257
  if (!sessionID) {
159
- throw new Error("Failed to create session")
258
+ throw new Error('Failed to create session');
160
259
  }
161
260
 
162
261
  // Create event handler for the selected JSON standard
163
- const eventHandler = createEventHandler(jsonStandard, sessionID)
262
+ const eventHandler = createEventHandler(jsonStandard, sessionID);
164
263
 
165
264
  // Subscribe to all bus events and output in selected format
166
265
  const eventPromise = new Promise((resolve) => {
167
266
  unsub = Bus.subscribeAll((event) => {
168
267
  // Output events in selected JSON format
169
268
  if (event.type === 'message.part.updated') {
170
- const part = event.properties.part
171
- if (part.sessionID !== sessionID) return
269
+ const part = event.properties.part;
270
+ if (part.sessionID !== sessionID) {
271
+ return;
272
+ }
172
273
 
173
274
  // Output different event types
174
275
  if (part.type === 'step-start') {
@@ -176,8 +277,8 @@ async function runServerMode(request, providerID, modelID, systemMessage, append
176
277
  type: 'step_start',
177
278
  timestamp: Date.now(),
178
279
  sessionID,
179
- part
180
- })
280
+ part,
281
+ });
181
282
  }
182
283
 
183
284
  if (part.type === 'step-finish') {
@@ -185,8 +286,8 @@ async function runServerMode(request, providerID, modelID, systemMessage, append
185
286
  type: 'step_finish',
186
287
  timestamp: Date.now(),
187
288
  sessionID,
188
- part
189
- })
289
+ part,
290
+ });
190
291
  }
191
292
 
192
293
  if (part.type === 'text' && part.time?.end) {
@@ -194,8 +295,8 @@ async function runServerMode(request, providerID, modelID, systemMessage, append
194
295
  type: 'text',
195
296
  timestamp: Date.now(),
196
297
  sessionID,
197
- part
198
- })
298
+ part,
299
+ });
199
300
  }
200
301
 
201
302
  if (part.type === 'tool' && part.state.status === 'completed') {
@@ -203,89 +304,108 @@ async function runServerMode(request, providerID, modelID, systemMessage, append
203
304
  type: 'tool_use',
204
305
  timestamp: Date.now(),
205
306
  sessionID,
206
- part
207
- })
307
+ part,
308
+ });
208
309
  }
209
310
  }
210
311
 
211
312
  // Handle session idle to know when to stop
212
- if (event.type === 'session.idle' && event.properties.sessionID === sessionID) {
213
- resolve()
313
+ if (
314
+ event.type === 'session.idle' &&
315
+ event.properties.sessionID === sessionID
316
+ ) {
317
+ resolve();
214
318
  }
215
319
 
216
320
  // Handle errors
217
321
  if (event.type === 'session.error') {
218
- const props = event.properties
219
- if (props.sessionID !== sessionID || !props.error) return
220
- hasError = true
322
+ const props = event.properties;
323
+ if (props.sessionID !== sessionID || !props.error) {
324
+ return;
325
+ }
326
+ hasError = true;
221
327
  eventHandler.output({
222
328
  type: 'error',
223
329
  timestamp: Date.now(),
224
330
  sessionID,
225
- error: props.error
226
- })
331
+ error: props.error,
332
+ });
227
333
  }
228
- })
229
- })
334
+ });
335
+ });
230
336
 
231
337
  // Send message to session with specified model (default: opencode/grok-code)
232
- const message = request.message || "hi"
233
- const parts = [{ type: "text", text: message }]
338
+ const message = request.message || 'hi';
339
+ const parts = [{ type: 'text', text: message }];
234
340
 
235
341
  // Start the prompt (don't wait for response, events come via Bus)
236
- fetch(`http://${server.hostname}:${server.port}/session/${sessionID}/message`, {
237
- method: 'POST',
238
- headers: { 'Content-Type': 'application/json' },
239
- body: JSON.stringify({
240
- parts,
241
- model: {
242
- providerID,
243
- modelID
244
- },
245
- system: systemMessage,
246
- appendSystem: appendSystemMessage
247
- })
248
- }).catch((error) => {
249
- hasError = true
342
+ fetch(
343
+ `http://${server.hostname}:${server.port}/session/${sessionID}/message`,
344
+ {
345
+ method: 'POST',
346
+ headers: { 'Content-Type': 'application/json' },
347
+ body: JSON.stringify({
348
+ parts,
349
+ model: {
350
+ providerID,
351
+ modelID,
352
+ },
353
+ system: systemMessage,
354
+ appendSystem: appendSystemMessage,
355
+ }),
356
+ }
357
+ ).catch((error) => {
358
+ hasError = true;
250
359
  eventHandler.output({
251
360
  type: 'error',
252
361
  timestamp: Date.now(),
253
362
  sessionID,
254
- error: error instanceof Error ? error.message : String(error)
255
- })
256
- })
363
+ error: error instanceof Error ? error.message : String(error),
364
+ });
365
+ });
257
366
 
258
367
  // Wait for session to become idle
259
- await eventPromise
368
+ await eventPromise;
260
369
  } finally {
261
370
  // Always clean up resources
262
- if (unsub) unsub()
263
- server.stop()
264
- await Instance.dispose()
371
+ if (unsub) {
372
+ unsub();
373
+ }
374
+ server.stop();
375
+ await Instance.dispose();
265
376
  }
266
377
  }
267
378
 
268
- async function runDirectMode(request, providerID, modelID, systemMessage, appendSystemMessage, jsonStandard) {
379
+ async function runDirectMode(
380
+ request,
381
+ providerID,
382
+ modelID,
383
+ systemMessage,
384
+ appendSystemMessage,
385
+ jsonStandard
386
+ ) {
269
387
  // DIRECT MODE: Run in single process without server
270
- let unsub = null
388
+ let unsub = null;
271
389
 
272
390
  try {
273
391
  // Create a session directly
274
392
  const session = await Session.createNext({
275
- directory: process.cwd()
276
- })
277
- const sessionID = session.id
393
+ directory: process.cwd(),
394
+ });
395
+ const sessionID = session.id;
278
396
 
279
397
  // Create event handler for the selected JSON standard
280
- const eventHandler = createEventHandler(jsonStandard, sessionID)
398
+ const eventHandler = createEventHandler(jsonStandard, sessionID);
281
399
 
282
400
  // Subscribe to all bus events and output in selected format
283
401
  const eventPromise = new Promise((resolve) => {
284
402
  unsub = Bus.subscribeAll((event) => {
285
403
  // Output events in selected JSON format
286
404
  if (event.type === 'message.part.updated') {
287
- const part = event.properties.part
288
- if (part.sessionID !== sessionID) return
405
+ const part = event.properties.part;
406
+ if (part.sessionID !== sessionID) {
407
+ return;
408
+ }
289
409
 
290
410
  // Output different event types
291
411
  if (part.type === 'step-start') {
@@ -293,8 +413,8 @@ async function runDirectMode(request, providerID, modelID, systemMessage, append
293
413
  type: 'step_start',
294
414
  timestamp: Date.now(),
295
415
  sessionID,
296
- part
297
- })
416
+ part,
417
+ });
298
418
  }
299
419
 
300
420
  if (part.type === 'step-finish') {
@@ -302,8 +422,8 @@ async function runDirectMode(request, providerID, modelID, systemMessage, append
302
422
  type: 'step_finish',
303
423
  timestamp: Date.now(),
304
424
  sessionID,
305
- part
306
- })
425
+ part,
426
+ });
307
427
  }
308
428
 
309
429
  if (part.type === 'text' && part.time?.end) {
@@ -311,8 +431,8 @@ async function runDirectMode(request, providerID, modelID, systemMessage, append
311
431
  type: 'text',
312
432
  timestamp: Date.now(),
313
433
  sessionID,
314
- part
315
- })
434
+ part,
435
+ });
316
436
  }
317
437
 
318
438
  if (part.type === 'tool' && part.state.status === 'completed') {
@@ -320,34 +440,39 @@ async function runDirectMode(request, providerID, modelID, systemMessage, append
320
440
  type: 'tool_use',
321
441
  timestamp: Date.now(),
322
442
  sessionID,
323
- part
324
- })
443
+ part,
444
+ });
325
445
  }
326
446
  }
327
447
 
328
448
  // Handle session idle to know when to stop
329
- if (event.type === 'session.idle' && event.properties.sessionID === sessionID) {
330
- resolve()
449
+ if (
450
+ event.type === 'session.idle' &&
451
+ event.properties.sessionID === sessionID
452
+ ) {
453
+ resolve();
331
454
  }
332
455
 
333
456
  // Handle errors
334
457
  if (event.type === 'session.error') {
335
- const props = event.properties
336
- if (props.sessionID !== sessionID || !props.error) return
337
- hasError = true
458
+ const props = event.properties;
459
+ if (props.sessionID !== sessionID || !props.error) {
460
+ return;
461
+ }
462
+ hasError = true;
338
463
  eventHandler.output({
339
464
  type: 'error',
340
465
  timestamp: Date.now(),
341
466
  sessionID,
342
- error: props.error
343
- })
467
+ error: props.error,
468
+ });
344
469
  }
345
- })
346
- })
470
+ });
471
+ });
347
472
 
348
473
  // Send message to session directly
349
- const message = request.message || "hi"
350
- const parts = [{ type: "text", text: message }]
474
+ const message = request.message || 'hi';
475
+ const parts = [{ type: 'text', text: message }];
351
476
 
352
477
  // Start the prompt directly without HTTP
353
478
  SessionPrompt.prompt({
@@ -355,26 +480,28 @@ async function runDirectMode(request, providerID, modelID, systemMessage, append
355
480
  parts,
356
481
  model: {
357
482
  providerID,
358
- modelID
483
+ modelID,
359
484
  },
360
485
  system: systemMessage,
361
- appendSystem: appendSystemMessage
486
+ appendSystem: appendSystemMessage,
362
487
  }).catch((error) => {
363
- hasError = true
488
+ hasError = true;
364
489
  eventHandler.output({
365
490
  type: 'error',
366
491
  timestamp: Date.now(),
367
492
  sessionID,
368
- error: error instanceof Error ? error.message : String(error)
369
- })
370
- })
493
+ error: error instanceof Error ? error.message : String(error),
494
+ });
495
+ });
371
496
 
372
497
  // Wait for session to become idle
373
- await eventPromise
498
+ await eventPromise;
374
499
  } finally {
375
500
  // Always clean up resources
376
- if (unsub) unsub()
377
- await Instance.dispose()
501
+ if (unsub) {
502
+ unsub();
503
+ }
504
+ await Instance.dispose();
378
505
  }
379
506
  }
380
507
 
@@ -386,61 +513,81 @@ async function main() {
386
513
  .usage('$0 [command] [options]')
387
514
  // MCP subcommand
388
515
  .command(McpCommand)
516
+ // Auth subcommand
517
+ .command(AuthCommand)
389
518
  // Default run mode (when piping stdin)
390
519
  .option('model', {
391
520
  type: 'string',
392
521
  description: 'Model to use in format providerID/modelID',
393
- default: 'opencode/grok-code'
522
+ default: 'opencode/grok-code',
394
523
  })
395
524
  .option('json-standard', {
396
525
  type: 'string',
397
- description: 'JSON output format standard: "opencode" (default) or "claude" (experimental)',
526
+ description:
527
+ 'JSON output format standard: "opencode" (default) or "claude" (experimental)',
398
528
  default: 'opencode',
399
- choices: ['opencode', 'claude']
529
+ choices: ['opencode', 'claude'],
400
530
  })
401
531
  .option('system-message', {
402
532
  type: 'string',
403
- description: 'Full override of the system message'
533
+ description: 'Full override of the system message',
404
534
  })
405
535
  .option('system-message-file', {
406
536
  type: 'string',
407
- description: 'Full override of the system message from file'
537
+ description: 'Full override of the system message from file',
408
538
  })
409
539
  .option('append-system-message', {
410
540
  type: 'string',
411
- description: 'Append to the default system message'
541
+ description: 'Append to the default system message',
412
542
  })
413
543
  .option('append-system-message-file', {
414
544
  type: 'string',
415
- description: 'Append to the default system message from file'
545
+ description: 'Append to the default system message from file',
416
546
  })
417
547
  .option('server', {
418
548
  type: 'boolean',
419
549
  description: 'Run in server mode (default)',
420
- default: true
550
+ default: true,
421
551
  })
422
- .help()
423
- .argv
552
+ .option('verbose', {
553
+ type: 'boolean',
554
+ description:
555
+ 'Enable verbose mode to debug API requests (shows system prompt, token counts, etc.)',
556
+ default: false,
557
+ })
558
+ .option('use-existing-claude-oauth', {
559
+ type: 'boolean',
560
+ description:
561
+ 'Use existing Claude OAuth credentials from ~/.claude/.credentials.json (from Claude Code CLI)',
562
+ default: false,
563
+ })
564
+ .help().argv;
424
565
 
425
566
  // If a command was executed (like mcp), yargs handles it
426
567
  // Otherwise, check if we should run in agent mode (stdin piped)
427
- const commandExecuted = argv._ && argv._.length > 0
568
+ const commandExecuted = argv._ && argv._.length > 0;
428
569
 
429
570
  if (!commandExecuted) {
430
571
  // No command specified, run in default agent mode (stdin processing)
431
- await runAgentMode(argv)
572
+ await runAgentMode(argv);
432
573
  }
433
574
  } catch (error) {
434
- hasError = true
435
- console.error(JSON.stringify({
436
- type: 'error',
437
- timestamp: Date.now(),
438
- errorType: error instanceof Error ? error.name : 'Error',
439
- message: error instanceof Error ? error.message : String(error),
440
- stack: error instanceof Error ? error.stack : undefined
441
- }, null, 2))
442
- process.exit(1)
575
+ hasError = true;
576
+ console.error(
577
+ JSON.stringify(
578
+ {
579
+ type: 'error',
580
+ timestamp: Date.now(),
581
+ errorType: error instanceof Error ? error.name : 'Error',
582
+ message: error instanceof Error ? error.message : String(error),
583
+ stack: error instanceof Error ? error.stack : undefined,
584
+ },
585
+ null,
586
+ 2
587
+ )
588
+ );
589
+ process.exit(1);
443
590
  }
444
591
  }
445
592
 
446
- main()
593
+ main();