@ouro.bot/cli 0.1.0-alpha.17 → 0.1.0-alpha.170
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 +3 -2
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +3 -3
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +187 -191
- package/changelog.json +1145 -0
- package/dist/heart/active-work.js +633 -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/commitments.js +105 -0
- package/dist/heart/config.js +40 -30
- package/dist/heart/core.js +608 -123
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/auth-flow.js +477 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/daemon-cli.js +2473 -206
- package/dist/heart/daemon/daemon-entry.js +140 -4
- package/dist/heart/daemon/daemon-health.js +136 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon-tombstone.js +123 -0
- package/dist/heart/daemon/daemon.js +446 -17
- package/dist/heart/daemon/habit-migration.js +181 -0
- package/dist/heart/daemon/habit-parser.js +119 -0
- package/dist/heart/daemon/habit-scheduler.js +369 -0
- package/dist/heart/daemon/hatch-animation.js +10 -3
- package/dist/heart/daemon/hatch-flow.js +30 -111
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/launchd.js +161 -0
- package/dist/heart/daemon/log-tailer.js +4 -3
- package/dist/heart/daemon/mcp-server.js +591 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-path-installer.js +78 -34
- package/dist/heart/daemon/ouro-version-manager.js +171 -0
- package/dist/heart/daemon/outlook-http.js +399 -0
- package/dist/heart/daemon/outlook-read.js +1291 -0
- package/dist/heart/daemon/outlook-render.js +1032 -0
- package/dist/heart/daemon/outlook-types.js +7 -0
- package/dist/heart/daemon/outlook-view.js +194 -0
- package/dist/heart/daemon/process-manager.js +75 -0
- package/dist/heart/daemon/run-hooks.js +39 -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/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +50 -2
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +202 -0
- package/dist/heart/daemon/specialist-orchestrator.js +2 -2
- package/dist/heart/daemon/specialist-prompt.js +13 -8
- package/dist/heart/daemon/specialist-tools.js +53 -4
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +507 -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 +136 -55
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/migrate-config.js +127 -0
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/obligations.js +197 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +88 -0
- package/dist/heart/provider-ping.js +172 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +196 -36
- package/dist/heart/providers/azure.js +116 -10
- package/dist/heart/providers/github-copilot.js +157 -0
- package/dist/heart/providers/minimax.js +33 -3
- package/dist/heart/providers/openai-codex.js +51 -16
- package/dist/heart/session-activity.js +172 -0
- package/dist/heart/session-recall.js +216 -0
- package/dist/heart/streaming.js +62 -32
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +122 -32
- package/dist/mind/bundle-manifest.js +14 -0
- package/dist/mind/context.js +125 -14
- package/dist/mind/{memory.js → diary.js} +70 -56
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +56 -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/journal-index.js +161 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/obligations.js +134 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +649 -89
- 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/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +63 -3
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-CBueh0d3.css +1 -0
- package/dist/outlook-ui/assets/index-GtHoFpRM.js +61 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-semantic.js +6 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +254 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +149 -2
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +135 -2
- package/dist/repertoire/guardrails.js +291 -0
- package/dist/repertoire/mcp-client.js +254 -0
- package/dist/repertoire/mcp-manager.js +198 -0
- package/dist/repertoire/skills.js +14 -23
- package/dist/repertoire/tasks/board.js +43 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +28 -10
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +11 -28
- package/dist/repertoire/tools-base.js +991 -272
- package/dist/repertoire/tools-bluebubbles.js +94 -0
- package/dist/repertoire/tools-github.js +1 -6
- package/dist/repertoire/tools-teams.js +12 -59
- package/dist/repertoire/tools.js +281 -122
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +97 -0
- package/dist/senses/bluebubbles-client.js +102 -3
- package/dist/senses/bluebubbles-entry.js +2 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +118 -23
- 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 +1008 -102
- package/dist/senses/cli-entry.js +1 -1
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +419 -152
- package/dist/senses/commands.js +38 -1
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +112 -19
- package/dist/senses/inner-dialog.js +592 -84
- package/dist/senses/pipeline.js +502 -0
- package/dist/senses/shared-turn.js +196 -0
- package/dist/senses/surface-tool.js +95 -0
- package/dist/senses/teams.js +667 -120
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +13 -5
- package/skills/configure-dev-tools.md +81 -0
- package/subagents/README.md +4 -70
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/senses/debug-activity.js +0 -107
- package/subagents/work-doer.md +0 -235
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -382
|
@@ -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,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.170",
|
|
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
|
},
|
|
@@ -10,14 +11,17 @@
|
|
|
10
11
|
"dist/",
|
|
11
12
|
"AdoptionSpecialist.ouro/",
|
|
12
13
|
"subagents/",
|
|
13
|
-
"
|
|
14
|
+
"skills/",
|
|
15
|
+
"assets/",
|
|
16
|
+
"changelog.json"
|
|
14
17
|
],
|
|
15
18
|
"exports": {
|
|
16
19
|
".": "./dist/heart/daemon/daemon-cli.js",
|
|
17
20
|
"./runOuroCli": "./dist/heart/daemon/daemon-cli.js"
|
|
18
21
|
},
|
|
19
22
|
"scripts": {
|
|
20
|
-
"dev": "tsc && node dist/
|
|
23
|
+
"dev": "tsc && node dist/heart/daemon/ouro-bot-entry.js dev",
|
|
24
|
+
"cli": "tsc && node dist/senses/cli-entry.js",
|
|
21
25
|
"daemon": "tsc && node dist/heart/daemon/daemon-entry.js",
|
|
22
26
|
"ouro": "tsc && node dist/heart/daemon/ouro-entry.js",
|
|
23
27
|
"teams": "tsc && node dist/senses/teams-entry.js --agent ouroboros",
|
|
@@ -25,21 +29,25 @@
|
|
|
25
29
|
"test": "vitest run",
|
|
26
30
|
"test:coverage:vitest": "vitest run --coverage",
|
|
27
31
|
"test:coverage": "node scripts/run-coverage-gate.cjs",
|
|
28
|
-
"build": "tsc",
|
|
32
|
+
"build": "tsc && (cd packages/outlook-ui && npm install --ignore-scripts 2>/dev/null && npm run build && cp -r dist ../../dist/outlook-ui) || echo 'outlook-ui build skipped'",
|
|
29
33
|
"lint": "eslint src/",
|
|
30
34
|
"audit:nerves": "npm run build && node dist/nerves/coverage/cli-main.js"
|
|
31
35
|
},
|
|
32
36
|
"dependencies": {
|
|
33
37
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
38
|
+
"@azure/identity": "^4.13.0",
|
|
34
39
|
"@microsoft/teams.apps": "^2.0.5",
|
|
35
40
|
"@microsoft/teams.dev": "^2.0.5",
|
|
36
|
-
"
|
|
41
|
+
"fast-glob": "^3.3.3",
|
|
42
|
+
"openai": "^6.27.0",
|
|
43
|
+
"semver": "^7.7.4"
|
|
37
44
|
},
|
|
38
45
|
"repository": {
|
|
39
46
|
"type": "git",
|
|
40
47
|
"url": "https://github.com/ouroborosbot/ouroboros"
|
|
41
48
|
},
|
|
42
49
|
"devDependencies": {
|
|
50
|
+
"@types/semver": "^7.7.1",
|
|
43
51
|
"@vitest/coverage-v8": "^4.0.18",
|
|
44
52
|
"eslint": "^10.0.2",
|
|
45
53
|
"typescript": "^5.7.0",
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Configure Dev Tools for MCP Agent Bridge
|
|
2
|
+
|
|
3
|
+
Set up your development tools (Claude Code, Codex) to communicate with Ouroboros agents via MCP. One command does everything.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
### Claude Code
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
ouro setup --tool claude-code --agent <agent-name>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This command:
|
|
14
|
+
1. Registers the MCP server with Claude Code via `claude mcp add`
|
|
15
|
+
2. Configures lifecycle hooks (SessionStart, Stop, PostToolUse) for passive awareness
|
|
16
|
+
3. Detects dev vs installed mode automatically and uses the correct command path
|
|
17
|
+
|
|
18
|
+
### Codex
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
ouro setup --tool codex --agent <agent-name>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This command:
|
|
25
|
+
1. Registers the MCP server with Codex via `codex mcp add`
|
|
26
|
+
2. Detects dev vs installed mode automatically
|
|
27
|
+
|
|
28
|
+
## Verification
|
|
29
|
+
|
|
30
|
+
After setup, verify the connection:
|
|
31
|
+
|
|
32
|
+
1. **Check daemon is running**: `ouro up` (installed) or `ouro dev --repo-path <path>` (dev mode).
|
|
33
|
+
2. **Test from Claude Code**: Start a new session and use the `status` tool.
|
|
34
|
+
3. **Test from Codex**: Run `codex exec "Use the <agent-name> status tool"`.
|
|
35
|
+
4. **Check registration**: `claude mcp list` or `codex mcp list`.
|
|
36
|
+
|
|
37
|
+
## Available MCP Tools
|
|
38
|
+
|
|
39
|
+
Once connected, these tools are available:
|
|
40
|
+
|
|
41
|
+
### Conversation tools (new)
|
|
42
|
+
- **send_message** -- Send a message and get a synchronous agent response (full turn with tools)
|
|
43
|
+
- **check_response** -- Check for pending messages from the agent (after ponder or proactive surface)
|
|
44
|
+
|
|
45
|
+
### Read-only tools
|
|
46
|
+
- **ask** -- Ask the agent a question (uses memory and context)
|
|
47
|
+
- **status** -- Get agent's current status and activity
|
|
48
|
+
- **catchup** -- Get recent activity summary
|
|
49
|
+
- **get_context** -- Get agent's current working context
|
|
50
|
+
- **search_memory** -- Search agent's memory for specific topics
|
|
51
|
+
- **get_task** -- Get details of the agent's current task
|
|
52
|
+
- **check_scope** -- Verify if something is in scope for current work
|
|
53
|
+
- **check_guidance** -- Get guidance on how to approach something
|
|
54
|
+
|
|
55
|
+
### Write tools
|
|
56
|
+
- **delegate** -- Request the agent to handle a task (runs full conversation turn)
|
|
57
|
+
- **request_decision** -- Ask agent to make a decision about something
|
|
58
|
+
- **report_progress** -- Report progress on delegated work
|
|
59
|
+
- **report_blocker** -- Report a blocker on delegated work
|
|
60
|
+
- **report_complete** -- Report completion of delegated work
|
|
61
|
+
|
|
62
|
+
## Troubleshooting
|
|
63
|
+
|
|
64
|
+
### "Daemon not running" error
|
|
65
|
+
Most read-only tools work without the daemon (reads filesystem directly). For write operations and `send_message`, start the daemon with `ouro up` or `ouro dev`.
|
|
66
|
+
|
|
67
|
+
### MCP server not appearing
|
|
68
|
+
- Run `claude mcp list` or `codex mcp list` to verify registration
|
|
69
|
+
- Re-run `ouro setup` to fix
|
|
70
|
+
- Restart your dev tool (MCP loads at session start)
|
|
71
|
+
|
|
72
|
+
### Connection timeouts
|
|
73
|
+
- Ensure `dist/` is built: `npm run build`
|
|
74
|
+
- Check that the entry point path is correct (setup auto-detects this)
|
|
75
|
+
|
|
76
|
+
### Removing
|
|
77
|
+
```bash
|
|
78
|
+
claude mcp remove ouro-<agent-name>
|
|
79
|
+
# or
|
|
80
|
+
codex mcp remove ouro-<agent-name>
|
|
81
|
+
```
|
package/subagents/README.md
CHANGED
|
@@ -1,73 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Workflow Skills (Moved)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The workflow skills (`work-planner`, `work-doer`, `work-merger`) have moved to the shared skills repository:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**https://github.com/ouroborosbot/ouroboros-skills**
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Copy or symlink these files into Claude's sub-agent directory:
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
# Claude Code
|
|
13
|
-
cp subagents/*.md ~/.claude/agents/
|
|
14
|
-
# or
|
|
15
|
-
ln -s "$(pwd)"/subagents/*.md ~/.claude/agents/
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
### Codex / skill-based harnesses
|
|
19
|
-
|
|
20
|
-
For tools that support skills but not Claude sub-agents, install these as skills:
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
mkdir -p ~/.codex/skills/work-planner ~/.codex/skills/work-doer ~/.codex/skills/work-merger
|
|
24
|
-
|
|
25
|
-
# Hard-link to keep one source of truth
|
|
26
|
-
ln -f "$(pwd)/subagents/work-planner.md" ~/.codex/skills/work-planner/SKILL.md
|
|
27
|
-
ln -f "$(pwd)/subagents/work-doer.md" ~/.codex/skills/work-doer/SKILL.md
|
|
28
|
-
ln -f "$(pwd)/subagents/work-merger.md" ~/.codex/skills/work-merger/SKILL.md
|
|
29
|
-
```
|
|
30
|
-
|
|
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.
|
|
32
|
-
|
|
33
|
-
Optional UI metadata:
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
mkdir -p ~/.codex/skills/work-planner/agents ~/.codex/skills/work-doer/agents ~/.codex/skills/work-merger/agents
|
|
37
|
-
cat > ~/.codex/skills/work-planner/agents/openai.yaml << 'EOF'
|
|
38
|
-
interface:
|
|
39
|
-
display_name: "Work Planner"
|
|
40
|
-
short_description: "Create and gate planning/doing task docs"
|
|
41
|
-
default_prompt: "Use $work-planner to create or update a planning doc, then stop at NEEDS_REVIEW."
|
|
42
|
-
EOF
|
|
43
|
-
cat > ~/.codex/skills/work-doer/agents/openai.yaml << 'EOF'
|
|
44
|
-
interface:
|
|
45
|
-
display_name: "Work Doer"
|
|
46
|
-
short_description: "Execute approved doing docs with strict TDD"
|
|
47
|
-
default_prompt: "Use $work-doer to execute an approved doing doc unit by unit."
|
|
48
|
-
EOF
|
|
49
|
-
cat > ~/.codex/skills/work-merger/agents/openai.yaml << 'EOF'
|
|
50
|
-
interface:
|
|
51
|
-
display_name: "Work Merger"
|
|
52
|
-
short_description: "Merge feature branch into main via PR after work-doer completes"
|
|
53
|
-
default_prompt: "Use $work-merger to merge the current feature branch into main."
|
|
54
|
-
EOF
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Restart the harness after install so new skills are discovered.
|
|
58
|
-
|
|
59
|
-
## Available sub-agents
|
|
60
|
-
|
|
61
|
-
| File | Purpose |
|
|
62
|
-
|------|---------|
|
|
63
|
-
| `work-planner.md` | Interactive task planner. Generates planning docs through conversation, then converts to doing docs after human approval. |
|
|
64
|
-
| `work-doer.md` | Task executor. Reads a doing doc and works through each unit sequentially with strict TDD. |
|
|
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. |
|
|
66
|
-
|
|
67
|
-
## Workflow
|
|
68
|
-
|
|
69
|
-
1. Human describes a task
|
|
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)
|
|
7
|
+
To install or update these skills, use the **skill-management** skill from that repo. It covers browsing available skills, installing them into your agent's local skills directory, and keeping them up to date.
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.installSubagentsForAvailableCli = installSubagentsForAvailableCli;
|
|
37
|
-
const fs = __importStar(require("fs"));
|
|
38
|
-
const os = __importStar(require("os"));
|
|
39
|
-
const path = __importStar(require("path"));
|
|
40
|
-
const child_process_1 = require("child_process");
|
|
41
|
-
const runtime_1 = require("../../nerves/runtime");
|
|
42
|
-
function detectCliBinary(binary) {
|
|
43
|
-
const result = (0, child_process_1.spawnSync)("which", [binary], { encoding: "utf-8" });
|
|
44
|
-
if (result.status !== 0)
|
|
45
|
-
return null;
|
|
46
|
-
const resolved = result.stdout.trim();
|
|
47
|
-
return resolved.length > 0 ? resolved : null;
|
|
48
|
-
}
|
|
49
|
-
function listSubagentSources(subagentsDir) {
|
|
50
|
-
if (!fs.existsSync(subagentsDir))
|
|
51
|
-
return [];
|
|
52
|
-
return fs.readdirSync(subagentsDir)
|
|
53
|
-
.filter((name) => name.endsWith(".md"))
|
|
54
|
-
.filter((name) => name !== "README.md")
|
|
55
|
-
.map((name) => path.join(subagentsDir, name))
|
|
56
|
-
.sort((a, b) => a.localeCompare(b));
|
|
57
|
-
}
|
|
58
|
-
function pathExists(target) {
|
|
59
|
-
try {
|
|
60
|
-
fs.lstatSync(target);
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function ensureSymlink(source, target) {
|
|
68
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
69
|
-
if (pathExists(target)) {
|
|
70
|
-
const stats = fs.lstatSync(target);
|
|
71
|
-
if (stats.isSymbolicLink()) {
|
|
72
|
-
const linkedPath = fs.readlinkSync(target);
|
|
73
|
-
if (linkedPath === source)
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
fs.unlinkSync(target);
|
|
77
|
-
}
|
|
78
|
-
fs.symlinkSync(source, target);
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
81
|
-
async function installSubagentsForAvailableCli(options = {}) {
|
|
82
|
-
const repoRoot = options.repoRoot ?? path.resolve(__dirname, "..", "..", "..");
|
|
83
|
-
const homeDir = options.homeDir ?? os.homedir();
|
|
84
|
-
const which = options.which ?? detectCliBinary;
|
|
85
|
-
const subagentsDir = path.join(repoRoot, "subagents");
|
|
86
|
-
const sources = listSubagentSources(subagentsDir);
|
|
87
|
-
const notes = [];
|
|
88
|
-
(0, runtime_1.emitNervesEvent)({
|
|
89
|
-
component: "daemon",
|
|
90
|
-
event: "daemon.subagent_install_start",
|
|
91
|
-
message: "starting subagent auto-install",
|
|
92
|
-
meta: { sources: sources.length },
|
|
93
|
-
});
|
|
94
|
-
if (sources.length === 0) {
|
|
95
|
-
notes.push(`no subagent files found at ${subagentsDir}`);
|
|
96
|
-
return { claudeInstalled: 0, codexInstalled: 0, notes };
|
|
97
|
-
}
|
|
98
|
-
let claudeInstalled = 0;
|
|
99
|
-
let codexInstalled = 0;
|
|
100
|
-
const claudePath = which("claude");
|
|
101
|
-
if (!claudePath) {
|
|
102
|
-
notes.push("claude CLI not found; skipping subagent install");
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
const claudeAgentsDir = path.join(homeDir, ".claude", "agents");
|
|
106
|
-
for (const source of sources) {
|
|
107
|
-
const target = path.join(claudeAgentsDir, path.basename(source));
|
|
108
|
-
if (ensureSymlink(source, target)) {
|
|
109
|
-
claudeInstalled += 1;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
const codexPath = which("codex");
|
|
114
|
-
if (!codexPath) {
|
|
115
|
-
notes.push("codex CLI not found; skipping subagent install");
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
const codexSkillsDir = path.join(homeDir, ".codex", "skills");
|
|
119
|
-
for (const source of sources) {
|
|
120
|
-
const skillName = path.basename(source, ".md");
|
|
121
|
-
const target = path.join(codexSkillsDir, skillName, "SKILL.md");
|
|
122
|
-
if (ensureSymlink(source, target)) {
|
|
123
|
-
codexInstalled += 1;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
(0, runtime_1.emitNervesEvent)({
|
|
128
|
-
component: "daemon",
|
|
129
|
-
event: "daemon.subagent_install_end",
|
|
130
|
-
message: "completed subagent auto-install",
|
|
131
|
-
meta: { claudeInstalled, codexInstalled, notes: notes.length },
|
|
132
|
-
});
|
|
133
|
-
return { claudeInstalled, codexInstalled, notes };
|
|
134
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
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
|
-
}
|