overlord-cli 0.2605111118.0 → 0.2605111338.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.
package/README.md CHANGED
@@ -119,7 +119,7 @@ Agents can find docs here: https://www.ovld.ai/docs/for-agents
119
119
  - `permission-request` - notify Overlord that the agent is requesting tool permission
120
120
  - `read-context` - read shared persistent context for this ticket
121
121
  - `write-context` - write shared persistent context for future sessions
122
- - `deliver` - finish work, send artifacts, and move the ticket to review
122
+ - `deliver` - create a local JJ/Git checkpoint when available, send artifacts/rationales, and move the ticket to review
123
123
  - `attachment-prepare-upload` - get a signed upload URL for an objective attachment
124
124
  - `attachment-finalize-upload` - finalize an uploaded objective attachment row after storage upload
125
125
  - `attachment-download-url` - get a signed download URL for an existing objective attachment
@@ -359,6 +359,164 @@ async function resolveSnapshotContext(flags) {
359
359
  return null;
360
360
  }
361
361
 
362
+ /**
363
+ * Shallow-merge snapshot metadata: launch/env context first, then deliver payload
364
+ * (payload wins on duplicate keys) so agents can add jj ids without losing workspace
365
+ * paths from OVERLORD_SNAPSHOT_JSON.
366
+ * @param {Record<string, unknown> | null | undefined} fromEnvOrFlags
367
+ * @param {Record<string, unknown> | null | undefined} fromPayload
368
+ * @returns {Record<string, unknown> | null}
369
+ */
370
+ function mergeDeliverSnapshot(fromEnvOrFlags, fromPayload) {
371
+ const base =
372
+ fromEnvOrFlags && typeof fromEnvOrFlags === 'object' && !Array.isArray(fromEnvOrFlags)
373
+ ? { ...fromEnvOrFlags }
374
+ : {};
375
+ const overlay =
376
+ fromPayload && typeof fromPayload === 'object' && !Array.isArray(fromPayload)
377
+ ? { ...fromPayload }
378
+ : {};
379
+ const merged = { ...base, ...overlay };
380
+ return Object.keys(merged).length > 0 ? merged : null;
381
+ }
382
+
383
+ function runLocalCommand(command, args, options = {}) {
384
+ return execFileSync(command, args, {
385
+ cwd: options.cwd,
386
+ encoding: 'utf8',
387
+ stdio: ['ignore', 'pipe', 'pipe'],
388
+ timeout: options.timeoutMs ?? 60000,
389
+ maxBuffer: 10 * 1024 * 1024
390
+ });
391
+ }
392
+
393
+ function commandSucceeds(command, args, cwd) {
394
+ try {
395
+ runLocalCommand(command, args, { cwd, timeoutMs: 15000 });
396
+ return true;
397
+ } catch {
398
+ return false;
399
+ }
400
+ }
401
+
402
+ function detectLocalCheckpointBackend(workspacePath, preference) {
403
+ if (!workspacePath || !fs.existsSync(workspacePath)) return null;
404
+ if (preference === 'jj') return 'jj';
405
+ if (preference === 'git') return 'git';
406
+ if (
407
+ fs.existsSync(path.join(workspacePath, '.jj')) ||
408
+ commandSucceeds('jj', ['--repository', workspacePath, 'root'], workspacePath)
409
+ ) {
410
+ return 'jj';
411
+ }
412
+ if (commandSucceeds('git', ['-C', workspacePath, 'rev-parse', '--show-toplevel'], workspacePath)) {
413
+ return 'git';
414
+ }
415
+ return null;
416
+ }
417
+
418
+ function createLocalDeliveryCheckpoint(flags, snapshot) {
419
+ if (flags['skip-checkpoint']) return null;
420
+
421
+ const preference = String(flags['checkpoint-backend'] ?? 'auto').trim();
422
+ if (!['auto', 'jj', 'git'].includes(preference)) {
423
+ throw new Error('--checkpoint-backend must be one of: auto, jj, git');
424
+ }
425
+
426
+ const workspacePath = path.resolve(
427
+ String(snapshot?.workspacePath ?? process.env.OVERLORD_WORKSPACE_PATH ?? process.cwd())
428
+ );
429
+ if (!fs.existsSync(workspacePath)) return null;
430
+
431
+ const backend = detectLocalCheckpointBackend(workspacePath, preference);
432
+ if (!backend) return null;
433
+
434
+ if (backend === 'jj') {
435
+ try {
436
+ runLocalCommand('jj', ['--repository', workspacePath, 'util', 'snapshot'], {
437
+ cwd: workspacePath
438
+ });
439
+ const commit = runLocalCommand(
440
+ 'jj',
441
+ ['--repository', workspacePath, 'log', '-r', '@', '-T', 'change_id ++ " " ++ commit_id'],
442
+ { cwd: workspacePath }
443
+ );
444
+ const operation = runLocalCommand(
445
+ 'jj',
446
+ [
447
+ '--repository',
448
+ workspacePath,
449
+ 'op',
450
+ 'log',
451
+ '--at-op=@',
452
+ '--ignore-working-copy',
453
+ '-n',
454
+ '1',
455
+ '-T',
456
+ 'id'
457
+ ],
458
+ { cwd: workspacePath }
459
+ );
460
+ const diffStat = runLocalCommand('jj', ['--repository', workspacePath, 'diff', '--stat'], {
461
+ cwd: workspacePath
462
+ });
463
+ const [jjChangeId = null, jjCommitId = null] = commit.trim().split(/\s+/, 2);
464
+ return {
465
+ checkpoint: {
466
+ diffStat: diffStat.trim() || null,
467
+ kind: 'delivery'
468
+ },
469
+ snapshot: {
470
+ backend: 'jj',
471
+ workspacePath,
472
+ workspaceName: snapshot?.workspaceName ?? path.basename(workspacePath),
473
+ jjChangeId,
474
+ jjCommitId,
475
+ jjOperationId: operation.trim() || null,
476
+ diffStat: diffStat.trim() || null
477
+ }
478
+ };
479
+ } catch (error) {
480
+ throw new Error(
481
+ `Failed to create JJ delivery checkpoint in ${workspacePath}: ${
482
+ error instanceof Error ? error.message : String(error)
483
+ }\nRe-run with --skip-checkpoint only if you intentionally want to deliver without local JJ provenance.`
484
+ );
485
+ }
486
+ }
487
+
488
+ if (preference === 'jj') {
489
+ throw new Error(`JJ checkpoint requested, but ${workspacePath} is not a JJ repository.`);
490
+ }
491
+
492
+ try {
493
+ const gitCommitId = runLocalCommand('git', ['-C', workspacePath, 'rev-parse', 'HEAD'], {
494
+ cwd: workspacePath
495
+ }).trim();
496
+ const diffStat = runLocalCommand('git', ['-C', workspacePath, 'diff', '--stat', 'HEAD'], {
497
+ cwd: workspacePath
498
+ }).trim();
499
+ return {
500
+ checkpoint: {
501
+ diffStat: diffStat || null,
502
+ kind: 'delivery'
503
+ },
504
+ snapshot: {
505
+ backend: 'git',
506
+ workspacePath,
507
+ workspaceName: snapshot?.workspaceName ?? path.basename(workspacePath),
508
+ gitCommitId: gitCommitId || null,
509
+ diffStat: diffStat || null
510
+ }
511
+ };
512
+ } catch {
513
+ if (preference === 'git') {
514
+ throw new Error(`Git checkpoint requested, but metadata could not be read in ${workspacePath}.`);
515
+ }
516
+ return null;
517
+ }
518
+ }
519
+
362
520
  function normalizeRepoRelativeFilePath(filePath, repoRoot) {
363
521
  if (typeof filePath !== 'string') return null;
364
522
 
@@ -853,7 +1011,14 @@ async function protocolDeliver(args) {
853
1011
 
854
1012
  const changeRationales =
855
1013
  deliverPayload?.changeRationales ?? (await resolveChangeRationales(flags));
856
- const snapshot = (await resolveSnapshotContext(flags)) ?? deliverPayload?.snapshot ?? null;
1014
+ let snapshot = mergeDeliverSnapshot(
1015
+ await resolveSnapshotContext(flags),
1016
+ deliverPayload?.snapshot ?? null
1017
+ );
1018
+ const checkpointResult = createLocalDeliveryCheckpoint(flags, snapshot);
1019
+ if (checkpointResult) {
1020
+ snapshot = mergeDeliverSnapshot(snapshot, checkpointResult.snapshot);
1021
+ }
857
1022
  validateDeliverFileChanges(flags, changeRationales);
858
1023
 
859
1024
  const body = {
@@ -861,6 +1026,9 @@ async function protocolDeliver(args) {
861
1026
  ticketId,
862
1027
  summary,
863
1028
  artifacts,
1029
+ ...(checkpointResult?.checkpoint
1030
+ ? { checkpoint: { ...checkpointResult.checkpoint, summary } }
1031
+ : {}),
864
1032
  ...(snapshot ? { snapshot } : {}),
865
1033
  ...(changeRationales.length > 0 ? { changeRationales } : {})
866
1034
  };
@@ -1621,10 +1789,13 @@ deliver:
1621
1789
  --artifacts-file <path|->
1622
1790
  --change-rationales-json <json>
1623
1791
  --change-rationales-file <path|->
1792
+ --checkpoint-backend <auto|jj|git> Backend for the local delivery checkpoint (default: auto)
1793
+ --skip-checkpoint Bypass automatic local checkpoint creation
1624
1794
  --skip-file-change-check Bypass local git vs changeRationales validation
1625
1795
  Notes:
1626
1796
  Use --payload-file - to read the full delivery JSON from stdin without creating a scratch file.
1627
1797
  Do not combine --payload-file with --artifacts-json/--artifacts-file or change-rationale flags.
1798
+ Deliver creates a local checkpoint before the API request when the current workspace is JJ- or Git-managed.
1628
1799
  In a git workspace, deliver validates that changed files are represented by changeRationales unless skipped.
1629
1800
 
1630
1801
  prompt:
@@ -1761,6 +1932,7 @@ Examples:
1761
1932
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --artifacts-file ./artifacts.json
1762
1933
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-file ./deliver.json
1763
1934
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-file -
1935
+ ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --checkpoint-backend jj
1764
1936
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --skip-file-change-check
1765
1937
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --timeout 60000
1766
1938
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overlord-cli",
3
- "version": "0.2605111118.0",
3
+ "version": "0.2605111338.0",
4
4
  "description": "Overlord CLI — launch AI agents on tickets from anywhere",
5
5
  "homepage": "https://www.ovld.ai/docs/",
6
6
  "type": "module",
@@ -65,7 +65,7 @@ ovld protocol deliver --session-key <sessionKey> \
65
65
  --change-rationales-json '[{"label":"Short reviewer title","file_path":"path/to/file.ts","summary":"What changed.","why":"Why it changed.","impact":"Behavioral impact.","hunks":[{"header":"@@ -10,6 +10,14 @@"}]}]'
66
66
  ```
67
67
 
68
- For larger delivery payloads, prefer `--payload-file -` and stream the full JSON on stdin so no scratch file needs to be created or removed. If you use `--payload-file`, `--artifacts-file`, or `--change-rationales-file` with a real path, treat that file as ephemeral scratch data outside the repository and remove it after delivery.
68
+ `ovld protocol deliver` automatically creates a local checkpoint before the API request when the workspace is JJ- or Git-managed; use `--skip-checkpoint` only when intentionally bypassing local provenance. For larger delivery payloads, prefer `--payload-file -` and stream the full JSON on stdin so no scratch file needs to be created or removed. If you use `--payload-file`, `--artifacts-file`, or `--change-rationales-file` with a real path, treat that file as ephemeral scratch data outside the repository and remove it after delivery.
69
69
 
70
70
  ## Mode 2: Asked From Chat To Use Overlord
71
71
 
@@ -73,14 +73,15 @@ Use this mode when the conversation starts normally and the user asks Claude to
73
73
 
74
74
  1. If the user wants to create tickets (and does not ask to start execution), run `ovld protocol create --agent claude-code --objective "..."`.
75
75
  - When `--session-key` and `--ticket-id` are provided, it creates a follow-up draft.
76
- - When session flags are omitted, it resolves the project by matching current working directory (or `--working-directory`) to the caller's configured Overlord `project_user.local_working_directory`, then creates a standalone draft.
77
- 2. Default to `create` for new tickets. Only use `/overlord:spawn` or `ovld protocol spawn --agent claude-code --objective "..."` when the user explicitly asks to create and execute immediately.
78
- `spawn` creates the ticket in `execute` status and attaches immediately.
76
+ - When session flags are omitted, it resolves the project by matching current working directory (or `--working-directory`) to Overlord `local_working_directory`, then creates a standalone draft.
77
+ 2. Default to `create` for new tickets. Only use `/overlord:prompt` or `ovld protocol prompt --agent claude-code --objective "..."` when the user explicitly asks to create and execute immediately.
78
+ `prompt` creates the ticket in `execute` status and attaches immediately.
79
79
  3. If the user already has a ticket ID and only wants to inspect it, use `/overlord:load` or run `ovld protocol load-context --ticket-id <ticket_id>`.
80
80
  4. If the user wants to route the current session onto an existing ticket by ID, use `/overlord:connect` or run `ovld protocol connect --ticket-id <ticket_id>`.
81
- 5. If the user wants to find a ticket but does not know the ID, use `ovld attach` for interactive ticket search and agent launch, or ask the user for the ticket ID if staying strictly inside chat is the better fit.
82
- 6. If you need other lifecycle commands or flags, run `ovld protocol help` and use the real subcommand list instead of guessing.
83
- 7. Once you attach to a ticket, switch back to Mode 1 and follow the full ticket lifecycle.
81
+ 5. If the user wants to establish a persistent session with a ticket by ID, use `/overlord:attach` or run `ovld protocol attach --ticket-id <ticket_id>`.
82
+ 6. If the user wants to find a ticket but does not know the ID, use `ovld attach` for interactive ticket search and agent launch, or run `ovld protocol search-tickets --query "..." --status next-up,execute` and ask the user to confirm.
83
+ 7. If you need other lifecycle commands or flags, run `ovld protocol help` and use the real subcommand list instead of guessing.
84
+ 8. Once you attach to a ticket, switch back to Mode 1 and follow the full ticket lifecycle.
84
85
 
85
86
  ## Change Rationales
86
87
 
@@ -108,7 +109,7 @@ Record only meaningful behavioral changes. Skip formatting-only noise.
108
109
 
109
110
  When creating tickets from within a repository:
110
111
  - Prefer `create` by default for draft ticket creation.
111
- - Use `spawn` only when the user explicitly asks to start execution immediately.
112
+ - Use `prompt` only when the user explicitly asks to start execution immediately.
112
113
  - Both commands can resolve the project from the current working directory; use `--working-directory` to override.
113
114
 
114
115
  ```bash
@@ -116,7 +117,7 @@ ovld protocol create --agent claude-code --objective "Capture follow-up work fro
116
117
  ```
117
118
 
118
119
  ```bash
119
- ovld protocol spawn --agent claude-code --objective "Implement feature X" --priority medium
120
+ ovld protocol prompt --agent claude-code --objective "Implement feature X" --priority medium
120
121
  ```
121
122
 
122
123
  To inspect project resolution explicitly:
@@ -148,6 +149,14 @@ ovld protocol attachment-download-url --session-key <sessionKey> --ticket-id $TI
148
149
 
149
150
  The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach.
150
151
 
152
+ ## Defaults And Notes
153
+
154
+ - API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt. The CLI defaults them to `claude-code`/`cli`; the MCP tool defaults to `mcp`. Override with `--agent` / `--method` when calling from a different runtime.
155
+ - `permission-request` is invoked by the Claude Code permission hook installed by the bundle. Agents do not normally call it directly.
156
+ - `record_change_rationales` (MCP) and `ovld protocol record-change-rationales` (CLI) both write to the same `file_changes` table. The dedicated CLI route is `POST /api/protocol/record-change-rationales`.
157
+ - Objective attachment tools follow the `<verb>_<noun>` MCP naming: `list_attachments`, `prepare_attachment_upload`, `finalize_attachment_upload`, `get_attachment_download_url`, `upload_attachment_file`. CLI commands use `attachment-*` and require `--objective-id` for upload/finalize.
158
+ - "Artifacts" in `deliver` are the structured records an agent submits at delivery time (next_steps, test_results, migration, decision, note, url) — not user-uploaded files. Files attached by users live on objectives via the attachment tools above.
159
+
151
160
  ## Rules
152
161
 
153
162
  - Always attach first and always deliver last once you are on a ticket.
@@ -155,10 +164,9 @@ The `attach` and `load-context` responses already include `attachments` and `obj
155
164
  - Do not invent protocol subcommands. Use `ovld protocol help` when unsure.
156
165
  - The `summary` in deliver is what the PM reads first, so write it as a narrative, not a command list.
157
166
  - Use `write-context` for facts a future agent session should know.
158
- - If `ovld` reports `OVERLORD_URL` is unreachable, request permission escalation or network access before retrying.
159
167
  - If a protocol or MCP call fails with auth/session errors, run `ovld auth repair` yourself before asking the user to log in again or proceed without Overlord updates.
160
168
  - If you must run `ovld auth login`, always include `--organization-id <id>` — use the organization ID from the ticket prompt context to select the organization non-interactively and avoid a blocking TTY prompt.
161
169
  - Do not add or commit changes unless the user explicitly asks you to commit.
162
170
  - Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
163
171
 
164
- <!-- version: 0.2.6 -->
172
+ <!-- version: 0.4.5 -->
@@ -17,17 +17,99 @@ Use this skill whenever Cursor needs to work with Overlord, whether the session
17
17
  6. If blocked, call `ovld protocol ask` and stop.
18
18
  7. Deliver last with `ovld protocol deliver`, including `changeRationales` for each meaningful behavioral file change.
19
19
 
20
+ ### Mode 1 Reference
21
+
22
+ Attach:
23
+
24
+ ```bash
25
+ ovld protocol attach --ticket-id $TICKET_ID
26
+ ```
27
+
28
+ Update:
29
+
30
+ ```bash
31
+ ovld protocol update --session-key <sessionKey> --ticket-id $TICKET_ID --summary "What you did and why." --phase execute
32
+ ```
33
+
34
+ Supported `--phase` values:
35
+ - `draft`
36
+ - `execute`
37
+ - `review`
38
+ - `deliver`
39
+ - `complete`
40
+ - `blocked`
41
+ - `cancelled`
42
+
43
+ Event types:
44
+ - `update` for standard progress updates
45
+ - `user_follow_up` for human follow-up messages after the initial ticket
46
+ - `alert` for warnings or non-blocking issues
47
+
48
+ Ask when blocked:
49
+
50
+ ```bash
51
+ ovld protocol ask --session-key <sessionKey> --ticket-id $TICKET_ID --question "Specific question for the PM."
52
+ ```
53
+
54
+ Deliver:
55
+
56
+ ```bash
57
+ ovld protocol deliver --session-key <sessionKey> \
58
+ --ticket-id $TICKET_ID \
59
+ --summary "Narrative: what you did, next steps." \
60
+ --artifacts-json '[{"type":"next_steps","label":"Next steps","content":"..."}]' \
61
+ --change-rationales-json '[{"label":"Short reviewer title","file_path":"path/to/file.ts","summary":"What changed.","why":"Why it changed.","impact":"Behavioral impact.","hunks":[{"header":"@@ -10,6 +10,14 @@"}]}]'
62
+ ```
63
+
64
+ `ovld protocol deliver` automatically creates a local checkpoint before the API request when the workspace is JJ- or Git-managed; use `--skip-checkpoint` only when intentionally bypassing local provenance. For larger delivery payloads, prefer `--payload-file -` and stream the full JSON on stdin so no scratch file needs to be created or removed.
65
+
20
66
  ## Mode 2: Asked From Chat To Use Overlord
21
67
 
22
68
  1. If the user wants to create tickets (and does not ask to start execution), use `/create` or run `ovld protocol create --agent cursor --objective "..."`.
23
- 2. Default to `create` for new tickets. Only use `/spawn` or `ovld protocol spawn --agent cursor --objective "..."` when the user explicitly asks to create and execute immediately.
69
+ - When `--session-key` and `--ticket-id` are provided, it creates a follow-up draft.
70
+ - When session flags are omitted, it resolves the project by matching current working directory (or `--working-directory`) to Overlord `local_working_directory`, then creates a standalone draft.
71
+ 2. Default to `create` for new tickets. Only use `/prompt` or `ovld protocol prompt --agent cursor --objective "..."` when the user explicitly asks to create and execute immediately.
24
72
  3. If the user already has a ticket ID and only wants to inspect it, use `/load` or run `ovld protocol load-context --ticket-id <ticket_id>`.
25
73
  4. If the user wants to route the current session onto an existing ticket by ID, use `/connect` or run `ovld protocol connect --ticket-id <ticket_id>`.
26
- 5. If the user wants to search for tickets by keyword or status, use the `search_tickets` MCP tool.
27
- 6. If you need other lifecycle commands or flags, run `ovld protocol help` and use the real subcommand list instead of guessing.
28
- 7. Once you attach to a ticket, switch back to Mode 1 and follow the full ticket lifecycle.
74
+ 5. If the user wants to establish a persistent session with a ticket by ID, use `/attach` or run `ovld protocol attach --ticket-id <ticket_id>`.
75
+ 6. If the user wants to find a ticket by keyword/status/project/creator/date, run `ovld protocol search-tickets --query "..." --status next-up,execute`.
76
+ 7. If you need other lifecycle commands or flags, run `ovld protocol help` and use the real subcommand list instead of guessing.
77
+ 8. Once you attach to a ticket, switch back to Mode 1 and follow the full ticket lifecycle.
78
+
79
+ ## Change Rationales
80
+
81
+ Always include `changeRationales` when delivering. Optionally include them on updates during long-running work.
82
+
83
+ Before delivering, make sure every meaningful git-tracked file change is represented in `changeRationales`; do not send `file_changes` as an artifact.
29
84
 
30
- ## Choosing `--execution-target` When Creating Tickets
85
+ ```bash
86
+ ovld protocol record-change-rationales --session-key <sessionKey> --ticket-id $TICKET_ID \
87
+ --summary "Recorded rationale details for the latest code changes." --phase execute \
88
+ --change-rationales-json '[{"label":"Add backoff","file_path":"lib/api.ts","summary":"Added retry.","why":"Transient failures.","impact":"Retries 3x.","hunks":[{"header":"@@ -22,4 +22,18 @@"}]}]'
89
+ ```
90
+
91
+ ```bash
92
+ ovld protocol update --session-key <sessionKey> --ticket-id $TICKET_ID \
93
+ --summary "Added retry logic." --phase execute \
94
+ --change-rationales-json '[{"label":"Add backoff","file_path":"lib/api.ts","summary":"Added retry.","why":"Transient failures.","impact":"Retries 3x.","hunks":[{"header":"@@ -22,4 +22,18 @@"}]}]'
95
+ ```
96
+
97
+ Record only meaningful behavioral changes. Skip formatting-only noise.
98
+
99
+ ## Project Discovery And Ticket Creation
100
+
101
+ When creating tickets from within a repository:
102
+ - Prefer `create` by default for draft ticket creation.
103
+ - Use `prompt` only when the user explicitly asks to start execution immediately.
104
+ - Both commands resolve the project from the current working directory; use `--working-directory` to override or `--project-id` to be explicit.
105
+
106
+ ```bash
107
+ ovld protocol create --agent cursor --objective "Capture follow-up work from this repository"
108
+ ovld protocol prompt --agent cursor --objective "Implement feature X" --priority medium
109
+ ovld protocol discover-project
110
+ ```
111
+
112
+ ### Choosing `--execution-target`
31
113
 
32
114
  Pass `--execution-target agent` or `--execution-target human` (default: `human`) when creating tickets.
33
115
 
@@ -36,14 +118,37 @@ Pass `--execution-target agent` or `--execution-target human` (default: `human`)
36
118
 
37
119
  When in doubt, ask yourself: *can this be done entirely inside a terminal or browser by an AI without human intervention?* If yes → `agent`. If it requires a human to log in, decide, or act in the real world → `human`.
38
120
 
121
+ ## Context And Artifacts
122
+
123
+ ```bash
124
+ ovld protocol read-context --session-key <sessionKey> --ticket-id $TICKET_ID
125
+ ovld protocol write-context --session-key <sessionKey> --ticket-id $TICKET_ID --key "key" --value '"json-value"'
126
+ ovld protocol attachment-list --session-key <sessionKey> --ticket-id $TICKET_ID
127
+ ovld protocol attachment-upload-file --session-key <sessionKey> --ticket-id $TICKET_ID --objective-id <objective-id> --file ./spec.pdf --content-type application/pdf
128
+ ovld protocol attachment-download-url --session-key <sessionKey> --ticket-id $TICKET_ID --attachment-id <attachment-id>
129
+ ```
130
+
131
+ The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach.
132
+
133
+ Objective attachment uploads also expose two-step variants — `attachment-prepare-upload` and `attachment-finalize-upload` — for callers that need a signed URL directly. Prefer `attachment-upload-file` for one-shot uploads.
134
+
135
+ "Artifacts" in `deliver` are the structured records an agent submits at delivery time (next_steps, test_results, migration, decision, note, url) — not user-uploaded files.
136
+
137
+ ## Defaults And Notes
138
+
139
+ - The Overlord API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt, but the CLI defaults them to `cursor`/`cli` (override with `--agent` / `--method`). The MCP tools default to `mcp`.
140
+ - `permission-request` is invoked by the installed permission hook; agents normally do not call it directly.
141
+ - The `record_change_rationales` MCP tool and `ovld protocol record-change-rationales` CLI both write to the same `file_changes` table; pick whichever fits your runtime.
142
+
39
143
  ## Rules
40
144
 
41
145
  - Always attach first and always deliver last once you are on a ticket.
42
146
  - Use `ovld protocol` commands, the plugin commands, and the MCP tool instead of ad hoc scripts.
43
147
  - Do not invent protocol subcommands. Use `ovld protocol help` when unsure.
44
- - If `ovld` reports `OVERLORD_URL` is unreachable, request permission escalation or network access before retrying.
45
- - If a protocol or MCP call fails with auth/session errors, run `ovld auth repair` yourself before asking the user to log in again or proceed without Overlord updates.
46
148
  - Include at least one progress update before delivering.
47
- - When creating follow-up tickets, always set `--execution-target` explicitly using the `agent`/`human` rule above.
149
+ - The `summary` in deliver is what the PM reads first, so write it as a narrative, not a command list.
150
+ - If a protocol or MCP call fails with auth/session errors, run `ovld auth repair` yourself before asking the user to log in again or proceed without Overlord updates.
151
+ - If you must run `ovld auth login`, always include `--organization-id <id>` — use the organization ID from the ticket prompt context to select the organization non-interactively and avoid a blocking TTY prompt.
152
+ - Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
48
153
 
49
- <!-- version: 0.2.4 -->
154
+ <!-- version: 0.4.5 -->
@@ -49,22 +49,18 @@ const tools = [
49
49
  inputSchema: {
50
50
  type: 'object',
51
51
  properties: {
52
- ticket_id: { type: 'string', description: 'Target ticket ID' },
52
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
53
53
  agent: { type: 'string' },
54
- model: { type: 'string' },
55
54
  method: { type: 'string' },
56
- external_session_id: { type: ['string', 'null'] },
57
- metadata: { type: 'object' }
55
+ external_session_id: { type: ['string', 'null'] }
58
56
  },
59
57
  required: ['ticket_id']
60
58
  },
61
59
  toCliFlags: args => ({
62
60
  'ticket-id': args.ticket_id,
63
61
  agent: args.agent,
64
- model: args.model,
65
62
  method: args.method,
66
- 'external-session-id': args.external_session_id,
67
- 'metadata-json': args.metadata
63
+ 'external-session-id': args.external_session_id
68
64
  }),
69
65
  subcommand: 'attach'
70
66
  },
@@ -74,7 +70,7 @@ const tools = [
74
70
  inputSchema: {
75
71
  type: 'object',
76
72
  properties: {
77
- ticket_id: { type: 'string' },
73
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
78
74
  agent: { type: 'string' },
79
75
  method: { type: 'string' }
80
76
  },
@@ -103,8 +99,8 @@ const tools = [
103
99
  subcommand: 'load-context'
104
100
  },
105
101
  {
106
- name: 'spawn_ticket',
107
- description: 'Create a follow-up ticket and attach to it immediately.',
102
+ name: 'prompt_ticket',
103
+ description: 'Create a ticket and attach to it immediately.',
108
104
  inputSchema: {
109
105
  type: 'object',
110
106
  properties: {
@@ -120,8 +116,6 @@ const tools = [
120
116
  parent_session_key: { type: 'string' },
121
117
  parent_ticket_id: { type: 'string' },
122
118
  agent: { type: 'string' },
123
- model: { type: 'string' },
124
- metadata: { type: 'object' },
125
119
  method: { type: 'string' }
126
120
  },
127
121
  required: ['objective']
@@ -139,11 +133,9 @@ const tools = [
139
133
  'parent-session-key': args.parent_session_key,
140
134
  'parent-ticket-id': args.parent_ticket_id,
141
135
  agent: args.agent,
142
- model: args.model,
143
- 'metadata-json': args.metadata,
144
136
  method: args.method
145
137
  }),
146
- subcommand: 'spawn'
138
+ subcommand: 'prompt'
147
139
  },
148
140
  {
149
141
  name: 'post_update',
@@ -152,7 +144,7 @@ const tools = [
152
144
  type: 'object',
153
145
  properties: {
154
146
  session_key: { type: 'string' },
155
- ticket_id: { type: 'string' },
147
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
156
148
  summary: { type: 'string' },
157
149
  phase: { type: 'string', enum: ['draft', 'execute', 'review', 'deliver', 'complete', 'blocked', 'cancelled'] },
158
150
  event_type: { type: 'string', enum: ['update', 'user_follow_up', 'alert'] },
@@ -183,7 +175,7 @@ const tools = [
183
175
  type: 'object',
184
176
  properties: {
185
177
  session_key: { type: 'string' },
186
- ticket_id: { type: 'string' },
178
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
187
179
  summary: { type: 'string' },
188
180
  phase: { type: 'string', enum: ['draft', 'execute', 'review', 'deliver', 'complete', 'blocked', 'cancelled'] },
189
181
  change_rationales: { type: 'array' }
@@ -206,7 +198,7 @@ const tools = [
206
198
  type: 'object',
207
199
  properties: {
208
200
  session_key: { type: 'string' },
209
- ticket_id: { type: 'string' },
201
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
210
202
  question: { type: 'string' },
211
203
  phase: { type: 'string', enum: ['draft', 'execute', 'review', 'deliver', 'complete', 'blocked', 'cancelled'] },
212
204
  payload: { type: 'object' }
@@ -229,7 +221,7 @@ const tools = [
229
221
  type: 'object',
230
222
  properties: {
231
223
  session_key: { type: 'string' },
232
- ticket_id: { type: 'string' },
224
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
233
225
  query: { type: 'string' },
234
226
  limit: { type: 'number' }
235
227
  },
@@ -250,7 +242,7 @@ const tools = [
250
242
  type: 'object',
251
243
  properties: {
252
244
  session_key: { type: 'string' },
253
- ticket_id: { type: 'string' },
245
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
254
246
  key: { type: 'string' },
255
247
  value: {},
256
248
  tags: { type: 'array', items: { type: 'string' } }
@@ -274,10 +266,26 @@ const tools = [
274
266
  type: 'object',
275
267
  properties: {
276
268
  session_key: { type: 'string' },
277
- ticket_id: { type: 'string' },
269
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
278
270
  summary: { type: 'string' },
279
271
  artifacts: { type: 'array' },
280
272
  change_rationales: { type: 'array' },
273
+ snapshot: {
274
+ type: 'object',
275
+ description:
276
+ 'Optional snapshot metadata (jj/git-worktree, workspace, jj ids). Merged with OVERLORD_SNAPSHOT_JSON when both are set.'
277
+ },
278
+ checkpoint: {
279
+ type: 'object',
280
+ description:
281
+ 'Optional checkpoint metadata. The local CLI creates this automatically for JJ/Git workspaces during deliver.'
282
+ },
283
+ checkpoint_backend: {
284
+ type: 'string',
285
+ enum: ['auto', 'jj', 'git'],
286
+ description: 'Backend for automatic local checkpointing when routed through the CLI.'
287
+ },
288
+ skip_checkpoint: { type: 'boolean' },
281
289
  skip_file_change_check: { type: 'boolean' }
282
290
  },
283
291
  required: ['session_key', 'ticket_id', 'summary']
@@ -286,13 +294,18 @@ const tools = [
286
294
  'session-key': args.session_key,
287
295
  'ticket-id': args.ticket_id,
288
296
  'payload-file': '-',
297
+ 'checkpoint-backend': args.checkpoint_backend,
298
+ 'skip-checkpoint': args.skip_checkpoint,
289
299
  'skip-file-change-check': args.skip_file_change_check
290
300
  }),
291
- toCliStdin: args => JSON.stringify({
292
- summary: args.summary,
293
- ...(Array.isArray(args.artifacts) ? { artifacts: args.artifacts } : {}),
294
- ...(Array.isArray(args.change_rationales) ? { changeRationales: args.change_rationales } : {})
295
- }),
301
+ toCliStdin: args =>
302
+ JSON.stringify({
303
+ summary: args.summary,
304
+ ...(Array.isArray(args.artifacts) ? { artifacts: args.artifacts } : {}),
305
+ ...(Array.isArray(args.change_rationales) ? { changeRationales: args.change_rationales } : {}),
306
+ ...(args.snapshot && typeof args.snapshot === 'object' ? { snapshot: args.snapshot } : {}),
307
+ ...(args.checkpoint && typeof args.checkpoint === 'object' ? { checkpoint: args.checkpoint } : {})
308
+ }),
296
309
  subcommand: 'deliver'
297
310
  },
298
311
  {
@@ -303,7 +316,7 @@ const tools = [
303
316
  type: 'object',
304
317
  properties: {
305
318
  session_key: { type: 'string' },
306
- ticket_id: { type: 'string' },
319
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
307
320
  objective_id: { type: 'string' }
308
321
  },
309
322
  required: ['session_key', 'ticket_id']
@@ -322,7 +335,7 @@ const tools = [
322
335
  type: 'object',
323
336
  properties: {
324
337
  session_key: { type: 'string' },
325
- ticket_id: { type: 'string' },
338
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
326
339
  objective_id: { type: 'string' },
327
340
  file_name: { type: 'string' },
328
341
  label: { type: 'string' },
@@ -351,7 +364,7 @@ const tools = [
351
364
  type: 'object',
352
365
  properties: {
353
366
  session_key: { type: 'string' },
354
- ticket_id: { type: 'string' },
367
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
355
368
  objective_id: { type: 'string' },
356
369
  storage_path: { type: 'string' },
357
370
  label: { type: 'string' },
@@ -380,7 +393,7 @@ const tools = [
380
393
  type: 'object',
381
394
  properties: {
382
395
  session_key: { type: 'string' },
383
- ticket_id: { type: 'string' },
396
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
384
397
  objective_id: { type: 'string' },
385
398
  attachment_id: { type: 'string' },
386
399
  storage_path: { type: 'string' },
@@ -405,7 +418,7 @@ const tools = [
405
418
  type: 'object',
406
419
  properties: {
407
420
  session_key: { type: 'string' },
408
- ticket_id: { type: 'string' },
421
+ ticket_id: { type: 'string', description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.' },
409
422
  objective_id: { type: 'string' },
410
423
  file: { type: 'string' },
411
424
  file_name: { type: 'string' },
@@ -632,10 +645,10 @@ async function handleRequest(message) {
632
645
  },
633
646
  serverInfo: {
634
647
  name: 'overlord',
635
- version: '0.2.1'
648
+ version: '0.1.2'
636
649
  },
637
650
  instructions:
638
- 'Use these tools to drive Overlord ticket workflows through the installed ovld CLI. Most operations expect a session key from attach or connect. If the CLI reports that OVERLORD_URL is unreachable, request permission escalation or network access before retrying.'
651
+ 'Use these tools to drive Overlord ticket workflows through the installed ovld CLI. Most operations expect a session key from attach or connect.'
639
652
  });
640
653
  return;
641
654
  }
@@ -63,18 +63,45 @@ ovld protocol deliver --session-key <sessionKey> \
63
63
  --change-rationales-json '[{"label":"Short reviewer title","file_path":"path/to/file.ts","summary":"What changed.","why":"Why it changed.","impact":"Behavioral impact.","hunks":[{"header":"@@ -10,6 +10,14 @@"}]}]'
64
64
  ```
65
65
 
66
- For larger delivery payloads, prefer `--payload-file -` and stream the full JSON on stdin so no scratch file needs to be created or removed. If you use `--payload-file`, `--artifacts-file`, or `--change-rationales-file` with a real path, treat that file as ephemeral scratch data outside the repository and remove it after delivery.
66
+ `ovld protocol deliver` automatically creates a local checkpoint before the API request when the workspace is JJ- or Git-managed; use `--skip-checkpoint` only when intentionally bypassing local provenance. For larger delivery payloads, prefer `--payload-file -` and stream the full JSON on stdin so no scratch file needs to be created or removed. If you use `--payload-file`, `--artifacts-file`, or `--change-rationales-file` with a real path, treat that file as ephemeral scratch data outside the repository and remove it after delivery.
67
67
 
68
68
  ## Mode 2: Asked From Chat To Use Overlord
69
69
 
70
70
  1. If the user wants to create tickets (and does not ask to start execution), run `ovld protocol create --agent codex --objective "..."`.
71
- 2. Default to `create` for new tickets. Only use `ovld protocol spawn --agent codex --objective "..."` when the user explicitly asks to create and execute immediately.
71
+ - When `--session-key` and `--ticket-id` are provided, it creates a follow-up draft.
72
+ - When session flags are omitted, it resolves the project by matching current working directory (or `--working-directory`) to Overlord `local_working_directory`, then creates a standalone draft.
73
+ 2. Default to `create` for new tickets. Only use `ovld protocol prompt --agent codex --objective "..."` when the user explicitly asks to create and execute immediately.
74
+ `prompt` creates the ticket in `execute` status and attaches immediately.
72
75
  3. If the user wants to inspect an existing ticket without starting work, use `ovld protocol load-context --ticket-id <ticket_id>`.
73
- 4. If the user wants to work an existing ticket, attach with `ovld protocol attach --ticket-id <ticket_id>` and then switch to Mode 1.
74
- 5. If the user wants to find existing tickets by keyword or workflow state, use the `search_tickets` tool.
75
- 6. If you need to understand project routing before spawning, use `ovld protocol discover-project`.
76
+ 4. If the user wants to work an existing ticket, attach with `ovld protocol attach --ticket-id <ticket_id>` and then switch to Mode 1. Use `ovld protocol connect --ticket-id <ticket_id>` instead when you only need a session key without the full ticket payload.
77
+ 5. If the user wants to find existing tickets by keyword, status, project, creator, or update window, run `ovld protocol search-tickets --query "..." --status next-up,execute --limit 10`. The MCP `search_tickets` tool exposes the same filters.
78
+ 6. If you need to understand project routing before prompting, use `ovld protocol discover-project`.
76
79
  7. If you need other lifecycle commands or flags, run `ovld protocol help` and use the real subcommand list instead of guessing.
77
80
 
81
+ ## Project Discovery And Ticket Creation
82
+
83
+ When creating tickets from within a repository:
84
+ - Prefer `create` by default for draft ticket creation.
85
+ - Use `prompt` only when the user explicitly asks to start execution immediately.
86
+ - Both commands can resolve the project from the current working directory; use `--working-directory` to override.
87
+
88
+ ```bash
89
+ ovld protocol create --agent codex --objective "Capture follow-up work from this repository"
90
+ ```
91
+
92
+ ```bash
93
+ ovld protocol prompt --agent codex --objective "Implement feature X" --priority medium
94
+ ```
95
+
96
+ ### Choosing `--execution-target`
97
+
98
+ Pass `--execution-target agent` or `--execution-target human` (default: `human`) when creating tickets.
99
+
100
+ - **`agent`** — any task an AI agent can complete in a computer environment: coding, internet research, document editing, data analysis, automated testing, etc.
101
+ - **`human`** — any task requiring human presence or judgment: setting credentials or tokens in a third-party UI (e.g. Vercel, AWS), sending physical mail, making a product or business decision, physical-world actions.
102
+
103
+ When in doubt, ask yourself: *can this be done entirely inside a terminal or browser by an AI without human intervention?* If yes → `agent`. If it requires a human to log in, decide, or act in the real world → `human`.
104
+
78
105
  ## Change Rationales
79
106
 
80
107
  Always include `changeRationales` when delivering. Optionally include them on updates during long-running work.
@@ -109,38 +136,22 @@ ovld protocol attachment-download-url --session-key <sessionKey> --ticket-id $TI
109
136
 
110
137
  The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach.
111
138
 
112
- ## Project Discovery And Ticket Creation
113
-
114
- When creating tickets from within a repository:
115
- - Prefer `create` by default for draft ticket creation.
116
- - Use `spawn` only when the user explicitly asks to start execution immediately.
117
- - Both commands can resolve the project from the current working directory; use `--working-directory` to override.
139
+ ## Defaults And Notes
118
140
 
119
- ```bash
120
- ovld protocol create --agent codex --objective "Capture follow-up work from this repository"
121
- ```
122
-
123
- ```bash
124
- ovld protocol spawn --agent codex --objective "Implement feature X" --priority medium
125
- ```
126
-
127
- ### Choosing `--execution-target`
128
-
129
- Pass `--execution-target agent` or `--execution-target human` (default: `human`) when creating tickets.
130
-
131
- - **`agent`** — any task an AI agent can complete in a computer environment: coding, internet research, document editing, data analysis, automated testing, etc.
132
- - **`human`** — any task requiring human presence or judgment: setting credentials or tokens in a third-party UI (e.g. Vercel, AWS), sending physical mail, making a product or business decision, physical-world actions.
133
-
134
- When in doubt, ask yourself: *can this be done entirely inside a terminal or browser by an AI without human intervention?* If yes → `agent`. If it requires a human to log in, decide, or act in the real world → `human`.
141
+ - API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt. The CLI defaults them to `codex`/`cli`; the MCP tool defaults to `mcp`. Override with `--agent` / `--method` when calling from a different runtime.
142
+ - `permission-request` is invoked by the local Codex plugin's permission rules; agents do not normally call it directly.
143
+ - `record_change_rationales` (MCP) and `ovld protocol record-change-rationales` (CLI) both write to the `file_changes` table; the dedicated route is `POST /api/protocol/record-change-rationales`.
144
+ - Objective attachment MCP tools use `<verb>_<noun>` names — `list_attachments`, `prepare_attachment_upload`, `finalize_attachment_upload`, `get_attachment_download_url`, `upload_attachment_file`. CLI commands use `attachment-*` and require `--objective-id` for upload/finalize.
145
+ - "Artifacts" in `deliver` are the structured records an agent submits at delivery time (next_steps, test_results, migration, decision, note, url) — not user-uploaded files.
135
146
 
136
147
  ## Rules
137
148
 
138
149
  - The authoritative lifecycle is the `ovld protocol` CLI once you are on a ticket.
139
150
  - Always attach first and always deliver last once you are working a ticket.
140
- - Prefer the installed `ovld` CLI and the plugin's MCP tools instead of ad hoc repo scripts.
151
+ - Prefer the installed `ovld protocol` CLI and the plugin's MCP tools instead of ad hoc repo scripts.
141
152
  - Do not create or rely on a local Codex `AGENTS.md` bundle for Overlord.
142
- - If `ovld` reports `OVERLORD_URL` is unreachable, request permission escalation or network access before retrying.
143
153
  - When the ticket was launched by Overlord, the ticket prompt remains authoritative for the specific task objective and ticket-level constraints.
144
154
  - If a protocol or MCP call fails with auth/session errors, run `ovld auth repair` yourself before asking the user to log in again or proceed without Overlord updates.
155
+ - If you must run `ovld auth login`, always include `--organization-id <id>` — use the organization ID from the ticket prompt context to select the organization non-interactively and avoid a blocking TTY prompt.
145
156
 
146
- <!-- version: 0.2.5 -->
157
+ <!-- version: 0.4.5 -->