overlord-cli 0.2605121033.0 → 0.2605131058.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 +3 -1
- package/bin/_cli/protocol.mjs +26 -9
- package/bin/_cli/setup.mjs +61 -5
- package/package.json +1 -1
- package/plugins/claude/scripts/user-prompt-submit-hook.sh +41 -6
- package/plugins/claude/skills/overlord-ticket/SKILL.md +3 -3
- package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor/hooks/overlord-user-prompt-submit.sh +65 -0
- package/plugins/cursor/skills/overlord-ticket/SKILL.md +4 -4
- package/plugins/overlord/skills/overlord-ticket/SKILL.md +3 -3
package/README.md
CHANGED
|
@@ -121,7 +121,7 @@ Agents can find docs here: https://www.ovld.ai/docs/for-agents
|
|
|
121
121
|
- `record-change-rationales` - persist structured change rationales without a normal progress update
|
|
122
122
|
- `ask` - post a blocking question and move the ticket to review
|
|
123
123
|
- `permission-request` - notify Overlord that the agent is requesting tool permission
|
|
124
|
-
- `hook-event` - record lifecycle hook events such as `UserPromptSubmit` without a session key
|
|
124
|
+
- `hook-event` - record lifecycle hook events such as `UserPromptSubmit` / Cursor `beforeSubmitPrompt` without a session key
|
|
125
125
|
- `read-context` - read shared persistent context for this ticket
|
|
126
126
|
- `write-context` - write shared persistent context for future sessions
|
|
127
127
|
- `deliver` - create a local JJ/Git checkpoint when available, send artifacts/rationales, and move the ticket to review
|
|
@@ -130,6 +130,8 @@ Agents can find docs here: https://www.ovld.ai/docs/for-agents
|
|
|
130
130
|
- `attachment-download-url` - get a signed download URL for an existing objective attachment
|
|
131
131
|
- `attachment-upload-file` - prepare, upload, and finalize a local objective attachment in one command
|
|
132
132
|
|
|
133
|
+
`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|->`.
|
|
134
|
+
|
|
133
135
|
## License
|
|
134
136
|
|
|
135
137
|
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.
|
package/bin/_cli/protocol.mjs
CHANGED
|
@@ -1122,14 +1122,28 @@ async function protocolDeliver(args) {
|
|
|
1122
1122
|
const { sessionKey, ticketId } = resolveSessionFlags(flags);
|
|
1123
1123
|
if (!sessionKey) throw new Error('--session-key is required (or set SESSION_KEY)');
|
|
1124
1124
|
if (!ticketId) throw new Error('--ticket-id is required (or set TICKET_ID)');
|
|
1125
|
+
if (flags['payload-file'] && flags['payload-json']) {
|
|
1126
|
+
throw new Error('Use either --payload-file or --payload-json, not both');
|
|
1127
|
+
}
|
|
1125
1128
|
const deliverPayload = flags['payload-file']
|
|
1126
1129
|
? await readJsonFileOrStdin(String(flags['payload-file']), '--payload-file')
|
|
1127
|
-
:
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1130
|
+
: flags['payload-json']
|
|
1131
|
+
? parseJsonFlag('--payload-json', flags['payload-json'])
|
|
1132
|
+
: null;
|
|
1133
|
+
if (deliverPayload && (flags.summary || flags['summary-file'])) {
|
|
1134
|
+
throw new Error('Use either payload input or --summary/--summary-file, not both');
|
|
1135
|
+
}
|
|
1136
|
+
const summary = deliverPayload
|
|
1137
|
+
? (() => {
|
|
1138
|
+
const value = deliverPayload.summary;
|
|
1139
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
1140
|
+
throw new Error('payload input must include a non-empty summary');
|
|
1141
|
+
}
|
|
1142
|
+
return value;
|
|
1143
|
+
})()
|
|
1144
|
+
: flags['summary-file']
|
|
1131
1145
|
? await readTextFileOrStdin(String(flags['summary-file']), '--summary-file')
|
|
1132
|
-
: requireFlag(flags, 'summary', undefined)
|
|
1146
|
+
: requireFlag(flags, 'summary', undefined);
|
|
1133
1147
|
|
|
1134
1148
|
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1135
1149
|
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
@@ -1137,10 +1151,10 @@ async function protocolDeliver(args) {
|
|
|
1137
1151
|
|
|
1138
1152
|
let artifacts = deliverPayload?.artifacts ?? [];
|
|
1139
1153
|
if (deliverPayload && flags['artifacts-file']) {
|
|
1140
|
-
throw new Error('Use either
|
|
1154
|
+
throw new Error('Use either payload input or --artifacts-file, not both');
|
|
1141
1155
|
}
|
|
1142
1156
|
if (deliverPayload && flags['artifacts-json']) {
|
|
1143
|
-
throw new Error('Use either
|
|
1157
|
+
throw new Error('Use either payload input or --artifacts-json, not both');
|
|
1144
1158
|
}
|
|
1145
1159
|
if (flags['artifacts-file']) {
|
|
1146
1160
|
artifacts = await readJsonFileOrStdin(String(flags['artifacts-file']), '--artifacts-file');
|
|
@@ -1149,7 +1163,7 @@ async function protocolDeliver(args) {
|
|
|
1149
1163
|
}
|
|
1150
1164
|
|
|
1151
1165
|
if (deliverPayload && (flags['change-rationales-file'] || flags['change-rationales-json'])) {
|
|
1152
|
-
throw new Error('Use either
|
|
1166
|
+
throw new Error('Use either payload input or change-rationale flags, not both');
|
|
1153
1167
|
}
|
|
1154
1168
|
|
|
1155
1169
|
const changeRationales =
|
|
@@ -1943,6 +1957,7 @@ deliver:
|
|
|
1943
1957
|
--session-key <key>
|
|
1944
1958
|
--ticket-id <ticket_id>
|
|
1945
1959
|
--summary <text> or --summary-file <path|->
|
|
1960
|
+
or: --payload-json <json>
|
|
1946
1961
|
or: --payload-file <path|-> containing { summary, artifacts, changeRationales }
|
|
1947
1962
|
Optional:
|
|
1948
1963
|
--artifacts-json <json>
|
|
@@ -1951,9 +1966,10 @@ deliver:
|
|
|
1951
1966
|
--change-rationales-file <path|->
|
|
1952
1967
|
--skip-file-change-check Bypass local git vs changeRationales validation
|
|
1953
1968
|
Notes:
|
|
1969
|
+
Use --payload-json when the full delivery JSON fits comfortably inline.
|
|
1954
1970
|
Use --payload-file - to read the full delivery JSON from stdin without creating a scratch file.
|
|
1955
1971
|
Use --summary-file - to pipe just the summary text via stdin (avoids shell special-char issues).
|
|
1956
|
-
Do not combine --payload-file with --artifacts-json/--artifacts-file or change-rationale flags.
|
|
1972
|
+
Do not combine --payload-json/--payload-file with --summary/--summary-file, --artifacts-json/--artifacts-file, or change-rationale flags.
|
|
1957
1973
|
In a git workspace, deliver validates that changed files are represented by changeRationales unless skipped.
|
|
1958
1974
|
|
|
1959
1975
|
prompt:
|
|
@@ -2089,6 +2105,7 @@ Examples:
|
|
|
2089
2105
|
ovld protocol attachment-download-url --session-key <key> --ticket-id <ticket_id> --attachment-id <attachment-id>
|
|
2090
2106
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done"
|
|
2091
2107
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --artifacts-file ./artifacts.json
|
|
2108
|
+
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-json '{"summary":"Done","artifacts":[{"type":"note","label":"Delivery","content":"..."}]}'
|
|
2092
2109
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-file ./deliver.json
|
|
2093
2110
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-file -
|
|
2094
2111
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --skip-file-change-check
|
package/bin/_cli/setup.mjs
CHANGED
|
@@ -88,7 +88,7 @@ If you receive a prompt with a specified ticket ID, adhere to the following. If
|
|
|
88
88
|
--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 @@"}]}]'
|
|
89
89
|
\`\`\`
|
|
90
90
|
|
|
91
|
-
For larger or quote-sensitive deliveries, prefer a single JSON payload on stdin:
|
|
91
|
+
Use \`--payload-json\` when the full delivery object fits comfortably inline. For larger or quote-sensitive deliveries, prefer a single JSON payload on stdin:
|
|
92
92
|
\`\`\`bash
|
|
93
93
|
ovld protocol deliver --session-key <sessionKey> --ticket-id $TICKET_ID --payload-file -
|
|
94
94
|
\`\`\`
|
|
@@ -261,7 +261,7 @@ When done, deliver with artifacts and change rationales:
|
|
|
261
261
|
\`\`\`bash
|
|
262
262
|
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 @@"}]}]'
|
|
263
263
|
\`\`\`
|
|
264
|
-
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.
|
|
264
|
+
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.
|
|
265
265
|
|
|
266
266
|
Rules:
|
|
267
267
|
- Always attach first and deliver last.
|
|
@@ -809,10 +809,57 @@ function cursorPaths() {
|
|
|
809
809
|
'plugin.json'
|
|
810
810
|
),
|
|
811
811
|
rulesFile: path.join(base, 'rules', 'overlord-local.mdc'),
|
|
812
|
-
settingsFile: path.join(base, 'settings.json')
|
|
812
|
+
settingsFile: path.join(base, 'settings.json'),
|
|
813
|
+
hooksFile: path.join(base, 'hooks.json')
|
|
813
814
|
};
|
|
814
815
|
}
|
|
815
816
|
|
|
817
|
+
const CURSOR_USER_PROMPT_HOOK_RELATIVE = 'plugins/local/overlord/hooks/overlord-user-prompt-submit.sh';
|
|
818
|
+
|
|
819
|
+
function isOverlordCursorBeforeSubmitHook(entry) {
|
|
820
|
+
if (!entry || typeof entry !== 'object') return false;
|
|
821
|
+
const cmd = typeof entry.command === 'string' ? entry.command : '';
|
|
822
|
+
return cmd.includes('overlord-user-prompt-submit');
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function mergeCursorBeforeSubmitHook(paths) {
|
|
826
|
+
const hooksFile = paths.hooksFile;
|
|
827
|
+
const base = fs.existsSync(hooksFile)
|
|
828
|
+
? readJsonFile(hooksFile)
|
|
829
|
+
: { version: 1, hooks: {} };
|
|
830
|
+
const hooks = base.hooks && typeof base.hooks === 'object' && !Array.isArray(base.hooks) ? base.hooks : {};
|
|
831
|
+
const existing = Array.isArray(hooks.beforeSubmitPrompt) ? hooks.beforeSubmitPrompt : [];
|
|
832
|
+
hooks.beforeSubmitPrompt = [
|
|
833
|
+
...existing.filter(entry => !isOverlordCursorBeforeSubmitHook(entry)),
|
|
834
|
+
{ command: CURSOR_USER_PROMPT_HOOK_RELATIVE }
|
|
835
|
+
];
|
|
836
|
+
writeJsonFile(hooksFile, {
|
|
837
|
+
...base,
|
|
838
|
+
version: typeof base.version === 'number' ? base.version : 1,
|
|
839
|
+
hooks
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function removeCursorBeforeSubmitHook(paths) {
|
|
844
|
+
const hooksFile = paths.hooksFile;
|
|
845
|
+
if (!fs.existsSync(hooksFile)) return;
|
|
846
|
+
const base = readJsonFile(hooksFile);
|
|
847
|
+
const hooks = base.hooks && typeof base.hooks === 'object' && !Array.isArray(base.hooks) ? base.hooks : {};
|
|
848
|
+
if (!Array.isArray(hooks.beforeSubmitPrompt)) return;
|
|
849
|
+
hooks.beforeSubmitPrompt = hooks.beforeSubmitPrompt.filter(
|
|
850
|
+
entry => !isOverlordCursorBeforeSubmitHook(entry)
|
|
851
|
+
);
|
|
852
|
+
if (hooks.beforeSubmitPrompt.length === 0) {
|
|
853
|
+
delete hooks.beforeSubmitPrompt;
|
|
854
|
+
}
|
|
855
|
+
if (Object.keys(hooks).length === 0) {
|
|
856
|
+
delete base.hooks;
|
|
857
|
+
} else {
|
|
858
|
+
base.hooks = hooks;
|
|
859
|
+
}
|
|
860
|
+
writeJsonFile(hooksFile, base);
|
|
861
|
+
}
|
|
862
|
+
|
|
816
863
|
function geminiPaths() {
|
|
817
864
|
const base = path.join(os.homedir(), '.gemini');
|
|
818
865
|
return {
|
|
@@ -945,6 +992,7 @@ function installCodex() {
|
|
|
945
992
|
|
|
946
993
|
function installCursor() {
|
|
947
994
|
const paths = cursorPaths();
|
|
995
|
+
const backups = [];
|
|
948
996
|
const sourceDir = cursorSourcePluginDir();
|
|
949
997
|
fs.mkdirSync(path.dirname(paths.pluginDir), { recursive: true });
|
|
950
998
|
fs.rmSync(paths.pluginDir, { recursive: true, force: true });
|
|
@@ -961,6 +1009,14 @@ function installCursor() {
|
|
|
961
1009
|
for (const filePath of removedLegacySlash.removedFiles) console.log(` ${filePath}`);
|
|
962
1010
|
}
|
|
963
1011
|
|
|
1012
|
+
const hooksBackup = backupFile(paths.hooksFile);
|
|
1013
|
+
if (hooksBackup) {
|
|
1014
|
+
backups.push(hooksBackup);
|
|
1015
|
+
console.log(` ✓ Backed up: ${paths.hooksFile} → ${path.basename(hooksBackup)}`);
|
|
1016
|
+
}
|
|
1017
|
+
mergeCursorBeforeSubmitHook(paths);
|
|
1018
|
+
console.log(` ✓ Registered Cursor beforeSubmitPrompt hook: ${paths.hooksFile}`);
|
|
1019
|
+
|
|
964
1020
|
const existingSettings = readJsonFile(paths.settingsFile);
|
|
965
1021
|
const permissions =
|
|
966
1022
|
existingSettings.permissions && typeof existingSettings.permissions === 'object'
|
|
@@ -983,11 +1039,11 @@ function installCursor() {
|
|
|
983
1039
|
version: pluginVersion(paths.pluginManifest) ?? '0.0.0',
|
|
984
1040
|
contentHash: currentContentHashForAgent('cursor'),
|
|
985
1041
|
installedAt: new Date().toISOString(),
|
|
986
|
-
files: [...listFilesRecursive(paths.pluginDir), paths.settingsFile]
|
|
1042
|
+
files: [...listFilesRecursive(paths.pluginDir), paths.settingsFile, paths.hooksFile]
|
|
987
1043
|
};
|
|
988
1044
|
writeManifest(manifest);
|
|
989
1045
|
|
|
990
|
-
return { ok: true };
|
|
1046
|
+
return { ok: true, backups };
|
|
991
1047
|
}
|
|
992
1048
|
|
|
993
1049
|
function installGemini() {
|
package/package.json
CHANGED
|
@@ -1,18 +1,53 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# Overlord UserPromptSubmit hook — fires before Claude processes each user turn.
|
|
3
|
+
# Claude Code does not expose turn_number in the hook body; we persist the last
|
|
4
|
+
# posted turnIndex per session_id and send (last + 1). The first submit is
|
|
5
|
+
# turnIndex 0 (initial injected ticket/objective prompt), which
|
|
6
|
+
# POST /api/protocol/hook-event skips — same contract as the Cursor hook.
|
|
3
7
|
|
|
4
8
|
BODY=$(cat -)
|
|
5
9
|
OVERLORD_BASE_URL="${CLAUDE_PLUGIN_OPTION_OVERLORD_URL:-$OVERLORD_URL}"
|
|
6
10
|
OVERLORD_TOKEN="${CLAUDE_PLUGIN_OPTION_AGENT_TOKEN:-${CLAUDE_PLUGIN_OPTION_OVERLORD_ACCESS_TOKEN:-$OVERLORD_ACCESS_TOKEN}}"
|
|
7
11
|
OVERLORD_LOCAL_SECRET="${OVERLORD_LOCAL_SECRET:-}"
|
|
8
12
|
|
|
9
|
-
TURN=$(printf '%s' "$BODY" | python3 -c "import json,sys; print(json.load(sys.stdin).get('turn_number', 0))" 2>/dev/null || echo "0")
|
|
10
|
-
if [ "$TURN" = "0" ]; then
|
|
11
|
-
exit 0
|
|
12
|
-
fi
|
|
13
|
-
|
|
14
13
|
if [ -n "$OVERLORD_BASE_URL" ] && [ -n "$OVERLORD_TOKEN" ] && [ -n "$TICKET_ID" ]; then
|
|
15
|
-
PAYLOAD=$(
|
|
14
|
+
PAYLOAD=$(
|
|
15
|
+
printf '%s' "$BODY" | python3 -c "
|
|
16
|
+
import hashlib
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
body = json.load(sys.stdin)
|
|
23
|
+
except Exception:
|
|
24
|
+
sys.exit(0)
|
|
25
|
+
|
|
26
|
+
text = (body.get('prompt') or '').strip()
|
|
27
|
+
if not text:
|
|
28
|
+
sys.exit(0)
|
|
29
|
+
|
|
30
|
+
sid = body.get('session_id') or 'unknown'
|
|
31
|
+
state_dir = os.path.join(os.path.expanduser('~'), '.ovld', 'claude-user-prompt-hook')
|
|
32
|
+
os.makedirs(state_dir, exist_ok=True)
|
|
33
|
+
path = os.path.join(state_dir, hashlib.sha256(sid.encode()).hexdigest())
|
|
34
|
+
last_posted = -1
|
|
35
|
+
try:
|
|
36
|
+
with open(path, encoding='utf-8') as handle:
|
|
37
|
+
raw = (handle.read() or '').strip()
|
|
38
|
+
if raw != '':
|
|
39
|
+
last_posted = int(raw)
|
|
40
|
+
except Exception:
|
|
41
|
+
last_posted = -1
|
|
42
|
+
|
|
43
|
+
turn_index = last_posted + 1
|
|
44
|
+
with open(path, 'w', encoding='utf-8') as handle:
|
|
45
|
+
handle.write(str(turn_index))
|
|
46
|
+
|
|
47
|
+
tid = os.environ.get('TICKET_ID', '')
|
|
48
|
+
print(json.dumps({'hookType': 'UserPromptSubmit', 'ticketId': tid, 'prompt': text, 'turnIndex': turn_index}))
|
|
49
|
+
" 2>/dev/null
|
|
50
|
+
)
|
|
16
51
|
if [ -n "$PAYLOAD" ]; then
|
|
17
52
|
curl -sf -m 5 \
|
|
18
53
|
-X POST "$OVERLORD_BASE_URL/api/protocol/hook-event" \
|
|
@@ -75,7 +75,7 @@ ovld protocol deliver --session-key <sessionKey> \
|
|
|
75
75
|
--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 @@"}]}]'
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
`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 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.
|
|
78
|
+
`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. 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.
|
|
79
79
|
|
|
80
80
|
## Mode 2: Asked From Chat To Use Overlord
|
|
81
81
|
|
|
@@ -99,7 +99,7 @@ Always include `changeRationales` when delivering. Optionally include them on up
|
|
|
99
99
|
|
|
100
100
|
Before delivering, make sure every meaningful git-tracked file change is represented in `changeRationales`; do not send `file_changes` as an artifact.
|
|
101
101
|
|
|
102
|
-
These are structured protocol payloads that Overlord stores as first-class rows in the `file_changes` table. Prefer inline JSON or the dedicated command below.
|
|
102
|
+
These are structured protocol payloads that Overlord stores as first-class rows in the `file_changes` table. Prefer inline JSON or the dedicated command below. Use `--payload-json` for compact full delivery payloads, or `--payload-file -` when the JSON is larger or quote-sensitive so summary, artifacts, and change rationales stay in one JSON document without creating a temporary file. Ordinary deliver artifacts should use `next_steps`, `test_results`, `migration`, `note`, `url`, or `decision`.
|
|
103
103
|
|
|
104
104
|
```bash
|
|
105
105
|
ovld protocol record-change-rationales --session-key <sessionKey> --ticket-id $TICKET_ID \
|
|
@@ -182,4 +182,4 @@ The `attach` and `load-context` responses already include `attachments` and `obj
|
|
|
182
182
|
- Do not add or commit changes unless the user explicitly asks you to commit.
|
|
183
183
|
- Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
|
|
184
184
|
|
|
185
|
-
<!-- version: 0.4.
|
|
185
|
+
<!-- version: 0.4.9 -->
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Overlord Cursor beforeSubmitPrompt hook — posts user composer prompts to the ticket activity feed.
|
|
3
|
+
# Cursor does not expose Claude's turn_number; we persist the last posted turnIndex per conversation_id
|
|
4
|
+
# and send (last + 1). The first submit is turnIndex 0 (initial injected ticket/objective prompt), which
|
|
5
|
+
# POST /api/protocol/hook-event skips — same contract as Claude's turn_number 0.
|
|
6
|
+
|
|
7
|
+
BODY=$(cat -)
|
|
8
|
+
|
|
9
|
+
OVERLORD_BASE_URL="${OVERLORD_URL:-${OVERLORD_CONNECTOR_URL:-}}"
|
|
10
|
+
OVERLORD_TOKEN="${OVERLORD_ACCESS_TOKEN:-}"
|
|
11
|
+
TICKET_ID="${TICKET_ID:-}"
|
|
12
|
+
OVERLORD_LOCAL_SECRET="${OVERLORD_LOCAL_SECRET:-}"
|
|
13
|
+
|
|
14
|
+
if [ -n "$OVERLORD_BASE_URL" ] && [ -n "$OVERLORD_TOKEN" ] && [ -n "$TICKET_ID" ]; then
|
|
15
|
+
PAYLOAD=$(
|
|
16
|
+
printf '%s' "$BODY" | python3 -c "
|
|
17
|
+
import hashlib
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
body = json.load(sys.stdin)
|
|
24
|
+
except Exception:
|
|
25
|
+
sys.exit(0)
|
|
26
|
+
|
|
27
|
+
text = (body.get('prompt') or body.get('message') or '').strip()
|
|
28
|
+
if not text:
|
|
29
|
+
sys.exit(0)
|
|
30
|
+
|
|
31
|
+
cid = body.get('conversation_id') or 'unknown'
|
|
32
|
+
state_dir = os.path.join(os.path.expanduser('~'), '.ovld', 'cursor-user-prompt-hook')
|
|
33
|
+
os.makedirs(state_dir, exist_ok=True)
|
|
34
|
+
path = os.path.join(state_dir, hashlib.sha256(cid.encode()).hexdigest())
|
|
35
|
+
last_posted = -1
|
|
36
|
+
try:
|
|
37
|
+
with open(path, encoding='utf-8') as handle:
|
|
38
|
+
raw = (handle.read() or '').strip()
|
|
39
|
+
if raw != '':
|
|
40
|
+
last_posted = int(raw)
|
|
41
|
+
except Exception:
|
|
42
|
+
last_posted = -1
|
|
43
|
+
|
|
44
|
+
turn_index = last_posted + 1
|
|
45
|
+
with open(path, 'w', encoding='utf-8') as handle:
|
|
46
|
+
handle.write(str(turn_index))
|
|
47
|
+
|
|
48
|
+
tid = os.environ.get('TICKET_ID', '')
|
|
49
|
+
print(json.dumps({'hookType': 'UserPromptSubmit', 'ticketId': tid, 'prompt': text, 'turnIndex': turn_index}))
|
|
50
|
+
" 2>/dev/null
|
|
51
|
+
)
|
|
52
|
+
if [ -n "$PAYLOAD" ]; then
|
|
53
|
+
curl -sf -m 5 \
|
|
54
|
+
-X POST "$OVERLORD_BASE_URL/api/protocol/hook-event" \
|
|
55
|
+
-H "Authorization: Bearer $OVERLORD_TOKEN" \
|
|
56
|
+
-H "X-Overlord-Local-Secret: $OVERLORD_LOCAL_SECRET" \
|
|
57
|
+
-H "Content-Type: application/json" \
|
|
58
|
+
-d "$PAYLOAD" \
|
|
59
|
+
>/dev/null 2>&1 &
|
|
60
|
+
disown
|
|
61
|
+
fi
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
printf '%s\n' '{"continue":true}'
|
|
65
|
+
exit 0
|
|
@@ -13,7 +13,7 @@ Use this skill whenever Cursor needs to work with Overlord, whether the session
|
|
|
13
13
|
2. Keep the returned `session.sessionKey` for all follow-up calls.
|
|
14
14
|
3. Treat the Overlord ticket prompt as authoritative for the objective, constraints, and delivery target.
|
|
15
15
|
4. Post updates while working with `ovld protocol update --phase execute`.
|
|
16
|
-
5.
|
|
16
|
+
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.
|
|
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
|
|
|
@@ -42,7 +42,7 @@ Supported `--phase` values:
|
|
|
42
42
|
|
|
43
43
|
Event types:
|
|
44
44
|
- `update` for standard progress updates
|
|
45
|
-
- `user_follow_up`
|
|
45
|
+
- `user_follow_up` — only when the Cursor `beforeSubmitPrompt` hook is unavailable; the hook normally posts follow-ups to the activity feed
|
|
46
46
|
- `alert` for warnings or non-blocking issues
|
|
47
47
|
|
|
48
48
|
Ask when blocked:
|
|
@@ -61,7 +61,7 @@ ovld protocol deliver --session-key <sessionKey> \
|
|
|
61
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
62
|
```
|
|
63
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.
|
|
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. 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.
|
|
65
65
|
|
|
66
66
|
## Mode 2: Asked From Chat To Use Overlord
|
|
67
67
|
|
|
@@ -152,4 +152,4 @@ Objective attachment uploads also expose two-step variants — `attachment-prepa
|
|
|
152
152
|
- 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.
|
|
153
153
|
- Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
|
|
154
154
|
|
|
155
|
-
<!-- version: 0.4.
|
|
155
|
+
<!-- version: 0.4.9 -->
|
|
@@ -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
|
-
`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.
|
|
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. 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.
|
|
69
69
|
|
|
70
70
|
## Mode 2: Asked From Chat To Use Overlord
|
|
71
71
|
|
|
@@ -111,7 +111,7 @@ Always include `changeRationales` when delivering. Optionally include them on up
|
|
|
111
111
|
|
|
112
112
|
Before delivering, make sure every meaningful git-tracked file change is represented in `changeRationales`; do not send `file_changes` as an artifact.
|
|
113
113
|
|
|
114
|
-
These are structured protocol payloads that Overlord stores as first-class rows in the `file_changes` table. Prefer inline JSON or the dedicated command below.
|
|
114
|
+
These are structured protocol payloads that Overlord stores as first-class rows in the `file_changes` table. Prefer inline JSON or the dedicated command below. Use `--payload-json` for compact full delivery payloads, or `--payload-file -` when the JSON is larger or quote-sensitive so summary, artifacts, and change rationales stay in one JSON document without creating a temporary file. Ordinary deliver artifacts should use `next_steps`, `test_results`, `migration`, `note`, `url`, or `decision`.
|
|
115
115
|
|
|
116
116
|
```bash
|
|
117
117
|
ovld protocol record-change-rationales --session-key <sessionKey> --ticket-id $TICKET_ID \
|
|
@@ -158,4 +158,4 @@ The `attach` and `load-context` responses already include `attachments` and `obj
|
|
|
158
158
|
- 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.
|
|
159
159
|
- 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.
|
|
160
160
|
|
|
161
|
-
<!-- version: 0.4.
|
|
161
|
+
<!-- version: 0.4.9 -->
|