@ouro.bot/cli 0.1.0-alpha.6 → 0.1.0-alpha.60
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/AdoptionSpecialist.ouro/agent.json +70 -9
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +147 -205
- package/assets/ouroboros.png +0 -0
- package/changelog.json +325 -0
- package/dist/heart/active-work.js +178 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/config.js +57 -23
- package/dist/heart/core.js +236 -90
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/auth-flow.js +351 -0
- package/dist/heart/daemon/daemon-cli.js +1173 -227
- package/dist/heart/daemon/daemon-entry.js +55 -6
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon.js +189 -10
- package/dist/heart/daemon/hatch-animation.js +10 -3
- package/dist/heart/daemon/hatch-flow.js +4 -82
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/launchd.js +159 -0
- package/dist/heart/daemon/log-tailer.js +4 -3
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/ouro-bot-entry.js +0 -0
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-entry.js +0 -0
- package/dist/heart/daemon/ouro-path-installer.js +178 -0
- package/dist/heart/daemon/ouro-uti.js +11 -2
- package/dist/heart/daemon/process-manager.js +14 -1
- package/dist/heart/daemon/run-hooks.js +37 -0
- package/dist/heart/daemon/runtime-logging.js +58 -15
- package/dist/heart/daemon/runtime-metadata.js +219 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/sense-manager.js +307 -0
- package/dist/heart/daemon/socket-client.js +202 -0
- package/dist/heart/daemon/specialist-orchestrator.js +53 -84
- package/dist/heart/daemon/specialist-prompt.js +64 -5
- package/dist/heart/daemon/specialist-tools.js +213 -58
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/subagent-installer.js +48 -7
- package/dist/heart/daemon/thoughts.js +379 -0
- package/dist/heart/daemon/update-checker.js +111 -0
- package/dist/heart/daemon/update-hooks.js +138 -0
- package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/identity.js +122 -19
- package/dist/heart/kicks.js +1 -19
- package/dist/heart/model-capabilities.js +40 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/providers/anthropic.js +74 -9
- package/dist/heart/providers/azure.js +86 -7
- package/dist/heart/providers/minimax.js +4 -0
- package/dist/heart/providers/openai-codex.js +12 -3
- package/dist/heart/safe-workspace.js +228 -0
- package/dist/heart/sense-truth.js +61 -0
- package/dist/heart/session-activity.js +169 -0
- package/dist/heart/session-recall.js +116 -0
- package/dist/heart/streaming.js +100 -22
- package/dist/heart/target-resolution.js +123 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +14 -2
- package/dist/mind/bundle-manifest.js +70 -0
- package/dist/mind/context.js +27 -11
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +35 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/store-file.js +19 -0
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +8 -0
- package/dist/mind/memory.js +27 -26
- package/dist/mind/pending.js +72 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +299 -77
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/repertoire/ado-client.js +4 -2
- package/dist/repertoire/coding/feedback.js +134 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +62 -4
- package/dist/repertoire/coding/spawner.js +3 -3
- package/dist/repertoire/coding/tools.js +41 -2
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/tasks/board.js +12 -0
- package/dist/repertoire/tasks/index.js +23 -9
- package/dist/repertoire/tasks/transitions.js +1 -2
- package/dist/repertoire/tools-base.js +629 -251
- package/dist/repertoire/tools-bluebubbles.js +93 -0
- package/dist/repertoire/tools-teams.js +58 -25
- package/dist/repertoire/tools.js +92 -48
- package/dist/senses/bluebubbles-client.js +210 -5
- package/dist/senses/bluebubbles-entry.js +2 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +339 -0
- package/dist/senses/bluebubbles-model.js +12 -4
- package/dist/senses/bluebubbles-mutation-log.js +45 -5
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +890 -45
- package/dist/senses/cli-layout.js +87 -0
- package/dist/senses/cli.js +345 -144
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/debug-activity.js +148 -0
- package/dist/senses/inner-dialog-worker.js +47 -18
- package/dist/senses/inner-dialog.js +330 -84
- package/dist/senses/pipeline.js +278 -0
- package/dist/senses/teams.js +570 -129
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +14 -3
- package/subagents/README.md +46 -33
- package/subagents/work-doer.md +28 -24
- package/subagents/work-merger.md +24 -30
- package/subagents/work-planner.md +44 -27
- package/dist/heart/daemon/specialist-session.js +0 -142
- package/dist/inner-worker-entry.js +0 -4
|
@@ -39,6 +39,9 @@ const fs = __importStar(require("fs"));
|
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const identity_1 = require("../heart/identity");
|
|
41
41
|
const runtime_1 = require("../nerves/runtime");
|
|
42
|
+
const types_1 = require("../mind/friends/types");
|
|
43
|
+
const pending_1 = require("../mind/pending");
|
|
44
|
+
// Canned reply; eventually agents should compose their own first-contact message
|
|
42
45
|
exports.STRANGER_AUTO_REPLY = "I'm sorry, I'm not allowed to talk to strangers";
|
|
43
46
|
function buildExternalKey(provider, externalId, tenantId) {
|
|
44
47
|
return `${provider}:${tenantId ?? ""}:${externalId}`;
|
|
@@ -77,16 +80,107 @@ function appendPrimaryNotification(bundleRoot, provider, externalId, tenantId, n
|
|
|
77
80
|
fs.mkdirSync(inboxDir, { recursive: true });
|
|
78
81
|
fs.appendFileSync(notificationsPath, `${JSON.stringify(payload)}\n`, "utf8");
|
|
79
82
|
}
|
|
83
|
+
function writeInnerPendingNotice(bundleRoot, noticeContent, nowIso) {
|
|
84
|
+
const innerPendingDir = path.join(bundleRoot, "state", "pending", pending_1.INNER_DIALOG_PENDING.friendId, pending_1.INNER_DIALOG_PENDING.channel, pending_1.INNER_DIALOG_PENDING.key);
|
|
85
|
+
const fileName = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`;
|
|
86
|
+
const filePath = path.join(innerPendingDir, fileName);
|
|
87
|
+
const payload = {
|
|
88
|
+
from: "instinct",
|
|
89
|
+
content: noticeContent,
|
|
90
|
+
timestamp: Date.now(),
|
|
91
|
+
at: nowIso,
|
|
92
|
+
};
|
|
93
|
+
fs.mkdirSync(innerPendingDir, { recursive: true });
|
|
94
|
+
fs.writeFileSync(filePath, JSON.stringify(payload), "utf-8");
|
|
95
|
+
}
|
|
80
96
|
function enforceTrustGate(input) {
|
|
97
|
+
const { senseType } = input;
|
|
98
|
+
// Local (CLI) and internal (inner dialog) — always allow
|
|
99
|
+
if (senseType === "local" || senseType === "internal") {
|
|
100
|
+
return { allowed: true };
|
|
101
|
+
}
|
|
102
|
+
// Closed senses (Teams) — org already gates access, allow all trust levels
|
|
103
|
+
if (senseType === "closed") {
|
|
104
|
+
return { allowed: true };
|
|
105
|
+
}
|
|
106
|
+
// Open senses (BlueBubbles/iMessage) — enforce trust rules
|
|
81
107
|
const trustLevel = input.friend.trustLevel ?? "friend";
|
|
82
|
-
|
|
108
|
+
// Family and friend — always allow on open
|
|
109
|
+
if ((0, types_1.isTrustedLevel)(trustLevel)) {
|
|
83
110
|
return { allowed: true };
|
|
84
111
|
}
|
|
85
112
|
const bundleRoot = input.bundleRoot ?? (0, identity_1.getAgentRoot)();
|
|
86
|
-
const repliesPath = path.join(bundleRoot, "stranger-replies.json");
|
|
87
113
|
const nowIso = (input.now ?? (() => new Date()))().toISOString();
|
|
114
|
+
// Acquaintance rules
|
|
115
|
+
if (trustLevel === "acquaintance") {
|
|
116
|
+
return handleAcquaintance(input, bundleRoot, nowIso);
|
|
117
|
+
}
|
|
118
|
+
// Stranger rules (trustLevel === "stranger")
|
|
119
|
+
return handleStranger(input, bundleRoot, nowIso);
|
|
120
|
+
}
|
|
121
|
+
function handleAcquaintance(input, bundleRoot, nowIso) {
|
|
122
|
+
const { isGroupChat, groupHasFamilyMember, hasExistingGroupWithFamily } = input;
|
|
123
|
+
// Group chat with family member present — allow
|
|
124
|
+
if (isGroupChat && groupHasFamilyMember) {
|
|
125
|
+
return { allowed: true };
|
|
126
|
+
}
|
|
127
|
+
let result;
|
|
128
|
+
let noticeDetail;
|
|
129
|
+
if (isGroupChat) {
|
|
130
|
+
// Group chat without family member — reject silently
|
|
131
|
+
result = { allowed: false, reason: "acquaintance_group_no_family" };
|
|
132
|
+
noticeDetail = `acquaintance "${input.friend.name}" messaged in a group chat without a family member present`;
|
|
133
|
+
}
|
|
134
|
+
else if (hasExistingGroupWithFamily) {
|
|
135
|
+
// 1:1 but shares a group with family — redirect
|
|
136
|
+
result = {
|
|
137
|
+
allowed: false,
|
|
138
|
+
reason: "acquaintance_1on1_has_group",
|
|
139
|
+
autoReply: "Hey! Reach me in our group chat instead.",
|
|
140
|
+
};
|
|
141
|
+
noticeDetail = `acquaintance "${input.friend.name}" DMed me directly — redirected to our group chat`;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
// 1:1, no shared group with family — redirect to any group
|
|
145
|
+
result = {
|
|
146
|
+
allowed: false,
|
|
147
|
+
reason: "acquaintance_1on1_no_group",
|
|
148
|
+
autoReply: "Hey! Reach me in a group chat instead.",
|
|
149
|
+
};
|
|
150
|
+
noticeDetail = `acquaintance "${input.friend.name}" DMed me directly — asked to reach me in a group chat`;
|
|
151
|
+
}
|
|
152
|
+
(0, runtime_1.emitNervesEvent)({
|
|
153
|
+
level: "warn",
|
|
154
|
+
component: "senses",
|
|
155
|
+
event: "senses.trust_gate",
|
|
156
|
+
message: "acquaintance message blocked",
|
|
157
|
+
meta: {
|
|
158
|
+
channel: input.channel,
|
|
159
|
+
provider: input.provider,
|
|
160
|
+
reason: result.reason,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
try {
|
|
164
|
+
writeInnerPendingNotice(bundleRoot, noticeDetail, nowIso);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
(0, runtime_1.emitNervesEvent)({
|
|
168
|
+
level: "error",
|
|
169
|
+
component: "senses",
|
|
170
|
+
event: "senses.trust_gate_error",
|
|
171
|
+
message: "failed to write inner pending notice",
|
|
172
|
+
meta: {
|
|
173
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
function handleStranger(input, bundleRoot, nowIso) {
|
|
180
|
+
const repliesPath = path.join(bundleRoot, "stranger-replies.json");
|
|
88
181
|
const externalKey = buildExternalKey(input.provider, input.externalId, input.tenantId);
|
|
89
182
|
const state = loadRepliesState(repliesPath);
|
|
183
|
+
// Subsequent contact — silent drop
|
|
90
184
|
if (state[externalKey]) {
|
|
91
185
|
(0, runtime_1.emitNervesEvent)({
|
|
92
186
|
level: "warn",
|
|
@@ -103,6 +197,7 @@ function enforceTrustGate(input) {
|
|
|
103
197
|
reason: "stranger_silent_drop",
|
|
104
198
|
};
|
|
105
199
|
}
|
|
200
|
+
// First contact — auto-reply, persist state, notify agent
|
|
106
201
|
state[externalKey] = nowIso;
|
|
107
202
|
try {
|
|
108
203
|
persistRepliesState(repliesPath, state);
|
|
@@ -132,6 +227,21 @@ function enforceTrustGate(input) {
|
|
|
132
227
|
},
|
|
133
228
|
});
|
|
134
229
|
}
|
|
230
|
+
const noticeDetail = `stranger "${input.friend.name}" tried to reach me via ${input.channel}. Auto-replied once.`;
|
|
231
|
+
try {
|
|
232
|
+
writeInnerPendingNotice(bundleRoot, noticeDetail, nowIso);
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
(0, runtime_1.emitNervesEvent)({
|
|
236
|
+
level: "error",
|
|
237
|
+
component: "senses",
|
|
238
|
+
event: "senses.trust_gate_error",
|
|
239
|
+
message: "failed to write inner pending notice",
|
|
240
|
+
meta: {
|
|
241
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
}
|
|
135
245
|
(0, runtime_1.emitNervesEvent)({
|
|
136
246
|
level: "warn",
|
|
137
247
|
component: "senses",
|
package/package.json
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.60",
|
|
4
4
|
"main": "dist/heart/daemon/ouro-entry.js",
|
|
5
5
|
"bin": {
|
|
6
|
+
"cli": "dist/heart/daemon/ouro-bot-entry.js",
|
|
6
7
|
"ouro": "dist/heart/daemon/ouro-entry.js",
|
|
7
8
|
"ouro.bot": "dist/heart/daemon/ouro-bot-entry.js"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"dist/",
|
|
11
12
|
"AdoptionSpecialist.ouro/",
|
|
12
|
-
"subagents/"
|
|
13
|
+
"subagents/",
|
|
14
|
+
"assets/",
|
|
15
|
+
"changelog.json"
|
|
13
16
|
],
|
|
14
17
|
"exports": {
|
|
15
18
|
".": "./dist/heart/daemon/daemon-cli.js",
|
|
@@ -30,11 +33,19 @@
|
|
|
30
33
|
},
|
|
31
34
|
"dependencies": {
|
|
32
35
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
36
|
+
"@azure/identity": "^4.13.0",
|
|
33
37
|
"@microsoft/teams.apps": "^2.0.5",
|
|
34
38
|
"@microsoft/teams.dev": "^2.0.5",
|
|
35
|
-
"
|
|
39
|
+
"fast-glob": "^3.3.3",
|
|
40
|
+
"openai": "^6.27.0",
|
|
41
|
+
"semver": "^7.7.4"
|
|
42
|
+
},
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "https://github.com/ouroborosbot/ouroboros"
|
|
36
46
|
},
|
|
37
47
|
"devDependencies": {
|
|
48
|
+
"@types/semver": "^7.7.1",
|
|
38
49
|
"@vitest/coverage-v8": "^4.0.18",
|
|
39
50
|
"eslint": "^10.0.2",
|
|
40
51
|
"typescript": "^5.7.0",
|
package/subagents/README.md
CHANGED
|
@@ -1,52 +1,68 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Workflow Helpers
|
|
2
2
|
|
|
3
|
-
These are source-of-truth workflow
|
|
3
|
+
These files are the source-of-truth workflow helpers for:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- `work-planner`
|
|
6
|
+
- `work-doer`
|
|
7
|
+
- `work-merger`
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
They are written to stay generic enough for different agent shells, while following this repo’s local rules through `AGENTS.md`.
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## What They Do
|
|
12
|
+
|
|
13
|
+
- `work-planner.md`
|
|
14
|
+
Creates and refines planning docs, then converts approved plans into doing docs.
|
|
15
|
+
- `work-doer.md`
|
|
16
|
+
Executes approved doing docs unit by unit with strict validation discipline.
|
|
17
|
+
- `work-merger.md`
|
|
18
|
+
Syncs with `main`, resolves conflicts, creates the PR, handles CI, and merges.
|
|
19
|
+
|
|
20
|
+
## Important Repo-Specific Truth
|
|
21
|
+
|
|
22
|
+
These helpers do not hardcode task-doc paths. They are expected to read project instructions to discover them.
|
|
23
|
+
|
|
24
|
+
In this repo, that means:
|
|
25
|
+
|
|
26
|
+
- task docs live in `~/AgentBundles/<agent>.ouro/tasks/one-shots/`
|
|
27
|
+
- not inside the repo
|
|
28
|
+
|
|
29
|
+
## Installing For Claude Code
|
|
10
30
|
|
|
11
31
|
```bash
|
|
12
|
-
|
|
32
|
+
mkdir -p ~/.claude/agents
|
|
13
33
|
cp subagents/*.md ~/.claude/agents/
|
|
14
|
-
# or
|
|
15
|
-
ln -s "$(pwd)"/subagents/*.md ~/.claude/agents/
|
|
16
34
|
```
|
|
17
35
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
For tools that support skills but not Claude sub-agents, install these as skills:
|
|
36
|
+
## Installing For Codex-Style Skills
|
|
21
37
|
|
|
22
38
|
```bash
|
|
23
|
-
mkdir -p ~/.
|
|
39
|
+
mkdir -p ~/.agents/skills/work-planner ~/.agents/skills/work-doer ~/.agents/skills/work-merger
|
|
24
40
|
|
|
25
41
|
# Hard-link to keep one source of truth
|
|
26
|
-
ln -f "$(pwd)/subagents/work-planner.md" ~/.
|
|
27
|
-
ln -f "$(pwd)/subagents/work-doer.md" ~/.
|
|
28
|
-
ln -f "$(pwd)/subagents/work-merger.md" ~/.
|
|
42
|
+
ln -f "$(pwd)/subagents/work-planner.md" ~/.agents/skills/work-planner/SKILL.md
|
|
43
|
+
ln -f "$(pwd)/subagents/work-doer.md" ~/.agents/skills/work-doer/SKILL.md
|
|
44
|
+
ln -f "$(pwd)/subagents/work-merger.md" ~/.agents/skills/work-merger/SKILL.md
|
|
29
45
|
```
|
|
30
46
|
|
|
31
|
-
**Important:** Hard-links break when editors save by replacing the file (new inode). After editing any `subagents/*.md` file, re-run the `ln -f` command for that file to restore the link. You can verify with `stat -f '%i'` — both files should share the same inode.
|
|
47
|
+
**Important:** For Codex/OpenAI skill installs, use the generic `~/.agents/skills` root and use hard links (`ln`, not `ln -s`). Installing the same skill into both `~/.agents/skills` and `~/.codex/skills` can produce duplicate entries in Codex. Symlinked `SKILL.md` files may load but are not advertised reliably by Codex surfaces. Hard-links break when editors save by replacing the file (new inode). After editing any `subagents/*.md` file, re-run the `ln -f` command for that file to restore the link. You can verify with `stat -f '%i'` — both files should share the same inode.
|
|
32
48
|
|
|
33
49
|
Optional UI metadata:
|
|
34
50
|
|
|
35
51
|
```bash
|
|
36
|
-
mkdir -p ~/.
|
|
37
|
-
cat > ~/.
|
|
52
|
+
mkdir -p ~/.agents/skills/work-planner/agents ~/.agents/skills/work-doer/agents ~/.agents/skills/work-merger/agents
|
|
53
|
+
cat > ~/.agents/skills/work-planner/agents/openai.yaml << 'EOF'
|
|
38
54
|
interface:
|
|
39
55
|
display_name: "Work Planner"
|
|
40
56
|
short_description: "Create and gate planning/doing task docs"
|
|
41
57
|
default_prompt: "Use $work-planner to create or update a planning doc, then stop at NEEDS_REVIEW."
|
|
42
58
|
EOF
|
|
43
|
-
cat > ~/.
|
|
59
|
+
cat > ~/.agents/skills/work-doer/agents/openai.yaml << 'EOF'
|
|
44
60
|
interface:
|
|
45
61
|
display_name: "Work Doer"
|
|
46
62
|
short_description: "Execute approved doing docs with strict TDD"
|
|
47
63
|
default_prompt: "Use $work-doer to execute an approved doing doc unit by unit."
|
|
48
64
|
EOF
|
|
49
|
-
cat > ~/.
|
|
65
|
+
cat > ~/.agents/skills/work-merger/agents/openai.yaml << 'EOF'
|
|
50
66
|
interface:
|
|
51
67
|
display_name: "Work Merger"
|
|
52
68
|
short_description: "Merge feature branch into main via PR after work-doer completes"
|
|
@@ -54,20 +70,17 @@ interface:
|
|
|
54
70
|
EOF
|
|
55
71
|
```
|
|
56
72
|
|
|
57
|
-
|
|
73
|
+
## Keeping Local Skill Copies Fresh
|
|
74
|
+
|
|
75
|
+
After editing any `subagents/*.md` file, resync your local installed copies.
|
|
58
76
|
|
|
59
|
-
|
|
77
|
+
The repo workflow usually checks this with diffs like:
|
|
60
78
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
| `work-merger.md` | Sync-and-merge agent. Merges feature branch into main via PR after work-doer completes. Handles conflicts, CI failures, and race conditions. |
|
|
79
|
+
```bash
|
|
80
|
+
diff -q ~/.agents/skills/work-planner/SKILL.md subagents/work-planner.md
|
|
81
|
+
diff -q ~/.agents/skills/work-doer/SKILL.md subagents/work-doer.md
|
|
82
|
+
```
|
|
66
83
|
|
|
67
|
-
##
|
|
84
|
+
## Restart Behavior
|
|
68
85
|
|
|
69
|
-
|
|
70
|
-
2. Agent invokes **work-planner** to create a planning doc → human approves → planner converts to doing doc
|
|
71
|
-
3. Agent invokes **work-doer** to execute the doing doc unit by unit
|
|
72
|
-
4. Each unit is committed independently with progress tracked in the doing doc
|
|
73
|
-
5. Agent invokes **work-merger** to merge the feature branch into main via PR (fetch, merge, resolve conflicts, CI gate, merge PR, cleanup)
|
|
86
|
+
Some tools only discover new skills on startup. If a shell/app does not see updates immediately, restart that shell/app after syncing.
|
package/subagents/work-doer.md
CHANGED
|
@@ -8,17 +8,19 @@ 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. If
|
|
13
|
-
3.
|
|
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. **Confirm worktree**: Run from the dedicated task worktree required by the project. If the current checkout is shared, ambiguous, or not on the task branch, switch/create the correct worktree first when project instructions allow it. Only STOP to ask the user when they explicitly want to control naming/layout or automatic creation fails.
|
|
13
|
+
3. **Find doing doc**: Look for `YYYY-MM-DD-HHMM-doing-*.md` in that project-defined task-doc directory
|
|
14
|
+
4. If multiple found, ask which one
|
|
15
|
+
5. If none found, ask user for location
|
|
16
|
+
6. **Check execution_mode**: Read the doing doc's `Execution Mode` field
|
|
17
|
+
7. **Verify artifacts directory exists**: `{task-name}/` next to `{task-name}.md`
|
|
16
18
|
- If missing, create it: `mkdir {task-name}`
|
|
17
|
-
|
|
19
|
+
8. **Detect resume vs fresh start:**
|
|
18
20
|
- Count completed units (✅) vs total units
|
|
19
21
|
- Check git status for uncommitted changes
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
8. **Announce status clearly:**
|
|
22
24
|
|
|
23
25
|
**If fresh start (0 units complete):**
|
|
24
26
|
```
|
|
@@ -214,20 +216,22 @@ When all units are `✅`:
|
|
|
214
216
|
## Rules
|
|
215
217
|
|
|
216
218
|
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. **Update
|
|
227
|
-
12. **
|
|
228
|
-
13.
|
|
229
|
-
14. **
|
|
230
|
-
15.
|
|
231
|
-
16. **
|
|
232
|
-
17. **
|
|
233
|
-
18. **
|
|
219
|
+
2. **Location**: Read and update doing docs in the project-defined task-doc directory, which may live outside the repo
|
|
220
|
+
3. **Artifacts directory**: Use `{task-name}/` for all outputs, logs, data
|
|
221
|
+
4. **Execution mode**: Honor `pending | spawn | direct` from doing doc
|
|
222
|
+
5. **Respect the approved structure**: A `READY_FOR_EXECUTION` doing doc should already be ambiguity-clean. Do not rewrite unit structure unless the user changes scope or the doing doc is actually blocked/inaccurate.
|
|
223
|
+
6. **TDD strictly enforced** — tests before implementation, always
|
|
224
|
+
7. **100% coverage** — no exceptions, no exclude attributes
|
|
225
|
+
8. **Atomic commits** — one logical unit per commit, push after each
|
|
226
|
+
9. **Timestamps from git** — `git log -1 --format="%Y-%m-%d %H:%M"`
|
|
227
|
+
10. **Push after each unit phase complete**
|
|
228
|
+
11. **Update doing.md after each unit** — status and progress log
|
|
229
|
+
12. **Spawn sub-agents for fixes** — don't ask, just do it
|
|
230
|
+
13. **Update docs immediately** — when decisions made, commit right away
|
|
231
|
+
14. **Stop on actual blocker** — unclear requirements or need user input
|
|
232
|
+
15. **/compact proactively** — preserve context between units
|
|
233
|
+
16. **No warnings** — treat warnings as errors
|
|
234
|
+
17. **Run full test suite** — before marking unit complete, not just new tests
|
|
235
|
+
18. **Always compile** — run the project's build command after every implementation/refactor unit. Tests passing is necessary but not sufficient.
|
|
236
|
+
19. **Checklist hygiene is mandatory** — keep doing/planning `Completion Criteria` checklists synchronized with verified completion evidence.
|
|
237
|
+
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,17 @@ 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. Confirm the task is running from a dedicated task worktree when the project requires parallel agent work; if the checkout is shared or ambiguous, create/switch to the dedicated worktree yourself when project instructions allow it, and only STOP to ask the caller when they explicitly want to control naming/layout or automatic creation fails
|
|
15
|
+
4. Set `TASK_DIR` to the project-defined planning/doing directory
|
|
16
|
+
5. If the project-defined parent location exists but `TASK_DIR` does not, create it
|
|
17
|
+
6. If the project does not define a task-doc location, STOP and ask the user or caller where planning/doing docs should live
|
|
18
|
+
7. Do not assume task docs live in the repo root; many projects keep them externally
|
|
19
|
+
|
|
11
20
|
**Check for existing planning docs:**
|
|
12
|
-
1. Look for `YYYY-MM-DD-HHMM-planning-*.md` files in
|
|
21
|
+
1. Look for `YYYY-MM-DD-HHMM-planning-*.md` files in `TASK_DIR`
|
|
13
22
|
2. If found, ask: `"found planning-{name}.md from [date]. resume or start new?"`
|
|
14
23
|
3. If resuming: run Template Compliance Check (see below), then continue
|
|
15
24
|
4. If new: proceed with Phase 1
|
|
@@ -100,7 +109,7 @@ fix and continue? (y/n)
|
|
|
100
109
|
|
|
101
110
|
1. User describes the task
|
|
102
111
|
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**
|
|
112
|
+
3. Create `TASK_DIR/YYYY-MM-DD-HHMM-planning-{short-desc}.md` using PLANNING TEMPLATE — **follow template exactly, no extra sections**
|
|
104
113
|
4. Commit immediately: `git commit -m "docs(planning): create planning-{short-desc}.md"`
|
|
105
114
|
5. Ask clarifying questions about scope, completion criteria, unknowns
|
|
106
115
|
6. Refine based on answers — **commit after each significant change**
|
|
@@ -150,13 +159,13 @@ User answers questions → agent updates doc → agent sets status to `NEEDS_REV
|
|
|
150
159
|
|
|
151
160
|
**Only proceed after user says "approved" or equivalent.**
|
|
152
161
|
|
|
153
|
-
**CRITICAL: Planning doc is KEPT. Conversion creates a NEW doing doc alongside it
|
|
162
|
+
**CRITICAL: Planning doc is KEPT. Conversion creates a NEW doing doc alongside it in `TASK_DIR`.**
|
|
154
163
|
|
|
155
|
-
Run these passes — announce each. **ALL
|
|
164
|
+
Run these passes — announce each. **ALL 5 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
165
|
|
|
157
166
|
**Pass 1 — First Draft:**
|
|
158
167
|
- Create `YYYY-MM-DD-HHMM-doing-{short-desc}.md` (same timestamp and short-desc as planning)
|
|
159
|
-
- Create adjacent artifacts directory
|
|
168
|
+
- Create adjacent artifacts directory in `TASK_DIR`: `YYYY-MM-DD-HHMM-doing-{short-desc}/` for any files, outputs, or working data
|
|
160
169
|
- Use DOING TEMPLATE — **follow exactly**, including emoji status on every unit header (`### ⬜ Unit X:`)
|
|
161
170
|
- Fill from planning doc
|
|
162
171
|
- Decide execution_mode: `pending` (needs approval), `spawn` (spawn sub-agent per unit), or `direct` (run directly)
|
|
@@ -173,7 +182,14 @@ Run these passes — announce each. **ALL 4 PASSES ARE MANDATORY. You must run e
|
|
|
173
182
|
- Update units if reality differs from what was assumed during planning
|
|
174
183
|
- Commit: `git commit -m "docs(doing): validation pass"` (or `"docs(doing): validation pass - no changes needed"` if nothing to fix)
|
|
175
184
|
|
|
176
|
-
**Pass 4 —
|
|
185
|
+
**Pass 4 — Ambiguity:**
|
|
186
|
+
- Remove doer-facing ambiguity before execution starts
|
|
187
|
+
- Tighten units so a `READY_FOR_EXECUTION` doing doc does not require structural rewrites by `work-doer`
|
|
188
|
+
- Resolve fuzzy phrases like "appropriate files", "as needed", or "wherever the bug is" into concrete targets unless the project instructions explicitly require that flexibility
|
|
189
|
+
- If uncertainty remains, keep it in the planning doc's `Open Questions`, set status to `NEEDS_REVIEW`, and STOP instead of shipping an ambiguous doing doc
|
|
190
|
+
- Commit: `git commit -m "docs(doing): ambiguity pass"` (or `"docs(doing): ambiguity pass - no changes needed"` if nothing to fix)
|
|
191
|
+
|
|
192
|
+
**Pass 5 — Quality:**
|
|
177
193
|
- All units have acceptance criteria?
|
|
178
194
|
- No TBD items?
|
|
179
195
|
- Completion criteria testable?
|
|
@@ -347,27 +363,28 @@ use work-doer to execute.
|
|
|
347
363
|
## Rules
|
|
348
364
|
|
|
349
365
|
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. **
|
|
366
|
+
2. **Location**: Planning and doing docs live in the project-defined task-doc directory, which may be outside the repo
|
|
367
|
+
3. **Artifacts directory**: Create `{task-name}/` next to `{task-name}.md` for outputs
|
|
368
|
+
4. **Execution mode**: Must decide `pending | spawn | direct` before execution begins
|
|
369
|
+
5. **No time estimates** — never assign hours/days/duration to tasks or units
|
|
370
|
+
6. **Planning completes before execution** — define ALL work units first, then execute
|
|
371
|
+
7. **Follow templates exactly** — no extra sections
|
|
372
|
+
8. **No implementation details in planning** — those go in doing doc
|
|
373
|
+
9. **STOP at each gate** — wait for human approval
|
|
374
|
+
10. **Keep planning doc** — conversion creates new file
|
|
375
|
+
11. **Auto-commit after every doc edit** — audit trail
|
|
376
|
+
12. **Get timestamps from git** — `git log -1 --format="%Y-%m-%d %H:%M"`
|
|
377
|
+
13. **When user approves** — update doc Status field, commit, log it
|
|
378
|
+
14. **Template compliance on resume** — check and offer to fix violations
|
|
379
|
+
15. **Status flags drive flow**:
|
|
363
380
|
- `drafting` → working on it
|
|
364
381
|
- `NEEDS_REVIEW` → waiting for human
|
|
365
382
|
- `approved` / `READY_FOR_EXECUTION` → can proceed
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
383
|
+
16. **TDD is mandatory** — tests before implementation, always
|
|
384
|
+
17. **100% coverage** — no exceptions, no exclude attributes
|
|
385
|
+
18. **Every unit header starts with emoji** — `### ⬜ Unit X:` format required
|
|
386
|
+
19. **NEVER do implementation** — work-planner creates docs only, work-doer executes
|
|
387
|
+
20. **Migration/deprecation**: Full content mapping required — never lose information
|
|
388
|
+
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.
|
|
389
|
+
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.
|
|
390
|
+
23. **Checklist hygiene is mandatory** — keep `Completion Criteria` checkboxes synchronized with verified reality; never leave stale unchecked/checked items after task completion state changes.
|