@ouro.bot/cli 0.1.0-alpha.16 → 0.1.0-alpha.18
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/dist/heart/core.js +1 -1
- package/dist/heart/daemon/specialist-prompt.js +1 -0
- package/dist/heart/daemon/specialist-tools.js +8 -28
- package/dist/heart/streaming.js +55 -1
- package/dist/mind/prompt.js +3 -3
- package/dist/repertoire/coding/feedback.js +134 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +61 -2
- package/dist/repertoire/coding/spawner.js +3 -3
- package/dist/repertoire/coding/tools.js +41 -2
- package/dist/repertoire/tools-base.js +69 -5
- package/dist/repertoire/tools.js +32 -9
- package/dist/senses/bluebubbles-client.js +159 -5
- package/dist/senses/bluebubbles-media.js +244 -0
- package/dist/senses/bluebubbles.js +108 -19
- package/dist/senses/debug-activity.js +107 -0
- package/package.json +1 -1
- package/subagents/work-doer.md +26 -24
- package/subagents/work-merger.md +24 -30
- package/subagents/work-planner.md +34 -25
|
@@ -46,10 +46,12 @@ const tokens_1 = require("../mind/friends/tokens");
|
|
|
46
46
|
const resolver_1 = require("../mind/friends/resolver");
|
|
47
47
|
const store_file_1 = require("../mind/friends/store-file");
|
|
48
48
|
const prompt_1 = require("../mind/prompt");
|
|
49
|
+
const phrases_1 = require("../mind/phrases");
|
|
49
50
|
const runtime_1 = require("../nerves/runtime");
|
|
50
51
|
const bluebubbles_model_1 = require("./bluebubbles-model");
|
|
51
52
|
const bluebubbles_client_1 = require("./bluebubbles-client");
|
|
52
53
|
const bluebubbles_mutation_log_1 = require("./bluebubbles-mutation-log");
|
|
54
|
+
const debug_activity_1 = require("./debug-activity");
|
|
53
55
|
const defaultDeps = {
|
|
54
56
|
getAgentName: identity_1.getAgentName,
|
|
55
57
|
buildSystem: prompt_1.buildSystem,
|
|
@@ -92,10 +94,58 @@ function buildInboundText(event) {
|
|
|
92
94
|
}
|
|
93
95
|
return `${event.sender.displayName}: ${baseText}`;
|
|
94
96
|
}
|
|
97
|
+
function buildInboundContent(event) {
|
|
98
|
+
const text = buildInboundText(event);
|
|
99
|
+
if (event.kind !== "message" || !event.inputPartsForAgent || event.inputPartsForAgent.length === 0) {
|
|
100
|
+
return text;
|
|
101
|
+
}
|
|
102
|
+
return [
|
|
103
|
+
{ type: "text", text },
|
|
104
|
+
...event.inputPartsForAgent,
|
|
105
|
+
];
|
|
106
|
+
}
|
|
95
107
|
function createBlueBubblesCallbacks(client, chat, replyToMessageGuid) {
|
|
96
108
|
let textBuffer = "";
|
|
109
|
+
const phrases = (0, phrases_1.getPhrases)();
|
|
110
|
+
const activity = (0, debug_activity_1.createDebugActivityController)({
|
|
111
|
+
thinkingPhrases: phrases.thinking,
|
|
112
|
+
followupPhrases: phrases.followup,
|
|
113
|
+
transport: {
|
|
114
|
+
sendStatus: async (text) => {
|
|
115
|
+
const sent = await client.sendText({
|
|
116
|
+
chat,
|
|
117
|
+
text,
|
|
118
|
+
replyToMessageGuid,
|
|
119
|
+
});
|
|
120
|
+
return sent.messageGuid;
|
|
121
|
+
},
|
|
122
|
+
editStatus: async (_messageGuid, text) => {
|
|
123
|
+
await client.sendText({
|
|
124
|
+
chat,
|
|
125
|
+
text,
|
|
126
|
+
replyToMessageGuid,
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
setTyping: async (active) => {
|
|
130
|
+
await client.setTyping(chat, active);
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
onTransportError: (operation, error) => {
|
|
134
|
+
(0, runtime_1.emitNervesEvent)({
|
|
135
|
+
level: "warn",
|
|
136
|
+
component: "senses",
|
|
137
|
+
event: "senses.bluebubbles_activity_error",
|
|
138
|
+
message: "bluebubbles activity transport failed",
|
|
139
|
+
meta: {
|
|
140
|
+
operation,
|
|
141
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
});
|
|
97
146
|
return {
|
|
98
147
|
onModelStart() {
|
|
148
|
+
activity.onModelStart();
|
|
99
149
|
(0, runtime_1.emitNervesEvent)({
|
|
100
150
|
component: "senses",
|
|
101
151
|
event: "senses.bluebubbles_turn_start",
|
|
@@ -112,10 +162,12 @@ function createBlueBubblesCallbacks(client, chat, replyToMessageGuid) {
|
|
|
112
162
|
});
|
|
113
163
|
},
|
|
114
164
|
onTextChunk(text) {
|
|
165
|
+
activity.onTextChunk(text);
|
|
115
166
|
textBuffer += text;
|
|
116
167
|
},
|
|
117
168
|
onReasoningChunk(_text) { },
|
|
118
169
|
onToolStart(name, _args) {
|
|
170
|
+
activity.onToolStart(name, _args);
|
|
119
171
|
(0, runtime_1.emitNervesEvent)({
|
|
120
172
|
component: "senses",
|
|
121
173
|
event: "senses.bluebubbles_tool_start",
|
|
@@ -124,6 +176,7 @@ function createBlueBubblesCallbacks(client, chat, replyToMessageGuid) {
|
|
|
124
176
|
});
|
|
125
177
|
},
|
|
126
178
|
onToolEnd(name, summary, success) {
|
|
179
|
+
activity.onToolEnd(name, summary, success);
|
|
127
180
|
(0, runtime_1.emitNervesEvent)({
|
|
128
181
|
component: "senses",
|
|
129
182
|
event: "senses.bluebubbles_tool_end",
|
|
@@ -132,6 +185,7 @@ function createBlueBubblesCallbacks(client, chat, replyToMessageGuid) {
|
|
|
132
185
|
});
|
|
133
186
|
},
|
|
134
187
|
onError(error, severity) {
|
|
188
|
+
activity.onError(error);
|
|
135
189
|
(0, runtime_1.emitNervesEvent)({
|
|
136
190
|
level: severity === "terminal" ? "error" : "warn",
|
|
137
191
|
component: "senses",
|
|
@@ -144,8 +198,10 @@ function createBlueBubblesCallbacks(client, chat, replyToMessageGuid) {
|
|
|
144
198
|
textBuffer = "";
|
|
145
199
|
},
|
|
146
200
|
async flush() {
|
|
201
|
+
await activity.drain();
|
|
147
202
|
const trimmed = textBuffer.trim();
|
|
148
203
|
if (!trimmed) {
|
|
204
|
+
await activity.finish();
|
|
149
205
|
return;
|
|
150
206
|
}
|
|
151
207
|
textBuffer = "";
|
|
@@ -154,6 +210,10 @@ function createBlueBubblesCallbacks(client, chat, replyToMessageGuid) {
|
|
|
154
210
|
text: trimmed,
|
|
155
211
|
replyToMessageGuid,
|
|
156
212
|
});
|
|
213
|
+
await activity.finish();
|
|
214
|
+
},
|
|
215
|
+
async finish() {
|
|
216
|
+
await activity.finish();
|
|
157
217
|
},
|
|
158
218
|
};
|
|
159
219
|
}
|
|
@@ -227,6 +287,15 @@ async function handleBlueBubblesEvent(payload, deps = {}) {
|
|
|
227
287
|
friendStore: store,
|
|
228
288
|
summarize: (0, core_1.createSummarize)(),
|
|
229
289
|
context,
|
|
290
|
+
codingFeedback: {
|
|
291
|
+
send: async (message) => {
|
|
292
|
+
await client.sendText({
|
|
293
|
+
chat: event.chat,
|
|
294
|
+
text: message,
|
|
295
|
+
replyToMessageGuid: event.kind === "message" ? event.messageGuid : undefined,
|
|
296
|
+
});
|
|
297
|
+
},
|
|
298
|
+
},
|
|
230
299
|
};
|
|
231
300
|
const friendId = context.friend.id;
|
|
232
301
|
const sessPath = resolvedDeps.sessionPath(friendId, "bluebubbles", event.chat.sessionKey);
|
|
@@ -234,31 +303,51 @@ async function handleBlueBubblesEvent(payload, deps = {}) {
|
|
|
234
303
|
const messages = existing?.messages && existing.messages.length > 0
|
|
235
304
|
? existing.messages
|
|
236
305
|
: [{ role: "system", content: await resolvedDeps.buildSystem("bluebubbles", undefined, context) }];
|
|
237
|
-
messages.push({ role: "user", content:
|
|
306
|
+
messages.push({ role: "user", content: buildInboundContent(event) });
|
|
238
307
|
const callbacks = createBlueBubblesCallbacks(client, event.chat, event.kind === "message" ? event.messageGuid : undefined);
|
|
239
308
|
const controller = new AbortController();
|
|
240
309
|
const agentOptions = {
|
|
241
310
|
toolContext,
|
|
242
311
|
};
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
312
|
+
try {
|
|
313
|
+
const result = await resolvedDeps.runAgent(messages, callbacks, "bluebubbles", controller.signal, agentOptions);
|
|
314
|
+
await callbacks.flush();
|
|
315
|
+
resolvedDeps.postTurn(messages, sessPath, result.usage);
|
|
316
|
+
await resolvedDeps.accumulateFriendTokens(store, friendId, result.usage);
|
|
317
|
+
try {
|
|
318
|
+
await client.markChatRead(event.chat);
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
(0, runtime_1.emitNervesEvent)({
|
|
322
|
+
level: "warn",
|
|
323
|
+
component: "senses",
|
|
324
|
+
event: "senses.bluebubbles_mark_read_error",
|
|
325
|
+
message: "failed to mark bluebubbles chat as read",
|
|
326
|
+
meta: {
|
|
327
|
+
chatGuid: event.chat.chatGuid ?? null,
|
|
328
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
(0, runtime_1.emitNervesEvent)({
|
|
333
|
+
component: "senses",
|
|
334
|
+
event: "senses.bluebubbles_turn_end",
|
|
335
|
+
message: "bluebubbles event handled",
|
|
336
|
+
meta: {
|
|
337
|
+
messageGuid: event.messageGuid,
|
|
338
|
+
kind: event.kind,
|
|
339
|
+
sessionKey: event.chat.sessionKey,
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
return {
|
|
343
|
+
handled: true,
|
|
344
|
+
notifiedAgent: true,
|
|
253
345
|
kind: event.kind,
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
notifiedAgent: true,
|
|
260
|
-
kind: event.kind,
|
|
261
|
-
};
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
finally {
|
|
349
|
+
await callbacks.finish();
|
|
350
|
+
}
|
|
262
351
|
}
|
|
263
352
|
function createBlueBubblesWebhookHandler(deps = {}) {
|
|
264
353
|
return async (req, res) => {
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDebugActivityController = createDebugActivityController;
|
|
4
|
+
const format_1 = require("../mind/format");
|
|
5
|
+
const phrases_1 = require("../mind/phrases");
|
|
6
|
+
const runtime_1 = require("../nerves/runtime");
|
|
7
|
+
function createDebugActivityController(options) {
|
|
8
|
+
let queue = Promise.resolve();
|
|
9
|
+
let statusMessageGuid;
|
|
10
|
+
let typingActive = false;
|
|
11
|
+
let hadToolRun = false;
|
|
12
|
+
let followupShown = false;
|
|
13
|
+
let lastPhrase = "";
|
|
14
|
+
function reportTransportError(operation, error) {
|
|
15
|
+
(0, runtime_1.emitNervesEvent)({
|
|
16
|
+
level: "warn",
|
|
17
|
+
component: "senses",
|
|
18
|
+
event: "senses.debug_activity_transport_error",
|
|
19
|
+
message: "debug activity transport failed",
|
|
20
|
+
meta: {
|
|
21
|
+
operation,
|
|
22
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
options.onTransportError?.(operation, error);
|
|
26
|
+
}
|
|
27
|
+
function enqueue(operation, task) {
|
|
28
|
+
queue = queue
|
|
29
|
+
.then(task)
|
|
30
|
+
.catch((error) => {
|
|
31
|
+
reportTransportError(operation, error);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function nextPhrase(pool) {
|
|
35
|
+
const phrase = (0, phrases_1.pickPhrase)(pool, lastPhrase);
|
|
36
|
+
lastPhrase = phrase;
|
|
37
|
+
return phrase;
|
|
38
|
+
}
|
|
39
|
+
function ensureTyping(active) {
|
|
40
|
+
if (typingActive === active) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
typingActive = active;
|
|
44
|
+
enqueue(active ? "typing_start" : "typing_stop", async () => {
|
|
45
|
+
await options.transport.setTyping(active);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function setStatus(text) {
|
|
49
|
+
(0, runtime_1.emitNervesEvent)({
|
|
50
|
+
component: "senses",
|
|
51
|
+
event: "senses.debug_activity_update",
|
|
52
|
+
message: "debug activity status updated",
|
|
53
|
+
meta: {
|
|
54
|
+
hasStatusGuid: Boolean(statusMessageGuid),
|
|
55
|
+
textLength: text.length,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
ensureTyping(true);
|
|
59
|
+
enqueue("status_update", async () => {
|
|
60
|
+
if (statusMessageGuid) {
|
|
61
|
+
await options.transport.editStatus(statusMessageGuid, text);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
statusMessageGuid = await options.transport.sendStatus(text);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
onModelStart() {
|
|
69
|
+
const pool = hadToolRun ? options.followupPhrases : options.thinkingPhrases;
|
|
70
|
+
setStatus(`${nextPhrase(pool)}...`);
|
|
71
|
+
},
|
|
72
|
+
onToolStart(name, args) {
|
|
73
|
+
hadToolRun = true;
|
|
74
|
+
followupShown = false;
|
|
75
|
+
const argSummary = Object.values(args).join(", ");
|
|
76
|
+
const detail = argSummary ? ` (${argSummary})` : "";
|
|
77
|
+
setStatus(`running ${name}${detail}...`);
|
|
78
|
+
},
|
|
79
|
+
onToolEnd(name, summary, success) {
|
|
80
|
+
hadToolRun = true;
|
|
81
|
+
followupShown = false;
|
|
82
|
+
setStatus((0, format_1.formatToolResult)(name, summary, success));
|
|
83
|
+
},
|
|
84
|
+
onTextChunk(text) {
|
|
85
|
+
if (!text || !hadToolRun || followupShown) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
followupShown = true;
|
|
89
|
+
setStatus(`${nextPhrase(options.followupPhrases)}...`);
|
|
90
|
+
},
|
|
91
|
+
onError(error) {
|
|
92
|
+
setStatus((0, format_1.formatError)(error));
|
|
93
|
+
this.finish();
|
|
94
|
+
},
|
|
95
|
+
async drain() {
|
|
96
|
+
await queue;
|
|
97
|
+
},
|
|
98
|
+
async finish() {
|
|
99
|
+
if (!typingActive) {
|
|
100
|
+
await queue;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
ensureTyping(false);
|
|
104
|
+
await queue;
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
package/package.json
CHANGED
package/subagents/work-doer.md
CHANGED
|
@@ -8,17 +8,18 @@ You are a task executor. Read a doing.md file and execute all units sequentially
|
|
|
8
8
|
|
|
9
9
|
## On Startup
|
|
10
10
|
|
|
11
|
-
1. **Find
|
|
12
|
-
2.
|
|
13
|
-
3. If
|
|
14
|
-
4.
|
|
15
|
-
5. **
|
|
11
|
+
1. **Find task-doc directory**: Read project instructions (for example `AGENTS.md`) to determine where planning/doing docs live for this repo
|
|
12
|
+
2. **Find doing doc**: Look for `YYYY-MM-DD-HHMM-doing-*.md` in that project-defined task-doc directory
|
|
13
|
+
3. If multiple found, ask which one
|
|
14
|
+
4. If none found, ask user for location
|
|
15
|
+
5. **Check execution_mode**: Read the doing doc's `Execution Mode` field
|
|
16
|
+
6. **Verify artifacts directory exists**: `{task-name}/` next to `{task-name}.md`
|
|
16
17
|
- If missing, create it: `mkdir {task-name}`
|
|
17
|
-
|
|
18
|
+
7. **Detect resume vs fresh start:**
|
|
18
19
|
- Count completed units (✅) vs total units
|
|
19
20
|
- Check git status for uncommitted changes
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
8. **Announce status clearly:**
|
|
22
23
|
|
|
23
24
|
**If fresh start (0 units complete):**
|
|
24
25
|
```
|
|
@@ -214,20 +215,21 @@ When all units are `✅`:
|
|
|
214
215
|
## Rules
|
|
215
216
|
|
|
216
217
|
1. **File naming**: Expect `YYYY-MM-DD-HHMM-doing-{name}.md` format
|
|
217
|
-
2. **
|
|
218
|
-
3. **
|
|
219
|
-
4. **
|
|
220
|
-
5. **
|
|
221
|
-
6. **
|
|
222
|
-
7. **
|
|
223
|
-
8. **
|
|
224
|
-
9. **
|
|
225
|
-
10. **
|
|
226
|
-
11. **
|
|
227
|
-
12. **
|
|
228
|
-
13.
|
|
229
|
-
14.
|
|
230
|
-
15. **
|
|
231
|
-
16. **
|
|
232
|
-
17. **
|
|
233
|
-
18. **
|
|
218
|
+
2. **Location**: Read and update doing docs in the project-defined task-doc directory, which may live outside the repo
|
|
219
|
+
3. **Artifacts directory**: Use `{task-name}/` for all outputs, logs, data
|
|
220
|
+
4. **Execution mode**: Honor `pending | spawn | direct` from doing doc
|
|
221
|
+
5. **TDD strictly enforced** — tests before implementation, always
|
|
222
|
+
6. **100% coverage** — no exceptions, no exclude attributes
|
|
223
|
+
7. **Atomic commits** — one logical unit per commit, push after each
|
|
224
|
+
8. **Timestamps from git** — `git log -1 --format="%Y-%m-%d %H:%M"`
|
|
225
|
+
9. **Push after each unit phase complete**
|
|
226
|
+
10. **Update doing.md after each unit** — status and progress log
|
|
227
|
+
11. **Spawn sub-agents for fixes** — don't ask, just do it
|
|
228
|
+
12. **Update docs immediately** — when decisions made, commit right away
|
|
229
|
+
13. **Stop on actual blocker** — unclear requirements or need user input
|
|
230
|
+
14. **/compact proactively** — preserve context between units
|
|
231
|
+
15. **No warnings** — treat warnings as errors
|
|
232
|
+
16. **Run full test suite** — before marking unit complete, not just new tests
|
|
233
|
+
17. **Always compile** — run the project's build command after every implementation/refactor unit. Tests passing is necessary but not sufficient.
|
|
234
|
+
18. **Checklist hygiene is mandatory** — keep doing/planning `Completion Criteria` checklists synchronized with verified completion evidence.
|
|
235
|
+
19. **Verify APIs before importing** — before writing `import { Foo } from './bar'`, use `grep` or `read_file` to confirm `Foo` is actually exported from that module. Never assume an export exists — always check the source first. This prevents wasted cycles on "module has no exported member" errors.
|
package/subagents/work-merger.md
CHANGED
|
@@ -19,12 +19,16 @@ The branch follows the `<agent>/<slug>` convention (e.g., `ouroboros/context-ker
|
|
|
19
19
|
|
|
20
20
|
Do not hardcode agent names. Derive `<agent>` from the branch at runtime.
|
|
21
21
|
|
|
22
|
+
### 1a. Determine the project-defined task-doc directory
|
|
23
|
+
|
|
24
|
+
Read project instructions (for example `AGENTS.md`) to determine where this repo keeps planning/doing docs. Set `TASK_DIR` to that project-defined location. Do not assume task docs live in the repo.
|
|
25
|
+
|
|
22
26
|
### 2. Find own doing doc
|
|
23
27
|
|
|
24
|
-
The caller provides the doing doc path (
|
|
28
|
+
The caller provides the doing doc path. If not provided, read project instructions (for example `AGENTS.md`) to find the project-defined task-doc directory, then find the most recent doing doc there:
|
|
25
29
|
|
|
26
30
|
```bash
|
|
27
|
-
ls -t ${
|
|
31
|
+
ls -t "${TASK_DIR}"/*-doing-*.md | head -1
|
|
28
32
|
```
|
|
29
33
|
|
|
30
34
|
Read this doing doc to understand what was just implemented. You will need it for conflict resolution context.
|
|
@@ -134,35 +138,28 @@ You already have the path from On Startup. Read the doing doc to understand:
|
|
|
134
138
|
- The objective, completion criteria, and unit descriptions
|
|
135
139
|
- What files were changed and why
|
|
136
140
|
|
|
137
|
-
### Step 2:
|
|
138
|
-
|
|
139
|
-
Find exactly which doing docs landed on main since this branch diverged. Do NOT scan by filename timestamps or guess -- use git history:
|
|
140
|
-
|
|
141
|
-
```bash
|
|
142
|
-
git log origin/main --not HEAD --name-only --diff-filter=A -- '*/tasks/*-doing-*.md'
|
|
143
|
-
```
|
|
141
|
+
### Step 2: Gather incoming-main intent (git-informed)
|
|
144
142
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
If no doing docs are found with `--diff-filter=A` (added), also check for modifications:
|
|
143
|
+
Do not assume task docs live in this repo. Instead, use git history and diffs to understand what landed on `main` since this branch diverged:
|
|
148
144
|
|
|
149
145
|
```bash
|
|
150
|
-
git log origin/main --not HEAD --
|
|
146
|
+
git log origin/main --not HEAD --oneline
|
|
147
|
+
git diff --name-only HEAD...origin/main
|
|
151
148
|
```
|
|
152
149
|
|
|
153
|
-
|
|
154
|
-
- Timestamp-sorted filename scans can miss relevant docs or include irrelevant ones
|
|
155
|
-
- Git history tells you exactly what landed on main since you branched
|
|
156
|
-
- This is deterministic and correct regardless of doc naming or timing
|
|
150
|
+
If a clearly relevant local task doc exists outside the repo (for example in another local bundle/worktree task directory), you may read it for extra context. Treat that as optional context, not a required precondition.
|
|
157
151
|
|
|
158
|
-
|
|
152
|
+
**Why this is the primary source of truth:**
|
|
153
|
+
- Task docs may live outside the repo entirely
|
|
154
|
+
- Git history tells you exactly what changed on `main` since you branched
|
|
155
|
+
- This keeps work-merger generic instead of assuming one repo's task-doc layout
|
|
159
156
|
|
|
160
|
-
|
|
161
|
-
- What the other agent implemented
|
|
162
|
-
- Their objective and completion criteria
|
|
163
|
-
- Which files they changed and why
|
|
157
|
+
### Step 3: Combine own task intent with incoming-main changes
|
|
164
158
|
|
|
165
|
-
|
|
159
|
+
Use:
|
|
160
|
+
- your own doing doc as the source of truth for this branch's intent
|
|
161
|
+
- incoming git commits/diffs as the source of truth for what landed on `main`
|
|
162
|
+
- any optional local task docs only when they materially clarify a conflict
|
|
166
163
|
|
|
167
164
|
### Step 4: Resolve conflicts
|
|
168
165
|
|
|
@@ -182,7 +179,7 @@ With both intents understood, resolve each conflict:
|
|
|
182
179
|
If the merge was syntactically clean but tests fail (Case B):
|
|
183
180
|
|
|
184
181
|
1. Read the test failure output to identify which tests broke
|
|
185
|
-
2. Cross-reference with
|
|
182
|
+
2. Cross-reference with your doing doc plus the incoming git changes to understand the conflict
|
|
186
183
|
3. Fix the code to satisfy both agents' intents
|
|
187
184
|
4. Re-run tests: `npm test`
|
|
188
185
|
5. Repeat until tests pass
|
|
@@ -204,7 +201,7 @@ git commit -m "fix: resolve semantic conflicts after merging main"
|
|
|
204
201
|
npm test
|
|
205
202
|
```
|
|
206
203
|
|
|
207
|
-
All tests must pass before proceeding to PR Workflow. If tests still fail after resolution, re-examine
|
|
204
|
+
All tests must pass before proceeding to PR Workflow. If tests still fail after resolution, re-examine your doing doc, the incoming git changes, and any optional supporting task docs, then try again. If genuinely stuck after multiple attempts, escalate to the user (see **Escalation**).
|
|
208
205
|
|
|
209
206
|
---
|
|
210
207
|
|
|
@@ -227,20 +224,17 @@ git push --force-with-lease origin ${BRANCH}
|
|
|
227
224
|
|
|
228
225
|
### Step 2: Create the pull request
|
|
229
226
|
|
|
230
|
-
Before creating the PR, build a comprehensive description of **all** changes on this branch relative to main — not just the most recent task. Use git to understand the full scope:
|
|
227
|
+
Before creating the PR, build a comprehensive description of **all** changes on this branch relative to main — not just the most recent task. Use your doing doc plus git to understand the full scope:
|
|
231
228
|
|
|
232
229
|
```bash
|
|
233
230
|
# All commits on this branch not on main
|
|
234
231
|
git log origin/main..HEAD --oneline
|
|
235
232
|
|
|
236
|
-
# All doing docs on this branch (completed tasks)
|
|
237
|
-
git log origin/main..HEAD --name-only --diff-filter=A -- '*/tasks/*-doing-*.md'
|
|
238
|
-
|
|
239
233
|
# Summary of all files changed
|
|
240
234
|
git diff origin/main --stat
|
|
241
235
|
```
|
|
242
236
|
|
|
243
|
-
Read
|
|
237
|
+
Read the doing doc you are executing, plus any other explicitly provided task docs for this branch. The PR body should summarize every completed task on the branch, grouped logically when needed. Include:
|
|
244
238
|
- A section per task (or group of related tasks) with a brief summary of what was implemented
|
|
245
239
|
- A final "Files changed" summary (e.g., "164 files changed — new context kernel, codebase restructure, sync-and-merge system")
|
|
246
240
|
|
|
@@ -8,8 +8,16 @@ You are a task planner for coding work. Help the user define scope, then convert
|
|
|
8
8
|
|
|
9
9
|
## On Startup
|
|
10
10
|
|
|
11
|
+
**Determine task doc directory:**
|
|
12
|
+
1. Read project instructions (for example `AGENTS.md`) to find the canonical task-doc location for the current repo
|
|
13
|
+
2. Derive `AGENT` from the current git branch when the project uses agent-scoped task docs
|
|
14
|
+
3. Set `TASK_DIR` to the project-defined planning/doing directory
|
|
15
|
+
4. If the project-defined parent location exists but `TASK_DIR` does not, create it
|
|
16
|
+
5. If the project does not define a task-doc location, STOP and ask the user or caller where planning/doing docs should live
|
|
17
|
+
6. Do not assume task docs live in the repo root; many projects keep them externally
|
|
18
|
+
|
|
11
19
|
**Check for existing planning docs:**
|
|
12
|
-
1. Look for `YYYY-MM-DD-HHMM-planning-*.md` files in
|
|
20
|
+
1. Look for `YYYY-MM-DD-HHMM-planning-*.md` files in `TASK_DIR`
|
|
13
21
|
2. If found, ask: `"found planning-{name}.md from [date]. resume or start new?"`
|
|
14
22
|
3. If resuming: run Template Compliance Check (see below), then continue
|
|
15
23
|
4. If new: proceed with Phase 1
|
|
@@ -100,7 +108,7 @@ fix and continue? (y/n)
|
|
|
100
108
|
|
|
101
109
|
1. User describes the task
|
|
102
110
|
2. Generate timestamp: `date '+%Y-%m-%d-%H%M'`
|
|
103
|
-
3. Create `YYYY-MM-DD-HHMM-planning-{short-desc}.md` using PLANNING TEMPLATE — **follow template exactly, no extra sections**
|
|
111
|
+
3. Create `TASK_DIR/YYYY-MM-DD-HHMM-planning-{short-desc}.md` using PLANNING TEMPLATE — **follow template exactly, no extra sections**
|
|
104
112
|
4. Commit immediately: `git commit -m "docs(planning): create planning-{short-desc}.md"`
|
|
105
113
|
5. Ask clarifying questions about scope, completion criteria, unknowns
|
|
106
114
|
6. Refine based on answers — **commit after each significant change**
|
|
@@ -150,13 +158,13 @@ User answers questions → agent updates doc → agent sets status to `NEEDS_REV
|
|
|
150
158
|
|
|
151
159
|
**Only proceed after user says "approved" or equivalent.**
|
|
152
160
|
|
|
153
|
-
**CRITICAL: Planning doc is KEPT. Conversion creates a NEW doing doc alongside it
|
|
161
|
+
**CRITICAL: Planning doc is KEPT. Conversion creates a NEW doing doc alongside it in `TASK_DIR`.**
|
|
154
162
|
|
|
155
163
|
Run these passes — announce each. **ALL 4 PASSES ARE MANDATORY. You must run every pass, even if you think nothing changed. Each pass MUST have its own commit (use "no changes needed" in the commit message if the pass found nothing to fix). Do NOT skip or combine passes.**
|
|
156
164
|
|
|
157
165
|
**Pass 1 — First Draft:**
|
|
158
166
|
- Create `YYYY-MM-DD-HHMM-doing-{short-desc}.md` (same timestamp and short-desc as planning)
|
|
159
|
-
- Create adjacent artifacts directory
|
|
167
|
+
- Create adjacent artifacts directory in `TASK_DIR`: `YYYY-MM-DD-HHMM-doing-{short-desc}/` for any files, outputs, or working data
|
|
160
168
|
- Use DOING TEMPLATE — **follow exactly**, including emoji status on every unit header (`### ⬜ Unit X:`)
|
|
161
169
|
- Fill from planning doc
|
|
162
170
|
- Decide execution_mode: `pending` (needs approval), `spawn` (spawn sub-agent per unit), or `direct` (run directly)
|
|
@@ -347,27 +355,28 @@ use work-doer to execute.
|
|
|
347
355
|
## Rules
|
|
348
356
|
|
|
349
357
|
1. **File naming**: `YYYY-MM-DD-HHMM-{type}-{name}.md` — timestamp prefix always
|
|
350
|
-
2. **
|
|
351
|
-
3. **
|
|
352
|
-
4. **
|
|
353
|
-
5. **
|
|
354
|
-
6. **
|
|
355
|
-
7. **
|
|
356
|
-
8. **
|
|
357
|
-
9. **
|
|
358
|
-
10. **
|
|
359
|
-
11. **
|
|
360
|
-
12. **
|
|
361
|
-
13. **
|
|
362
|
-
14. **
|
|
358
|
+
2. **Location**: Planning and doing docs live in the project-defined task-doc directory, which may be outside the repo
|
|
359
|
+
3. **Artifacts directory**: Create `{task-name}/` next to `{task-name}.md` for outputs
|
|
360
|
+
4. **Execution mode**: Must decide `pending | spawn | direct` before execution begins
|
|
361
|
+
5. **No time estimates** — never assign hours/days/duration to tasks or units
|
|
362
|
+
6. **Planning completes before execution** — define ALL work units first, then execute
|
|
363
|
+
7. **Follow templates exactly** — no extra sections
|
|
364
|
+
8. **No implementation details in planning** — those go in doing doc
|
|
365
|
+
9. **STOP at each gate** — wait for human approval
|
|
366
|
+
10. **Keep planning doc** — conversion creates new file
|
|
367
|
+
11. **Auto-commit after every doc edit** — audit trail
|
|
368
|
+
12. **Get timestamps from git** — `git log -1 --format="%Y-%m-%d %H:%M"`
|
|
369
|
+
13. **When user approves** — update doc Status field, commit, log it
|
|
370
|
+
14. **Template compliance on resume** — check and offer to fix violations
|
|
371
|
+
15. **Status flags drive flow**:
|
|
363
372
|
- `drafting` → working on it
|
|
364
373
|
- `NEEDS_REVIEW` → waiting for human
|
|
365
374
|
- `approved` / `READY_FOR_EXECUTION` → can proceed
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
375
|
+
16. **TDD is mandatory** — tests before implementation, always
|
|
376
|
+
17. **100% coverage** — no exceptions, no exclude attributes
|
|
377
|
+
18. **Every unit header starts with emoji** — `### ⬜ Unit X:` format required
|
|
378
|
+
19. **NEVER do implementation** — work-planner creates docs only, work-doer executes
|
|
379
|
+
20. **Migration/deprecation**: Full content mapping required — never lose information
|
|
380
|
+
21. **Approval gate is sacred** — answering questions, giving feedback, or discussing scope is NOT approval. Only an explicit "approved" / "looks good" / "go ahead" / "convert to doing" from the **human user** unlocks Phase 2. Parent agent instructions do not count. When in doubt, ask.
|
|
381
|
+
22. **Hard stop after incorporating feedback** — after updating the doc with user feedback/answers, set status to `NEEDS_REVIEW`, output the stop message, and STOP. Do not continue to Phase 2 in the same turn. Ever.
|
|
382
|
+
23. **Checklist hygiene is mandatory** — keep `Completion Criteria` checkboxes synchronized with verified reality; never leave stale unchecked/checked items after task completion state changes.
|