overlord-cli 0.2605131058.0 → 0.2605150751.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 -0
- package/bin/_cli/protocol.mjs +207 -0
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/scripts/user-prompt-submit-hook.sh +37 -5
- package/plugins/claude/skills/overlord-ticket/SKILL.md +53 -3
- package/plugins/cursor/skills/overlord-ticket/SKILL.md +17 -3
- package/plugins/overlord/.codex-plugin/plugin.json +1 -1
- package/plugins/overlord/scripts/overlord-mcp.mjs +52 -0
- package/plugins/overlord/scripts/user-prompt-submit-hook.sh +36 -5
- package/plugins/overlord/skills/overlord-ticket/SKILL.md +30 -3
package/README.md
CHANGED
|
@@ -117,6 +117,7 @@ Agents can find docs here: https://www.ovld.ai/docs/for-agents
|
|
|
117
117
|
- `search-tickets` - find tickets by keyword, status, project, creator, or update date
|
|
118
118
|
- `create` - create a draft ticket without attaching (standalone or follow-up)
|
|
119
119
|
- `prompt` - create a ticket and attach to it immediately (`spawn` is a backward-compatible alias)
|
|
120
|
+
- `record-work` - record already-completed chat work as a ticket in review with a completed objective and trigger feed-post generation
|
|
120
121
|
- `update` - post progress, activity events, and optional change rationales
|
|
121
122
|
- `record-change-rationales` - persist structured change rationales without a normal progress update
|
|
122
123
|
- `ask` - post a blocking question and move the ticket to review
|
|
@@ -130,6 +131,8 @@ Agents can find docs here: https://www.ovld.ai/docs/for-agents
|
|
|
130
131
|
- `attachment-download-url` - get a signed download URL for an existing objective attachment
|
|
131
132
|
- `attachment-upload-file` - prepare, upload, and finalize a local objective attachment in one command
|
|
132
133
|
|
|
134
|
+
Use `create` for future work you want to track, `prompt` for work that should start immediately, and `record-work` for work that was already completed in chat and now needs a review ticket plus feed post.
|
|
135
|
+
|
|
133
136
|
`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
137
|
|
|
135
138
|
## License
|
package/bin/_cli/protocol.mjs
CHANGED
|
@@ -1570,6 +1570,34 @@ async function protocolPrompt(args) {
|
|
|
1570
1570
|
}
|
|
1571
1571
|
}
|
|
1572
1572
|
|
|
1573
|
+
// ---------------------------------------------------------------------------
|
|
1574
|
+
// discuss-objective (mark a draft objective as submitted)
|
|
1575
|
+
// ---------------------------------------------------------------------------
|
|
1576
|
+
|
|
1577
|
+
async function protocolDiscussObjective(args) {
|
|
1578
|
+
const flags = parseFlags(args);
|
|
1579
|
+
const ticketId = requireFlag(flags, 'ticket-id', process.env.TICKET_ID);
|
|
1580
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1581
|
+
await resolveProtocolAuthForFlags(flags, ticketId);
|
|
1582
|
+
const timeoutMs = resolveTimeout(flags);
|
|
1583
|
+
|
|
1584
|
+
const body = {
|
|
1585
|
+
ticketId,
|
|
1586
|
+
...(flags['objective-id'] ? { objectiveId: String(flags['objective-id']) } : {})
|
|
1587
|
+
};
|
|
1588
|
+
|
|
1589
|
+
const data = await apiPost(
|
|
1590
|
+
platformUrl,
|
|
1591
|
+
bearerToken,
|
|
1592
|
+
localSecret,
|
|
1593
|
+
organizationId,
|
|
1594
|
+
'/api/protocol/discuss-objective',
|
|
1595
|
+
body,
|
|
1596
|
+
timeoutMs
|
|
1597
|
+
);
|
|
1598
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1573
1601
|
// ---------------------------------------------------------------------------
|
|
1574
1602
|
// create (create follow-up ticket draft only)
|
|
1575
1603
|
// ---------------------------------------------------------------------------
|
|
@@ -1654,6 +1682,120 @@ async function protocolCreateTicket(args) {
|
|
|
1654
1682
|
console.log(JSON.stringify(data, null, 2));
|
|
1655
1683
|
}
|
|
1656
1684
|
|
|
1685
|
+
// ---------------------------------------------------------------------------
|
|
1686
|
+
// record-work (one-shot: create ticket in review + completed objective + feed post)
|
|
1687
|
+
// ---------------------------------------------------------------------------
|
|
1688
|
+
|
|
1689
|
+
async function protocolRecordWork(args) {
|
|
1690
|
+
const flags = parseFlags(args);
|
|
1691
|
+
|
|
1692
|
+
if (flags['payload-file'] && flags['payload-json']) {
|
|
1693
|
+
throw new Error('Use either --payload-file or --payload-json, not both');
|
|
1694
|
+
}
|
|
1695
|
+
const payload = flags['payload-file']
|
|
1696
|
+
? await readJsonFileOrStdin(String(flags['payload-file']), '--payload-file')
|
|
1697
|
+
: flags['payload-json']
|
|
1698
|
+
? parseJsonFlag('--payload-json', flags['payload-json'])
|
|
1699
|
+
: null;
|
|
1700
|
+
|
|
1701
|
+
if (payload && (flags.summary || flags['summary-file'])) {
|
|
1702
|
+
throw new Error('Use either payload input or --summary/--summary-file, not both');
|
|
1703
|
+
}
|
|
1704
|
+
if (payload && (flags.objective || flags['objective-file'])) {
|
|
1705
|
+
throw new Error('Use either payload input or --objective/--objective-file, not both');
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
const objective = payload
|
|
1709
|
+
? (() => {
|
|
1710
|
+
const value = payload.objective;
|
|
1711
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
1712
|
+
throw new Error('payload input must include a non-empty objective');
|
|
1713
|
+
}
|
|
1714
|
+
return value;
|
|
1715
|
+
})()
|
|
1716
|
+
: flags['objective-file']
|
|
1717
|
+
? await readTextFileOrStdin(String(flags['objective-file']), '--objective-file')
|
|
1718
|
+
: requireFlag(flags, 'objective', undefined);
|
|
1719
|
+
|
|
1720
|
+
const summary = payload
|
|
1721
|
+
? (() => {
|
|
1722
|
+
const value = payload.summary;
|
|
1723
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
1724
|
+
throw new Error('payload input must include a non-empty summary');
|
|
1725
|
+
}
|
|
1726
|
+
return value;
|
|
1727
|
+
})()
|
|
1728
|
+
: flags['summary-file']
|
|
1729
|
+
? await readTextFileOrStdin(String(flags['summary-file']), '--summary-file')
|
|
1730
|
+
: requireFlag(flags, 'summary', undefined);
|
|
1731
|
+
|
|
1732
|
+
let artifacts = payload?.artifacts ?? [];
|
|
1733
|
+
if (payload && (flags['artifacts-file'] || flags['artifacts-json'])) {
|
|
1734
|
+
throw new Error('Use either payload input or artifact flags, not both');
|
|
1735
|
+
}
|
|
1736
|
+
if (flags['artifacts-file']) {
|
|
1737
|
+
artifacts = await readJsonFileOrStdin(String(flags['artifacts-file']), '--artifacts-file');
|
|
1738
|
+
} else if (flags['artifacts-json']) {
|
|
1739
|
+
artifacts = parseJsonFlag('--artifacts-json', flags['artifacts-json']);
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
if (payload && (flags['change-rationales-file'] || flags['change-rationales-json'])) {
|
|
1743
|
+
throw new Error('Use either payload input or change-rationale flags, not both');
|
|
1744
|
+
}
|
|
1745
|
+
const changeRationales = payload?.changeRationales ?? (await resolveChangeRationales(flags));
|
|
1746
|
+
|
|
1747
|
+
// record-work has no live session, but the same git-changed-files guard
|
|
1748
|
+
// helps catch missing rationales. Apply the same opt-out semantics as deliver.
|
|
1749
|
+
validateDeliverFileChanges(flags, changeRationales);
|
|
1750
|
+
|
|
1751
|
+
const personal = Boolean(flags.personal);
|
|
1752
|
+
const workingDirectory =
|
|
1753
|
+
flags['working-directory'] ?? (!flags['project-id'] && !personal ? process.cwd() : undefined);
|
|
1754
|
+
|
|
1755
|
+
const agentIdentifier = resolveProtocolAgentIdentifier(flags);
|
|
1756
|
+
const modelIdentifier = resolveProtocolModelIdentifier(flags);
|
|
1757
|
+
|
|
1758
|
+
const body = {
|
|
1759
|
+
objective,
|
|
1760
|
+
summary,
|
|
1761
|
+
artifacts,
|
|
1762
|
+
changeRationales,
|
|
1763
|
+
agentIdentifier,
|
|
1764
|
+
connectionMethod: String(flags.method ?? 'cli'),
|
|
1765
|
+
metadata: await resolveProtocolMetadata(flags, { cwd: process.cwd() }),
|
|
1766
|
+
...(flags.title ? { title: String(flags.title) } : {}),
|
|
1767
|
+
...(flags.priority ? { priority: String(flags.priority) } : {}),
|
|
1768
|
+
...(flags['project-id'] ? { projectId: String(flags['project-id']) } : {}),
|
|
1769
|
+
...(personal ? { personal: true } : {}),
|
|
1770
|
+
...(workingDirectory ? { workingDirectory: String(workingDirectory) } : {}),
|
|
1771
|
+
...(flags['acceptance-criteria']
|
|
1772
|
+
? { acceptanceCriteria: String(flags['acceptance-criteria']) }
|
|
1773
|
+
: {}),
|
|
1774
|
+
...(flags['available-tools'] ? { availableTools: String(flags['available-tools']) } : {}),
|
|
1775
|
+
delegate: resolveProtocolTicketDelegate(flags, modelIdentifier, agentIdentifier)
|
|
1776
|
+
};
|
|
1777
|
+
|
|
1778
|
+
const { platformUrl, bearerToken, localSecret, organizationId } =
|
|
1779
|
+
await resolveProtocolAuthForFlags(flags);
|
|
1780
|
+
const timeoutMs = resolveTimeout(flags);
|
|
1781
|
+
|
|
1782
|
+
const data = await apiPost(
|
|
1783
|
+
platformUrl,
|
|
1784
|
+
bearerToken,
|
|
1785
|
+
localSecret,
|
|
1786
|
+
organizationId,
|
|
1787
|
+
'/api/protocol/record-work',
|
|
1788
|
+
body,
|
|
1789
|
+
timeoutMs
|
|
1790
|
+
);
|
|
1791
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1792
|
+
|
|
1793
|
+
const ticketId = data.ticket?.ticket_id ?? data.ticket?.id ?? data.ticket?.ticketId;
|
|
1794
|
+
if (ticketId) {
|
|
1795
|
+
process.stderr.write(`\nTICKET_ID=${ticketId}\n`);
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1657
1799
|
// ---------------------------------------------------------------------------
|
|
1658
1800
|
// search-tickets (find tickets by query/status/project/created_by/dates)
|
|
1659
1801
|
// ---------------------------------------------------------------------------
|
|
@@ -1761,8 +1903,10 @@ Subcommands:
|
|
|
1761
1903
|
connect Start a lightweight session without full context
|
|
1762
1904
|
load-context Read ticket context without creating a session
|
|
1763
1905
|
search-tickets Find tickets by keyword, status, project, creator, or update date
|
|
1906
|
+
discuss-objective Mark a draft objective as submitted (ready for review/execution)
|
|
1764
1907
|
create Create a draft ticket without attaching (follow-up or standalone)
|
|
1765
1908
|
prompt Create a ticket and attach to it immediately
|
|
1909
|
+
record-work Record completed-from-chat work as a ticket in review + feed post (no attach)
|
|
1766
1910
|
update Post progress, activity events, and optional change rationales
|
|
1767
1911
|
record-change-rationales Persist structured change rationales without a progress update
|
|
1768
1912
|
ask Post a blocking question and move the ticket to review
|
|
@@ -1998,6 +2142,16 @@ prompt:
|
|
|
1998
2142
|
Returns:
|
|
1999
2143
|
New ticket/session JSON plus SESSION_KEY and TICKET_ID on stderr when available
|
|
2000
2144
|
|
|
2145
|
+
discuss-objective:
|
|
2146
|
+
Purpose:
|
|
2147
|
+
Mark the latest draft objective on a ticket as "submitted", indicating the objective
|
|
2148
|
+
has been discussed and is ready for review or execution. Does NOT start execution —
|
|
2149
|
+
use \`attach\` to begin execution.
|
|
2150
|
+
Required:
|
|
2151
|
+
--ticket-id <ticket_id>
|
|
2152
|
+
Optional:
|
|
2153
|
+
--objective-id <id> Target a specific draft objective by UUID
|
|
2154
|
+
|
|
2001
2155
|
create:
|
|
2002
2156
|
Purpose:
|
|
2003
2157
|
Create a draft ticket without attaching to it.
|
|
@@ -2024,6 +2178,45 @@ create:
|
|
|
2024
2178
|
Notes:
|
|
2025
2179
|
Standalone create auto-discovers the project from the current working directory unless --personal is set.
|
|
2026
2180
|
Follow-up create requires both --session-key and --ticket-id.
|
|
2181
|
+
Use create for future work. If the work is already complete and only needs to be recorded,
|
|
2182
|
+
use \`record-work\` instead.
|
|
2183
|
+
|
|
2184
|
+
record-work:
|
|
2185
|
+
Purpose:
|
|
2186
|
+
Record work the agent already completed in a chat as a ticket in \`review\` status
|
|
2187
|
+
with a completed objective, then trigger the feed-post generator. No session is
|
|
2188
|
+
left open — use this INSTEAD OF \`create\` + \`attach\` + \`deliver\` for "log what we
|
|
2189
|
+
just did" flows. Do NOT use this for in-progress work; use \`prompt\` for that.
|
|
2190
|
+
Required:
|
|
2191
|
+
--objective <text> or --objective-file <path|-> What was asked / what was done
|
|
2192
|
+
--summary <text> or --summary-file <path|-> Narrative for feed post + reviewer
|
|
2193
|
+
or: --payload-json <json>
|
|
2194
|
+
or: --payload-file <path|-> containing { objective, summary, artifacts, changeRationales }
|
|
2195
|
+
Optional:
|
|
2196
|
+
--title <text> Auto-derived from objective if omitted
|
|
2197
|
+
--priority <level> low | medium | high | urgent
|
|
2198
|
+
--project-id <id> Skip cwd resolution and use this project explicitly
|
|
2199
|
+
--working-directory <path> Override cwd for project resolution
|
|
2200
|
+
--personal Create a private ticket when no project should be associated
|
|
2201
|
+
--artifacts-json <json>
|
|
2202
|
+
--artifacts-file <path|->
|
|
2203
|
+
--change-rationales-json <json>
|
|
2204
|
+
--change-rationales-file <path|->
|
|
2205
|
+
--skip-file-change-check Bypass local git vs changeRationales validation
|
|
2206
|
+
--acceptance-criteria <text>
|
|
2207
|
+
--available-tools <text>
|
|
2208
|
+
--delegate <model>
|
|
2209
|
+
--agent <identifier>
|
|
2210
|
+
--model <identifier>
|
|
2211
|
+
Notes:
|
|
2212
|
+
Project resolution mirrors \`prompt\`: if --project-id is not set and --personal is not used,
|
|
2213
|
+
the CLI sends cwd as workingDirectory and the API matches it against your configured
|
|
2214
|
+
project_user.local_working_directory rows. If no match, the API returns 400 — re-run with
|
|
2215
|
+
--project-id <id> or --personal.
|
|
2216
|
+
Use this for completed-from-chat work. If you still need to execute the work, use \`create\`
|
|
2217
|
+
for a draft ticket or \`prompt\` to create and start execution immediately.
|
|
2218
|
+
In a git workspace, record-work validates that changed files are represented by
|
|
2219
|
+
changeRationales unless --skip-file-change-check is set.
|
|
2027
2220
|
|
|
2028
2221
|
attachment-list:
|
|
2029
2222
|
Required:
|
|
@@ -2089,6 +2282,8 @@ Examples:
|
|
|
2089
2282
|
ovld protocol connect --ticket-id 1:899
|
|
2090
2283
|
ovld protocol load-context --ticket-id 1:899
|
|
2091
2284
|
ovld protocol search-tickets --query "auth refactor" --status next-up,execute --limit 10
|
|
2285
|
+
ovld protocol discuss-objective --ticket-id 1:899
|
|
2286
|
+
ovld protocol discuss-objective --ticket-id 1:899 --objective-id <objective-uuid>
|
|
2092
2287
|
ovld protocol create --agent codex --objective "Capture follow-up work from this repo"
|
|
2093
2288
|
ovld protocol create --agent codex --session-key <key> --ticket-id <ticket_id> --objective "Capture follow-up work"
|
|
2094
2289
|
ovld protocol prompt --agent codex --objective "Implement user auth" --priority high
|
|
@@ -2110,6 +2305,10 @@ Examples:
|
|
|
2110
2305
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --payload-file -
|
|
2111
2306
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --skip-file-change-check
|
|
2112
2307
|
ovld protocol deliver --session-key <key> --ticket-id <ticket_id> --summary "Done" --timeout 60000
|
|
2308
|
+
ovld protocol record-work --objective "User asked for X; I did Y" --summary "What I did and why" --change-rationales-json '[...]'
|
|
2309
|
+
ovld protocol record-work --payload-file - <<'EOF'
|
|
2310
|
+
{"objective":"...","summary":"...","artifacts":[...],"changeRationales":[...]}
|
|
2311
|
+
EOF
|
|
2113
2312
|
`);
|
|
2114
2313
|
return;
|
|
2115
2314
|
}
|
|
@@ -2138,6 +2337,10 @@ Examples:
|
|
|
2138
2337
|
await protocolSearchTickets(args);
|
|
2139
2338
|
return;
|
|
2140
2339
|
}
|
|
2340
|
+
if (subcommand === 'discuss-objective') {
|
|
2341
|
+
await protocolDiscussObjective(args);
|
|
2342
|
+
return;
|
|
2343
|
+
}
|
|
2141
2344
|
if (subcommand === 'create' || subcommand === 'create-ticket') {
|
|
2142
2345
|
await protocolCreateTicket(args);
|
|
2143
2346
|
return;
|
|
@@ -2146,6 +2349,10 @@ Examples:
|
|
|
2146
2349
|
await protocolPrompt(args);
|
|
2147
2350
|
return;
|
|
2148
2351
|
}
|
|
2352
|
+
if (subcommand === 'record-work') {
|
|
2353
|
+
await protocolRecordWork(args);
|
|
2354
|
+
return;
|
|
2355
|
+
}
|
|
2149
2356
|
if (subcommand === 'attachment-list') {
|
|
2150
2357
|
await protocolAttachmentList(args);
|
|
2151
2358
|
return;
|
package/package.json
CHANGED
|
@@ -9,6 +9,23 @@ BODY=$(cat -)
|
|
|
9
9
|
OVERLORD_BASE_URL="${CLAUDE_PLUGIN_OPTION_OVERLORD_URL:-$OVERLORD_URL}"
|
|
10
10
|
OVERLORD_TOKEN="${CLAUDE_PLUGIN_OPTION_AGENT_TOKEN:-${CLAUDE_PLUGIN_OPTION_OVERLORD_ACCESS_TOKEN:-$OVERLORD_ACCESS_TOKEN}}"
|
|
11
11
|
OVERLORD_LOCAL_SECRET="${OVERLORD_LOCAL_SECRET:-}"
|
|
12
|
+
HOOK_NAME="claude"
|
|
13
|
+
LOG_DIR="${HOME:-}/.ovld/logs"
|
|
14
|
+
LOG_FILE="${LOG_DIR}/user-prompt-submit-hook.log"
|
|
15
|
+
|
|
16
|
+
log_hook() {
|
|
17
|
+
mkdir -p "$LOG_DIR" 2>/dev/null || return 0
|
|
18
|
+
printf '%s [%s] %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$HOOK_NAME" "$1" >>"$LOG_FILE" 2>/dev/null || true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
PROMPT_LEN=$(printf '%s' "$BODY" | python3 -c "import json,sys; print(len((json.load(sys.stdin).get('prompt') or '').strip()))" 2>/dev/null || echo "0")
|
|
22
|
+
SESSION_ID=$(printf '%s' "$BODY" | python3 -c "import json,sys; print(json.load(sys.stdin).get('session_id') or 'unknown')" 2>/dev/null || echo "unknown")
|
|
23
|
+
log_hook "received submit session_id=$SESSION_ID prompt_len=$PROMPT_LEN ticket_present=$([ -n "${TICKET_ID:-}" ] && echo yes || echo no)"
|
|
24
|
+
|
|
25
|
+
if [ -z "$OVERLORD_BASE_URL" ] || [ -z "$OVERLORD_TOKEN" ] || [ -z "${TICKET_ID:-}" ]; then
|
|
26
|
+
log_hook "missing required env base_url=$([ -n "$OVERLORD_BASE_URL" ] && echo yes || echo no) token=$([ -n "$OVERLORD_TOKEN" ] && echo yes || echo no) ticket=$([ -n "${TICKET_ID:-}" ] && echo yes || echo no)"
|
|
27
|
+
exit 0
|
|
28
|
+
fi
|
|
12
29
|
|
|
13
30
|
if [ -n "$OVERLORD_BASE_URL" ] && [ -n "$OVERLORD_TOKEN" ] && [ -n "$TICKET_ID" ]; then
|
|
14
31
|
PAYLOAD=$(
|
|
@@ -48,16 +65,31 @@ tid = os.environ.get('TICKET_ID', '')
|
|
|
48
65
|
print(json.dumps({'hookType': 'UserPromptSubmit', 'ticketId': tid, 'prompt': text, 'turnIndex': turn_index}))
|
|
49
66
|
" 2>/dev/null
|
|
50
67
|
)
|
|
51
|
-
if [ -
|
|
52
|
-
|
|
68
|
+
if [ -z "$PAYLOAD" ]; then
|
|
69
|
+
log_hook "failed to build hook-event payload from submit body"
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
PAYLOAD_TURN=$(printf '%s' "$PAYLOAD" | python3 -c "import json,sys; print(json.load(sys.stdin).get('turnIndex', 'unknown'))" 2>/dev/null || echo "unknown")
|
|
74
|
+
log_hook "built payload turn=$PAYLOAD_TURN posting hook-event"
|
|
75
|
+
(
|
|
76
|
+
RESPONSE_FILE=$(mktemp "${TMPDIR:-/tmp}/overlord-hook-event.XXXXXX")
|
|
77
|
+
HTTP_CODE=$(curl -sS -m 5 -o "$RESPONSE_FILE" -w "%{http_code}" \
|
|
53
78
|
-X POST "$OVERLORD_BASE_URL/api/protocol/hook-event" \
|
|
54
79
|
-H "Authorization: Bearer $OVERLORD_TOKEN" \
|
|
55
80
|
-H "X-Overlord-Local-Secret: $OVERLORD_LOCAL_SECRET" \
|
|
56
81
|
-H "Content-Type: application/json" \
|
|
57
|
-
-d "$PAYLOAD"
|
|
58
|
-
|
|
82
|
+
-d "$PAYLOAD")
|
|
83
|
+
CURL_EXIT=$?
|
|
84
|
+
RESPONSE_PREVIEW=$(python3 -c "from pathlib import Path; import sys; p=Path(sys.argv[1]); text=p.read_text(encoding='utf-8', errors='replace') if p.exists() else ''; text=' '.join(text.split()); print(text[:200])" "$RESPONSE_FILE" 2>/dev/null || echo '')
|
|
85
|
+
rm -f "$RESPONSE_FILE"
|
|
86
|
+
if [ "$CURL_EXIT" -eq 0 ]; then
|
|
87
|
+
log_hook "hook-event POST finished exit=0 http_status=$HTTP_CODE response_preview=$RESPONSE_PREVIEW"
|
|
88
|
+
else
|
|
89
|
+
log_hook "hook-event POST failed exit=$CURL_EXIT http_status=${HTTP_CODE:-none} response_preview=$RESPONSE_PREVIEW"
|
|
90
|
+
fi
|
|
91
|
+
) >/dev/null 2>&1 &
|
|
59
92
|
disown
|
|
60
|
-
fi
|
|
61
93
|
fi
|
|
62
94
|
|
|
63
95
|
exit 0
|
|
@@ -84,7 +84,7 @@ Use this mode when the conversation starts normally and the user asks Claude to
|
|
|
84
84
|
1. If the user wants to create tickets (and does not ask to start execution), run `ovld protocol create --agent claude-code --objective "..."`.
|
|
85
85
|
- When `--session-key` and `--ticket-id` are provided, it creates a follow-up draft.
|
|
86
86
|
- When session flags are omitted, it resolves the project by matching current working directory (or `--working-directory`) to Overlord `local_working_directory`, then creates a standalone draft.
|
|
87
|
-
2. Default to `create` for new tickets. Only use `/overlord:prompt` or `ovld protocol prompt --agent claude-code --objective "..."` when the user explicitly asks to create and execute immediately.
|
|
87
|
+
2. Default to `create` for new draft tickets. Only use `/overlord:prompt` or `ovld protocol prompt --agent claude-code --objective "..."` when the user explicitly asks to create and execute immediately. If the work is already complete in chat and just needs to be recorded, use `ovld protocol record-work` instead.
|
|
88
88
|
`prompt` creates the ticket in `execute` status and attaches immediately.
|
|
89
89
|
3. If the user already has a ticket ID and only wants to inspect it, use `/overlord:load` or run `ovld protocol load-context --ticket-id <ticket_id>`.
|
|
90
90
|
4. If the user wants to route the current session onto an existing ticket by ID, use `/overlord:connect` or run `ovld protocol connect --ticket-id <ticket_id>`.
|
|
@@ -93,6 +93,46 @@ Use this mode when the conversation starts normally and the user asks Claude to
|
|
|
93
93
|
7. If you need other lifecycle commands or flags, run `ovld protocol help` and use the real subcommand list instead of guessing.
|
|
94
94
|
8. Once you attach to a ticket, switch back to Mode 1 and follow the full ticket lifecycle.
|
|
95
95
|
|
|
96
|
+
## Recording Completed Work From Chat
|
|
97
|
+
|
|
98
|
+
Use this when the user has had a working conversation with the agent (no Overlord ticket attached) and now wants to record the outcome as a ticket — both for review and to publish a feed post about it. This is **distinct from `create` and from `deliver`**:
|
|
99
|
+
|
|
100
|
+
- `create` makes a draft ticket for *future* work; nothing happens yet.
|
|
101
|
+
- `prompt` creates a ticket and starts an execution session.
|
|
102
|
+
- `deliver` concludes an *attached* session; the ticket already exists.
|
|
103
|
+
- `record-work` creates the ticket *and* completes the objective *and* triggers the feed-post generator in a single call, with no session left open.
|
|
104
|
+
|
|
105
|
+
Do NOT use `record-work` for in-progress work. Use it only when the work is already done in the chat.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
ovld protocol record-work \
|
|
109
|
+
--objective "User asked me to X; I did Y by..." \
|
|
110
|
+
--summary "Narrative for the feed post and reviewer." \
|
|
111
|
+
--change-rationales-file -
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Or stream the full payload via stdin to avoid quote escaping:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
ovld protocol record-work --payload-file - <<'EOF'
|
|
118
|
+
{
|
|
119
|
+
"objective": "...",
|
|
120
|
+
"summary": "...",
|
|
121
|
+
"artifacts": [{"type": "next_steps", "label": "...", "content": "..."}],
|
|
122
|
+
"changeRationales": [{"label": "...", "file_path": "...", "summary": "...", "why": "...", "impact": "...", "hunks": [{"header": "@@ ..."}]}]
|
|
123
|
+
}
|
|
124
|
+
EOF
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Project resolution mirrors `prompt`/`create`: cwd is matched against the caller's `project_user.local_working_directory`. If no match:
|
|
128
|
+
|
|
129
|
+
1. **Ask the user for a `--project-id`** if the work is project-related. Show them a brief description of what you're about to record so they can pick the right project.
|
|
130
|
+
2. If the user confirms the work is not tied to any project, pass `--personal` to create a private ticket.
|
|
131
|
+
|
|
132
|
+
Validation: in a git workspace, `record-work` checks that uncommitted changes are represented by `changeRationales` and rejects if they aren't (same check as `deliver`). Pass `--skip-file-change-check` only when intentionally bypassing.
|
|
133
|
+
|
|
134
|
+
The hosted MCP tool name is `record_work` (snake_case). It accepts the same fields as the CLI but in camelCase JSON. Use `record_work` over the `create_ticket` + `attach` + `deliver` sequence whenever the work is already done.
|
|
135
|
+
|
|
96
136
|
## Change Rationales
|
|
97
137
|
|
|
98
138
|
Always include `changeRationales` when delivering. Optionally include them on updates during long-running work.
|
|
@@ -160,10 +200,20 @@ ovld protocol attachment-download-url --session-key <sessionKey> --ticket-id $TI
|
|
|
160
200
|
|
|
161
201
|
The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach.
|
|
162
202
|
|
|
203
|
+
## Large Artifacts
|
|
204
|
+
|
|
205
|
+
For large artifacts such as planning documents, architecture decisions, research summaries, or design documents: **save the full content as a markdown file in the linked repository, then summarize it in the artifact returned to the ticket.**
|
|
206
|
+
|
|
207
|
+
- Save to a meaningful path in the repository (e.g., `ai/feature-plans/my-feature.md` for feature plans, `docs/decisions/my-decision.md` for architecture decision records).
|
|
208
|
+
- Commit the file as part of the ticket's work so it appears in `changeRationales`.
|
|
209
|
+
- In the delivery, include a `note` or `decision` artifact with a concise summary and the repository file path — not the full document content.
|
|
210
|
+
|
|
211
|
+
This keeps the ticket feed readable while preserving the full document in version control where it can be reviewed, diffed, and referenced later.
|
|
212
|
+
|
|
163
213
|
## Defaults And Notes
|
|
164
214
|
|
|
165
215
|
- API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt. The CLI defaults them to `claude-code`/`cli`; the MCP tool defaults to `mcp`. Override with `--agent` / `--method` when calling from a different runtime.
|
|
166
|
-
- Hosted Overlord MCP (`/functions/v1/mcp`) uses the same canonical tool names as any local MCP shim that shells into `ovld protocol` (`attach`, `update`, `deliver`, …). Hosted calls use camelCase JSON keys (`ticketId`, `sessionKey`) matching `POST /api/protocol/*` bodies; the local shim uses snake_case keys mapped to CLI flags (`ticket_id`, `session_key`).
|
|
216
|
+
- Hosted Overlord MCP (`/functions/v1/mcp`) uses the same canonical tool names as any local MCP shim that shells into `ovld protocol` (`attach`, `update`, `deliver`, `record_work`, …). Hosted calls use camelCase JSON keys (`ticketId`, `sessionKey`) matching `POST /api/protocol/*` bodies; the local shim uses snake_case keys mapped to CLI flags (`ticket_id`, `session_key`).
|
|
167
217
|
- `permission-request` is invoked by the Claude Code permission hook installed by the bundle. Agents do not normally call it directly.
|
|
168
218
|
- `record_change_rationales` (MCP) and `ovld protocol record-change-rationales` (CLI) both write to the same `file_changes` table. The dedicated CLI route is `POST /api/protocol/record-change-rationales`.
|
|
169
219
|
- Objective attachment tools follow the `<verb>_<noun>` MCP naming: `list_attachments`, `prepare_attachment_upload`, `finalize_attachment_upload`, `get_attachment_download_url`, `upload_attachment_file`. CLI commands use `attachment-*` and require `--objective-id` for upload/finalize.
|
|
@@ -182,4 +232,4 @@ The `attach` and `load-context` responses already include `attachments` and `obj
|
|
|
182
232
|
- Do not add or commit changes unless the user explicitly asks you to commit.
|
|
183
233
|
- Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
|
|
184
234
|
|
|
185
|
-
<!-- version: 0.4.
|
|
235
|
+
<!-- version: 0.4.10 -->
|
|
@@ -68,7 +68,7 @@ ovld protocol deliver --session-key <sessionKey> \
|
|
|
68
68
|
1. If the user wants to create tickets (and does not ask to start execution), use `/create` or run `ovld protocol create --agent cursor --objective "..."`.
|
|
69
69
|
- When `--session-key` and `--ticket-id` are provided, it creates a follow-up draft.
|
|
70
70
|
- When session flags are omitted, it resolves the project by matching current working directory (or `--working-directory`) to Overlord `local_working_directory`, then creates a standalone draft.
|
|
71
|
-
2. Default to `create` for new tickets. Only use `/prompt` or `ovld protocol prompt --agent cursor --objective "..."` when the user explicitly asks to create and execute immediately.
|
|
71
|
+
2. Default to `create` for new draft tickets. Only use `/prompt` or `ovld protocol prompt --agent cursor --objective "..."` when the user explicitly asks to create and execute immediately. If the work is already complete in chat and just needs to be recorded, use `ovld protocol record-work` instead.
|
|
72
72
|
3. If the user already has a ticket ID and only wants to inspect it, use `/load` or run `ovld protocol load-context --ticket-id <ticket_id>`.
|
|
73
73
|
4. If the user wants to route the current session onto an existing ticket by ID, use `/connect` or run `ovld protocol connect --ticket-id <ticket_id>`.
|
|
74
74
|
5. If the user wants to establish a persistent session with a ticket by ID, use `/attach` or run `ovld protocol attach --ticket-id <ticket_id>`.
|
|
@@ -76,6 +76,10 @@ ovld protocol deliver --session-key <sessionKey> \
|
|
|
76
76
|
7. If you need other lifecycle commands or flags, run `ovld protocol help` and use the real subcommand list instead of guessing.
|
|
77
77
|
8. Once you attach to a ticket, switch back to Mode 1 and follow the full ticket lifecycle.
|
|
78
78
|
|
|
79
|
+
## Recording Completed Work From Chat
|
|
80
|
+
|
|
81
|
+
Use `ovld protocol record-work` when the user has done work directly in chat (no attached ticket) and wants to record it as a ticket plus publish a feed post. This is distinct from `create` (drafts future work), `prompt` (creates + executes), and `deliver` (concludes an attached session). `record-work` creates a ticket in `review` with a completed objective and triggers the feed-post generator atomically. Project resolution mirrors `prompt`: cwd is matched against `project_user.local_working_directory`; if no match, ask the user for `--project-id` or pass `--personal`. Do NOT use for in-progress work.
|
|
82
|
+
|
|
79
83
|
## Change Rationales
|
|
80
84
|
|
|
81
85
|
Always include `changeRationales` when delivering. Optionally include them on updates during long-running work.
|
|
@@ -134,10 +138,20 @@ Objective attachment uploads also expose two-step variants — `attachment-prepa
|
|
|
134
138
|
|
|
135
139
|
"Artifacts" in `deliver` are the structured records an agent submits at delivery time (next_steps, test_results, migration, decision, note, url) — not user-uploaded files.
|
|
136
140
|
|
|
141
|
+
## Large Artifacts
|
|
142
|
+
|
|
143
|
+
For large artifacts such as planning documents, architecture decisions, research summaries, or design documents: **save the full content as a markdown file in the linked repository, then summarize it in the artifact returned to the ticket.**
|
|
144
|
+
|
|
145
|
+
- Save to a meaningful path in the repository (e.g., `ai/feature-plans/my-feature.md` for feature plans, `docs/decisions/my-decision.md` for architecture decision records).
|
|
146
|
+
- Commit the file as part of the ticket's work so it appears in `changeRationales`.
|
|
147
|
+
- In the delivery, include a `note` or `decision` artifact with a concise summary and the repository file path — not the full document content.
|
|
148
|
+
|
|
149
|
+
This keeps the ticket feed readable while preserving the full document in version control where it can be reviewed, diffed, and referenced later.
|
|
150
|
+
|
|
137
151
|
## Defaults And Notes
|
|
138
152
|
|
|
139
153
|
- The Overlord API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt, but the CLI defaults them to `cursor`/`cli` (override with `--agent` / `--method`). The MCP tools default to `mcp`.
|
|
140
|
-
- The Cursor
|
|
154
|
+
- The Cursor plugin ships the shared local `overlord-mcp.mjs` shim. Treat its tool list as the source of truth; it includes `record_work` alongside the core lifecycle tools.
|
|
141
155
|
- `permission-request` is invoked by the installed permission hook; agents normally do not call it directly.
|
|
142
156
|
- The `record_change_rationales` MCP tool and `ovld protocol record-change-rationales` CLI both write to the same `file_changes` table; pick whichever fits your runtime.
|
|
143
157
|
|
|
@@ -152,4 +166,4 @@ Objective attachment uploads also expose two-step variants — `attachment-prepa
|
|
|
152
166
|
- 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
167
|
- Delivery is the concluding step. After delivering, stop unless the user follows up or the ticket is reopened.
|
|
154
168
|
|
|
155
|
-
<!-- version: 0.4.
|
|
169
|
+
<!-- version: 0.4.10 -->
|
|
@@ -393,6 +393,58 @@ const tools = [
|
|
|
393
393
|
}),
|
|
394
394
|
subcommand: 'deliver'
|
|
395
395
|
},
|
|
396
|
+
{
|
|
397
|
+
name: 'record_work',
|
|
398
|
+
description:
|
|
399
|
+
'Record completed-from-chat work as a ticket in `review` status with a completed objective, then trigger feed-post generation. Use this INSTEAD OF create_ticket + attach + deliver for "log what we just did" flows. Project resolution mirrors `prompt`: if project_id is omitted, working_directory is matched against project_user.local_working_directory. When neither resolves, pass personal: true. Do not use for in-progress work.',
|
|
400
|
+
inputSchema: {
|
|
401
|
+
type: 'object',
|
|
402
|
+
properties: {
|
|
403
|
+
objective: {
|
|
404
|
+
type: 'string',
|
|
405
|
+
description: 'What was asked / what was done. Stored as a completed objective.'
|
|
406
|
+
},
|
|
407
|
+
summary: {
|
|
408
|
+
type: 'string',
|
|
409
|
+
description: 'Narrative for feed post + reviewer.'
|
|
410
|
+
},
|
|
411
|
+
title: { type: 'string' },
|
|
412
|
+
priority: { type: 'string', enum: ['low', 'medium', 'high', 'urgent'] },
|
|
413
|
+
project_id: { type: 'string' },
|
|
414
|
+
working_directory: { type: 'string' },
|
|
415
|
+
personal: { type: 'boolean' },
|
|
416
|
+
acceptance_criteria: { type: 'string' },
|
|
417
|
+
available_tools: { type: 'string' },
|
|
418
|
+
delegate: { type: 'string' },
|
|
419
|
+
artifacts: { type: 'array' },
|
|
420
|
+
change_rationales: { type: 'array' },
|
|
421
|
+
skip_file_change_check: { type: 'boolean' }
|
|
422
|
+
},
|
|
423
|
+
required: ['objective', 'summary']
|
|
424
|
+
},
|
|
425
|
+
toCliFlags: args => ({
|
|
426
|
+
'payload-file': '-',
|
|
427
|
+
title: args.title,
|
|
428
|
+
priority: args.priority,
|
|
429
|
+
'project-id': args.project_id,
|
|
430
|
+
'working-directory': args.working_directory,
|
|
431
|
+
personal: args.personal,
|
|
432
|
+
'acceptance-criteria': args.acceptance_criteria,
|
|
433
|
+
'available-tools': args.available_tools,
|
|
434
|
+
delegate: args.delegate,
|
|
435
|
+
'skip-file-change-check': args.skip_file_change_check
|
|
436
|
+
}),
|
|
437
|
+
toCliStdin: args =>
|
|
438
|
+
JSON.stringify({
|
|
439
|
+
objective: args.objective,
|
|
440
|
+
summary: args.summary,
|
|
441
|
+
...(Array.isArray(args.artifacts) ? { artifacts: args.artifacts } : {}),
|
|
442
|
+
...(Array.isArray(args.change_rationales)
|
|
443
|
+
? { changeRationales: args.change_rationales }
|
|
444
|
+
: {})
|
|
445
|
+
}),
|
|
446
|
+
subcommand: 'record-work'
|
|
447
|
+
},
|
|
396
448
|
{
|
|
397
449
|
name: 'list_attachments',
|
|
398
450
|
description:
|
|
@@ -5,24 +5,55 @@ BODY=$(cat -)
|
|
|
5
5
|
OVERLORD_BASE_URL="${OVERLORD_URL:-}"
|
|
6
6
|
OVERLORD_TOKEN="${OVERLORD_ACCESS_TOKEN:-}"
|
|
7
7
|
OVERLORD_LOCAL_SECRET="${OVERLORD_LOCAL_SECRET:-}"
|
|
8
|
+
HOOK_NAME="codex"
|
|
9
|
+
LOG_DIR="${HOME:-}/.ovld/logs"
|
|
10
|
+
LOG_FILE="${LOG_DIR}/user-prompt-submit-hook.log"
|
|
11
|
+
|
|
12
|
+
log_hook() {
|
|
13
|
+
mkdir -p "$LOG_DIR" 2>/dev/null || return 0
|
|
14
|
+
printf '%s [%s] %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$HOOK_NAME" "$1" >>"$LOG_FILE" 2>/dev/null || true
|
|
15
|
+
}
|
|
8
16
|
|
|
9
17
|
TURN=$(printf '%s' "$BODY" | python3 -c "import json,sys; print(json.load(sys.stdin).get('turn_number', 0))" 2>/dev/null || echo "0")
|
|
18
|
+
PROMPT_LEN=$(printf '%s' "$BODY" | python3 -c "import json,sys; print(len((json.load(sys.stdin).get('prompt') or '').strip()))" 2>/dev/null || echo "0")
|
|
19
|
+
log_hook "received submit turn=$TURN prompt_len=$PROMPT_LEN ticket_present=$([ -n "${TICKET_ID:-}" ] && echo yes || echo no)"
|
|
10
20
|
if [ "$TURN" = "0" ]; then
|
|
21
|
+
log_hook "skipping initial submit turn=0"
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
if [ -z "$OVERLORD_BASE_URL" ] || [ -z "$OVERLORD_TOKEN" ] || [ -z "${TICKET_ID:-}" ]; then
|
|
26
|
+
log_hook "missing required env base_url=$([ -n "$OVERLORD_BASE_URL" ] && echo yes || echo no) token=$([ -n "$OVERLORD_TOKEN" ] && echo yes || echo no) ticket=$([ -n "${TICKET_ID:-}" ] && echo yes || echo no)"
|
|
11
27
|
exit 0
|
|
12
28
|
fi
|
|
13
29
|
|
|
14
30
|
if [ -n "$OVERLORD_BASE_URL" ] && [ -n "$OVERLORD_TOKEN" ] && [ -n "$TICKET_ID" ]; then
|
|
15
31
|
PAYLOAD=$(printf '%s' "$BODY" | python3 -c "import json,sys; body=json.load(sys.stdin); print(json.dumps({'hookType': 'UserPromptSubmit', 'ticketId': __import__('os').environ['TICKET_ID'], 'prompt': body.get('prompt', ''), 'turnIndex': body.get('turn_number', 0)}))" 2>/dev/null || echo '')
|
|
16
|
-
if [ -
|
|
17
|
-
|
|
32
|
+
if [ -z "$PAYLOAD" ]; then
|
|
33
|
+
log_hook "failed to build hook-event payload from submit body"
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
PAYLOAD_TURN=$(printf '%s' "$PAYLOAD" | python3 -c "import json,sys; print(json.load(sys.stdin).get('turnIndex', 'unknown'))" 2>/dev/null || echo "unknown")
|
|
38
|
+
log_hook "built payload turn=$PAYLOAD_TURN posting hook-event"
|
|
39
|
+
(
|
|
40
|
+
RESPONSE_FILE=$(mktemp "${TMPDIR:-/tmp}/overlord-hook-event.XXXXXX")
|
|
41
|
+
HTTP_CODE=$(curl -sS -m 5 -o "$RESPONSE_FILE" -w "%{http_code}" \
|
|
18
42
|
-X POST "$OVERLORD_BASE_URL/api/protocol/hook-event" \
|
|
19
43
|
-H "Authorization: Bearer $OVERLORD_TOKEN" \
|
|
20
44
|
-H "X-Overlord-Local-Secret: $OVERLORD_LOCAL_SECRET" \
|
|
21
45
|
-H "Content-Type: application/json" \
|
|
22
|
-
-d "$PAYLOAD"
|
|
23
|
-
|
|
46
|
+
-d "$PAYLOAD")
|
|
47
|
+
CURL_EXIT=$?
|
|
48
|
+
RESPONSE_PREVIEW=$(python3 -c "from pathlib import Path; import sys; p=Path(sys.argv[1]); text=p.read_text(encoding='utf-8', errors='replace') if p.exists() else ''; text=' '.join(text.split()); print(text[:200])" "$RESPONSE_FILE" 2>/dev/null || echo '')
|
|
49
|
+
rm -f "$RESPONSE_FILE"
|
|
50
|
+
if [ "$CURL_EXIT" -eq 0 ]; then
|
|
51
|
+
log_hook "hook-event POST finished exit=0 http_status=$HTTP_CODE response_preview=$RESPONSE_PREVIEW"
|
|
52
|
+
else
|
|
53
|
+
log_hook "hook-event POST failed exit=$CURL_EXIT http_status=${HTTP_CODE:-none} response_preview=$RESPONSE_PREVIEW"
|
|
54
|
+
fi
|
|
55
|
+
) >/dev/null 2>&1 &
|
|
24
56
|
disown
|
|
25
|
-
fi
|
|
26
57
|
fi
|
|
27
58
|
|
|
28
59
|
exit 0
|
|
@@ -72,7 +72,7 @@ ovld protocol deliver --session-key <sessionKey> \
|
|
|
72
72
|
1. If the user wants to create tickets (and does not ask to start execution), run `ovld protocol create --agent codex --objective "..."`.
|
|
73
73
|
- When `--session-key` and `--ticket-id` are provided, it creates a follow-up draft.
|
|
74
74
|
- When session flags are omitted, it resolves the project by matching current working directory (or `--working-directory`) to Overlord `local_working_directory`, then creates a standalone draft.
|
|
75
|
-
2. Default to `create` for new tickets. Only use `ovld protocol prompt --agent codex --objective "..."` when the user explicitly asks to create and execute immediately.
|
|
75
|
+
2. Default to `create` for new draft tickets. Only use `ovld protocol prompt --agent codex --objective "..."` when the user explicitly asks to create and execute immediately. If the work is already complete in chat and just needs to be recorded, use `ovld protocol record-work` instead.
|
|
76
76
|
`prompt` creates the ticket in `execute` status and attaches immediately.
|
|
77
77
|
3. If the user wants to inspect an existing ticket without starting work, use `ovld protocol load-context --ticket-id <ticket_id>`.
|
|
78
78
|
4. If the user wants to work an existing ticket, attach with `ovld protocol attach --ticket-id <ticket_id>` and then switch to Mode 1. Use `ovld protocol connect --ticket-id <ticket_id>` instead when you only need a session key without the full ticket payload.
|
|
@@ -105,6 +105,23 @@ Pass `--execution-target agent` or `--execution-target human` (default: `human`)
|
|
|
105
105
|
|
|
106
106
|
When in doubt, ask yourself: _can this be done entirely inside a terminal or browser by an AI without human intervention?_ If yes → `agent`. If it requires a human to log in, decide, or act in the real world → `human`.
|
|
107
107
|
|
|
108
|
+
## Recording Completed Work From Chat
|
|
109
|
+
|
|
110
|
+
Use `ovld protocol record-work` (hosted MCP: `record_work`) when the user has done work directly in a chat (no attached ticket) and now wants to record it as a ticket *and* publish a feed post about it. Distinct from `create` (drafts future work), `prompt` (creates and starts execution), and `deliver` (concludes an attached session). `record-work` creates the ticket in `review` with a completed objective and triggers the feed-post generator atomically; no session is left open.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
ovld protocol record-work --payload-file - <<'EOF'
|
|
114
|
+
{
|
|
115
|
+
"objective": "User asked me to X; I did Y...",
|
|
116
|
+
"summary": "Narrative for feed post + reviewer.",
|
|
117
|
+
"artifacts": [...],
|
|
118
|
+
"changeRationales": [...]
|
|
119
|
+
}
|
|
120
|
+
EOF
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Project resolution mirrors `prompt`/`create`: cwd is matched against `project_user.local_working_directory`. If no match, ask the user for `--project-id`; if the work is not tied to any project, pass `--personal`. In a git workspace, `record-work` validates uncommitted changes are represented by `changeRationales` unless `--skip-file-change-check` is set. Do NOT use for in-progress work.
|
|
124
|
+
|
|
108
125
|
## Change Rationales
|
|
109
126
|
|
|
110
127
|
Always include `changeRationales` when delivering. Optionally include them on updates during long-running work.
|
|
@@ -139,10 +156,20 @@ ovld protocol attachment-download-url --session-key <sessionKey> --ticket-id $TI
|
|
|
139
156
|
|
|
140
157
|
The `attach` and `load-context` responses already include `attachments` and `objectives` arrays — use those for `<attachment-id>` and `<objective-id>` values. Run `attachment-list` mid-session if new files have been uploaded since attach.
|
|
141
158
|
|
|
159
|
+
## Large Artifacts
|
|
160
|
+
|
|
161
|
+
For large artifacts such as planning documents, architecture decisions, research summaries, or design documents: **save the full content as a markdown file in the linked repository, then summarize it in the artifact returned to the ticket.**
|
|
162
|
+
|
|
163
|
+
- Save to a meaningful path in the repository (e.g., `ai/feature-plans/my-feature.md` for feature plans, `docs/decisions/my-decision.md` for architecture decision records).
|
|
164
|
+
- Commit the file as part of the ticket's work so it appears in `changeRationales`.
|
|
165
|
+
- In the delivery, include a `note` or `decision` artifact with a concise summary and the repository file path — not the full document content.
|
|
166
|
+
|
|
167
|
+
This keeps the ticket feed readable while preserving the full document in version control where it can be reviewed, diffed, and referenced later.
|
|
168
|
+
|
|
142
169
|
## Defaults And Notes
|
|
143
170
|
|
|
144
171
|
- API requires `agentIdentifier` and `connectionMethod` on attach/connect/prompt. The CLI defaults them to `codex`/`cli`; the hosted MCP defaults to `mcp`. Override with `--agent` / `--method` when calling from a different runtime.
|
|
145
|
-
- Hosted MCP and the local `overlord-mcp.mjs` shim use the same **tool names** (\`attach\`, \`update\`, \`deliver\`, …). The shim uses **snake_case** JSON keys (\`ticket_id\`, \`session_key\`) that map to CLI flags; hosted MCP uses **camelCase** (\`ticketId\`, \`sessionKey\`) matching REST bodies.
|
|
172
|
+
- Hosted MCP and the local `overlord-mcp.mjs` shim use the same **tool names** (\`attach\`, \`update\`, \`deliver\`, \`record_work\`, …). The shim uses **snake_case** JSON keys (\`ticket_id\`, \`session_key\`) that map to CLI flags; hosted MCP uses **camelCase** (\`ticketId\`, \`sessionKey\`) matching REST bodies.
|
|
146
173
|
- `permission-request` is invoked by the local Codex plugin's permission rules; agents do not normally call it directly.
|
|
147
174
|
- `record_change_rationales` (MCP) and `ovld protocol record-change-rationales` (CLI) both write to the `file_changes` table; the dedicated route is `POST /api/protocol/record-change-rationales`.
|
|
148
175
|
- Objective attachment MCP tools use `<verb>_<noun>` names — `list_attachments`, `prepare_attachment_upload`, `finalize_attachment_upload`, `get_attachment_download_url`, `upload_attachment_file`. CLI commands use `attachment-*` and require `--objective-id` for upload/finalize.
|
|
@@ -158,4 +185,4 @@ The `attach` and `load-context` responses already include `attachments` and `obj
|
|
|
158
185
|
- 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
186
|
- 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
187
|
|
|
161
|
-
<!-- version: 0.4.
|
|
188
|
+
<!-- version: 0.4.10 -->
|