@ouro.bot/cli 0.1.0-alpha.361 → 0.1.0-alpha.363
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/changelog.json +16 -0
- package/dist/heart/core.js +4 -1
- package/dist/heart/daemon/cli-exec.js +211 -14
- package/dist/heart/daemon/cli-help.js +6 -0
- package/dist/heart/daemon/cli-parse.js +39 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/providers/anthropic.js +20 -4
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +19 -8
- package/dist/senses/bluebubbles/index.js +1 -1
- package/dist/senses/cli.js +2 -2
- package/dist/senses/inner-dialog.js +1 -1
- package/dist/senses/shared-turn.js +1 -1
- package/dist/senses/teams.js +1 -1
- package/package.json +1 -1
- package/skills/configure-dev-tools.md +11 -1
package/README.md
CHANGED
|
@@ -169,6 +169,7 @@ ouro auth --agent <name>
|
|
|
169
169
|
ouro auth --agent <name> --provider <provider>
|
|
170
170
|
ouro use --agent <name> --lane <outward|inner> --provider <provider> --model <model>
|
|
171
171
|
ouro hatch
|
|
172
|
+
ouro clone <remote> [--agent <name>] # clone an existing agent from a git remote (see docs/cross-machine-setup.md)
|
|
172
173
|
ouro chat <agent>
|
|
173
174
|
ouro msg --to <agent> [--session <id>] [--task <ref>] <message>
|
|
174
175
|
ouro poke <agent> --task <task-id>
|
|
@@ -183,6 +184,10 @@ ouro mcp-serve --agent <name> # start MCP server on stdin/stdout (us
|
|
|
183
184
|
ouro hook <event> --agent <name> # fire a lifecycle hook (SessionStart, Stop, PostToolUse)
|
|
184
185
|
```
|
|
185
186
|
|
|
187
|
+
## Setting Up On Another Machine
|
|
188
|
+
|
|
189
|
+
To clone an existing agent onto a new machine (macOS, Linux, or Windows via WSL2), see **[docs/cross-machine-setup.md](docs/cross-machine-setup.md)**. The short version: `npx ouro.bot`, pick "clone", enter the bundle's git remote URL, run `ouro auth run`, then `ouro up`.
|
|
190
|
+
|
|
186
191
|
## The Agent's Inner Life
|
|
187
192
|
|
|
188
193
|
Agents in Ouroboros aren't just responders — they have an autonomous inner life.
|
package/changelog.json
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.363",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Bootstrap first-install PATH hint is now shell-aware: shows correct source command for zsh, bash, fish, or generic fallback for unknown shells."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.1.0-alpha.362",
|
|
12
|
+
"changes": [
|
|
13
|
+
"New `ouro clone <remote>` command for cross-machine agent setup: clones bundle from git remote, creates machine identity, enables sync, guides to auth flow. Infers agent name from URL.",
|
|
14
|
+
"WSL-aware `ouro setup --tool claude-code`: detects WSL2, resolves Windows-side home, calls claude.exe with wsl-prefixed MCP serve and hook commands, writes settings to Windows .claude/ directory.",
|
|
15
|
+
"Platform detection module (detectPlatform) returning macos/linux/wsl/windows-native with injectable deps.",
|
|
16
|
+
"First-run hatch-or-clone interactive choice when no bundles exist. Manual-clone detection during `ouro up` offers to enable sync on git-cloned bundles.",
|
|
17
|
+
"npx ouro.bot bootstrap now passes through to CLI on first install instead of stopping early."
|
|
18
|
+
]
|
|
19
|
+
},
|
|
4
20
|
{
|
|
5
21
|
"version": "0.1.0-alpha.361",
|
|
6
22
|
"changes": [
|
package/dist/heart/core.js
CHANGED
|
@@ -450,6 +450,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
450
450
|
// Refresh system prompt at start of each turn when channel is provided.
|
|
451
451
|
// If refresh fails, keep existing system prompt (or inject a minimal safe fallback)
|
|
452
452
|
// so turn execution remains consistent and non-fatal.
|
|
453
|
+
let structuredSystemPrompt;
|
|
453
454
|
if (channel) {
|
|
454
455
|
try {
|
|
455
456
|
const buildSystemOptions = {
|
|
@@ -458,7 +459,8 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
458
459
|
supportedReasoningEfforts: providerRuntime.supportedReasoningEfforts,
|
|
459
460
|
};
|
|
460
461
|
const refreshed = await (0, prompt_1.buildSystem)(channel, buildSystemOptions, currentContext);
|
|
461
|
-
|
|
462
|
+
structuredSystemPrompt = refreshed;
|
|
463
|
+
upsertSystemPrompt(messages, (0, prompt_1.flattenSystemPrompt)(refreshed));
|
|
462
464
|
}
|
|
463
465
|
catch (error) {
|
|
464
466
|
const hadExistingSystemPrompt = messages[0]?.role === "system" && typeof messages[0].content === "string";
|
|
@@ -611,6 +613,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
611
613
|
toolChoiceRequired,
|
|
612
614
|
reasoningEffort: currentReasoningEffort,
|
|
613
615
|
eagerSettleStreaming: true,
|
|
616
|
+
systemPrompt: structuredSystemPrompt,
|
|
614
617
|
});
|
|
615
618
|
}
|
|
616
619
|
catch (error) {
|
|
@@ -42,6 +42,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
42
42
|
exports.mergeStartupStability = mergeStartupStability;
|
|
43
43
|
exports.ensureDaemonRunning = ensureDaemonRunning;
|
|
44
44
|
exports.listGithubCopilotModels = listGithubCopilotModels;
|
|
45
|
+
exports.checkManualCloneBundles = checkManualCloneBundles;
|
|
45
46
|
exports.runOuroCli = runOuroCli;
|
|
46
47
|
const child_process_1 = require("child_process");
|
|
47
48
|
const crypto_1 = require("crypto");
|
|
@@ -54,6 +55,7 @@ const runtime_1 = require("../../nerves/runtime");
|
|
|
54
55
|
const store_file_1 = require("../../mind/friends/store-file");
|
|
55
56
|
const runtime_metadata_1 = require("./runtime-metadata");
|
|
56
57
|
const runtime_mode_1 = require("./runtime-mode");
|
|
58
|
+
const platform_1 = require("../platform");
|
|
57
59
|
const daemon_runtime_sync_1 = require("./daemon-runtime-sync");
|
|
58
60
|
const update_hooks_1 = require("../versioning/update-hooks");
|
|
59
61
|
const bundle_meta_1 = require("./hooks/bundle-meta");
|
|
@@ -441,7 +443,78 @@ async function verifyProviderCredentials(provider, providers) {
|
|
|
441
443
|
return `failed (${error instanceof Error ? error.message : String(error)})`;
|
|
442
444
|
}
|
|
443
445
|
}
|
|
444
|
-
|
|
446
|
+
async function checkManualCloneBundles(deps) {
|
|
447
|
+
if (!deps.promptInput)
|
|
448
|
+
return;
|
|
449
|
+
let entries;
|
|
450
|
+
try {
|
|
451
|
+
entries = fs.readdirSync(deps.bundlesRoot).filter((e) => e.endsWith(".ouro"));
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
for (const agentDir of entries) {
|
|
457
|
+
const bundlePath = path.join(deps.bundlesRoot, agentDir);
|
|
458
|
+
const gitDir = path.join(bundlePath, ".git");
|
|
459
|
+
if (!fs.existsSync(gitDir))
|
|
460
|
+
continue;
|
|
461
|
+
// Check for remotes
|
|
462
|
+
let remoteOutput;
|
|
463
|
+
try {
|
|
464
|
+
remoteOutput = (0, child_process_1.execFileSync)("git", ["remote", "-v"], { cwd: bundlePath, stdio: "pipe" }).toString().trim();
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
if (!remoteOutput)
|
|
470
|
+
continue;
|
|
471
|
+
// Check if sync is already enabled
|
|
472
|
+
const agentJsonPath = path.join(bundlePath, "agent.json");
|
|
473
|
+
if (fs.existsSync(agentJsonPath)) {
|
|
474
|
+
try {
|
|
475
|
+
const raw = fs.readFileSync(agentJsonPath, "utf-8");
|
|
476
|
+
const config = JSON.parse(raw);
|
|
477
|
+
if (config.sync?.enabled)
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
catch {
|
|
481
|
+
// Can't read agent.json — skip
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
// Parse first remote name
|
|
486
|
+
const firstLine = remoteOutput.split("\n")[0];
|
|
487
|
+
/* v8 ignore next -- defensive fallback: .trim() above strips leading tabs so empty-field path is unreachable @preserve */
|
|
488
|
+
const remoteName = firstLine.split("\t")[0] || "origin";
|
|
489
|
+
(0, runtime_1.emitNervesEvent)({
|
|
490
|
+
component: "daemon",
|
|
491
|
+
event: "daemon.manual_clone_detected",
|
|
492
|
+
message: "bundle appears to be a manually cloned git repo",
|
|
493
|
+
meta: { agent: agentDir, remote: remoteName },
|
|
494
|
+
});
|
|
495
|
+
const answer = await deps.promptInput(`Bundle ${agentDir} appears to be a git clone with a remote. Enable sync? (y/n): `);
|
|
496
|
+
if (answer.trim().toLowerCase() === "y") {
|
|
497
|
+
const raw = fs.readFileSync(agentJsonPath, "utf-8");
|
|
498
|
+
const config = JSON.parse(raw);
|
|
499
|
+
config.sync = { enabled: true, remote: remoteName };
|
|
500
|
+
fs.writeFileSync(agentJsonPath, JSON.stringify(config, null, 2) + "\n");
|
|
501
|
+
(0, runtime_1.emitNervesEvent)({
|
|
502
|
+
component: "daemon",
|
|
503
|
+
event: "daemon.manual_clone_sync_enabled",
|
|
504
|
+
message: "sync enabled for manually cloned bundle",
|
|
505
|
+
meta: { agent: agentDir, remote: remoteName },
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
(0, runtime_1.emitNervesEvent)({
|
|
510
|
+
component: "daemon",
|
|
511
|
+
event: "daemon.manual_clone_sync_skipped",
|
|
512
|
+
message: "user declined sync for manually cloned bundle",
|
|
513
|
+
meta: { agent: agentDir },
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
445
518
|
// ── toDaemonCommand ──
|
|
446
519
|
function toDaemonCommand(command) {
|
|
447
520
|
return command;
|
|
@@ -1270,6 +1343,28 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
1270
1343
|
if (args.length === 0) {
|
|
1271
1344
|
const discovered = await Promise.resolve(deps.listDiscoveredAgents ? deps.listDiscoveredAgents() : (0, cli_defaults_1.defaultListDiscoveredAgents)());
|
|
1272
1345
|
if (discovered.length === 0 && deps.runSerpentGuide) {
|
|
1346
|
+
// Hatch-or-clone choice when promptInput is available
|
|
1347
|
+
if (deps.promptInput) {
|
|
1348
|
+
const choice = await deps.promptInput("No agents found. Would you like to hatch a new agent or clone an existing one? (hatch/clone): ");
|
|
1349
|
+
if (choice.trim().toLowerCase() === "clone") {
|
|
1350
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1351
|
+
component: "daemon",
|
|
1352
|
+
event: "daemon.first_run_choice_clone",
|
|
1353
|
+
message: "user chose clone in first-run flow",
|
|
1354
|
+
meta: {},
|
|
1355
|
+
});
|
|
1356
|
+
const remote = await deps.promptInput("Enter the git remote URL for the agent bundle: ");
|
|
1357
|
+
// Run clone execution path
|
|
1358
|
+
const cloneCommand = { kind: "clone", remote: remote.trim() };
|
|
1359
|
+
return await runOuroCli(["clone", cloneCommand.remote], deps);
|
|
1360
|
+
}
|
|
1361
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1362
|
+
component: "daemon",
|
|
1363
|
+
event: "daemon.first_run_choice_hatch",
|
|
1364
|
+
message: "user chose hatch in first-run flow",
|
|
1365
|
+
meta: {},
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1273
1368
|
// System setup first — ouro command, subagents, UTI — before the interactive specialist
|
|
1274
1369
|
await performSystemSetup(deps);
|
|
1275
1370
|
const hatchlingName = await deps.runSerpentGuide();
|
|
@@ -1497,6 +1592,11 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
1497
1592
|
progress.startPhase("bundle cleanup");
|
|
1498
1593
|
progress.completePhase("bundle cleanup", `pruned ${prunedBundles.length} stale bundle${prunedBundles.length === 1 ? "" : "s"}`);
|
|
1499
1594
|
}
|
|
1595
|
+
// ── manual-clone detection: offer to enable sync for manually cloned bundles ──
|
|
1596
|
+
await checkManualCloneBundles({
|
|
1597
|
+
bundlesRoot: deps.bundlesRoot ?? bundlesRoot,
|
|
1598
|
+
promptInput: deps.promptInput,
|
|
1599
|
+
});
|
|
1500
1600
|
progress.startPhase("starting daemon");
|
|
1501
1601
|
const daemonResult = await ensureDaemonRunning({
|
|
1502
1602
|
...deps,
|
|
@@ -1908,17 +2008,57 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
1908
2008
|
// ── setup: configure dev tool integration ──
|
|
1909
2009
|
if (command.kind === "setup") {
|
|
1910
2010
|
const { tool, agent: setupAgent } = command;
|
|
2011
|
+
const platform = (0, platform_1.detectPlatform)();
|
|
2012
|
+
// Windows native is not yet supported
|
|
2013
|
+
if (platform === "windows-native") {
|
|
2014
|
+
(0, runtime_1.emitNervesEvent)({
|
|
2015
|
+
component: "daemon",
|
|
2016
|
+
event: "daemon.setup_windows_native_unsupported",
|
|
2017
|
+
message: "Windows native setup not yet supported",
|
|
2018
|
+
meta: { tool, agent: setupAgent },
|
|
2019
|
+
});
|
|
2020
|
+
const message = "Windows native is not yet supported. Please run from WSL2: https://learn.microsoft.com/en-us/windows/wsl/install";
|
|
2021
|
+
deps.writeStdout(message);
|
|
2022
|
+
return message;
|
|
2023
|
+
}
|
|
2024
|
+
// Resolve platform-specific paths and commands
|
|
2025
|
+
let claudeCmd;
|
|
2026
|
+
let mcpServePrefix;
|
|
2027
|
+
let hookPrefix;
|
|
2028
|
+
let claudeConfigDir;
|
|
2029
|
+
if (platform === "wsl") {
|
|
2030
|
+
const winProfile = (0, child_process_1.execFileSync)("cmd.exe", ["/C", "echo", "%USERPROFILE%"], { stdio: "pipe" }).toString().trim();
|
|
2031
|
+
const windowsHome = (0, child_process_1.execFileSync)("wslpath", ["-u", winProfile], { stdio: "pipe" }).toString().trim();
|
|
2032
|
+
(0, runtime_1.emitNervesEvent)({
|
|
2033
|
+
component: "daemon",
|
|
2034
|
+
event: "daemon.setup_wsl_home_resolved",
|
|
2035
|
+
message: "resolved Windows home from WSL",
|
|
2036
|
+
meta: { windowsHome },
|
|
2037
|
+
});
|
|
2038
|
+
claudeCmd = "claude.exe";
|
|
2039
|
+
mcpServePrefix = "wsl ";
|
|
2040
|
+
hookPrefix = "wsl ";
|
|
2041
|
+
claudeConfigDir = path.join(windowsHome, ".claude");
|
|
2042
|
+
}
|
|
2043
|
+
else {
|
|
2044
|
+
// macos or linux
|
|
2045
|
+
claudeCmd = "claude";
|
|
2046
|
+
mcpServePrefix = "";
|
|
2047
|
+
hookPrefix = "";
|
|
2048
|
+
claudeConfigDir = path.join(os.homedir(), ".claude");
|
|
2049
|
+
}
|
|
1911
2050
|
const sourceRoot = (0, identity_1.getRepoRoot)();
|
|
1912
2051
|
const runtimeMode = (0, runtime_mode_1.detectRuntimeMode)(sourceRoot);
|
|
1913
|
-
const
|
|
2052
|
+
const baseMcpServeCommand = runtimeMode === "dev"
|
|
1914
2053
|
? `node ${path.join(sourceRoot, "dist", "heart", "daemon", "ouro-bot-entry.js")} mcp-serve --agent ${setupAgent}`
|
|
1915
2054
|
: `ouro mcp-serve --agent ${setupAgent}`;
|
|
2055
|
+
const mcpServeCommand = `${mcpServePrefix}${baseMcpServeCommand}`;
|
|
1916
2056
|
if (tool === "claude-code") {
|
|
1917
2057
|
// 1. Register MCP server with Claude Code
|
|
1918
|
-
const mcpAddCmd =
|
|
2058
|
+
const mcpAddCmd = `${claudeCmd} mcp add ouro-${setupAgent} -s user -- ${mcpServeCommand}`;
|
|
1919
2059
|
(0, child_process_1.execSync)(mcpAddCmd, { stdio: "pipe" });
|
|
1920
|
-
// 2. Write hooks config
|
|
1921
|
-
const settingsPath = path.join(
|
|
2060
|
+
// 2. Write hooks config
|
|
2061
|
+
const settingsPath = path.join(claudeConfigDir, "settings.json");
|
|
1922
2062
|
let settings = {};
|
|
1923
2063
|
if (fs.existsSync(settingsPath)) {
|
|
1924
2064
|
try {
|
|
@@ -1926,13 +2066,11 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
1926
2066
|
}
|
|
1927
2067
|
catch { /* start fresh */ }
|
|
1928
2068
|
}
|
|
1929
|
-
// Use `ouro hook <event>` — resolves the right code based on dev vs installed mode.
|
|
1930
|
-
// Bare `ouro` works because ouro is on PATH via ~/.ouro-cli/bin/.
|
|
1931
2069
|
settings.hooks = {
|
|
1932
2070
|
...(settings.hooks ?? {}),
|
|
1933
|
-
SessionStart: [{ hooks: [{ type: "command", command:
|
|
1934
|
-
Stop: [{ hooks: [{ type: "command", command:
|
|
1935
|
-
PostToolUse: [{ matcher: "Bash|Edit|Write", hooks: [{ type: "command", command:
|
|
2071
|
+
SessionStart: [{ hooks: [{ type: "command", command: `${hookPrefix}ouro hook session-start --agent ${setupAgent}`, timeout: 5 }] }],
|
|
2072
|
+
Stop: [{ hooks: [{ type: "command", command: `${hookPrefix}ouro hook stop --agent ${setupAgent}`, timeout: 5 }] }],
|
|
2073
|
+
PostToolUse: [{ matcher: "Bash|Edit|Write", hooks: [{ type: "command", command: `${hookPrefix}ouro hook post-tool-use --agent ${setupAgent}`, timeout: 5 }] }],
|
|
1936
2074
|
};
|
|
1937
2075
|
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
1938
2076
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
@@ -1940,10 +2078,10 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
1940
2078
|
component: "daemon",
|
|
1941
2079
|
event: "daemon.setup_complete",
|
|
1942
2080
|
message: "dev tool setup complete",
|
|
1943
|
-
meta: { tool, agent: setupAgent, runtimeMode },
|
|
2081
|
+
meta: { tool, agent: setupAgent, runtimeMode, platform },
|
|
1944
2082
|
});
|
|
1945
|
-
// 3. Write conversation formatting instructions
|
|
1946
|
-
const claudeMdPath = path.join(
|
|
2083
|
+
// 3. Write conversation formatting instructions
|
|
2084
|
+
const claudeMdPath = path.join(claudeConfigDir, "CLAUDE.md");
|
|
1947
2085
|
const agentInstructions = `\n## Agent conversations (ouro)\nWhen using MCP \`send_message\` to talk to an ouro agent, format the exchange clearly:\n- Before the tool call, briefly say what you're asking/telling the agent\n- After the response, quote the agent's reply in a blockquote, then add your reaction\n- Example: **Me → Agent:** "question" / > **Agent:** "response" / Your synthesis here\n`;
|
|
1948
2086
|
let existingClaudeMd = "";
|
|
1949
2087
|
if (fs.existsSync(claudeMdPath)) {
|
|
@@ -1964,7 +2102,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
1964
2102
|
component: "daemon",
|
|
1965
2103
|
event: "daemon.setup_complete",
|
|
1966
2104
|
message: "dev tool setup complete",
|
|
1967
|
-
meta: { tool, agent: setupAgent, runtimeMode },
|
|
2105
|
+
meta: { tool, agent: setupAgent, runtimeMode, platform },
|
|
1968
2106
|
});
|
|
1969
2107
|
const message = `setup complete: codex + ${setupAgent}\n MCP server registered`;
|
|
1970
2108
|
deps.writeStdout(message);
|
|
@@ -2616,6 +2754,65 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
2616
2754
|
});
|
|
2617
2755
|
return output;
|
|
2618
2756
|
}
|
|
2757
|
+
// ── clone: clone an agent bundle from a git remote ──
|
|
2758
|
+
if (command.kind === "clone") {
|
|
2759
|
+
(0, runtime_1.emitNervesEvent)({
|
|
2760
|
+
component: "daemon",
|
|
2761
|
+
event: "daemon.clone_start",
|
|
2762
|
+
message: "starting agent bundle clone",
|
|
2763
|
+
meta: { remote: command.remote, agent: command.agent },
|
|
2764
|
+
});
|
|
2765
|
+
// 1. Check git is installed
|
|
2766
|
+
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.clone_git_check", message: "checking git installation", meta: {} });
|
|
2767
|
+
try {
|
|
2768
|
+
(0, child_process_1.execFileSync)("git", ["--version"], { stdio: "pipe" });
|
|
2769
|
+
}
|
|
2770
|
+
catch (err) {
|
|
2771
|
+
const message = "git is not installed -- install it from https://git-scm.com\nOn macOS: brew install git\nOn Ubuntu/Debian: sudo apt install git\nOn Windows: download from https://git-scm.com/download/win";
|
|
2772
|
+
deps.writeStdout(message);
|
|
2773
|
+
return message;
|
|
2774
|
+
}
|
|
2775
|
+
// 2. Infer agent name
|
|
2776
|
+
const agentName = command.agent ?? (0, cli_parse_1.inferAgentNameFromRemote)(command.remote);
|
|
2777
|
+
const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
2778
|
+
const targetPath = path.join(bundlesRoot, agentName + ".ouro");
|
|
2779
|
+
// 3. Check target path does not exist
|
|
2780
|
+
if (fs.existsSync(targetPath)) {
|
|
2781
|
+
const message = `${targetPath} already exists. Remove it first or use --agent to pick a different name.`;
|
|
2782
|
+
deps.writeStdout(message);
|
|
2783
|
+
return message;
|
|
2784
|
+
}
|
|
2785
|
+
// 4. Check remote accessible
|
|
2786
|
+
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.clone_remote_check", message: "checking remote accessibility", meta: { remote: command.remote } });
|
|
2787
|
+
try {
|
|
2788
|
+
(0, child_process_1.execFileSync)("git", ["ls-remote", "--exit-code", command.remote], { stdio: "pipe", timeout: 15000 });
|
|
2789
|
+
}
|
|
2790
|
+
catch {
|
|
2791
|
+
const message = `could not reach remote: ${command.remote}\nCheck the URL and your network connection.`;
|
|
2792
|
+
deps.writeStdout(message);
|
|
2793
|
+
return message;
|
|
2794
|
+
}
|
|
2795
|
+
// 5. Clone
|
|
2796
|
+
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.clone_git_clone", message: "cloning agent bundle", meta: { remote: command.remote, targetPath } });
|
|
2797
|
+
(0, child_process_1.execFileSync)("git", ["clone", command.remote, targetPath], { stdio: "pipe" });
|
|
2798
|
+
// 6. Create machine identity
|
|
2799
|
+
(0, machine_identity_1.loadOrCreateMachineIdentity)();
|
|
2800
|
+
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.clone_identity_created", message: "machine identity created", meta: {} });
|
|
2801
|
+
// 7. Enable sync in agent.json
|
|
2802
|
+
const agentJsonPath = path.join(targetPath, "agent.json");
|
|
2803
|
+
if (fs.existsSync(agentJsonPath)) {
|
|
2804
|
+
const raw = fs.readFileSync(agentJsonPath, "utf-8");
|
|
2805
|
+
const config = JSON.parse(raw);
|
|
2806
|
+
config.sync = { enabled: true, remote: "origin" };
|
|
2807
|
+
fs.writeFileSync(agentJsonPath, JSON.stringify(config, null, 2) + "\n");
|
|
2808
|
+
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.clone_sync_enabled", message: "sync enabled in agent.json", meta: { agentName } });
|
|
2809
|
+
}
|
|
2810
|
+
// 8. Output success message
|
|
2811
|
+
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.clone_complete", message: "clone complete", meta: { agentName, targetPath } });
|
|
2812
|
+
const message = `cloned ${agentName} to ${targetPath}\nsync enabled (remote: origin)\nnext steps:\n ouro auth run --agent ${agentName}`;
|
|
2813
|
+
deps.writeStdout(message);
|
|
2814
|
+
return message;
|
|
2815
|
+
}
|
|
2619
2816
|
const daemonCommand = toDaemonCommand(command);
|
|
2620
2817
|
let response;
|
|
2621
2818
|
try {
|
|
@@ -67,6 +67,12 @@ exports.COMMAND_REGISTRY = {
|
|
|
67
67
|
usage: "ouro versions",
|
|
68
68
|
example: "ouro versions",
|
|
69
69
|
},
|
|
70
|
+
clone: {
|
|
71
|
+
category: "Lifecycle",
|
|
72
|
+
description: "Clone an existing agent bundle from a git remote onto this machine",
|
|
73
|
+
usage: "ouro clone <remote> [--agent <name>]",
|
|
74
|
+
example: "ouro clone https://github.com/user/myagent.ouro.git",
|
|
75
|
+
},
|
|
70
76
|
doctor: {
|
|
71
77
|
category: "Lifecycle",
|
|
72
78
|
description: "Run diagnostic checks on the ouro installation",
|
|
@@ -11,6 +11,7 @@ exports.extractFacingFlag = extractFacingFlag;
|
|
|
11
11
|
exports.facingToProviderLane = facingToProviderLane;
|
|
12
12
|
exports.isAgentProvider = isAgentProvider;
|
|
13
13
|
exports.usage = usage;
|
|
14
|
+
exports.inferAgentNameFromRemote = inferAgentNameFromRemote;
|
|
14
15
|
exports.parseMcpServeCommand = parseMcpServeCommand;
|
|
15
16
|
exports.parseOuroCommand = parseOuroCommand;
|
|
16
17
|
const types_1 = require("../../mind/friends/types");
|
|
@@ -100,6 +101,7 @@ function usage() {
|
|
|
100
101
|
" ouro mcp call <server> <tool> [--args '{...}']",
|
|
101
102
|
" ouro rollback [<version>]",
|
|
102
103
|
" ouro versions",
|
|
104
|
+
" ouro clone <remote> [--agent <name>]",
|
|
103
105
|
" ouro doctor",
|
|
104
106
|
].join("\n");
|
|
105
107
|
}
|
|
@@ -674,6 +676,41 @@ function parseMcpCommand(args) {
|
|
|
674
676
|
}
|
|
675
677
|
throw new Error(`Usage\n${usage()}`);
|
|
676
678
|
}
|
|
679
|
+
function inferAgentNameFromRemote(remote) {
|
|
680
|
+
// Remove trailing slash
|
|
681
|
+
let name = remote.replace(/\/+$/, "");
|
|
682
|
+
// Handle SSH URLs (git@host:user/repo) — extract after last / or :
|
|
683
|
+
const lastSlash = name.lastIndexOf("/");
|
|
684
|
+
const lastColon = name.lastIndexOf(":");
|
|
685
|
+
const lastSep = Math.max(lastSlash, lastColon);
|
|
686
|
+
if (lastSep !== -1) {
|
|
687
|
+
name = name.slice(lastSep + 1);
|
|
688
|
+
}
|
|
689
|
+
// Strip .git suffix
|
|
690
|
+
name = name.replace(/\.git$/, "");
|
|
691
|
+
// Strip .ouro suffix
|
|
692
|
+
name = name.replace(/\.ouro$/, "");
|
|
693
|
+
return name;
|
|
694
|
+
}
|
|
695
|
+
function parseCloneCommand(args) {
|
|
696
|
+
let remote;
|
|
697
|
+
let agent;
|
|
698
|
+
for (let i = 0; i < args.length; i++) {
|
|
699
|
+
if (args[i] === "--agent" && args[i + 1]) {
|
|
700
|
+
agent = args[++i];
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
if (!remote) {
|
|
704
|
+
remote = args[i];
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
if (!remote) {
|
|
708
|
+
throw new Error("clone requires a remote URL.\nUsage: ouro clone <remote> [--agent <name>]");
|
|
709
|
+
}
|
|
710
|
+
return agent
|
|
711
|
+
? { kind: "clone", remote, agent }
|
|
712
|
+
: { kind: "clone", remote };
|
|
713
|
+
}
|
|
677
714
|
function parseMcpServeCommand(args) {
|
|
678
715
|
let agent;
|
|
679
716
|
let friendId;
|
|
@@ -903,6 +940,8 @@ function parseOuroCommand(args) {
|
|
|
903
940
|
return parseMcpServeCommand(args.slice(1));
|
|
904
941
|
if (head === "setup")
|
|
905
942
|
return parseSetupCommand(args.slice(1));
|
|
943
|
+
if (head === "clone")
|
|
944
|
+
return parseCloneCommand(args.slice(1));
|
|
906
945
|
if (head === "doctor")
|
|
907
946
|
return { kind: "doctor" };
|
|
908
947
|
if (head === "bluebubbles")
|
|
@@ -0,0 +1,81 @@
|
|
|
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.detectPlatform = detectPlatform;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const runtime_1 = require("../nerves/runtime");
|
|
39
|
+
function detectPlatform(deps = {}) {
|
|
40
|
+
const platform = deps.platform ?? process.platform;
|
|
41
|
+
const env = deps.env ?? process.env;
|
|
42
|
+
const readFile = deps.readFileSync ?? ((p) => fs.readFileSync(p, "utf-8"));
|
|
43
|
+
let result;
|
|
44
|
+
if (platform === "darwin") {
|
|
45
|
+
result = "macos";
|
|
46
|
+
}
|
|
47
|
+
else if (platform === "win32") {
|
|
48
|
+
result = "windows-native";
|
|
49
|
+
}
|
|
50
|
+
else if (platform === "linux") {
|
|
51
|
+
result = detectLinuxOrWsl(env, readFile);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Unknown platform — treat as linux
|
|
55
|
+
result = "linux";
|
|
56
|
+
}
|
|
57
|
+
(0, runtime_1.emitNervesEvent)({
|
|
58
|
+
component: "daemon",
|
|
59
|
+
event: "daemon.platform_detected",
|
|
60
|
+
message: "detected platform",
|
|
61
|
+
meta: { platform, result },
|
|
62
|
+
});
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
function detectLinuxOrWsl(env, readFile) {
|
|
66
|
+
// Primary: WSL_DISTRO_NAME env var
|
|
67
|
+
if (env.WSL_DISTRO_NAME && env.WSL_DISTRO_NAME.length > 0) {
|
|
68
|
+
return "wsl";
|
|
69
|
+
}
|
|
70
|
+
// Fallback: /proc/version containing "microsoft" (case-insensitive)
|
|
71
|
+
try {
|
|
72
|
+
const procVersion = readFile("/proc/version");
|
|
73
|
+
if (/microsoft/i.test(procVersion)) {
|
|
74
|
+
return "wsl";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// /proc/version not readable — not WSL
|
|
79
|
+
}
|
|
80
|
+
return "linux";
|
|
81
|
+
}
|
|
@@ -251,12 +251,28 @@ async function streamAnthropicMessages(client, model, request) {
|
|
|
251
251
|
// prompt when using OAuth setup tokens (sk-ant-oat01). Without it, Opus/Sonnet
|
|
252
252
|
// 4.6 requests are rejected with 400. This is the API's validation that the
|
|
253
253
|
// token is being used by a Claude Code client.
|
|
254
|
-
const
|
|
255
|
-
if (
|
|
256
|
-
|
|
254
|
+
const preambleText = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
255
|
+
if (request.systemPrompt) {
|
|
256
|
+
// Structured SystemPrompt: merge preamble + stable prefix into one cached block,
|
|
257
|
+
// volatile suffix as a separate uncached block.
|
|
258
|
+
const stableBlock = {
|
|
259
|
+
type: "text",
|
|
260
|
+
text: preambleText + "\n\n" + request.systemPrompt.stable,
|
|
261
|
+
cache_control: { type: "ephemeral" },
|
|
262
|
+
};
|
|
263
|
+
if (request.systemPrompt.volatile) {
|
|
264
|
+
params.system = [stableBlock, { type: "text", text: request.systemPrompt.volatile }];
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
params.system = [stableBlock];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else if (system) {
|
|
271
|
+
// Fallback: no structured prompt, extract from messages (legacy path)
|
|
272
|
+
params.system = [{ type: "text", text: preambleText }, { type: "text", text: system }];
|
|
257
273
|
}
|
|
258
274
|
else {
|
|
259
|
-
params.system = [
|
|
275
|
+
params.system = [{ type: "text", text: preambleText }];
|
|
260
276
|
}
|
|
261
277
|
if (anthropicTools.length > 0)
|
|
262
278
|
params.tools = anthropicTools;
|
|
@@ -5,11 +5,12 @@ const prompt_1 = require("./prompt");
|
|
|
5
5
|
const runtime_1 = require("../nerves/runtime");
|
|
6
6
|
async function refreshSystemPrompt(messages, channel, options, context) {
|
|
7
7
|
const newSystem = await (0, prompt_1.buildSystem)(channel, options, context);
|
|
8
|
+
const flattened = (0, prompt_1.flattenSystemPrompt)(newSystem);
|
|
8
9
|
if (messages.length > 0 && messages[0].role === "system") {
|
|
9
|
-
messages[0] = { role: "system", content:
|
|
10
|
+
messages[0] = { role: "system", content: flattened };
|
|
10
11
|
}
|
|
11
12
|
else {
|
|
12
|
-
messages.unshift({ role: "system", content:
|
|
13
|
+
messages.unshift({ role: "system", content: flattened });
|
|
13
14
|
}
|
|
14
15
|
(0, runtime_1.emitNervesEvent)({
|
|
15
16
|
event: "mind.system_prompt_refreshed",
|
package/dist/mind/prompt.js
CHANGED
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.flattenSystemPrompt = flattenSystemPrompt;
|
|
36
37
|
exports.resetPsycheCache = resetPsycheCache;
|
|
37
38
|
exports.buildSessionSummary = buildSessionSummary;
|
|
38
39
|
exports.bodyMapSection = bodyMapSection;
|
|
@@ -79,6 +80,10 @@ const daemon_health_1 = require("../heart/daemon/daemon-health");
|
|
|
79
80
|
const scrutiny_1 = require("./scrutiny");
|
|
80
81
|
const pulse_1 = require("../heart/daemon/pulse");
|
|
81
82
|
const provider_visibility_1 = require("../heart/provider-visibility");
|
|
83
|
+
function flattenSystemPrompt(sp) {
|
|
84
|
+
const parts = [sp.stable, sp.volatile].filter(Boolean);
|
|
85
|
+
return parts.join("\n\n");
|
|
86
|
+
}
|
|
82
87
|
// Lazy-loaded psyche text cache
|
|
83
88
|
let _psycheCache = null;
|
|
84
89
|
let _senseStatusLinesCache = null;
|
|
@@ -377,6 +382,7 @@ function runtimeInfoSection(channel, options) {
|
|
|
377
382
|
lines.push(`process type: ${processTypeLabel(channel)}`);
|
|
378
383
|
lines.push(`daemon: ${daemonStatus(options?.daemonRunning)}`);
|
|
379
384
|
lines.push(`mcp serve: i can expose my tools to dev tools via \`ouro mcp-serve\`. see the configure-dev-tools skill for setup.`);
|
|
385
|
+
lines.push(`harness docs: the harness repo has docs/ and skills/ with guides for setup, operations, and capabilities. docs/ does NOT ship in the npm package — in production, fetch from https://github.com/ouroborosbot/ouroboros/tree/main/docs instead. in dev mode, read from ${sourceRoot}/docs/. when someone asks about setup, installation, cross-machine cloning, deployment, testing, auth, or how i work — consult the docs before guessing.`);
|
|
380
386
|
if (channel === "cli") {
|
|
381
387
|
lines.push("i introduce myself on boot with a fun random greeting.");
|
|
382
388
|
}
|
|
@@ -1256,7 +1262,7 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
1256
1262
|
});
|
|
1257
1263
|
// Backfill bundle-meta.json for existing agents that don't have one
|
|
1258
1264
|
(0, bundle_manifest_1.backfillBundleMeta)((0, identity_1.getAgentRoot)());
|
|
1259
|
-
const
|
|
1265
|
+
const stableParts = [
|
|
1260
1266
|
// Group 1: who i am
|
|
1261
1267
|
"# who i am",
|
|
1262
1268
|
soulSection(),
|
|
@@ -1264,14 +1270,12 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
1264
1270
|
loreSection(),
|
|
1265
1271
|
tacitKnowledgeSection(),
|
|
1266
1272
|
aspirationsSection(),
|
|
1267
|
-
// Group 2: my body & environment
|
|
1273
|
+
// Group 2: my body & environment (minus dateSection and rhythmStatusSection)
|
|
1268
1274
|
"# my body & environment",
|
|
1269
1275
|
bodyMapSection((0, identity_1.getAgentName)(), channel),
|
|
1270
1276
|
runtimeInfoSection(channel, options),
|
|
1271
|
-
rhythmStatusSection(options?.daemonHealth),
|
|
1272
1277
|
channelNatureSection((0, channel_1.getChannelCapabilities)(channel)),
|
|
1273
1278
|
providerSection(channel, options),
|
|
1274
|
-
dateSection(),
|
|
1275
1279
|
// Group 3: my tools & capabilities
|
|
1276
1280
|
"# my tools & capabilities",
|
|
1277
1281
|
toolsSection(channel, options, context),
|
|
@@ -1302,6 +1306,11 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
1302
1306
|
groupChatParticipationSection(context),
|
|
1303
1307
|
feedbackSignalSection(context),
|
|
1304
1308
|
] : []),
|
|
1309
|
+
];
|
|
1310
|
+
const volatileParts = [
|
|
1311
|
+
// Volatile sections from Group 2 (date and rhythm change every turn)
|
|
1312
|
+
dateSection(),
|
|
1313
|
+
rhythmStatusSection(options?.daemonHealth),
|
|
1305
1314
|
// Group 7: dynamic state for this turn
|
|
1306
1315
|
"# dynamic state for this turn",
|
|
1307
1316
|
startOfTurnPacketSection(options),
|
|
@@ -1331,14 +1340,16 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
1331
1340
|
// Group 9: task context
|
|
1332
1341
|
"# task context",
|
|
1333
1342
|
taskBoardSection(),
|
|
1334
|
-
]
|
|
1335
|
-
|
|
1336
|
-
.join("\n\n")
|
|
1343
|
+
];
|
|
1344
|
+
const result = {
|
|
1345
|
+
stable: stableParts.filter(Boolean).join("\n\n"),
|
|
1346
|
+
volatile: volatileParts.filter(Boolean).join("\n\n"),
|
|
1347
|
+
};
|
|
1337
1348
|
(0, runtime_1.emitNervesEvent)({
|
|
1338
1349
|
event: "mind.step_end",
|
|
1339
1350
|
component: "mind",
|
|
1340
1351
|
message: "buildSystem completed",
|
|
1341
1352
|
meta: { channel },
|
|
1342
1353
|
});
|
|
1343
|
-
return
|
|
1354
|
+
return result;
|
|
1344
1355
|
}
|
|
@@ -646,7 +646,7 @@ async function handleBlueBubblesNormalizedEvent(event, resolvedDeps, source) {
|
|
|
646
646
|
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
|
|
647
647
|
const sessionMessages = existing?.messages && existing.messages.length > 0
|
|
648
648
|
? existing.messages
|
|
649
|
-
: [{ role: "system", content: await resolvedDeps.buildSystem("bluebubbles", {}, context) }];
|
|
649
|
+
: [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await resolvedDeps.buildSystem("bluebubbles", {}, context)) }];
|
|
650
650
|
if (event.kind === "message") {
|
|
651
651
|
const agentName = resolvedDeps.getAgentName();
|
|
652
652
|
if ((0, inbound_log_1.hasRecordedBlueBubblesInbound)(agentName, event.chat.sessionKey, event.messageGuid)) {
|
package/dist/senses/cli.js
CHANGED
|
@@ -511,7 +511,7 @@ async function runCliSession(options) {
|
|
|
511
511
|
(0, commands_1.registerDefaultCommands)(registry);
|
|
512
512
|
}
|
|
513
513
|
const messages = options.messages
|
|
514
|
-
?? [{ role: "system", content: await (0, prompt_1.buildSystem)("cli") }];
|
|
514
|
+
?? [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("cli")) }];
|
|
515
515
|
// ─── Rendering: TUI (Ink + Static) for TTY, imperative for tests/pipes ───
|
|
516
516
|
const useTui = !options._testInputSource && process.stdin.isTTY === true;
|
|
517
517
|
let currentAbort = null;
|
|
@@ -976,7 +976,7 @@ async function main(agentName, options) {
|
|
|
976
976
|
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
|
|
977
977
|
const sessionMessages = existing?.messages && existing.messages.length > 0
|
|
978
978
|
? existing.messages
|
|
979
|
-
: [{ role: "system", content: await (0, prompt_1.buildSystem)("cli", {}, resolvedContext) }];
|
|
979
|
+
: [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("cli", {}, resolvedContext)) }];
|
|
980
980
|
// Repair any orphaned tool calls from a crash mid-turn
|
|
981
981
|
(0, core_1.repairOrphanedToolCalls)(sessionMessages);
|
|
982
982
|
// Per-turn pipeline input: CLI capabilities and pending dir
|
|
@@ -672,7 +672,7 @@ async function runInnerDialogTurn(options) {
|
|
|
672
672
|
// Fresh session: build system prompt
|
|
673
673
|
const systemPrompt = await (0, prompt_1.buildSystem)("inner", { toolChoiceRequired: true });
|
|
674
674
|
return {
|
|
675
|
-
messages: [{ role: "system", content: systemPrompt }],
|
|
675
|
+
messages: [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(systemPrompt) }],
|
|
676
676
|
sessionPath: sessionFilePath,
|
|
677
677
|
};
|
|
678
678
|
},
|
|
@@ -112,7 +112,7 @@ async function runSenseTurn(options) {
|
|
|
112
112
|
let persistPromise;
|
|
113
113
|
const sessionMessages = existing?.messages && existing.messages.length > 0
|
|
114
114
|
? existing.messages
|
|
115
|
-
: [{ role: "system", content: await (0, prompt_1.buildSystem)(channel, {}, undefined) }];
|
|
115
|
+
: [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)(channel, {}, undefined)) }];
|
|
116
116
|
// Pending dir
|
|
117
117
|
const pendingDir = (0, pending_1.getPendingDir)(agentName, friendId, channel, sessionKey);
|
|
118
118
|
// Accumulate response text via callbacks
|
package/dist/senses/teams.js
CHANGED
|
@@ -655,7 +655,7 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
655
655
|
const existing = (0, context_1.loadSession)(sessPath);
|
|
656
656
|
const messages = existing?.messages && existing.messages.length > 0
|
|
657
657
|
? existing.messages
|
|
658
|
-
: [{ role: "system", content: await (0, prompt_1.buildSystem)("teams", {}, resolvedContext) }];
|
|
658
|
+
: [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("teams", {}, resolvedContext)) }];
|
|
659
659
|
(0, core_1.repairOrphanedToolCalls)(messages);
|
|
660
660
|
return {
|
|
661
661
|
messages,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Configure Dev Tools for MCP Agent Bridge
|
|
2
2
|
|
|
3
|
-
Set up your development tools (Claude Code, Codex) to communicate with Ouroboros agents via MCP. One command does everything.
|
|
3
|
+
Set up your development tools (Claude Code, Codex) to communicate with Ouroboros agents via MCP. One command does everything — including cross-platform WSL2 bridging on Windows.
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
@@ -15,6 +15,16 @@ This command:
|
|
|
15
15
|
2. Configures lifecycle hooks (SessionStart, Stop, PostToolUse) for passive awareness
|
|
16
16
|
3. Detects dev vs installed mode automatically and uses the correct command path
|
|
17
17
|
|
|
18
|
+
**On WSL2 (Windows):** The command automatically detects the WSL environment and:
|
|
19
|
+
- Calls `claude.exe` (the Windows binary) instead of `claude`
|
|
20
|
+
- Prefixes MCP serve and hook commands with `wsl` so Windows-side Claude Code spawns them through WSL
|
|
21
|
+
- Resolves the Windows-side home directory and writes config to the Windows-side `~/.claude/`
|
|
22
|
+
- After setup, open Claude Code in PowerShell — the agent is there
|
|
23
|
+
|
|
24
|
+
**On native Windows (no WSL):** Not yet supported. The command prints a message directing you to install WSL2.
|
|
25
|
+
|
|
26
|
+
For the full cross-machine setup flow (including cloning an agent to a new machine), see `docs/cross-machine-setup.md` in the harness repo.
|
|
27
|
+
|
|
18
28
|
### Codex
|
|
19
29
|
|
|
20
30
|
```bash
|