overlord-cli 4.0.0 → 4.1.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.
@@ -6,7 +6,14 @@ import { stdin as input, stdout as output } from 'node:process';
6
6
  import { buildAuthHeaders, resolveAuth } from './credentials.mjs';
7
7
  import { runLauncherCommand } from './launcher.mjs';
8
8
 
9
- const PROMPT_AGENTS = ['claude', 'codex', 'cursor', 'gemini', 'opencode'];
9
+ const PROMPT_AGENT_IDENTIFIERS = {
10
+ claude: 'claude-code',
11
+ codex: 'codex',
12
+ cursor: 'cursor',
13
+ gemini: 'gemini',
14
+ opencode: 'opencode'
15
+ };
16
+ const PROMPT_AGENTS = Object.keys(PROMPT_AGENT_IDENTIFIERS);
10
17
 
11
18
  function parseFlags(args) {
12
19
  const flags = {};
@@ -40,10 +47,10 @@ function parseFlags(args) {
40
47
 
41
48
  function buildUsage(commandName) {
42
49
  if (commandName === 'prompt') {
43
- return 'Usage: ovld prompt "<objective>" [--title "..."] [--acceptance-criteria "..."] [--available-tools "..."] [--execution-target agent|human] [--priority low|medium|high|urgent] [--project-id <id>] [--agent <agent>]';
50
+ return 'Usage: ovld prompt "<objective>" [--title "..."] [--acceptance-criteria "..."] [--available-tools "..."] [--execution-target agent|human] [--priority low|medium|high|urgent] [--project-id <id>] [--agent <agent>] [--delegate <agent>]';
44
51
  }
45
52
 
46
- return 'Usage: ovld create "<objective>" [--title "..."] [--acceptance-criteria "..."] [--available-tools "..."] [--execution-target agent|human] [--priority low|medium|high|urgent] [--project-id <id>]';
53
+ return 'Usage: ovld create "<objective>" [--title "..."] [--acceptance-criteria "..."] [--available-tools "..."] [--execution-target agent|human] [--priority low|medium|high|urgent] [--project-id <id>] [--delegate <agent>]';
47
54
  }
48
55
 
49
56
  function ensureObjective(commandName, objective) {
@@ -151,7 +158,9 @@ async function createTicket(platformUrl, agentToken, localSecret, body) {
151
158
 
152
159
  const data = await res.json().catch(() => ({}));
153
160
  if (!res.ok) {
154
- throw new Error(`Failed to create ticket (${res.status}): ${data.error ?? JSON.stringify(data)}`);
161
+ throw new Error(
162
+ `Failed to create ticket (${res.status}): ${data.error ?? JSON.stringify(data)}`
163
+ );
155
164
  }
156
165
 
157
166
  return data.ticket;
@@ -179,6 +188,20 @@ function resolveAgent(agent) {
179
188
  return normalizedAgent;
180
189
  }
181
190
 
191
+ export function resolvePromptAgentIdentifier(agent) {
192
+ return PROMPT_AGENT_IDENTIFIERS[agent] ?? agent;
193
+ }
194
+
195
+ export function resolveTicketCreationDelegate(flags = {}, selectedAgent = null) {
196
+ const explicitDelegate = typeof flags.delegate === 'string' ? flags.delegate.trim() : '';
197
+ if (explicitDelegate) return explicitDelegate;
198
+
199
+ if (selectedAgent) return resolvePromptAgentIdentifier(selectedAgent);
200
+
201
+ const envAgent = process.env.AGENT_IDENTIFIER?.trim();
202
+ return envAgent || null;
203
+ }
204
+
182
205
  async function runTicketCreationFlow(args, { commandName, launchAgent }) {
183
206
  const { flags, positionals } = parseFlags(args);
184
207
  const objective = String(flags.objective ?? positionals.join(' ')).trim();
@@ -200,6 +223,18 @@ async function runTicketCreationFlow(args, { commandName, launchAgent }) {
200
223
  renderItem: project => projectLabel(project)
201
224
  }));
202
225
 
226
+ const selectedAgent = launchAgent
227
+ ? (resolveAgent(typeof flags.agent === 'string' ? flags.agent : '') ??
228
+ (await promptForSelection({
229
+ items: PROMPT_AGENTS,
230
+ label: 'Agents',
231
+ prompt: 'Select an agent by number:',
232
+ renderItem: agent => agent
233
+ })))
234
+ : null;
235
+
236
+ const ticketDelegate = resolveTicketCreationDelegate(flags, selectedAgent);
237
+
203
238
  const ticket = await createTicket(platformUrl, agentToken, localSecret, {
204
239
  objective,
205
240
  title: String(flags.title ?? ''),
@@ -207,7 +242,8 @@ async function runTicketCreationFlow(args, { commandName, launchAgent }) {
207
242
  availableTools: String(flags['available-tools'] ?? ''),
208
243
  executionTarget: String(flags['execution-target'] ?? 'agent'),
209
244
  priority: String(flags.priority ?? 'medium'),
210
- projectId: selectedProject.id
245
+ projectId: selectedProject.id,
246
+ ...(ticketDelegate ? { delegate: ticketDelegate } : {})
211
247
  });
212
248
 
213
249
  if (!launchAgent) {
@@ -215,15 +251,6 @@ async function runTicketCreationFlow(args, { commandName, launchAgent }) {
215
251
  return;
216
252
  }
217
253
 
218
- const selectedAgent =
219
- resolveAgent(typeof flags.agent === 'string' ? flags.agent : '') ??
220
- (await promptForSelection({
221
- items: PROMPT_AGENTS,
222
- label: 'Agents',
223
- prompt: 'Select an agent by number:',
224
- renderItem: agent => agent
225
- }));
226
-
227
254
  process.env.TICKET_ID = ticket.id;
228
255
  await runLauncherCommand('run', [selectedAgent, '--ticket-id', ticket.id]);
229
256
  }
@@ -35,6 +35,22 @@ function parseFlags(args) {
35
35
  return result;
36
36
  }
37
37
 
38
+ export function resolveProtocolAgentIdentifier(flags = {}) {
39
+ const explicitAgent = typeof flags.agent === 'string' ? flags.agent.trim() : '';
40
+ if (explicitAgent) return explicitAgent;
41
+
42
+ const envAgent = process.env.AGENT_IDENTIFIER?.trim();
43
+ return envAgent || 'claude-code';
44
+ }
45
+
46
+ export function resolveProtocolTicketDelegate(flags = {}, agentIdentifier = '') {
47
+ const explicitDelegate = typeof flags.delegate === 'string' ? flags.delegate.trim() : '';
48
+ if (explicitDelegate) return explicitDelegate;
49
+
50
+ const resolvedAgent = String(agentIdentifier).trim();
51
+ return resolvedAgent || null;
52
+ }
53
+
38
54
  /**
39
55
  * Default request timeout in milliseconds. Overridable via --timeout flag or
40
56
  * OVERLORD_TIMEOUT env var. A bounded timeout prevents indefinite spinner hangs
@@ -427,7 +443,7 @@ async function protocolAttach(args) {
427
443
 
428
444
  const body = {
429
445
  ticketId,
430
- agentIdentifier: String(flags.agent ?? process.env.AGENT_IDENTIFIER ?? 'claude-code'),
446
+ agentIdentifier: resolveProtocolAgentIdentifier(flags),
431
447
  connectionMethod: String(flags.method ?? 'cli'),
432
448
  ...(externalSessionId !== undefined ? { externalSessionId } : {}),
433
449
  metadata: {
@@ -915,7 +931,7 @@ async function protocolConnect(args) {
915
931
 
916
932
  const body = {
917
933
  ticketId,
918
- agentIdentifier: String(flags.agent ?? process.env.AGENT_IDENTIFIER ?? 'claude-code'),
934
+ agentIdentifier: resolveProtocolAgentIdentifier(flags),
919
935
  connectionMethod: String(flags.method ?? 'cli'),
920
936
  metadata: {}
921
937
  };
@@ -969,6 +985,7 @@ async function protocolSpawn(args) {
969
985
  const objective = requireFlag(flags, 'objective', undefined);
970
986
  const { platformUrl, agentToken, localSecret } = resolveAuth();
971
987
  const timeoutMs = resolveTimeout(flags);
988
+ const agentIdentifier = resolveProtocolAgentIdentifier(flags);
972
989
 
973
990
  // When --project-id is not provided, auto-send cwd as workingDirectory
974
991
  // so the server can resolve the project from the local_working_directory setting.
@@ -976,7 +993,7 @@ async function protocolSpawn(args) {
976
993
 
977
994
  const body = {
978
995
  objective,
979
- agentIdentifier: String(flags.agent ?? process.env.AGENT_IDENTIFIER ?? 'claude-code'),
996
+ agentIdentifier,
980
997
  connectionMethod: String(flags.method ?? 'cli'),
981
998
  metadata: {},
982
999
  ...(flags.title ? { title: String(flags.title) } : {}),
@@ -986,7 +1003,7 @@ async function protocolSpawn(args) {
986
1003
  ...(flags['acceptance-criteria'] ? { acceptanceCriteria: String(flags['acceptance-criteria']) } : {}),
987
1004
  ...(flags['available-tools'] ? { availableTools: String(flags['available-tools']) } : {}),
988
1005
  ...(flags['execution-target'] ? { executionTarget: String(flags['execution-target']) } : {}),
989
- ...(flags.delegate ? { delegate: String(flags.delegate) } : {}),
1006
+ delegate: resolveProtocolTicketDelegate(flags, agentIdentifier),
990
1007
  ...(flags['parent-session-key'] ? { parentSessionKey: String(flags['parent-session-key']) } : {}),
991
1008
  ...(flags['parent-ticket-id'] ? { parentTicketId: String(flags['parent-ticket-id'] ?? process.env.TICKET_ID ?? '') } : {})
992
1009
  };
@@ -1260,12 +1277,12 @@ artifact-upload-file:
1260
1277
  Examples:
1261
1278
  ovld protocol discover-project
1262
1279
  ovld protocol discover-project --working-directory /path/to/repo
1263
- ovld protocol spawn --objective "Implement feature X" # auto-resolves project from cwd
1280
+ ovld protocol spawn --agent codex --objective "Implement feature X" # auto-resolves project from cwd
1264
1281
  ovld protocol attach --ticket-id abc-123
1265
1282
  ovld protocol attach --ticket-id abc-123 --external-session-id null
1266
1283
  ovld protocol connect --ticket-id abc-123
1267
1284
  ovld protocol load-context --ticket-id abc-123
1268
- ovld protocol spawn --objective "Implement user auth" --priority high
1285
+ ovld protocol spawn --agent codex --objective "Implement user auth" --priority high
1269
1286
  ovld protocol update --session-key <key> --ticket-id <id> --summary "Did X" --phase execute
1270
1287
  ovld protocol update --session-key <key> --ticket-id <id> --summary-file ./update.txt --event-type user_follow_up
1271
1288
  ovld protocol record-change-rationales --session-key <key> --ticket-id <id> --change-rationales-json '[{"label":"...","file_path":"...","summary":"...","why":"...","impact":"...","hunks":[{"header":"@@ ... @@"}]}]'
@@ -367,7 +367,7 @@ argument-hint: <objective or raw flags>
367
367
  disable-model-invocation: true
368
368
  ---
369
369
 
370
- Run \`ovld protocol spawn\` with \`$ARGUMENTS\`. If no flags are present, treat the arguments as the objective and call \`ovld protocol spawn --objective "<objective>"\`.`
370
+ Run \`ovld protocol spawn --agent claude-code\` with \`$ARGUMENTS\`. If no flags are present, treat the arguments as the objective and call \`ovld protocol spawn --agent claude-code --objective "<objective>"\`.`
371
371
  }
372
372
  ];
373
373
  }
@@ -388,7 +388,7 @@ Run \`ovld protocol spawn\` with \`$ARGUMENTS\`. If no flags are present, treat
388
388
  {
389
389
  path: path.join(base, 'spawn.md'),
390
390
  content:
391
- 'Create a new Overlord ticket.\n\nRun `ovld protocol spawn --objective "<objective>"` using the text after `/spawn` unless raw flags were provided.\n'
391
+ 'Create a new Overlord ticket.\n\nRun `ovld protocol spawn --agent cursor --objective "<objective>"` using the text after `/spawn` unless raw flags were provided. If raw flags were provided, pass them after `ovld protocol spawn --agent cursor`.\n'
392
392
  }
393
393
  ];
394
394
  }
@@ -409,7 +409,7 @@ Run \`ovld protocol spawn\` with \`$ARGUMENTS\`. If no flags are present, treat
409
409
  {
410
410
  path: path.join(base, 'spawn.toml'),
411
411
  content:
412
- 'description = "Create a new Overlord ticket from the current conversation."\nprompt = """\nRun `ovld protocol spawn --objective "<objective>"` using `{{args}}` as the objective unless raw flags were provided.\n"""\n'
412
+ 'description = "Create a new Overlord ticket from the current conversation."\nprompt = """\nRun `ovld protocol spawn --agent gemini --objective "<objective>"` using `{{args}}` as the objective unless raw flags were provided. If raw flags were provided, pass them after `ovld protocol spawn --agent gemini`.\n"""\n'
413
413
  }
414
414
  ];
415
415
  }
@@ -441,7 +441,7 @@ description: Create a new Overlord ticket from the current conversation
441
441
  agent: build
442
442
  ---
443
443
 
444
- Run \`ovld protocol spawn\` with \`$ARGUMENTS\`. If no flags are present, treat the arguments as the objective and call \`ovld protocol spawn --objective "<objective>"\`.`
444
+ Run \`ovld protocol spawn --agent opencode\` with \`$ARGUMENTS\`. If no flags are present, treat the arguments as the objective and call \`ovld protocol spawn --agent opencode --objective "<objective>"\`.`
445
445
  }
446
446
  ];
447
447
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overlord-cli",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "description": "Overlord CLI — launch AI agents on tickets from anywhere",
5
5
  "type": "module",
6
6
  "bin": {
@@ -7,9 +7,9 @@ disable-model-invocation: true
7
7
  Create a new Overlord ticket from the user's request.
8
8
 
9
9
  Use `$ARGUMENTS` as the input.
10
- If it already contains flags such as `--title`, `--priority`, `--project-id`, or `--execution-target`, pass those flags through after `ovld protocol spawn`.
10
+ If it already contains flags such as `--title`, `--priority`, `--project-id`, or `--execution-target`, pass those flags through after `ovld protocol spawn --agent claude-code`.
11
11
  Otherwise, treat `$ARGUMENTS` as the objective text and run:
12
- `ovld protocol spawn --objective "<objective>"`
12
+ `ovld protocol spawn --agent claude-code --objective "<objective>"`
13
13
 
14
14
  If no objective was provided, ask the user for one and stop.
15
15
 
@@ -71,7 +71,7 @@ correct project by matching your current working directory against each project'
71
71
  configured "Local working directory". No `--project-id` flag is needed:
72
72
 
73
73
  ```bash
74
- ovld protocol spawn --objective "Implement feature X" --priority medium
74
+ ovld protocol spawn --agent claude-code --objective "Implement feature X" --priority medium
75
75
  ```
76
76
 
77
77
  To discover which project maps to the current directory: