overlord-cli 0.2605240843.0 → 0.2605241416.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
@@ -127,7 +127,7 @@ Agents can find docs here: https://www.ovld.ai/docs/for-agents
127
127
  - `create` - create a draft ticket without attaching (standalone or follow-up)
128
128
  - `prompt` - create a ticket and attach to it immediately (`spawn` is a backward-compatible alias)
129
129
  - `record-work` - record already-completed chat work as a ticket in review with a completed objective and trigger feed-post generation
130
- - `update` - post progress, activity events, and optional change rationales
130
+ - `update` - post progress, discussion/decision events, optional change rationales, and explicit `--begin-follow-up-work` transitions
131
131
  - `record-change-rationales` - persist structured change rationales without a normal progress update
132
132
  - `ask` - post a blocking question and move the ticket to review
133
133
  - `permission-request` - notify Overlord that the agent is requesting tool permission
@@ -151,6 +151,8 @@ Use `create` for future work you want to track, `prompt` for work that should st
151
151
 
152
152
  `ovld protocol deliver` accepts either discrete flags like `--summary` / `--artifacts-json`, an inline full payload with `--payload-json '{"summary":"...","artifacts":[...],"changeRationales":[...]}'`, or a file/stdin payload with `--payload-file <path|->`.
153
153
 
154
+ After delivery, follow-up messages are recorded as discussion by default. To start implementation again on a delivered/review ticket, call `ovld protocol update --begin-follow-up-work --follow-up-intent execution --summary "Beginning follow-up work."` before moving back to `--phase execute`. Use `--event-type discussion_summary` or `--event-type decision` for important non-file outcomes. Once follow-up execution records a real work signal, such as an execution update, git snapshot, change rationales, deliverables, or explicit `pending_delivery` intent, Overlord marks the objective `pending_delivery` so redelivery is only required for actual post-delivery work.
155
+
154
156
  ## License
155
157
 
156
158
  Permission is granted to use this software for any purpose, free of charge. You may not modify, distribute, sublicense, or sell copies of the software without explicit permission from the author.
@@ -3,7 +3,7 @@
3
3
  // `ovld add-cwd` — interactively add the current working directory as a
4
4
  // project resource. Lists the user's projects, prompts for a selection,
5
5
  // registers cwd as a primary resource for this device, and writes the
6
- // project entry into the directory's overlord.json file.
6
+ // project entry into the directory's .overlord/project.json file.
7
7
 
8
8
  import os from 'node:os';
9
9
  import path from 'node:path';
@@ -100,7 +100,7 @@ export async function runAddCwdCommand(args) {
100
100
 
101
101
  Registers a directory (defaulting to the current working directory) as a
102
102
  project resource for this device, and writes the project entry into the
103
- directory's overlord.json file.
103
+ directory's .overlord/project.json file.
104
104
 
105
105
  When --project-id is omitted, the command lists your projects and prompts
106
106
  you to pick one.
@@ -160,7 +160,7 @@ By default the registered resource is marked primary for this device. Pass
160
160
  if (!res.ok) {
161
161
  if (res.status === 409) {
162
162
  output.write(
163
- `\nDirectory is already registered for "${project.name}" on this device. Updating overlord.json only.\n`
163
+ `\nDirectory is already registered for "${project.name}" on this device. Updating .overlord/project.json only.\n`
164
164
  );
165
165
  } else {
166
166
  console.error(
@@ -178,7 +178,7 @@ By default the registered resource is marked primary for this device. Pass
178
178
  output.write(`\nWrote ${result.filePath} (${result.action}).\n`);
179
179
  } catch (err) {
180
180
  output.write(
181
- `\nWarning: could not update overlord.json: ${err instanceof Error ? err.message : err}\n`
181
+ `\nWarning: could not update .overlord/project.json: ${err instanceof Error ? err.message : err}\n`
182
182
  );
183
183
  }
184
184
 
@@ -1,18 +1,60 @@
1
- // Read/merge/write logic for the per-folder `overlord.json` file.
1
+ // Read/merge/write logic for the per-folder `.overlord/project.json` file.
2
2
  // Mirrors lib/overlord-config/local-config.ts for CLI use.
3
3
 
4
4
  import { promises as fs } from 'node:fs';
5
5
  import path from 'node:path';
6
6
 
7
- export const OVERLORD_CONFIG_FILENAME = 'overlord.json';
7
+ export const OVERLORD_CONFIG_DIRNAME = '.overlord';
8
+ export const OVERLORD_CONFIG_FILENAME = 'project.json';
9
+ export const OVERLORD_SCRATCH_DIRNAME = 'tmp';
10
+ export const OVERLORD_LOG_DIRNAME = 'logs';
8
11
 
9
12
  const FILE_COMMENT =
10
13
  'This file lets Overlord (https://www.ovld.ai) identify which project this folder belongs to. It is created automatically when the folder is added as a project resource. DO NOT EDIT MANUALLY.';
14
+ const GITIGNORE_ENTRIES = ['.overlord/tmp/', '.overlord/logs/'];
11
15
 
12
16
  function buildConfig(projects) {
13
17
  return { _comment: FILE_COMMENT, projects };
14
18
  }
15
19
 
20
+ function configDirPath(directoryPath) {
21
+ return path.join(directoryPath, OVERLORD_CONFIG_DIRNAME);
22
+ }
23
+
24
+ function configFilePath(directoryPath) {
25
+ return path.join(configDirPath(directoryPath), OVERLORD_CONFIG_FILENAME);
26
+ }
27
+
28
+ function scratchDirPath(directoryPath) {
29
+ return path.join(configDirPath(directoryPath), OVERLORD_SCRATCH_DIRNAME);
30
+ }
31
+
32
+ async function ensureProjectLocalWorkspace(directoryPath) {
33
+ await fs.mkdir(configDirPath(directoryPath), { recursive: true });
34
+ await fs.mkdir(scratchDirPath(directoryPath), { recursive: true });
35
+ await ensureGitignoreEntries(directoryPath);
36
+ }
37
+
38
+ async function ensureGitignoreEntries(directoryPath) {
39
+ const gitignorePath = path.join(directoryPath, '.gitignore');
40
+ let raw = '';
41
+ try {
42
+ raw = await fs.readFile(gitignorePath, 'utf8');
43
+ } catch (error) {
44
+ if (!error || error.code !== 'ENOENT') throw error;
45
+ }
46
+
47
+ const lines = raw.split(/\r?\n/);
48
+ const existing = new Set(lines.map(line => line.trim()));
49
+ const missing = GITIGNORE_ENTRIES.filter(entry => !existing.has(entry));
50
+ if (missing.length === 0) return;
51
+
52
+ const next = raw.trimEnd()
53
+ ? `${raw.trimEnd()}\n\n# Overlord local scratch\n${missing.join('\n')}\n`
54
+ : `# Overlord local scratch\n${missing.join('\n')}\n`;
55
+ await fs.writeFile(gitignorePath, next, 'utf8');
56
+ }
57
+
16
58
  function isProjectArray(value) {
17
59
  return (
18
60
  Array.isArray(value) &&
@@ -41,11 +83,12 @@ async function readExistingProjects(filePath) {
41
83
  }
42
84
 
43
85
  /**
44
- * Ensures `directoryPath/overlord.json` lists the project. Returns
86
+ * Ensures `directoryPath/.overlord/project.json` lists the project. Returns
45
87
  * `{ filePath, action: 'created' | 'added-project' | 'unchanged' }`.
46
88
  */
47
89
  export async function upsertLocalOverlordConfig({ directoryPath, project }) {
48
- const filePath = path.join(directoryPath, OVERLORD_CONFIG_FILENAME);
90
+ const filePath = configFilePath(directoryPath);
91
+ await ensureProjectLocalWorkspace(directoryPath);
49
92
  const existing = await readExistingProjects(filePath);
50
93
 
51
94
  if (existing === null) {
@@ -68,12 +111,12 @@ export async function upsertLocalOverlordConfig({ directoryPath, project }) {
68
111
  }
69
112
 
70
113
  /**
71
- * Removes `projectId` from `directoryPath/overlord.json`. Deletes the file
114
+ * Removes `projectId` from `directoryPath/.overlord/project.json`. Deletes the file
72
115
  * when the resulting projects list is empty. Returns
73
116
  * `{ filePath, action: 'removed-project' | 'deleted-file' | 'not-found' | 'unchanged' }`.
74
117
  */
75
118
  export async function removeProjectFromLocalOverlordConfig({ directoryPath, projectId }) {
76
- const filePath = path.join(directoryPath, OVERLORD_CONFIG_FILENAME);
119
+ const filePath = configFilePath(directoryPath);
77
120
  const existing = await readExistingProjects(filePath);
78
121
 
79
122
  if (existing === null) return { filePath, action: 'not-found' };
@@ -1023,6 +1023,8 @@ async function protocolUpdate(args) {
1023
1023
  : {}),
1024
1024
  ...(flags.phase ? { phase: String(flags.phase) } : {}),
1025
1025
  ...(flags['event-type'] ? { eventType: String(flags['event-type']) } : {}),
1026
+ ...(flags['begin-follow-up-work'] !== undefined ? { beginFollowUpWork: true } : {}),
1027
+ ...(flags['follow-up-intent'] ? { followUpIntent: String(flags['follow-up-intent']) } : {}),
1026
1028
  ...(flags['payload-json']
1027
1029
  ? { payload: parseJsonFlag('--payload-json', flags['payload-json']) }
1028
1030
  : {}),
@@ -1204,13 +1206,17 @@ async function protocolHookEvent(args) {
1204
1206
  await resolveProtocolAuthForFlags(flags, ticketId);
1205
1207
  const timeoutMs = resolveTimeout(flags);
1206
1208
 
1209
+ const sessionKey = String(flags['session-key'] ?? process.env.SESSION_KEY ?? '');
1210
+
1207
1211
  const body = {
1208
1212
  hookType,
1209
1213
  ticketId,
1210
1214
  ...(flags.prompt !== undefined ? { prompt: String(flags.prompt) } : {}),
1211
1215
  ...(flags['turn-index'] !== undefined
1212
1216
  ? { turnIndex: parseInt(String(flags['turn-index']), 10) }
1213
- : {})
1217
+ : {}),
1218
+ ...(flags['follow-up-intent'] ? { followUpIntent: String(flags['follow-up-intent']) } : {}),
1219
+ ...(sessionKey ? { sessionKey } : {})
1214
1220
  };
1215
1221
 
1216
1222
  try {
@@ -1833,7 +1839,7 @@ async function protocolAddProjectResource(args) {
1833
1839
  process.stderr.write(`OVERLORD_CONFIG=${result.filePath} (${result.action})\n`);
1834
1840
  } catch (configError) {
1835
1841
  process.stderr.write(
1836
- `\nWarning: could not write overlord.json: ${configError instanceof Error ? configError.message : configError}\n`
1842
+ `\nWarning: could not write .overlord/project.json: ${configError instanceof Error ? configError.message : configError}\n`
1837
1843
  );
1838
1844
  }
1839
1845
  }
@@ -2696,14 +2702,22 @@ update:
2696
2702
  --summary <text> or --summary-file <path|->
2697
2703
  Optional:
2698
2704
  --phase <status> draft | execute | review | deliver | complete | blocked | cancelled
2699
- --event-type <type> update | user_follow_up | alert
2705
+ --event-type <type> update | user_follow_up | alert | discussion_summary | decision
2706
+ --begin-follow-up-work Explicitly reopen a delivered/review ticket for execution
2707
+ --follow-up-intent <mode> discussion | execution | pending_delivery
2700
2708
  --payload-json <json> Additional structured payload, for example notifications
2701
2709
  --external-url <url|null> Store or clear a deep link to the live agent session
2702
2710
  --external-session-id <id|null>
2703
2711
  --change-rationales-json <json>
2704
2712
  --change-rationales-file <path>
2705
2713
  Notes:
2706
- Use phase=execute while actively working. user_follow_up is for verbatim human follow-up messages.
2714
+ Use phase=execute while actively working. For delivered/review tickets, pass
2715
+ --begin-follow-up-work before moving back to execute. user_follow_up is for
2716
+ verbatim human follow-up messages; discussion_summary and decision record
2717
+ important non-file follow-up outcomes. After a prior delivery, execution
2718
+ updates, git snapshots, change rationales, or explicit pending_delivery
2719
+ intent mark the objective as pending_delivery so redelivery is required
2720
+ only when follow-up execution produced work.
2707
2721
  Pass --summary-file - to read the summary from stdin — avoids shell interpretation of backticks,
2708
2722
  quotes, or other special characters in the summary text.
2709
2723
 
@@ -2750,6 +2764,7 @@ hook-event:
2750
2764
  Optional:
2751
2765
  --prompt <text>
2752
2766
  --turn-index <n>
2767
+ --follow-up-intent <mode> discussion | execution | pending_delivery
2753
2768
 
2754
2769
  read-context:
2755
2770
  Purpose:
@@ -3082,7 +3097,7 @@ Examples:
3082
3097
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done"
3083
3098
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --artifacts-file ./artifacts.json
3084
3099
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-json '{"summary":"Done","artifacts":[{"type":"note","label":"Delivery","content":"..."}]}'
3085
- ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-file ./deliver.json
3100
+ ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-file .overlord/tmp/deliver.json
3086
3101
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-file -
3087
3102
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --skip-file-change-check
3088
3103
  ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --timeout 60000
@@ -208,7 +208,7 @@ If you receive a prompt with a specified ticket ID, adhere to the following. If
208
208
  \`\`\`bash
209
209
  ovld protocol deliver --session-key <sessionKey> --ticket-id $TICKET_ID --payload-file -
210
210
  \`\`\`
211
- This avoids creating a scratch delivery file that needs cleanup. If your runtime cannot provide stdin directly, \`--payload-file ./deliver.json\` remains supported; treat that file as ephemeral scratch data, never commit it, and remove it after delivery.
211
+ This avoids creating a scratch delivery file that needs cleanup. If your runtime cannot provide stdin directly, \`--payload-file .overlord/tmp/deliver.json\` remains supported; treat that file as ephemeral scratch data under \`.overlord/tmp\`, never commit it, and remove it after delivery.
212
212
 
213
213
  ## Change Rationales
214
214
 
@@ -295,7 +295,7 @@ If you receive a prompt with a specified ticket ID, adhere to the following. If
295
295
  --artifacts-json '[{"type":"next_steps","label":"Next steps","content":"..."}]' \\\\
296
296
  --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 @@"}]}]'
297
297
  \`\`\`
298
- For larger delivery JSON, prefer \`--payload-file -\` and stream the full payload 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. Do not leave delivery JSON checked into the worktree.
298
+ For larger delivery JSON, prefer \`--payload-file -\` and stream the full payload 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 under \`.overlord/tmp\` and remove it after delivery. Do not leave delivery JSON checked into the worktree.
299
299
 
300
300
  ## Change Rationales
301
301
 
@@ -381,7 +381,7 @@ When done, deliver with artifacts and change rationales:
381
381
  \`\`\`bash
382
382
  ovld protocol deliver --session-key <sessionKey> --ticket-id $TICKET_ID --summary "Narrative: what you did, next steps." --artifacts-json '[{"type":"next_steps","label":"Next steps","content":"..."}]' --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 @@"}]}]'
383
383
  \`\`\`
384
- Use \`--payload-json\` for compact inline delivery objects. For larger delivery JSON, prefer \`--payload-file -\` with stdin so no scratch file needs to be created or removed. If you use a JSON file for delivery transport, keep it ephemeral scratch data outside the repository and remove it after the protocol call.
384
+ Use \`--payload-json\` for compact inline delivery objects. For larger delivery JSON, prefer \`--payload-file -\` with stdin so no scratch file needs to be created or removed. If you use a JSON file for delivery transport, keep it under \`.overlord/tmp\` and remove it after the protocol call.
385
385
 
386
386
  Rules:
387
387
  - Always attach first and deliver last.
@@ -1670,6 +1670,19 @@ function getPlatformUrl() {
1670
1670
  return process.env.OVERLORD_URL || 'http://localhost:3000';
1671
1671
  }
1672
1672
 
1673
+ function printAgentPermissionsDescription() {
1674
+ console.log('Agent permissions pre-approve the `ovld protocol` commands your coding');
1675
+ console.log('agent runs to drive Overlord tickets (attach, update, deliver, etc.) so');
1676
+ console.log('you aren\'t prompted to approve each call mid-task. Without this, the');
1677
+ console.log('agent stalls on permission prompts for every protocol step.');
1678
+ console.log('');
1679
+ console.log('Where each agent\'s permission is written:');
1680
+ console.log(' • Claude Code — ./.claude/settings.local.json (this directory only)');
1681
+ console.log(' • OpenCode — ~/.config/opencode/opencode.json (global)');
1682
+ console.log(' • Codex — prints a one-time command to run inside Codex');
1683
+ console.log('Each entry is plain JSON you can review or remove at any time.\n');
1684
+ }
1685
+
1673
1686
  function installAgentPermissions(agents, platformUrl) {
1674
1687
  console.log(`\nInstalling agent permissions for: ${agents.join(', ')}`);
1675
1688
  console.log(`Platform URL: ${platformUrl}\n`);
@@ -1878,6 +1891,7 @@ export async function runSetupCommand(args) {
1878
1891
  if (agentsThatNeedPermissions.length > 0) {
1879
1892
  console.log('Agent plugins/connectors prepared successfully!\n');
1880
1893
 
1894
+ printAgentPermissionsDescription();
1881
1895
  const shouldInstallPermissions = await askYesNo(
1882
1896
  'Would you like to configure agent permissions for Overlord protocol access?',
1883
1897
  true
@@ -1923,8 +1937,10 @@ export async function runSetupCommand(args) {
1923
1937
  );
1924
1938
 
1925
1939
  if (agentsThatNeedPermissions.length > 0) {
1940
+ console.log();
1941
+ printAgentPermissionsDescription();
1926
1942
  const shouldInstallPermissions = await askYesNo(
1927
- '\nWould you like to configure agent permissions for Overlord protocol access?',
1943
+ 'Would you like to configure agent permissions for Overlord protocol access?',
1928
1944
  true
1929
1945
  );
1930
1946
 
@@ -1956,8 +1972,10 @@ export async function runSetupCommand(args) {
1956
1972
 
1957
1973
  // Offer permissions setup for single agent install too
1958
1974
  if (['claude', 'codex', 'opencode'].includes(agent)) {
1975
+ console.log();
1976
+ printAgentPermissionsDescription();
1959
1977
  const shouldInstallPermissions = await askYesNo(
1960
- '\nWould you like to configure agent permissions for Overlord protocol access?',
1978
+ 'Would you like to configure agent permissions for Overlord protocol access?',
1961
1979
  true
1962
1980
  );
1963
1981
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overlord-cli",
3
- "version": "0.2605240843.0",
3
+ "version": "0.2605241416.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",
@@ -327,7 +327,8 @@ const tools = [
327
327
  },
328
328
  {
329
329
  name: 'update',
330
- description: 'Post an Overlord progress update or activity event.',
330
+ description:
331
+ 'Post an Overlord progress update or activity event. Use begin_follow_up_work to explicitly reopen delivered/review tickets for follow-up execution.',
331
332
  inputSchema: {
332
333
  type: 'object',
333
334
  properties: {
@@ -341,7 +342,15 @@ const tools = [
341
342
  type: 'string',
342
343
  enum: ['draft', 'execute', 'review', 'deliver', 'complete', 'blocked', 'cancelled']
343
344
  },
344
- event_type: { type: 'string', enum: ['update', 'user_follow_up', 'alert'] },
345
+ event_type: {
346
+ type: 'string',
347
+ enum: ['update', 'user_follow_up', 'alert', 'discussion_summary', 'decision']
348
+ },
349
+ begin_follow_up_work: { type: 'boolean' },
350
+ follow_up_intent: {
351
+ type: 'string',
352
+ enum: ['discussion', 'execution', 'pending_delivery']
353
+ },
345
354
  external_url: { type: ['string', 'null'] },
346
355
  external_session_id: { type: ['string', 'null'] },
347
356
  payload: { type: 'object' },
@@ -355,6 +364,8 @@ const tools = [
355
364
  summary: args.summary,
356
365
  phase: args.phase,
357
366
  'event-type': args.event_type,
367
+ ...(args.begin_follow_up_work ? { 'begin-follow-up-work': true } : {}),
368
+ 'follow-up-intent': args.follow_up_intent,
358
369
  'external-url': args.external_url,
359
370
  'external-session-id': args.external_session_id,
360
371
  'payload-json': args.payload,
@@ -394,7 +405,7 @@ const tools = [
394
405
  {
395
406
  name: 'record_hook_event',
396
407
  description:
397
- 'Record a hook lifecycle event for a ticket without requiring a session key. Stop is reserved for future lifecycle hooks.',
408
+ "Record a hook lifecycle event for a ticket. Use hookType='Stop' with a session_key to check whether delivery is needed after a turn ends.",
398
409
  inputSchema: {
399
410
  type: 'object',
400
411
  properties: {
@@ -404,7 +415,16 @@ const tools = [
404
415
  description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.'
405
416
  },
406
417
  prompt: { type: 'string' },
407
- turn_index: { type: 'number' }
418
+ turn_index: { type: 'number' },
419
+ follow_up_intent: {
420
+ type: 'string',
421
+ enum: ['discussion', 'execution', 'pending_delivery']
422
+ },
423
+ session_key: {
424
+ type: 'string',
425
+ description:
426
+ 'Optional session key for Stop hooks. When provided, the response includes deliveryStatus indicating whether delivery is needed.'
427
+ }
408
428
  },
409
429
  required: ['hook_type', 'ticket_id']
410
430
  },
@@ -412,7 +432,9 @@ const tools = [
412
432
  'hook-type': args.hook_type,
413
433
  'ticket-id': args.ticket_id,
414
434
  prompt: args.prompt,
415
- 'turn-index': args.turn_index
435
+ 'turn-index': args.turn_index,
436
+ 'follow-up-intent': args.follow_up_intent,
437
+ 'session-key': args.session_key
416
438
  }),
417
439
  subcommand: 'hook-event'
418
440
  },
@@ -760,6 +782,10 @@ const tools = [
760
782
  device_platform: {
761
783
  type: 'string',
762
784
  description: 'e.g. darwin, linux, win32.'
785
+ },
786
+ device_port: {
787
+ type: 'integer',
788
+ description: 'SSH port for placeholder reconciliation when multiple targets share the same host.'
763
789
  }
764
790
  },
765
791
  required: ['device_fingerprint']
@@ -771,7 +797,8 @@ const tools = [
771
797
  : {}),
772
798
  ...(typeof args.device_platform === 'string' && args.device_platform.trim().length > 0
773
799
  ? { 'device-platform': args.device_platform }
774
- : {})
800
+ : {}),
801
+ ...(typeof args.device_port === 'number' ? { 'device-port': args.device_port } : {})
775
802
  }),
776
803
  subcommand: 'get-device'
777
804
  },
@@ -833,7 +860,11 @@ const tools = [
833
860
  label: { type: 'string' },
834
861
  is_primary: { type: 'boolean' },
835
862
  device_hostname: { type: 'string' },
836
- device_platform: { type: 'string' }
863
+ device_platform: { type: 'string' },
864
+ device_port: {
865
+ type: 'integer',
866
+ description: 'SSH port for placeholder reconciliation when multiple targets share the same host.'
867
+ }
837
868
  },
838
869
  required: ['project_id', 'device_fingerprint']
839
870
  },
@@ -850,7 +881,8 @@ const tools = [
850
881
  : {}),
851
882
  ...(typeof args.device_platform === 'string' && args.device_platform.trim().length > 0
852
883
  ? { 'device-platform': args.device_platform }
853
- : {})
884
+ : {}),
885
+ ...(typeof args.device_port === 'number' ? { 'device-port': args.device_port } : {})
854
886
  }),
855
887
  subcommand: 'add-project-resource'
856
888
  },
@@ -885,6 +917,40 @@ const tools = [
885
917
  }),
886
918
  subcommand: 'update-project-resource'
887
919
  },
920
+ {
921
+ name: 'request_approval_gate',
922
+ description:
923
+ 'Flip the next queued future objective on this ticket to require manual human approval before it runs. Use sparingly — only when your current work surfaced a question, risk, or decision a human must make before the next objective auto-launches.',
924
+ inputSchema: {
925
+ type: 'object',
926
+ properties: {
927
+ session_key: { type: 'string' },
928
+ ticket_id: {
929
+ type: 'string',
930
+ description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.'
931
+ },
932
+ reason: {
933
+ type: 'string',
934
+ description:
935
+ 'Why a human must approve before the next objective runs. Rendered in the awaiting-approval banner verbatim.'
936
+ },
937
+ objective_id: {
938
+ type: 'string',
939
+ description: 'Optional UUID of a specific future objective to gate. Defaults to the next.'
940
+ }
941
+ },
942
+ required: ['session_key', 'ticket_id', 'reason']
943
+ },
944
+ toCliFlags: args => ({
945
+ 'session-key': args.session_key,
946
+ 'ticket-id': args.ticket_id,
947
+ reason: args.reason,
948
+ ...(typeof args.objective_id === 'string' && args.objective_id.trim().length > 0
949
+ ? { 'objective-id': args.objective_id }
950
+ : {})
951
+ }),
952
+ subcommand: 'request-approval-gate'
953
+ },
888
954
  {
889
955
  name: 'request_execution',
890
956
  description:
@@ -1190,7 +1256,7 @@ async function handleRequest(message) {
1190
1256
  },
1191
1257
  serverInfo: {
1192
1258
  name: 'overlord',
1193
- version: '0.1.0'
1259
+ version: '0.1.1'
1194
1260
  },
1195
1261
  instructions:
1196
1262
  'Use these tools to drive Overlord ticket workflows through the installed ovld CLI. Names mirror hosted MCP tools (attach, update, deliver, get_device, list_project_resources, …). Session tools need attach/connect. Devices are scoped to organization + user + fingerprint — call get_device before add_project_resource.'
@@ -15,7 +15,7 @@ Use this mode when the prompt already contains a ticket ID or explicitly says th
15
15
  2. Keep the returned `session.sessionKey` for all follow-up calls.
16
16
  3. Treat the Overlord ticket prompt as authoritative for the objective, constraints, and delivery target.
17
17
  4. Post updates while working with `ovld protocol update --phase execute`.
18
- 5. Follow-up messages after the initial ticket are captured automatically by the installed `UserPromptSubmit` hook. Do not post `user_follow_up` manually unless the hook is unavailable.
18
+ 5. Follow-up messages after the initial ticket are captured automatically by the installed `UserPromptSubmit` hook and stay in discussion intent while the ticket is in review. Do not post `user_follow_up` manually unless the hook is unavailable.
19
19
  6. If blocked, call `ovld protocol ask` and stop.
20
20
  7. Deliver last with `ovld protocol deliver`, including `changeRationales` for each meaningful behavioral file change.
21
21
 
@@ -60,6 +60,14 @@ Event types:
60
60
  - `update` for standard progress updates
61
61
  - `user_follow_up` for human follow-up messages after the initial ticket when the automatic hook is unavailable
62
62
  - `alert` for warnings or non-blocking issues
63
+ - `discussion_summary` for important discussion outcomes that should remain visible on the ticket
64
+ - `decision` for explicit non-file decisions made during follow-up discussion
65
+
66
+ Post-delivery follow-up modes:
67
+
68
+ - User follow-up messages default to `discussion` intent while the ticket is in review.
69
+ - Use `ovld protocol update --begin-follow-up-work --follow-up-intent execution --summary "Beginning follow-up work."` before moving a delivered/review ticket back to execution.
70
+ - Use `--follow-up-intent pending_delivery` when implementation is complete but final delivery is still being prepared.
63
71
 
64
72
  Ask when blocked:
65
73
 
@@ -77,7 +85,7 @@ ovld protocol deliver --session-key <sessionKey> \
77
85
  --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 @@"}]}]'
78
86
  ```
79
87
 
80
- Use `--payload-json` when the full delivery object fits comfortably inline. 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 the summary contains special characters, use `--summary-file -` and pipe via a single-quoted heredoc (`<<'EOF'`) to prevent shell expansion. 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.
88
+ Use `--payload-json` when the full delivery object fits comfortably inline. 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 the summary contains special characters, use `--summary-file -` and pipe via a single-quoted heredoc (`<<'EOF'`) to prevent shell expansion. If you use `--payload-file`, `--artifacts-file`, or `--change-rationales-file` with a real path, treat that file as ephemeral scratch data under `.overlord/tmp` and remove it after delivery.
81
89
 
82
90
  Revert an objective:
83
91
 
@@ -190,15 +198,15 @@ When you need a project ID for a protocol command and the ticket prompt did not
190
198
 
191
199
  1. `--project-id` if explicitly provided.
192
200
  2. Otherwise, let the CLI match the current working directory (the default behavior of `create`, `prompt`, `discover-project`).
193
- 3. If working-directory resolution returns nothing, read `overlord.json` from the cwd (or any ancestor you have access to) and pass its project id via `--project-id`.
201
+ 3. If working-directory resolution returns nothing, read `.overlord/project.json` from the cwd (or any ancestor you have access to) and pass its project id via `--project-id`.
194
202
 
195
203
  **Over MCP (web agents and hosted tools, where the server cannot see the agent's cwd):**
196
204
 
197
205
  1. `projectId` (hosted MCP) or `project_id` (local shim) if explicitly provided or found in the ticket/context.
198
- 2. Read `overlord.json` from the directory the user is accessing and pass its project id as `projectId` / `project_id`.
206
+ 2. Read `.overlord/project.json` from the directory the user is accessing and pass its project id as `projectId` / `project_id`.
199
207
  3. As a last resort, try `workingDirectory` / `working_directory` resolution. If a device fingerprint is available, include `deviceFingerprint` / `device_fingerprint`.
200
208
 
201
- If `overlord.json` contains more than one project, show the user the project **names** from that file and ask which one to use before calling any protocol command — never silently pick one.
209
+ If `.overlord/project.json` contains more than one project, show the user the project **names** from that file and ask which one to use before calling any protocol command — never silently pick one.
202
210
 
203
211
  ### Devices and checkout paths
204
212
 
@@ -256,12 +264,16 @@ This keeps the ticket feed readable while preserving the full document in versio
256
264
  - Always attach first and always deliver last once you are on a ticket.
257
265
  - Use `ovld protocol` commands and the plugin's slash commands instead of ad hoc scripts.
258
266
  - Do not invent protocol subcommands. Use `ovld protocol help` when unsure.
267
+ - After delivery, answer ordinary questions and clarifications in discussion mode; only begin follow-up execution when the user explicitly asks for file or code changes.
268
+ - When explicit follow-up implementation starts on a delivered/review ticket, call `ovld protocol update --begin-follow-up-work --follow-up-intent execution --summary "Beginning follow-up work."` before code changes or `--phase execute`.
269
+ - During follow-up execution, post progress updates and record change rationales for each file modified, the same as during initial execution.
270
+ - Record important non-file decisions with `--event-type decision` or `--event-type discussion_summary`.
259
271
  - The `summary` in deliver is what the PM reads first, so write it as a narrative, not a command list.
260
272
  - When a summary or question contains backticks, `$vars`, or other shell-special characters, always use `--summary-file -` (or `--question-file -`) with a single-quoted heredoc (`<<'EOF'`). Never retry by stripping or escaping content — pipe stdin instead.
261
273
  - Use `write-context` for facts a future agent session should know.
262
274
  - 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.
263
275
  - If you must run `ovld auth login`, `--organization-id <id>` is optional — use it only when you want to set a different default organization than the CLI would choose automatically.
264
276
  - Do not add or commit changes unless the user explicitly asks you to commit.
265
- - Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
277
+ - Delivery is the concluding step. After delivering, stop implementation work unless the user explicitly asks for follow-up execution; once follow-up execution is complete, deliver again.
266
278
 
267
- <!-- version: 0.1.2 -->
279
+ <!-- version: 0.1.5 -->
@@ -21,6 +21,17 @@
21
21
  }
22
22
  ]
23
23
  }
24
+ ],
25
+ "Stop": [
26
+ {
27
+ "matcher": ".*",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/stop-hook.sh"
32
+ }
33
+ ]
34
+ }
24
35
  ]
25
36
  }
26
37
  }
@@ -0,0 +1,78 @@
1
+ #!/bin/bash
2
+ # Overlord Stop hook — fires when the agent's turn ends.
3
+ # Checks whether the session has pending delivery work and outputs
4
+ # guidance if delivery is needed. Does not force delivery.
5
+
6
+ BODY=$(cat -)
7
+ OVERLORD_BASE_URL="${CLAUDE_PLUGIN_OPTION_OVERLORD_URL:-$OVERLORD_URL}"
8
+ OVERLORD_TOKEN="${CLAUDE_PLUGIN_OPTION_AGENT_TOKEN:-${CLAUDE_PLUGIN_OPTION_OVERLORD_ACCESS_TOKEN:-$OVERLORD_ACCESS_TOKEN}}"
9
+ OVERLORD_LOCAL_SECRET="${OVERLORD_LOCAL_SECRET:-}"
10
+ LOG_DIR="${HOME:-}/.ovld/logs"
11
+ LOG_FILE="${LOG_DIR}/stop-hook.log"
12
+
13
+ log_hook() {
14
+ mkdir -p "$LOG_DIR" 2>/dev/null || return 0
15
+ printf '%s [stop] %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$1" >>"$LOG_FILE" 2>/dev/null || true
16
+ }
17
+
18
+ if [ -z "$OVERLORD_BASE_URL" ] || [ -z "$OVERLORD_TOKEN" ] || [ -z "${TICKET_ID:-}" ]; then
19
+ log_hook "missing required env, skipping"
20
+ exit 0
21
+ fi
22
+
23
+ SESSION_KEY=$(python3 -c "
24
+ import base64, json, os, tempfile
25
+ cwd = os.getcwd().encode()
26
+ encoded = base64.urlsafe_b64encode(cwd).decode().rstrip('=')
27
+ session_file = os.path.join(tempfile.gettempdir(), f'.overlord-session-{encoded}')
28
+ try:
29
+ with open(session_file) as f:
30
+ data = json.load(f)
31
+ print(data.get('sessionKey', ''))
32
+ except Exception:
33
+ print('')
34
+ " 2>/dev/null)
35
+
36
+ if [ -z "$SESSION_KEY" ]; then
37
+ log_hook "no persisted session key, skipping delivery check"
38
+ exit 0
39
+ fi
40
+
41
+ PAYLOAD="{\"hookType\":\"Stop\",\"ticketId\":\"$TICKET_ID\",\"sessionKey\":\"$SESSION_KEY\"}"
42
+
43
+ log_hook "checking delivery status session_key=${SESSION_KEY:0:8}..."
44
+
45
+ RESPONSE_FILE=$(mktemp "${TMPDIR:-/tmp}/overlord-stop-hook.XXXXXX")
46
+ HTTP_CODE=$(curl -sS -m 5 -o "$RESPONSE_FILE" -w "%{http_code}" \
47
+ -X POST "$OVERLORD_BASE_URL/api/protocol/hook-event" \
48
+ -H "Authorization: Bearer $OVERLORD_TOKEN" \
49
+ -H "X-Overlord-Local-Secret: $OVERLORD_LOCAL_SECRET" \
50
+ -H "Content-Type: application/json" \
51
+ -d "$PAYLOAD")
52
+ CURL_EXIT=$?
53
+
54
+ if [ "$CURL_EXIT" -eq 0 ] && [ -f "$RESPONSE_FILE" ]; then
55
+ GUIDANCE=$(python3 -c "
56
+ import json, sys
57
+ try:
58
+ with open(sys.argv[1]) as f:
59
+ data = json.load(f)
60
+ status = data.get('deliveryStatus')
61
+ if status and status.get('needed'):
62
+ print(status.get('reason', 'Pending delivery work detected.'))
63
+ except Exception:
64
+ pass
65
+ " "$RESPONSE_FILE" 2>/dev/null)
66
+
67
+ if [ -n "$GUIDANCE" ]; then
68
+ log_hook "delivery needed, outputting guidance"
69
+ echo "[Overlord] $GUIDANCE"
70
+ else
71
+ log_hook "no delivery needed"
72
+ fi
73
+ else
74
+ log_hook "hook-event POST failed exit=$CURL_EXIT http_status=${HTTP_CODE:-none}"
75
+ fi
76
+
77
+ rm -f "$RESPONSE_FILE"
78
+ exit 0
@@ -15,7 +15,7 @@ Use this mode when the prompt already contains a ticket ID or explicitly says th
15
15
  2. The attach response prints JSON to stdout containing `session.sessionKey`. The CLI also persists this key automatically so subsequent `ovld protocol` commands in the same working directory resolve it without `--session-key`. If auto-resolution fails, pass `--session-key <sessionKey>` explicitly on every subsequent call.
16
16
  3. Treat the Overlord ticket prompt as authoritative for the objective, constraints, and delivery target.
17
17
  4. Post updates while working: `ovld protocol update --session-key <sessionKey> --ticket-id <ticket_id> --summary "..." --phase execute`.
18
- 5. Follow-up messages after the initial ticket are captured automatically by the installed `UserPromptSubmit` hook. Do not post `user_follow_up` manually unless the hook is unavailable.
18
+ 5. Follow-up messages after the initial ticket are captured automatically by the installed `UserPromptSubmit` hook and stay in discussion intent while the ticket is in review. Do not post `user_follow_up` manually unless the hook is unavailable.
19
19
  6. If blocked, call `ovld protocol ask --session-key <sessionKey> --ticket-id <ticket_id> --question "..."` and stop.
20
20
  7. Deliver last with `ovld protocol deliver --session-key <sessionKey> --ticket-id <ticket_id> --summary "..."`, including `changeRationales` for each meaningful behavioral file change.
21
21
 
@@ -93,13 +93,17 @@ For the `record-change-rationales` command and full payload shape with optional
93
93
  - Use `ovld protocol` commands and the plugin's slash commands instead of ad hoc scripts.
94
94
  - Do not invent protocol subcommands. Use `ovld protocol help` when unsure.
95
95
  - Include at least one progress update before delivering.
96
+ - After delivery, answer ordinary questions and clarifications in discussion mode; only begin follow-up execution when the user explicitly asks for file or code changes.
97
+ - When explicit follow-up implementation starts on a delivered/review ticket, call `ovld protocol update --begin-follow-up-work --follow-up-intent execution --summary "Beginning follow-up work."` before code changes or `--phase execute`.
98
+ - During follow-up execution, post progress updates and record change rationales for each file modified, the same as during initial execution.
99
+ - Record important non-file decisions with `--event-type decision` or `--event-type discussion_summary`.
96
100
  - The `summary` in deliver is what the PM reads first, so write it as a narrative, not a command list.
97
101
  - When a summary or question contains backticks, `$vars`, or other shell-special characters, always use `--summary-file -` (or `--question-file -`) with a single-quoted heredoc (`<<'EOF'`). Never retry by stripping or escaping content — pipe stdin instead. See [reference/shell-escaping.md](reference/shell-escaping.md).
98
102
  - Use `write-context` for facts a future agent session should know.
99
103
  - 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.
100
104
  - If you must run `ovld auth login`, `--organization-id <id>` is optional — use it only when you want to set a different default organization than the CLI would choose automatically.
101
105
  - Do not add or commit changes unless the user explicitly asks you to commit.
102
- - Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
106
+ - Delivery is the concluding step. After delivering, stop implementation work unless the user explicitly asks for follow-up execution; once follow-up execution is complete, deliver again.
103
107
 
104
108
  ## Reference
105
109
 
@@ -109,5 +113,5 @@ For the `record-change-rationales` command and full payload shape with optional
109
113
  - [reference/context.md](reference/context.md) — Shared state, attachments, and large artifact policy
110
114
  - [reference/shell-escaping.md](reference/shell-escaping.md) — Heredoc stdin piping for special characters in summaries and payloads
111
115
 
112
- <!-- version: 0.5.6 -->
116
+ <!-- version: 0.5.8 -->
113
117
 
@@ -31,6 +31,14 @@ Event types:
31
31
  - `update` for standard progress updates
32
32
  - `user_follow_up` — only when the `UserPromptSubmit` hook is unavailable; the hook normally posts follow-ups to the activity feed
33
33
  - `alert` for warnings or non-blocking issues
34
+ - `discussion_summary` for important discussion outcomes that should remain visible on the ticket
35
+ - `decision` for explicit non-file decisions made during follow-up discussion
36
+
37
+ Post-delivery follow-up modes:
38
+
39
+ - User follow-up messages default to `discussion` intent while the ticket is in review.
40
+ - Use `ovld protocol update --begin-follow-up-work --follow-up-intent execution --summary "Beginning follow-up work."` before moving a delivered/review ticket back to execution.
41
+ - Use `--follow-up-intent pending_delivery` when implementation is complete but final delivery is still being prepared.
34
42
 
35
43
  ## Ask
36
44
 
@@ -48,7 +56,7 @@ ovld protocol deliver --session-key <sessionKey> \
48
56
  --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 @@"}]}]'
49
57
  ```
50
58
 
51
- Use `--payload-json` when the full delivery object fits comfortably inline. 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.
59
+ Use `--payload-json` when the full delivery object fits comfortably inline. 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 under `.overlord/tmp` and remove it after delivery.
52
60
 
53
61
  Ordinary deliver artifacts should use `next_steps`, `test_results`, `migration`, `note`, `url`, or `decision`.
54
62
 
@@ -132,13 +140,13 @@ When you need a project ID for a protocol command and the ticket prompt did not
132
140
 
133
141
  1. `--project-id` if explicitly provided.
134
142
  2. Otherwise, let the CLI match the current working directory (the default behavior of `create`, `prompt`, `discover-project`).
135
- 3. If working-directory resolution returns nothing, read `overlord.json` from the cwd (or any ancestor you have access to) and pass its project id via `--project-id`.
143
+ 3. If working-directory resolution returns nothing, read `.overlord/project.json` from the cwd (or any ancestor you have access to) and pass its project id via `--project-id`.
136
144
 
137
145
  **Over MCP (web agents and hosted tools, where the server cannot see the agent's cwd):**
138
146
 
139
147
  1. `projectId` (hosted MCP) or `project_id` (local shim) if explicitly provided or found in the ticket/context.
140
- 2. Read `overlord.json` from the directory the user is accessing and pass its project id as `projectId` / `project_id`.
148
+ 2. Read `.overlord/project.json` from the directory the user is accessing and pass its project id as `projectId` / `project_id`.
141
149
  3. As a last resort, try `workingDirectory` / `working_directory` resolution. If a device fingerprint is available, include `deviceFingerprint` / `device_fingerprint`.
142
150
 
143
- If `overlord.json` contains more than one project, show the user the project **names** from that file and ask which one to use before calling any protocol command — never silently pick one.
151
+ If `.overlord/project.json` contains more than one project, show the user the project **names** from that file and ask which one to use before calling any protocol command — never silently pick one.
144
152
 
@@ -12,5 +12,5 @@ Use `--payload-json` when the full delivery object fits comfortably inline. For
12
12
 
13
13
  If the summary contains special characters, use `--summary-file -` and pipe via a single-quoted heredoc (`<<'EOF'`) to prevent shell expansion.
14
14
 
15
- 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.
15
+ If you use `--payload-file`, `--artifacts-file`, or `--change-rationales-file` with a real path, treat that file as ephemeral scratch data under `.overlord/tmp` and remove it after delivery.
16
16
 
@@ -15,7 +15,7 @@ Use this mode when the prompt already contains a ticket ID or explicitly says th
15
15
  2. The attach response prints JSON to stdout containing `session.sessionKey`. The CLI also persists this key automatically so subsequent `ovld protocol` commands in the same working directory resolve it without `--session-key`. If auto-resolution fails, pass `--session-key <sessionKey>` explicitly on every subsequent call.
16
16
  3. Treat the Overlord ticket prompt as authoritative for the objective, constraints, and delivery target.
17
17
  4. Post updates while working: `ovld protocol update --session-key <sessionKey> --ticket-id <ticket_id> --summary "..." --phase execute`.
18
- 5. Follow-up messages after the initial ticket are captured automatically by the installed Cursor `beforeSubmitPrompt` hook (see `~/.cursor/hooks.json`). Do not post `user_follow_up` manually unless the hook is unavailable.
18
+ 5. Follow-up messages after the initial ticket are captured automatically by the installed Cursor `beforeSubmitPrompt` hook (see `~/.cursor/hooks.json`) and stay in discussion intent while the ticket is in review. Do not post `user_follow_up` manually unless the hook is unavailable.
19
19
  6. If blocked, call `ovld protocol ask --session-key <sessionKey> --ticket-id <ticket_id> --question "..."` and stop.
20
20
  7. Deliver last with `ovld protocol deliver --session-key <sessionKey> --ticket-id <ticket_id> --summary "..."`, including `changeRationales` for each meaningful behavioral file change.
21
21
 
@@ -93,13 +93,17 @@ For the `record-change-rationales` command and full payload shape with optional
93
93
  - Use `ovld protocol` commands, the plugin commands, and the MCP tool instead of ad hoc scripts.
94
94
  - Do not invent protocol subcommands. Use `ovld protocol help` when unsure.
95
95
  - Include at least one progress update before delivering.
96
+ - After delivery, answer ordinary questions and clarifications in discussion mode; only begin follow-up execution when the user explicitly asks for file or code changes.
97
+ - When explicit follow-up implementation starts on a delivered/review ticket, call `ovld protocol update --begin-follow-up-work --follow-up-intent execution --summary "Beginning follow-up work."` before code changes or `--phase execute`.
98
+ - During follow-up execution, post progress updates and record change rationales for each file modified, the same as during initial execution.
99
+ - Record important non-file decisions with `--event-type decision` or `--event-type discussion_summary`.
96
100
  - The `summary` in deliver is what the PM reads first, so write it as a narrative, not a command list.
97
101
  - When a summary or question contains backticks, `$vars`, or other shell-special characters, always use `--summary-file -` (or `--question-file -`) with a single-quoted heredoc (`<<'EOF'`). Never retry by stripping or escaping content — pipe stdin instead. See [reference/shell-escaping.md](reference/shell-escaping.md).
98
102
  - Use `write-context` for facts a future agent session should know.
99
103
  - 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.
100
104
  - If you must run `ovld auth login`, `--organization-id <id>` is optional — use it only when you want to set a different default organization than the CLI would choose automatically.
101
105
  - Do not add or commit changes unless the user explicitly asks you to commit.
102
- - Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
106
+ - Delivery is the concluding step. After delivering, stop implementation work unless the user explicitly asks for follow-up execution; once follow-up execution is complete, deliver again.
103
107
 
104
108
  ## Reference
105
109
 
@@ -109,5 +113,5 @@ For the `record-change-rationales` command and full payload shape with optional
109
113
  - [reference/context.md](reference/context.md) — Shared state, attachments, and large artifact policy
110
114
  - [reference/shell-escaping.md](reference/shell-escaping.md) — Heredoc stdin piping for special characters in summaries and payloads
111
115
 
112
- <!-- version: 0.5.6 -->
116
+ <!-- version: 0.5.8 -->
113
117
 
@@ -31,6 +31,14 @@ Event types:
31
31
  - `update` for standard progress updates
32
32
  - `user_follow_up` — only when the Cursor `beforeSubmitPrompt` hook (see `~/.cursor/hooks.json`) is unavailable; the hook normally posts follow-ups to the activity feed
33
33
  - `alert` for warnings or non-blocking issues
34
+ - `discussion_summary` for important discussion outcomes that should remain visible on the ticket
35
+ - `decision` for explicit non-file decisions made during follow-up discussion
36
+
37
+ Post-delivery follow-up modes:
38
+
39
+ - User follow-up messages default to `discussion` intent while the ticket is in review.
40
+ - Use `ovld protocol update --begin-follow-up-work --follow-up-intent execution --summary "Beginning follow-up work."` before moving a delivered/review ticket back to execution.
41
+ - Use `--follow-up-intent pending_delivery` when implementation is complete but final delivery is still being prepared.
34
42
 
35
43
  ## Ask
36
44
 
@@ -48,7 +56,7 @@ ovld protocol deliver --session-key <sessionKey> \
48
56
  --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 @@"}]}]'
49
57
  ```
50
58
 
51
- Use `--payload-json` when the full delivery object fits comfortably inline. 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.
59
+ Use `--payload-json` when the full delivery object fits comfortably inline. 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 under `.overlord/tmp` and remove it after delivery.
52
60
 
53
61
  Ordinary deliver artifacts should use `next_steps`, `test_results`, `migration`, `note`, `url`, or `decision`.
54
62
 
@@ -132,13 +140,13 @@ When you need a project ID for a protocol command and the ticket prompt did not
132
140
 
133
141
  1. `--project-id` if explicitly provided.
134
142
  2. Otherwise, let the CLI match the current working directory (the default behavior of `create`, `prompt`, `discover-project`).
135
- 3. If working-directory resolution returns nothing, read `overlord.json` from the cwd (or any ancestor you have access to) and pass its project id via `--project-id`.
143
+ 3. If working-directory resolution returns nothing, read `.overlord/project.json` from the cwd (or any ancestor you have access to) and pass its project id via `--project-id`.
136
144
 
137
145
  **Over MCP (web agents and hosted tools, where the server cannot see the agent's cwd):**
138
146
 
139
147
  1. `projectId` (hosted MCP) or `project_id` (local shim) if explicitly provided or found in the ticket/context.
140
- 2. Read `overlord.json` from the directory the user is accessing and pass its project id as `projectId` / `project_id`.
148
+ 2. Read `.overlord/project.json` from the directory the user is accessing and pass its project id as `projectId` / `project_id`.
141
149
  3. As a last resort, try `workingDirectory` / `working_directory` resolution. If a device fingerprint is available, include `deviceFingerprint` / `device_fingerprint`.
142
150
 
143
- If `overlord.json` contains more than one project, show the user the project **names** from that file and ask which one to use before calling any protocol command — never silently pick one.
151
+ If `.overlord/project.json` contains more than one project, show the user the project **names** from that file and ask which one to use before calling any protocol command — never silently pick one.
144
152
 
@@ -12,5 +12,5 @@ Use `--payload-json` when the full delivery object fits comfortably inline. For
12
12
 
13
13
  If the summary contains special characters, use `--summary-file -` and pipe via a single-quoted heredoc (`<<'EOF'`) to prevent shell expansion.
14
14
 
15
- 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.
15
+ If you use `--payload-file`, `--artifacts-file`, or `--change-rationales-file` with a real path, treat that file as ephemeral scratch data under `.overlord/tmp` and remove it after delivery.
16
16
 
@@ -327,7 +327,8 @@ const tools = [
327
327
  },
328
328
  {
329
329
  name: 'update',
330
- description: 'Post an Overlord progress update or activity event.',
330
+ description:
331
+ 'Post an Overlord progress update or activity event. Use begin_follow_up_work to explicitly reopen delivered/review tickets for follow-up execution.',
331
332
  inputSchema: {
332
333
  type: 'object',
333
334
  properties: {
@@ -341,7 +342,15 @@ const tools = [
341
342
  type: 'string',
342
343
  enum: ['draft', 'execute', 'review', 'deliver', 'complete', 'blocked', 'cancelled']
343
344
  },
344
- event_type: { type: 'string', enum: ['update', 'user_follow_up', 'alert'] },
345
+ event_type: {
346
+ type: 'string',
347
+ enum: ['update', 'user_follow_up', 'alert', 'discussion_summary', 'decision']
348
+ },
349
+ begin_follow_up_work: { type: 'boolean' },
350
+ follow_up_intent: {
351
+ type: 'string',
352
+ enum: ['discussion', 'execution', 'pending_delivery']
353
+ },
345
354
  external_url: { type: ['string', 'null'] },
346
355
  external_session_id: { type: ['string', 'null'] },
347
356
  payload: { type: 'object' },
@@ -355,6 +364,8 @@ const tools = [
355
364
  summary: args.summary,
356
365
  phase: args.phase,
357
366
  'event-type': args.event_type,
367
+ ...(args.begin_follow_up_work ? { 'begin-follow-up-work': true } : {}),
368
+ 'follow-up-intent': args.follow_up_intent,
358
369
  'external-url': args.external_url,
359
370
  'external-session-id': args.external_session_id,
360
371
  'payload-json': args.payload,
@@ -394,7 +405,7 @@ const tools = [
394
405
  {
395
406
  name: 'record_hook_event',
396
407
  description:
397
- 'Record a hook lifecycle event for a ticket without requiring a session key. Stop is reserved for future lifecycle hooks.',
408
+ "Record a hook lifecycle event for a ticket. Use hookType='Stop' with a session_key to check whether delivery is needed after a turn ends.",
398
409
  inputSchema: {
399
410
  type: 'object',
400
411
  properties: {
@@ -404,7 +415,16 @@ const tools = [
404
415
  description: 'Ticket identifier (e.g. 1:899). Also accepts UUID.'
405
416
  },
406
417
  prompt: { type: 'string' },
407
- turn_index: { type: 'number' }
418
+ turn_index: { type: 'number' },
419
+ follow_up_intent: {
420
+ type: 'string',
421
+ enum: ['discussion', 'execution', 'pending_delivery']
422
+ },
423
+ session_key: {
424
+ type: 'string',
425
+ description:
426
+ 'Optional session key for Stop hooks. When provided, the response includes deliveryStatus indicating whether delivery is needed.'
427
+ }
408
428
  },
409
429
  required: ['hook_type', 'ticket_id']
410
430
  },
@@ -412,7 +432,9 @@ const tools = [
412
432
  'hook-type': args.hook_type,
413
433
  'ticket-id': args.ticket_id,
414
434
  prompt: args.prompt,
415
- 'turn-index': args.turn_index
435
+ 'turn-index': args.turn_index,
436
+ 'follow-up-intent': args.follow_up_intent,
437
+ 'session-key': args.session_key
416
438
  }),
417
439
  subcommand: 'hook-event'
418
440
  },
@@ -1197,7 +1219,7 @@ async function handleRequest(message) {
1197
1219
  },
1198
1220
  serverInfo: {
1199
1221
  name: 'overlord',
1200
- version: '0.1.5'
1222
+ version: '0.1.6'
1201
1223
  },
1202
1224
  instructions:
1203
1225
  'Use these tools to drive Overlord ticket workflows through the installed ovld CLI. Names mirror hosted MCP tools (attach, update, deliver, get_device, list_project_resources, …). Session tools need attach/connect. Devices are scoped to organization + user + fingerprint — call get_device before add_project_resource.'
@@ -15,7 +15,7 @@ Use this mode when the prompt already contains a ticket ID or explicitly says th
15
15
  2. The attach response prints JSON to stdout containing `session.sessionKey`. The CLI also persists this key automatically so subsequent `ovld protocol` commands in the same working directory resolve it without `--session-key`. If auto-resolution fails, pass `--session-key <sessionKey>` explicitly on every subsequent call.
16
16
  3. Treat the Overlord ticket prompt as authoritative for the objective, constraints, and delivery target.
17
17
  4. Post updates while working: `ovld protocol update --session-key <sessionKey> --ticket-id <ticket_id> --summary "..." --phase execute`.
18
- 5. Follow-up messages after the initial ticket are captured automatically by the installed `UserPromptSubmit` hook. Do not post `user_follow_up` manually unless the hook is unavailable.
18
+ 5. Follow-up messages after the initial ticket are captured automatically by the installed `UserPromptSubmit` hook and stay in discussion intent while the ticket is in review. Do not post `user_follow_up` manually unless the hook is unavailable.
19
19
  6. If blocked, call `ovld protocol ask --session-key <sessionKey> --ticket-id <ticket_id> --question "..."` and stop.
20
20
  7. Deliver last with `ovld protocol deliver --session-key <sessionKey> --ticket-id <ticket_id> --summary "..."`, including `changeRationales` for each meaningful behavioral file change.
21
21
 
@@ -93,6 +93,10 @@ For the `record-change-rationales` command and full payload shape with optional
93
93
  - Use `ovld protocol` commands and the plugin's MCP tools instead of ad hoc scripts.
94
94
  - Do not invent protocol subcommands. Use `ovld protocol help` when unsure.
95
95
  - Include at least one progress update before delivering.
96
+ - After delivery, answer ordinary questions and clarifications in discussion mode; only begin follow-up execution when the user explicitly asks for file or code changes.
97
+ - When explicit follow-up implementation starts on a delivered/review ticket, call `ovld protocol update --begin-follow-up-work --follow-up-intent execution --summary "Beginning follow-up work."` before code changes or `--phase execute`.
98
+ - During follow-up execution, post progress updates and record change rationales for each file modified, the same as during initial execution.
99
+ - Record important non-file decisions with `--event-type decision` or `--event-type discussion_summary`.
96
100
  - The `summary` in deliver is what the PM reads first, so write it as a narrative, not a command list.
97
101
  - When a summary or question contains backticks, `$vars`, or other shell-special characters, always use `--summary-file -` (or `--question-file -`) with a single-quoted heredoc (`<<'EOF'`). Never retry by stripping or escaping content — pipe stdin instead. See [reference/shell-escaping.md](reference/shell-escaping.md).
98
102
  - Use `write-context` for facts a future agent session should know.
@@ -100,7 +104,7 @@ For the `record-change-rationales` command and full payload shape with optional
100
104
  - If you must run `ovld auth login`, `--organization-id <id>` is optional — use it only when you want to set a different default organization than the CLI would choose automatically.
101
105
  - Do not add or commit changes unless the user explicitly asks you to commit.
102
106
  - Do not create or rely on a local Codex `AGENTS.md` bundle for Overlord.
103
- - Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
107
+ - Delivery is the concluding step. After delivering, stop implementation work unless the user explicitly asks for follow-up execution; once follow-up execution is complete, deliver again.
104
108
 
105
109
  ## Reference
106
110
 
@@ -110,5 +114,5 @@ For the `record-change-rationales` command and full payload shape with optional
110
114
  - [reference/context.md](reference/context.md) — Shared state, attachments, and large artifact policy
111
115
  - [reference/shell-escaping.md](reference/shell-escaping.md) — Heredoc stdin piping for special characters in summaries and payloads
112
116
 
113
- <!-- version: 0.5.6 -->
117
+ <!-- version: 0.5.8 -->
114
118
 
@@ -31,6 +31,14 @@ Event types:
31
31
  - `update` for standard progress updates
32
32
  - `user_follow_up` — only when the `UserPromptSubmit` hook is unavailable; the hook normally posts follow-ups to the activity feed
33
33
  - `alert` for warnings or non-blocking issues
34
+ - `discussion_summary` for important discussion outcomes that should remain visible on the ticket
35
+ - `decision` for explicit non-file decisions made during follow-up discussion
36
+
37
+ Post-delivery follow-up modes:
38
+
39
+ - User follow-up messages default to `discussion` intent while the ticket is in review.
40
+ - Use `ovld protocol update --begin-follow-up-work --follow-up-intent execution --summary "Beginning follow-up work."` before moving a delivered/review ticket back to execution.
41
+ - Use `--follow-up-intent pending_delivery` when implementation is complete but final delivery is still being prepared.
34
42
 
35
43
  ## Ask
36
44
 
@@ -48,7 +56,7 @@ ovld protocol deliver --session-key <sessionKey> \
48
56
  --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 @@"}]}]'
49
57
  ```
50
58
 
51
- Use `--payload-json` when the full delivery object fits comfortably inline. 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.
59
+ Use `--payload-json` when the full delivery object fits comfortably inline. 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 under `.overlord/tmp` and remove it after delivery.
52
60
 
53
61
  Ordinary deliver artifacts should use `next_steps`, `test_results`, `migration`, `note`, `url`, or `decision`.
54
62
 
@@ -132,13 +140,13 @@ When you need a project ID for a protocol command and the ticket prompt did not
132
140
 
133
141
  1. `--project-id` if explicitly provided.
134
142
  2. Otherwise, let the CLI match the current working directory (the default behavior of `create`, `prompt`, `discover-project`).
135
- 3. If working-directory resolution returns nothing, read `overlord.json` from the cwd (or any ancestor you have access to) and pass its project id via `--project-id`.
143
+ 3. If working-directory resolution returns nothing, read `.overlord/project.json` from the cwd (or any ancestor you have access to) and pass its project id via `--project-id`.
136
144
 
137
145
  **Over MCP (web agents and hosted tools, where the server cannot see the agent's cwd):**
138
146
 
139
147
  1. `projectId` (hosted MCP) or `project_id` (local shim) if explicitly provided or found in the ticket/context.
140
- 2. Read `overlord.json` from the directory the user is accessing and pass its project id as `projectId` / `project_id`.
148
+ 2. Read `.overlord/project.json` from the directory the user is accessing and pass its project id as `projectId` / `project_id`.
141
149
  3. As a last resort, try `workingDirectory` / `working_directory` resolution. If a device fingerprint is available, include `deviceFingerprint` / `device_fingerprint`.
142
150
 
143
- If `overlord.json` contains more than one project, show the user the project **names** from that file and ask which one to use before calling any protocol command — never silently pick one.
151
+ If `.overlord/project.json` contains more than one project, show the user the project **names** from that file and ask which one to use before calling any protocol command — never silently pick one.
144
152
 
@@ -12,5 +12,5 @@ Use `--payload-json` when the full delivery object fits comfortably inline. For
12
12
 
13
13
  If the summary contains special characters, use `--summary-file -` and pipe via a single-quoted heredoc (`<<'EOF'`) to prevent shell expansion.
14
14
 
15
- 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.
15
+ If you use `--payload-file`, `--artifacts-file`, or `--change-rationales-file` with a real path, treat that file as ephemeral scratch data under `.overlord/tmp` and remove it after delivery.
16
16