@ouro.bot/cli 0.1.0-alpha.139 → 0.1.0-alpha.140
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/changelog.json +12 -0
- package/dist/heart/daemon/agent-service.js +467 -0
- package/dist/heart/daemon/daemon-cli.js +54 -0
- package/dist/heart/daemon/daemon.js +27 -0
- package/dist/heart/daemon/mcp-server.js +464 -0
- package/dist/mind/prompt.js +2 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/spawner.js +20 -3
- package/dist/repertoire/skills.js +17 -3
- package/package.json +2 -1
- package/skills/configure-dev-tools.md +109 -0
|
@@ -0,0 +1,464 @@
|
|
|
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.createMcpServer = createMcpServer;
|
|
37
|
+
exports.getToolSchemas = getToolSchemas;
|
|
38
|
+
const socket_client_1 = require("./socket-client");
|
|
39
|
+
const agentService = __importStar(require("./agent-service"));
|
|
40
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
/**
|
|
42
|
+
* Maps MCP tool names to daemon command kinds.
|
|
43
|
+
*/
|
|
44
|
+
const TOOL_TO_COMMAND = {
|
|
45
|
+
ask: "agent.ask",
|
|
46
|
+
status: "agent.status",
|
|
47
|
+
catchup: "agent.catchup",
|
|
48
|
+
delegate: "agent.delegate",
|
|
49
|
+
get_context: "agent.getContext",
|
|
50
|
+
search_memory: "agent.searchMemory",
|
|
51
|
+
get_task: "agent.getTask",
|
|
52
|
+
check_scope: "agent.checkScope",
|
|
53
|
+
request_decision: "agent.requestDecision",
|
|
54
|
+
check_guidance: "agent.checkGuidance",
|
|
55
|
+
report_progress: "agent.reportProgress",
|
|
56
|
+
report_blocker: "agent.reportBlocker",
|
|
57
|
+
report_complete: "agent.reportComplete",
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Create an MCP server that speaks JSON-RPC 2.0 over stdio.
|
|
61
|
+
* Handles initialize, initialized, tools/list, and tools/call.
|
|
62
|
+
* Forwards tool calls to the daemon via Unix socket.
|
|
63
|
+
*/
|
|
64
|
+
function createMcpServer(options) {
|
|
65
|
+
const { agent, friendId, socketPath, stdin, stdout } = options;
|
|
66
|
+
let buffer = "";
|
|
67
|
+
let running = false;
|
|
68
|
+
let useContentLengthFraming = true; // default to Content-Length, auto-detect from first message
|
|
69
|
+
function writeResponse(response) {
|
|
70
|
+
const body = JSON.stringify(response);
|
|
71
|
+
if (useContentLengthFraming) {
|
|
72
|
+
const header = `Content-Length: ${Buffer.byteLength(body)}\r\n\r\n`;
|
|
73
|
+
stdout.write(header + body);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
stdout.write(body + "\n");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function tryParseContentLength() {
|
|
80
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
81
|
+
/* v8 ignore start -- partial header delivery only in real I/O */
|
|
82
|
+
if (headerEnd === -1)
|
|
83
|
+
return false;
|
|
84
|
+
/* v8 ignore stop */
|
|
85
|
+
const headerSection = buffer.slice(0, headerEnd);
|
|
86
|
+
const contentLengthMatch = headerSection.match(/Content-Length:\s*(\d+)/i);
|
|
87
|
+
if (!contentLengthMatch) {
|
|
88
|
+
buffer = buffer.slice(headerEnd + 4);
|
|
89
|
+
return true; // consumed invalid header, try again
|
|
90
|
+
}
|
|
91
|
+
const contentLength = parseInt(contentLengthMatch[1], 10);
|
|
92
|
+
const bodyStart = headerEnd + 4;
|
|
93
|
+
/* v8 ignore start -- partial body delivery only in real I/O */
|
|
94
|
+
if (buffer.length < bodyStart + contentLength)
|
|
95
|
+
return false;
|
|
96
|
+
/* v8 ignore stop */
|
|
97
|
+
const body = buffer.slice(bodyStart, bodyStart + contentLength);
|
|
98
|
+
buffer = buffer.slice(bodyStart + contentLength);
|
|
99
|
+
parseAndDispatch(body);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
function tryParseNewlineDelimited() {
|
|
103
|
+
const newlineIdx = buffer.indexOf("\n");
|
|
104
|
+
/* v8 ignore start -- partial line delivery only in real I/O */
|
|
105
|
+
if (newlineIdx === -1)
|
|
106
|
+
return false;
|
|
107
|
+
/* v8 ignore stop */
|
|
108
|
+
const line = buffer.slice(0, newlineIdx).trim();
|
|
109
|
+
buffer = buffer.slice(newlineIdx + 1);
|
|
110
|
+
if (line.length === 0)
|
|
111
|
+
return true; // skip blank lines
|
|
112
|
+
parseAndDispatch(line);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
function parseAndDispatch(body) {
|
|
116
|
+
let request;
|
|
117
|
+
try {
|
|
118
|
+
request = JSON.parse(body);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
writeResponse({
|
|
122
|
+
jsonrpc: "2.0",
|
|
123
|
+
id: null,
|
|
124
|
+
error: { code: -32700, message: "Parse error" },
|
|
125
|
+
});
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
void handleRequest(request);
|
|
129
|
+
}
|
|
130
|
+
let framingDetected = false;
|
|
131
|
+
function handleData(chunk) {
|
|
132
|
+
buffer += chunk.toString("utf-8");
|
|
133
|
+
// Auto-detect framing from first message and mirror it in responses
|
|
134
|
+
if (!framingDetected && buffer.length > 0) {
|
|
135
|
+
useContentLengthFraming = buffer.startsWith("Content-Length:");
|
|
136
|
+
framingDetected = true;
|
|
137
|
+
}
|
|
138
|
+
// Support both Content-Length framing (Claude Code) and newline-delimited JSON (Codex)
|
|
139
|
+
while (buffer.length > 0) {
|
|
140
|
+
const hasContentLength = buffer.startsWith("Content-Length:");
|
|
141
|
+
const parsed = hasContentLength ? tryParseContentLength() : tryParseNewlineDelimited();
|
|
142
|
+
/* v8 ignore start -- break on partial message only in real I/O */
|
|
143
|
+
if (!parsed)
|
|
144
|
+
break;
|
|
145
|
+
/* v8 ignore stop */
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async function handleRequest(request) {
|
|
149
|
+
(0, runtime_1.emitNervesEvent)({
|
|
150
|
+
component: "daemon",
|
|
151
|
+
event: "daemon.mcp_request_start",
|
|
152
|
+
message: "handling MCP request",
|
|
153
|
+
meta: { method: request.method, agent },
|
|
154
|
+
});
|
|
155
|
+
// Notifications (no id) don't get responses
|
|
156
|
+
if (request.id === undefined) {
|
|
157
|
+
(0, runtime_1.emitNervesEvent)({
|
|
158
|
+
component: "daemon",
|
|
159
|
+
event: "daemon.mcp_request_end",
|
|
160
|
+
message: "handled MCP notification",
|
|
161
|
+
meta: { method: request.method, agent },
|
|
162
|
+
});
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
switch (request.method) {
|
|
166
|
+
case "initialize":
|
|
167
|
+
await handleInitialize(request);
|
|
168
|
+
break;
|
|
169
|
+
case "tools/list":
|
|
170
|
+
handleToolsList(request);
|
|
171
|
+
break;
|
|
172
|
+
case "tools/call":
|
|
173
|
+
await handleToolsCall(request);
|
|
174
|
+
break;
|
|
175
|
+
default:
|
|
176
|
+
writeResponse({
|
|
177
|
+
jsonrpc: "2.0",
|
|
178
|
+
id: request.id,
|
|
179
|
+
error: {
|
|
180
|
+
code: -32601,
|
|
181
|
+
message: `Method not found: ${request.method}`,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
(0, runtime_1.emitNervesEvent)({
|
|
187
|
+
component: "daemon",
|
|
188
|
+
event: "daemon.mcp_request_end",
|
|
189
|
+
message: "completed MCP request",
|
|
190
|
+
meta: { method: request.method, agent },
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async function handleInitialize(request) {
|
|
194
|
+
// MCP server works standalone (agent-service reads filesystem directly)
|
|
195
|
+
// Daemon is optional — only needed for commands without a direct service handler
|
|
196
|
+
writeResponse({
|
|
197
|
+
jsonrpc: "2.0",
|
|
198
|
+
id: request.id,
|
|
199
|
+
result: {
|
|
200
|
+
protocolVersion: "2024-11-05",
|
|
201
|
+
serverInfo: {
|
|
202
|
+
name: "ouro-mcp-server",
|
|
203
|
+
version: "0.1.0",
|
|
204
|
+
},
|
|
205
|
+
capabilities: {
|
|
206
|
+
tools: { listChanged: false },
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function handleToolsList(request) {
|
|
212
|
+
const tools = getToolSchemas();
|
|
213
|
+
writeResponse({
|
|
214
|
+
jsonrpc: "2.0",
|
|
215
|
+
id: request.id,
|
|
216
|
+
result: { tools },
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
/** Map tool name → agent-service handler function name */
|
|
220
|
+
const TOOL_TO_SERVICE = {
|
|
221
|
+
ask: "handleAgentAsk",
|
|
222
|
+
status: "handleAgentStatus",
|
|
223
|
+
catchup: "handleAgentCatchup",
|
|
224
|
+
delegate: "handleAgentDelegate",
|
|
225
|
+
get_context: "handleAgentGetContext",
|
|
226
|
+
search_memory: "handleAgentSearchMemory",
|
|
227
|
+
get_task: "handleAgentGetTask",
|
|
228
|
+
check_scope: "handleAgentCheckScope",
|
|
229
|
+
request_decision: "handleAgentRequestDecision",
|
|
230
|
+
check_guidance: "handleAgentCheckGuidance",
|
|
231
|
+
report_progress: "handleAgentReportProgress",
|
|
232
|
+
report_blocker: "handleAgentReportBlocker",
|
|
233
|
+
report_complete: "handleAgentReportComplete",
|
|
234
|
+
};
|
|
235
|
+
async function handleToolsCall(request) {
|
|
236
|
+
/* v8 ignore start — ?? fallbacks are defensive; MCP clients always send params */
|
|
237
|
+
const params = request.params ?? {};
|
|
238
|
+
const toolName = params.name;
|
|
239
|
+
const toolArgs = (params.arguments ?? {});
|
|
240
|
+
/* v8 ignore stop */
|
|
241
|
+
const commandKind = TOOL_TO_COMMAND[toolName];
|
|
242
|
+
if (!commandKind) {
|
|
243
|
+
writeResponse({
|
|
244
|
+
jsonrpc: "2.0",
|
|
245
|
+
id: request.id,
|
|
246
|
+
result: {
|
|
247
|
+
content: [{ type: "text", text: `Unknown tool: ${toolName}` }],
|
|
248
|
+
isError: true,
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
// Call agent-service directly (no daemon roundtrip needed for read-only ops)
|
|
254
|
+
const serviceHandler = TOOL_TO_SERVICE[toolName];
|
|
255
|
+
let response;
|
|
256
|
+
/* v8 ignore start — typeof guard always true; instanceof check defensive; else branch unreachable for known tools */
|
|
257
|
+
if (serviceHandler && typeof agentService[serviceHandler] === "function") {
|
|
258
|
+
const handlerFn = agentService[serviceHandler];
|
|
259
|
+
try {
|
|
260
|
+
response = await handlerFn({ agent, friendId, ...toolArgs });
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
264
|
+
response = { ok: false, error: `Service error: ${errorMessage}` };
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
try {
|
|
269
|
+
response = await (0, socket_client_1.sendDaemonCommand)(socketPath, {
|
|
270
|
+
kind: commandKind, agent, friendId, ...toolArgs,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
275
|
+
response = { ok: false, error: `Daemon error: ${errorMessage}` };
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/* v8 ignore stop */
|
|
279
|
+
const text = response.message
|
|
280
|
+
?? response.summary
|
|
281
|
+
?? JSON.stringify(response.data ?? { ok: response.ok });
|
|
282
|
+
writeResponse({
|
|
283
|
+
jsonrpc: "2.0",
|
|
284
|
+
id: request.id,
|
|
285
|
+
result: {
|
|
286
|
+
content: [{ type: "text", text }],
|
|
287
|
+
isError: !response.ok,
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
function onData(chunk) {
|
|
292
|
+
handleData(chunk);
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
agent,
|
|
296
|
+
friendId,
|
|
297
|
+
start() {
|
|
298
|
+
if (running)
|
|
299
|
+
return;
|
|
300
|
+
running = true;
|
|
301
|
+
stdin.on("data", onData);
|
|
302
|
+
(0, runtime_1.emitNervesEvent)({
|
|
303
|
+
component: "daemon",
|
|
304
|
+
event: "daemon.mcp_server_start",
|
|
305
|
+
message: "MCP server started",
|
|
306
|
+
meta: { agent, friendId, socketPath },
|
|
307
|
+
});
|
|
308
|
+
},
|
|
309
|
+
stop() {
|
|
310
|
+
if (!running)
|
|
311
|
+
return;
|
|
312
|
+
running = false;
|
|
313
|
+
stdin.removeListener("data", onData);
|
|
314
|
+
(0, runtime_1.emitNervesEvent)({
|
|
315
|
+
component: "daemon",
|
|
316
|
+
event: "daemon.mcp_server_stop",
|
|
317
|
+
message: "MCP server stopped",
|
|
318
|
+
meta: { agent, friendId },
|
|
319
|
+
});
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Returns the list of MCP tool schemas for all 13 agent tools.
|
|
325
|
+
* Each schema follows JSON Schema for inputSchema as required by MCP.
|
|
326
|
+
*/
|
|
327
|
+
function getToolSchemas() {
|
|
328
|
+
return [
|
|
329
|
+
{
|
|
330
|
+
name: "ask",
|
|
331
|
+
description: "Ask the agent a question. The agent uses its memory and recent session context to provide a useful answer.",
|
|
332
|
+
inputSchema: {
|
|
333
|
+
type: "object",
|
|
334
|
+
properties: {
|
|
335
|
+
question: { type: "string", description: "The question to ask the agent" },
|
|
336
|
+
},
|
|
337
|
+
required: ["question"],
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
name: "status",
|
|
342
|
+
description: "Get the agent's current status including active sessions, memory state, and activity level.",
|
|
343
|
+
inputSchema: {
|
|
344
|
+
type: "object",
|
|
345
|
+
properties: {},
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
name: "catchup",
|
|
350
|
+
description: "Get a summary of the agent's recent activity including recent sessions and what it has been working on.",
|
|
351
|
+
inputSchema: {
|
|
352
|
+
type: "object",
|
|
353
|
+
properties: {},
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: "delegate",
|
|
358
|
+
description: "Request the agent to handle a task. The agent queues the task and will work on it when available.",
|
|
359
|
+
inputSchema: {
|
|
360
|
+
type: "object",
|
|
361
|
+
properties: {
|
|
362
|
+
task: { type: "string", description: "Description of the task to delegate" },
|
|
363
|
+
context: { type: "string", description: "Additional context about the task" },
|
|
364
|
+
},
|
|
365
|
+
required: ["task"],
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
name: "get_context",
|
|
370
|
+
description: "Get the agent's current working context including memory summary, active tasks, and relevant state.",
|
|
371
|
+
inputSchema: {
|
|
372
|
+
type: "object",
|
|
373
|
+
properties: {},
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
name: "search_memory",
|
|
378
|
+
description: "Search the agent's memory for information about a specific topic. Returns matching lines from the agent's memory file.",
|
|
379
|
+
inputSchema: {
|
|
380
|
+
type: "object",
|
|
381
|
+
properties: {
|
|
382
|
+
query: { type: "string", description: "Search term to look for in agent memory" },
|
|
383
|
+
},
|
|
384
|
+
required: ["query"],
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
name: "get_task",
|
|
389
|
+
description: "Get details about the agent's current task or list of active tasks.",
|
|
390
|
+
inputSchema: {
|
|
391
|
+
type: "object",
|
|
392
|
+
properties: {},
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
name: "check_scope",
|
|
397
|
+
description: "Check whether a proposed item or change is in scope for the agent's current work.",
|
|
398
|
+
inputSchema: {
|
|
399
|
+
type: "object",
|
|
400
|
+
properties: {
|
|
401
|
+
item: { type: "string", description: "The item or change to check scope for" },
|
|
402
|
+
},
|
|
403
|
+
required: ["item"],
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
name: "request_decision",
|
|
408
|
+
description: "Ask the agent to make a decision about a topic. Optionally provide a list of options to choose from.",
|
|
409
|
+
inputSchema: {
|
|
410
|
+
type: "object",
|
|
411
|
+
properties: {
|
|
412
|
+
topic: { type: "string", description: "The topic requiring a decision" },
|
|
413
|
+
options: { type: "string", description: "Comma-separated list of options to consider" },
|
|
414
|
+
},
|
|
415
|
+
required: ["topic"],
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: "check_guidance",
|
|
420
|
+
description: "Get guidance from the agent on how to approach a topic. The agent searches its memory for relevant guidance.",
|
|
421
|
+
inputSchema: {
|
|
422
|
+
type: "object",
|
|
423
|
+
properties: {
|
|
424
|
+
topic: { type: "string", description: "The topic to get guidance on" },
|
|
425
|
+
},
|
|
426
|
+
required: ["topic"],
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
name: "report_progress",
|
|
431
|
+
description: "Report progress on delegated work back to the agent. The agent records the update.",
|
|
432
|
+
inputSchema: {
|
|
433
|
+
type: "object",
|
|
434
|
+
properties: {
|
|
435
|
+
summary: { type: "string", description: "Summary of progress made" },
|
|
436
|
+
},
|
|
437
|
+
required: ["summary"],
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
name: "report_blocker",
|
|
442
|
+
description: "Report a blocker on delegated work to the agent. The agent records the blocker for review.",
|
|
443
|
+
inputSchema: {
|
|
444
|
+
type: "object",
|
|
445
|
+
properties: {
|
|
446
|
+
blocker: { type: "string", description: "Description of the blocker" },
|
|
447
|
+
},
|
|
448
|
+
required: ["blocker"],
|
|
449
|
+
},
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
name: "report_complete",
|
|
453
|
+
description: "Report completion of delegated work to the agent. The agent records the completion.",
|
|
454
|
+
inputSchema: {
|
|
455
|
+
type: "object",
|
|
456
|
+
properties: {
|
|
457
|
+
summary: { type: "string", description: "Summary of what was completed" },
|
|
458
|
+
},
|
|
459
|
+
required: ["summary"],
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
];
|
|
463
|
+
}
|
|
464
|
+
// MCP server v0.1.0-alpha.140
|
package/dist/mind/prompt.js
CHANGED
|
@@ -198,6 +198,7 @@ my bones give me the \`ouro\` cli. always pass \`--agent ${agentName}\`:
|
|
|
198
198
|
ouro auth switch --agent ${agentName} --provider <provider>
|
|
199
199
|
ouro mcp list --agent ${agentName}
|
|
200
200
|
ouro mcp call --agent ${agentName} <server> <tool> --args '{...}'
|
|
201
|
+
ouro mcp-serve --agent ${agentName}
|
|
201
202
|
ouro versions --agent ${agentName}
|
|
202
203
|
ouro rollback --agent ${agentName} [<version>]
|
|
203
204
|
ouro --help
|
|
@@ -274,6 +275,7 @@ function runtimeInfoSection(channel) {
|
|
|
274
275
|
lines.push(`current sense: ${channel}`);
|
|
275
276
|
lines.push(`process type: ${processTypeLabel(channel)}`);
|
|
276
277
|
lines.push(`daemon: ${daemonStatus()}`);
|
|
278
|
+
lines.push(`mcp serve: i can expose my tools to dev tools via \`ouro mcp-serve\`. see the configure-dev-tools skill for setup.`);
|
|
277
279
|
if (channel === "cli") {
|
|
278
280
|
lines.push("i introduce myself on boot with a fun random greeting.");
|
|
279
281
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Codex JSONL event parser.
|
|
4
|
+
* Parses typed events from Codex --json output:
|
|
5
|
+
* thread.started, turn.started, turn.completed, item.completed.
|
|
6
|
+
* Maps events to coding session status transitions.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.parseCodexJsonlEvent = parseCodexJsonlEvent;
|
|
10
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
11
|
+
const KNOWN_TYPES = new Set(["thread.started", "turn.started", "turn.completed", "item.completed"]);
|
|
12
|
+
/**
|
|
13
|
+
* Parse a single JSONL line from Codex --json output.
|
|
14
|
+
* Returns null for invalid JSON, empty strings, or unknown event types.
|
|
15
|
+
*/
|
|
16
|
+
function parseCodexJsonlEvent(line) {
|
|
17
|
+
const trimmed = line.trim();
|
|
18
|
+
if (trimmed.length === 0)
|
|
19
|
+
return null;
|
|
20
|
+
let parsed;
|
|
21
|
+
try {
|
|
22
|
+
parsed = JSON.parse(trimmed);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
(0, runtime_1.emitNervesEvent)({
|
|
26
|
+
component: "repertoire",
|
|
27
|
+
event: "repertoire.codex_jsonl_parse_error",
|
|
28
|
+
message: "failed to parse codex JSONL line",
|
|
29
|
+
meta: { line: trimmed.slice(0, 100) },
|
|
30
|
+
});
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const type = parsed.type;
|
|
34
|
+
if (!type || !KNOWN_TYPES.has(type))
|
|
35
|
+
return null;
|
|
36
|
+
const event = {
|
|
37
|
+
type: type,
|
|
38
|
+
threadId: typeof parsed.thread_id === "string" ? parsed.thread_id : undefined,
|
|
39
|
+
turnId: typeof parsed.turn_id === "string" ? parsed.turn_id : undefined,
|
|
40
|
+
item: typeof parsed.item === "object" && parsed.item !== null ? parsed.item : undefined,
|
|
41
|
+
raw: parsed,
|
|
42
|
+
statusHint: mapStatusHint(type),
|
|
43
|
+
};
|
|
44
|
+
(0, runtime_1.emitNervesEvent)({
|
|
45
|
+
component: "repertoire",
|
|
46
|
+
event: "repertoire.codex_jsonl_event",
|
|
47
|
+
message: "parsed codex JSONL event",
|
|
48
|
+
meta: { type, threadId: event.threadId ?? null },
|
|
49
|
+
});
|
|
50
|
+
return event;
|
|
51
|
+
}
|
|
52
|
+
function mapStatusHint(type) {
|
|
53
|
+
switch (type) {
|
|
54
|
+
case "thread.started":
|
|
55
|
+
case "turn.started":
|
|
56
|
+
return "running";
|
|
57
|
+
case "turn.completed":
|
|
58
|
+
case "item.completed":
|
|
59
|
+
return null; // No automatic status change for completion events
|
|
60
|
+
/* v8 ignore next -- defensive default for unknown event types */
|
|
61
|
+
default:
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -39,7 +39,7 @@ const fs = __importStar(require("fs"));
|
|
|
39
39
|
const os = __importStar(require("os"));
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const runtime_1 = require("../../nerves/runtime");
|
|
42
|
-
function buildCommandArgs(runner, workdir) {
|
|
42
|
+
function buildCommandArgs(runner, workdir, parentAgent) {
|
|
43
43
|
if (runner === "claude") {
|
|
44
44
|
return {
|
|
45
45
|
command: "claude",
|
|
@@ -55,9 +55,26 @@ function buildCommandArgs(runner, workdir) {
|
|
|
55
55
|
],
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
+
const mcpConfig = JSON.stringify({
|
|
59
|
+
mcp_servers: {
|
|
60
|
+
ouro: {
|
|
61
|
+
command: "ouro",
|
|
62
|
+
args: ["mcp-serve", "--agent", parentAgent ?? "unknown"],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
58
66
|
return {
|
|
59
67
|
command: "codex",
|
|
60
|
-
args: [
|
|
68
|
+
args: [
|
|
69
|
+
"exec",
|
|
70
|
+
"--skip-git-repo-check",
|
|
71
|
+
"--cd",
|
|
72
|
+
workdir,
|
|
73
|
+
"--ephemeral",
|
|
74
|
+
"--json",
|
|
75
|
+
"-c",
|
|
76
|
+
mcpConfig,
|
|
77
|
+
],
|
|
61
78
|
};
|
|
62
79
|
}
|
|
63
80
|
function buildSpawnEnv(baseEnv, homeDir) {
|
|
@@ -108,7 +125,7 @@ function spawnCodingProcess(request, deps = {}) {
|
|
|
108
125
|
const homeDir = deps.homeDir ?? os.homedir();
|
|
109
126
|
const baseEnv = deps.baseEnv ?? process.env;
|
|
110
127
|
const prompt = buildPrompt(request, { existsSync, readFileSync });
|
|
111
|
-
const { command, args } = buildCommandArgs(request.runner, request.workdir);
|
|
128
|
+
const { command, args } = buildCommandArgs(request.runner, request.workdir, request.parentAgent);
|
|
112
129
|
const env = buildSpawnEnv(baseEnv, homeDir);
|
|
113
130
|
(0, runtime_1.emitNervesEvent)({
|
|
114
131
|
component: "repertoire",
|
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.getSkillsDir = getSkillsDir;
|
|
37
|
+
exports.getHarnessSkillsDir = getHarnessSkillsDir;
|
|
37
38
|
exports.listSkills = listSkills;
|
|
38
39
|
exports.loadSkill = loadSkill;
|
|
39
40
|
exports.getLoadedSkills = getLoadedSkills;
|
|
@@ -50,6 +51,10 @@ function getSkillsDir() {
|
|
|
50
51
|
function getProtocolMirrorDir() {
|
|
51
52
|
return path.join(getSkillsDir(), "protocols");
|
|
52
53
|
}
|
|
54
|
+
// Harness-level skills live in {repoRoot}/skills/ directory.
|
|
55
|
+
function getHarnessSkillsDir() {
|
|
56
|
+
return path.join((0, identity_1.getRepoRoot)(), "skills");
|
|
57
|
+
}
|
|
53
58
|
function listMarkdownBasenames(dir) {
|
|
54
59
|
if (!fs.existsSync(dir))
|
|
55
60
|
return [];
|
|
@@ -70,7 +75,10 @@ function listSkills() {
|
|
|
70
75
|
});
|
|
71
76
|
const baseSkills = listMarkdownBasenames(getSkillsDir());
|
|
72
77
|
const protocolMirrors = listMarkdownBasenames(getProtocolMirrorDir());
|
|
73
|
-
const
|
|
78
|
+
const harnessSkills = listMarkdownBasenames(getHarnessSkillsDir());
|
|
79
|
+
// Agent skills (base + protocol) come first; harness skills are fallback.
|
|
80
|
+
// Set deduplicates by name — agent overrides harness.
|
|
81
|
+
const skills = [...new Set([...baseSkills, ...protocolMirrors, ...harnessSkills])].sort();
|
|
74
82
|
(0, runtime_1.emitNervesEvent)({
|
|
75
83
|
event: "repertoire.load_end",
|
|
76
84
|
component: "repertoire",
|
|
@@ -88,6 +96,7 @@ function loadSkill(skillName) {
|
|
|
88
96
|
});
|
|
89
97
|
const directSkillPath = path.join(getSkillsDir(), `${skillName}.md`);
|
|
90
98
|
const protocolMirrorPath = path.join(getProtocolMirrorDir(), `${skillName}.md`);
|
|
99
|
+
const harnessSkillPath = path.join(getHarnessSkillsDir(), `${skillName}.md`);
|
|
91
100
|
let resolvedPath = null;
|
|
92
101
|
// 1) Direct agent skill.
|
|
93
102
|
if (fs.existsSync(directSkillPath)) {
|
|
@@ -97,6 +106,10 @@ function loadSkill(skillName) {
|
|
|
97
106
|
else if (fs.existsSync(protocolMirrorPath)) {
|
|
98
107
|
resolvedPath = protocolMirrorPath;
|
|
99
108
|
}
|
|
109
|
+
// 3) Harness-level skill (ships with npm package).
|
|
110
|
+
else if (fs.existsSync(harnessSkillPath)) {
|
|
111
|
+
resolvedPath = harnessSkillPath;
|
|
112
|
+
}
|
|
100
113
|
if (!resolvedPath) {
|
|
101
114
|
(0, runtime_1.emitNervesEvent)({
|
|
102
115
|
level: "error",
|
|
@@ -106,12 +119,13 @@ function loadSkill(skillName) {
|
|
|
106
119
|
meta: {
|
|
107
120
|
operation: "loadSkill",
|
|
108
121
|
skill: skillName,
|
|
109
|
-
checkedPaths: [directSkillPath, protocolMirrorPath],
|
|
122
|
+
checkedPaths: [directSkillPath, protocolMirrorPath, harnessSkillPath],
|
|
110
123
|
},
|
|
111
124
|
});
|
|
112
125
|
throw new Error(`skill '${skillName}' not found in:\n` +
|
|
113
126
|
`- ${directSkillPath}\n` +
|
|
114
|
-
`- ${protocolMirrorPath}`
|
|
127
|
+
`- ${protocolMirrorPath}\n` +
|
|
128
|
+
`- ${harnessSkillPath}`);
|
|
115
129
|
}
|
|
116
130
|
const content = fs.readFileSync(resolvedPath, "utf-8");
|
|
117
131
|
if (!loadedSkills.includes(skillName)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.140",
|
|
4
4
|
"main": "dist/heart/daemon/ouro-entry.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cli": "dist/heart/daemon/ouro-bot-entry.js",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"dist/",
|
|
12
12
|
"AdoptionSpecialist.ouro/",
|
|
13
13
|
"subagents/",
|
|
14
|
+
"skills/",
|
|
14
15
|
"assets/",
|
|
15
16
|
"changelog.json"
|
|
16
17
|
],
|