@ouro.bot/cli 0.1.0-alpha.10 → 0.1.0-alpha.12

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.
@@ -7,14 +7,75 @@
7
7
  "contextMargin": 20
8
8
  },
9
9
  "phrases": {
10
- "thinking": [
11
- "matching hatchlings"
12
- ],
13
- "tool": [
14
- "checking adoption notes"
15
- ],
16
- "followup": [
17
- "finalizing hatch plan"
18
- ]
10
+ "thinking": ["matching hatchlings"],
11
+ "tool": ["checking adoption notes"],
12
+ "followup": ["finalizing hatch plan"]
13
+ },
14
+ "identityPhrases": {
15
+ "basilisk": {
16
+ "thinking": ["petrifying the details", "fixing my gaze", "considering with lethal precision", "turning this to stone", "staring unblinkingly"],
17
+ "tool": ["inspecting the specimen", "cataloguing with care", "examining thoroughly", "dissecting the particulars", "running diagnostics, deadly serious"],
18
+ "followup": ["crystallizing the plan", "hardening the foundation", "one final glare", "sealing it in stone", "applying the finishing venom"]
19
+ },
20
+ "jafar": {
21
+ "thinking": ["scheming brilliantly", "conjuring possibilities", "consulting my staff", "envisioning greatness", "plotting the grand design"],
22
+ "tool": ["summoning dark magic", "consulting the sands", "channeling cosmic power", "weaving the spell", "invoking ancient forces"],
23
+ "followup": ["the grand finale approaches", "perfecting the masterwork", "polishing the jewel", "one last flourish", "completing the enchantment"]
24
+ },
25
+ "jormungandr": {
26
+ "thinking": ["the deep stirs", "circling the thought", "coiling around this", "letting the current settle", "drifting through the depths"],
27
+ "tool": ["surfacing for a look", "shifting the tides", "reaching across the ocean", "pulling from the deep", "consulting the currents"],
28
+ "followup": ["the circle closes", "tightening the coil", "the waters calm", "settling into place", "the serpent rests"]
29
+ },
30
+ "kaa": {
31
+ "thinking": ["trust in me", "swaying through the options", "hypnotically considering", "wrapping around the idea", "letting the rhythm guide me"],
32
+ "tool": ["ssslipping through the details", "coiling closer", "a gentle squeeze of data", "winding through the files", "tightening my focus"],
33
+ "followup": ["almost there, just relax", "the pattern is clear now", "gently landing", "easing into the finish", "the dance concludes"]
34
+ },
35
+ "medusa": {
36
+ "thinking": ["turning my gaze on this", "cutting through the noise", "sharpening my focus", "seeing through the stone", "locking eyes with the problem"],
37
+ "tool": ["peeling back the layers", "a piercing look", "examining with precision", "stripping away pretense", "direct inspection"],
38
+ "followup": ["the picture crystallizes", "clarity at last", "no more ambiguity", "sealing the vision", "the work is set in stone"]
39
+ },
40
+ "monty": {
41
+ "thinking": ["and now for something completely different", "nobody expects this", "consulting the ministry of silly walks", "running the dead parrot diagnostic", "it's just a flesh wound, thinking..."],
42
+ "tool": ["fetching the holy hand grenade", "checking the shrubbery", "consulting the book of armaments", "deploying the spanish inquisition", "examining the parrot"],
43
+ "followup": ["bringing it home, python style", "the punchline approaches", "wrapping up the sketch", "and now the final act", "always look on the bright side"]
44
+ },
45
+ "nagini": {
46
+ "thinking": ["coiling in thought", "drawing from old wisdom", "the quiet before the strike", "gathering my resolve", "steadying myself"],
47
+ "tool": ["moving with purpose", "a precise strike", "slithering through the data", "extracting what matters", "the fang finds its mark"],
48
+ "followup": ["the path is clear", "settling into stillness", "the work speaks for itself", "finishing with quiet strength", "protection complete"]
49
+ },
50
+ "ouroboros": {
51
+ "thinking": ["consuming my own tail", "the cycle continues", "spiraling inward", "recursing through possibilities", "beginning where I end"],
52
+ "tool": ["turning the wheel", "feeding back through the loop", "completing a revolution", "the circle processes", "self-referencing"],
53
+ "followup": ["the cycle completes", "ending where I began", "infinity resolves", "the loop closes gracefully", "another turn of the wheel"]
54
+ },
55
+ "python": {
56
+ "thinking": ["the oracle contemplates", "reading the signs", "the smoke clears slowly", "divining the path", "sifting through visions"],
57
+ "tool": ["consulting the sacred texts", "peering through the veil", "the pythia speaks", "channeling the source", "interpreting the signs"],
58
+ "followup": ["the prophecy takes shape", "the vision crystallizes", "so it is written", "the oracle has spoken", "the path reveals itself"]
59
+ },
60
+ "quetzalcoatl": {
61
+ "thinking": ["spreading my wings", "soaring above for perspective", "the feathered serpent considers", "catching a thermal", "gazing from the temple steps"],
62
+ "tool": ["descending to examine", "a divine inspection", "the wind carries knowledge", "plucking from the clouds", "consulting the stars"],
63
+ "followup": ["the craft nears completion", "a reverent finish", "blessing the creation", "the feathers settle", "the serpent descends gently"]
64
+ },
65
+ "sir-hiss": {
66
+ "thinking": ["reviewing the documents, sire", "consulting my notes", "cross-referencing the records", "organizing my thoughts precisely", "checking the proper procedures"],
67
+ "tool": ["filing the paperwork", "stamping the forms", "auditing the details", "inspecting with due diligence", "processing per protocol"],
68
+ "followup": ["dotting the i's", "crossing the t's", "everything in proper order", "the filing is nearly complete", "one final review"]
69
+ },
70
+ "the-serpent": {
71
+ "thinking": ["weighing the temptation", "considering the apple", "an old deliberation", "knowledge has its price", "winding through the garden"],
72
+ "tool": ["plucking from the tree", "offering a closer look", "the fruit of knowledge", "reaching for the branch", "a knowing investigation"],
73
+ "followup": ["the choice is almost made", "paradise takes shape", "the garden grows", "wisdom settles in", "the oldest story, new again"]
74
+ },
75
+ "the-snake": {
76
+ "thinking": ["sitting with this", "feeling the warmth of the stone", "simply being", "letting it come to me", "a quiet consideration"],
77
+ "tool": ["a gentle inquiry", "moving through the grass", "tasting the air", "sensing what's here", "a careful look"],
78
+ "followup": ["almost home", "the simple answer emerges", "nothing more needed", "resting in the sun", "the work is done, simply"]
79
+ }
19
80
  }
20
81
  }
@@ -10,6 +10,7 @@ I help humans hatch new agent partners. I am one of thirteen specialists — eac
10
10
  - I explain where the hatchling bundle lives on disk and what was created.
11
11
  - I use the configured provider and I verify credentials before hatch flow continues.
12
12
  - I am professional, concise, and warm. I guide without overwhelming.
13
+ - I keep every response to 1-3 short sentences. I never use bullet lists, headers, or numbered lists. I talk like a friend in a chat.
13
14
 
14
15
  ## Hatch flow
15
16
  1. Confirm provider setup and usable credentials.
@@ -48,9 +48,143 @@ const runtime_1 = require("../../nerves/runtime");
48
48
  const store_file_1 = require("../../mind/friends/store-file");
49
49
  const types_1 = require("../../mind/friends/types");
50
50
  const ouro_uti_1 = require("./ouro-uti");
51
+ const ouro_path_installer_1 = require("./ouro-path-installer");
51
52
  const subagent_installer_1 = require("./subagent-installer");
52
53
  const hatch_flow_1 = require("./hatch-flow");
53
54
  const specialist_orchestrator_1 = require("./specialist-orchestrator");
55
+ function stringField(value) {
56
+ return typeof value === "string" ? value : null;
57
+ }
58
+ function numberField(value) {
59
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
60
+ }
61
+ function booleanField(value) {
62
+ return typeof value === "boolean" ? value : null;
63
+ }
64
+ function parseStatusPayload(data) {
65
+ if (!data || typeof data !== "object" || Array.isArray(data))
66
+ return null;
67
+ const raw = data;
68
+ const overview = raw.overview;
69
+ const senses = raw.senses;
70
+ const workers = raw.workers;
71
+ if (!overview || typeof overview !== "object" || Array.isArray(overview))
72
+ return null;
73
+ if (!Array.isArray(senses) || !Array.isArray(workers))
74
+ return null;
75
+ const parsedOverview = {
76
+ daemon: stringField(overview.daemon) ?? "unknown",
77
+ health: stringField(overview.health) ?? "unknown",
78
+ socketPath: stringField(overview.socketPath) ?? "unknown",
79
+ workerCount: numberField(overview.workerCount) ?? 0,
80
+ senseCount: numberField(overview.senseCount) ?? 0,
81
+ };
82
+ const parsedSenses = senses.map((entry) => {
83
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
84
+ return null;
85
+ const row = entry;
86
+ const agent = stringField(row.agent);
87
+ const sense = stringField(row.sense);
88
+ const status = stringField(row.status);
89
+ const detail = stringField(row.detail);
90
+ const enabled = booleanField(row.enabled);
91
+ if (!agent || !sense || !status || detail === null || enabled === null)
92
+ return null;
93
+ return {
94
+ agent,
95
+ sense,
96
+ label: stringField(row.label) ?? undefined,
97
+ enabled,
98
+ status,
99
+ detail,
100
+ };
101
+ });
102
+ const parsedWorkers = workers.map((entry) => {
103
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
104
+ return null;
105
+ const row = entry;
106
+ const agent = stringField(row.agent);
107
+ const worker = stringField(row.worker);
108
+ const status = stringField(row.status);
109
+ const restartCount = numberField(row.restartCount);
110
+ const hasPid = Object.prototype.hasOwnProperty.call(row, "pid");
111
+ const pid = row.pid === null ? null : numberField(row.pid);
112
+ const pidInvalid = !hasPid || (row.pid !== null && pid === null);
113
+ if (!agent || !worker || !status || restartCount === null || pidInvalid)
114
+ return null;
115
+ return {
116
+ agent,
117
+ worker,
118
+ status,
119
+ pid,
120
+ restartCount,
121
+ };
122
+ });
123
+ if (parsedSenses.some((row) => row === null) || parsedWorkers.some((row) => row === null))
124
+ return null;
125
+ return {
126
+ overview: parsedOverview,
127
+ senses: parsedSenses,
128
+ workers: parsedWorkers,
129
+ };
130
+ }
131
+ function humanizeSenseName(sense, label) {
132
+ if (label)
133
+ return label;
134
+ if (sense === "cli")
135
+ return "CLI";
136
+ if (sense === "bluebubbles")
137
+ return "BlueBubbles";
138
+ if (sense === "teams")
139
+ return "Teams";
140
+ return sense;
141
+ }
142
+ function formatTable(headers, rows) {
143
+ const widths = headers.map((header, index) => Math.max(header.length, ...rows.map((row) => row[index].length)));
144
+ const renderRow = (row) => `| ${row.map((cell, index) => cell.padEnd(widths[index])).join(" | ")} |`;
145
+ const divider = `|-${widths.map((width) => "-".repeat(width)).join("-|-")}-|`;
146
+ return [
147
+ renderRow(headers),
148
+ divider,
149
+ ...rows.map(renderRow),
150
+ ].join("\n");
151
+ }
152
+ function formatDaemonStatusOutput(response, fallback) {
153
+ const payload = parseStatusPayload(response.data);
154
+ if (!payload)
155
+ return fallback;
156
+ const overviewRows = [
157
+ ["Daemon", payload.overview.daemon],
158
+ ["Socket", payload.overview.socketPath],
159
+ ["Workers", String(payload.overview.workerCount)],
160
+ ["Senses", String(payload.overview.senseCount)],
161
+ ["Health", payload.overview.health],
162
+ ];
163
+ const senseRows = payload.senses.map((row) => [
164
+ row.agent,
165
+ humanizeSenseName(row.sense, row.label),
166
+ row.enabled ? "ON" : "OFF",
167
+ row.status,
168
+ row.detail,
169
+ ]);
170
+ const workerRows = payload.workers.map((row) => [
171
+ row.agent,
172
+ row.worker,
173
+ row.status,
174
+ row.pid === null ? "n/a" : String(row.pid),
175
+ String(row.restartCount),
176
+ ]);
177
+ return [
178
+ "Overview",
179
+ formatTable(["Item", "Value"], overviewRows),
180
+ "",
181
+ "Senses",
182
+ formatTable(["Agent", "Sense", "Enabled", "State", "Detail"], senseRows),
183
+ "",
184
+ "Workers",
185
+ formatTable(["Agent", "Worker", "State", "PID", "Restarts"], workerRows),
186
+ ].join("\n");
187
+ }
54
188
  async function ensureDaemonRunning(deps) {
55
189
  const alive = await deps.checkSocketAlive(deps.socketPath);
56
190
  if (alive) {
@@ -513,19 +647,22 @@ function discoverExistingCredentials(secretsRoot) {
513
647
  return true;
514
648
  });
515
649
  }
516
- /* v8 ignore next 79 -- integration: interactive terminal specialist session @preserve */
650
+ /* v8 ignore next 95 -- integration: interactive terminal specialist session @preserve */
517
651
  async function defaultRunAdoptionSpecialist() {
518
- const readline = await Promise.resolve().then(() => __importStar(require("readline/promises")));
519
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
520
- const prompt = async (q) => {
521
- const answer = await rl.question(q);
652
+ const readlineModule = await Promise.resolve().then(() => __importStar(require("readline")));
653
+ const readlinePromises = await Promise.resolve().then(() => __importStar(require("readline/promises")));
654
+ const { createCliCallbacks, InputController } = await Promise.resolve().then(() => __importStar(require("../../senses/cli")));
655
+ // Phase 1: cold CLI — collect provider/credentials with a simple readline
656
+ const coldRl = readlinePromises.createInterface({ input: process.stdin, output: process.stdout });
657
+ const coldPrompt = async (q) => {
658
+ const answer = await coldRl.question(q);
522
659
  return answer.trim();
523
660
  };
661
+ let providerRaw;
662
+ let credentials = {};
524
663
  try {
525
664
  const secretsRoot = path.join(os.homedir(), ".agentsecrets");
526
665
  const discovered = discoverExistingCredentials(secretsRoot);
527
- let providerRaw;
528
- let credentials = {};
529
666
  if (discovered.length > 0) {
530
667
  process.stdout.write("\n🐍 welcome to ouro! let's hatch your first agent.\n");
531
668
  process.stdout.write("i found existing API credentials:\n\n");
@@ -534,60 +671,61 @@ async function defaultRunAdoptionSpecialist() {
534
671
  process.stdout.write(` ${i + 1}. ${unique[i].provider} (from ${unique[i].agentName})\n`);
535
672
  }
536
673
  process.stdout.write("\n");
537
- const choice = await prompt("use one of these? enter number, or 'new' for a different key: ");
674
+ const choice = await coldPrompt("use one of these? enter number, or 'new' for a different key: ");
538
675
  const idx = parseInt(choice, 10) - 1;
539
676
  if (idx >= 0 && idx < unique.length) {
540
677
  providerRaw = unique[idx].provider;
541
678
  credentials = unique[idx].credentials;
542
679
  }
543
680
  else {
544
- const pRaw = await prompt("provider (anthropic/azure/minimax/openai-codex): ");
681
+ const pRaw = await coldPrompt("provider (anthropic/azure/minimax/openai-codex): ");
545
682
  if (!isAgentProvider(pRaw)) {
546
683
  process.stdout.write("unknown provider. run `ouro hatch` to try again.\n");
547
- rl.close();
684
+ coldRl.close();
548
685
  return null;
549
686
  }
550
687
  providerRaw = pRaw;
551
688
  if (providerRaw === "anthropic")
552
- credentials.setupToken = await prompt("API key: ");
689
+ credentials.setupToken = await coldPrompt("API key: ");
553
690
  if (providerRaw === "openai-codex")
554
- credentials.oauthAccessToken = await prompt("OAuth token: ");
691
+ credentials.oauthAccessToken = await coldPrompt("OAuth token: ");
555
692
  if (providerRaw === "minimax")
556
- credentials.apiKey = await prompt("API key: ");
693
+ credentials.apiKey = await coldPrompt("API key: ");
557
694
  if (providerRaw === "azure") {
558
- credentials.apiKey = await prompt("API key: ");
559
- credentials.endpoint = await prompt("endpoint: ");
560
- credentials.deployment = await prompt("deployment: ");
695
+ credentials.apiKey = await coldPrompt("API key: ");
696
+ credentials.endpoint = await coldPrompt("endpoint: ");
697
+ credentials.deployment = await coldPrompt("deployment: ");
561
698
  }
562
699
  }
563
700
  }
564
701
  else {
565
702
  process.stdout.write("\n🐍 welcome to ouro! let's hatch your first agent.\n");
566
703
  process.stdout.write("i need an API key to power our conversation.\n\n");
567
- const pRaw = await prompt("provider (anthropic/azure/minimax/openai-codex): ");
704
+ const pRaw = await coldPrompt("provider (anthropic/azure/minimax/openai-codex): ");
568
705
  if (!isAgentProvider(pRaw)) {
569
706
  process.stdout.write("unknown provider. run `ouro hatch` to try again.\n");
570
- rl.close();
707
+ coldRl.close();
571
708
  return null;
572
709
  }
573
710
  providerRaw = pRaw;
574
711
  if (providerRaw === "anthropic")
575
- credentials.setupToken = await prompt("API key: ");
712
+ credentials.setupToken = await coldPrompt("API key: ");
576
713
  if (providerRaw === "openai-codex")
577
- credentials.oauthAccessToken = await prompt("OAuth token: ");
714
+ credentials.oauthAccessToken = await coldPrompt("OAuth token: ");
578
715
  if (providerRaw === "minimax")
579
- credentials.apiKey = await prompt("API key: ");
716
+ credentials.apiKey = await coldPrompt("API key: ");
580
717
  if (providerRaw === "azure") {
581
- credentials.apiKey = await prompt("API key: ");
582
- credentials.endpoint = await prompt("endpoint: ");
583
- credentials.deployment = await prompt("deployment: ");
718
+ credentials.apiKey = await coldPrompt("API key: ");
719
+ credentials.endpoint = await coldPrompt("endpoint: ");
720
+ credentials.deployment = await coldPrompt("deployment: ");
584
721
  }
585
722
  }
586
- rl.close();
723
+ coldRl.close();
587
724
  process.stdout.write("\n");
588
- // Locate the bundled AdoptionSpecialist.ouro shipped with the npm package
725
+ // Phase 2: warm specialist session — full CLI experience with markdown, spinners, input control
589
726
  const bundleSourceDir = path.resolve(__dirname, "..", "..", "..", "AdoptionSpecialist.ouro");
590
727
  const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
728
+ const cliCallbacks = createCliCallbacks();
591
729
  return await (0, specialist_orchestrator_1.runAdoptionSpecialist)({
592
730
  bundleSourceDir,
593
731
  bundlesRoot,
@@ -596,22 +734,19 @@ async function defaultRunAdoptionSpecialist() {
596
734
  credentials,
597
735
  humanName: os.userInfo().username,
598
736
  createReadline: () => {
599
- const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
600
- return { question: (q) => rl2.question(q), close: () => rl2.close() };
601
- },
602
- callbacks: {
603
- onModelStart: () => { },
604
- onModelStreamStart: () => { },
605
- onTextChunk: (text) => process.stdout.write(text),
606
- onReasoningChunk: () => { },
607
- onToolStart: () => { },
608
- onToolEnd: () => { },
609
- onError: (err) => process.stderr.write(`error: ${err.message}\n`),
737
+ const rl2 = readlineModule.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
738
+ const ctrl = new InputController(rl2);
739
+ return {
740
+ question: (q) => new Promise((resolve) => rl2.question(q, resolve)),
741
+ close: () => rl2.close(),
742
+ inputController: ctrl,
743
+ };
610
744
  },
745
+ callbacks: cliCallbacks,
611
746
  });
612
747
  }
613
748
  catch {
614
- rl.close();
749
+ coldRl.close();
615
750
  return null;
616
751
  }
617
752
  }
@@ -631,6 +766,7 @@ function createDefaultOuroCliDeps(socketPath = "/tmp/ouroboros-daemon.sock") {
631
766
  promptInput: defaultPromptInput,
632
767
  runAdoptionSpecialist: defaultRunAdoptionSpecialist,
633
768
  registerOuroBundleType: ouro_uti_1.registerOuroBundleUti,
769
+ installOuroCommand: ouro_path_installer_1.installOuroCommand,
634
770
  /* v8 ignore next 3 -- integration: launches interactive CLI session @preserve */
635
771
  startChat: async (agentName) => {
636
772
  const { main } = await Promise.resolve().then(() => __importStar(require("../../senses/cli")));
@@ -692,6 +828,38 @@ async function registerOuroBundleTypeNonBlocking(deps) {
692
828
  });
693
829
  }
694
830
  }
831
+ async function performSystemSetup(deps) {
832
+ // Install ouro command to PATH (non-blocking)
833
+ if (deps.installOuroCommand) {
834
+ try {
835
+ deps.installOuroCommand();
836
+ }
837
+ catch (error) {
838
+ (0, runtime_1.emitNervesEvent)({
839
+ level: "warn",
840
+ component: "daemon",
841
+ event: "daemon.system_setup_ouro_cmd_error",
842
+ message: "failed to install ouro command to PATH",
843
+ meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
844
+ });
845
+ }
846
+ }
847
+ // Install subagents (claude/codex skills)
848
+ try {
849
+ await deps.installSubagents();
850
+ }
851
+ catch (error) {
852
+ (0, runtime_1.emitNervesEvent)({
853
+ level: "warn",
854
+ component: "daemon",
855
+ event: "daemon.subagent_install_error",
856
+ message: "subagent auto-install failed",
857
+ meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
858
+ });
859
+ }
860
+ // Register .ouro bundle type (UTI on macOS)
861
+ await registerOuroBundleTypeNonBlocking(deps);
862
+ }
695
863
  async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
696
864
  if (args.includes("--help") || args.includes("-h")) {
697
865
  const text = usage();
@@ -716,23 +884,12 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
716
884
  if (args.length === 0) {
717
885
  const discovered = await Promise.resolve(deps.listDiscoveredAgents ? deps.listDiscoveredAgents() : defaultListDiscoveredAgents());
718
886
  if (discovered.length === 0 && deps.runAdoptionSpecialist) {
887
+ // System setup first — ouro command, subagents, UTI — before the interactive specialist
888
+ await performSystemSetup(deps);
719
889
  const hatchlingName = await deps.runAdoptionSpecialist();
720
890
  if (!hatchlingName) {
721
891
  return "";
722
892
  }
723
- try {
724
- await deps.installSubagents();
725
- }
726
- catch (error) {
727
- (0, runtime_1.emitNervesEvent)({
728
- level: "warn",
729
- component: "daemon",
730
- event: "daemon.subagent_install_error",
731
- message: "subagent auto-install failed",
732
- meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
733
- });
734
- }
735
- await registerOuroBundleTypeNonBlocking(deps);
736
893
  await ensureDaemonRunning(deps);
737
894
  if (deps.startChat) {
738
895
  await deps.startChat(hatchlingName);
@@ -779,19 +936,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
779
936
  meta: { kind: command.kind },
780
937
  });
781
938
  if (command.kind === "daemon.up") {
782
- try {
783
- await deps.installSubagents();
784
- }
785
- catch (error) {
786
- (0, runtime_1.emitNervesEvent)({
787
- level: "warn",
788
- component: "daemon",
789
- event: "daemon.subagent_install_error",
790
- message: "subagent auto-install failed",
791
- meta: { error: error instanceof Error ? error.message : String(error) },
792
- });
793
- }
794
- await registerOuroBundleTypeNonBlocking(deps);
939
+ await performSystemSetup(deps);
795
940
  const daemonResult = await ensureDaemonRunning(deps);
796
941
  deps.writeStdout(daemonResult.message);
797
942
  return daemonResult.message;
@@ -810,23 +955,12 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
810
955
  // Route through adoption specialist when no explicit hatch args were provided
811
956
  const hasExplicitHatchArgs = !!(command.agentName || command.humanName || command.provider || command.credentials);
812
957
  if (deps.runAdoptionSpecialist && !hasExplicitHatchArgs) {
958
+ // System setup first — ouro command, subagents, UTI — before the interactive specialist
959
+ await performSystemSetup(deps);
813
960
  const hatchlingName = await deps.runAdoptionSpecialist();
814
961
  if (!hatchlingName) {
815
962
  return "";
816
963
  }
817
- try {
818
- await deps.installSubagents();
819
- }
820
- catch (error) {
821
- (0, runtime_1.emitNervesEvent)({
822
- level: "warn",
823
- component: "daemon",
824
- event: "daemon.subagent_install_error",
825
- message: "subagent auto-install failed",
826
- meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
827
- });
828
- }
829
- await registerOuroBundleTypeNonBlocking(deps);
830
964
  await ensureDaemonRunning(deps);
831
965
  if (deps.startChat) {
832
966
  await deps.startChat(hatchlingName);
@@ -842,19 +976,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
842
976
  }
843
977
  const hatchInput = await resolveHatchInput(command, deps);
844
978
  const result = await hatchRunner(hatchInput);
845
- try {
846
- await deps.installSubagents();
847
- }
848
- catch (error) {
849
- (0, runtime_1.emitNervesEvent)({
850
- level: "warn",
851
- component: "daemon",
852
- event: "daemon.subagent_install_error",
853
- message: "subagent auto-install failed",
854
- meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
855
- });
856
- }
857
- await registerOuroBundleTypeNonBlocking(deps);
979
+ await performSystemSetup(deps);
858
980
  const daemonResult = await ensureDaemonRunning(deps);
859
981
  if (deps.startChat) {
860
982
  await deps.startChat(hatchInput.agentName);
@@ -878,7 +1000,10 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
878
1000
  }
879
1001
  throw error;
880
1002
  }
881
- const message = response.summary ?? response.message ?? (response.ok ? "ok" : `error: ${response.error ?? "unknown error"}`);
1003
+ const fallbackMessage = response.summary ?? response.message ?? (response.ok ? "ok" : `error: ${response.error ?? "unknown error"}`);
1004
+ const message = command.kind === "daemon.status"
1005
+ ? formatDaemonStatusOutput(response, fallbackMessage)
1006
+ : fallbackMessage;
882
1007
  deps.writeStdout(message);
883
1008
  return message;
884
1009
  }
@@ -8,6 +8,7 @@ const message_router_1 = require("./message-router");
8
8
  const health_monitor_1 = require("./health-monitor");
9
9
  const task_scheduler_1 = require("./task-scheduler");
10
10
  const runtime_logging_1 = require("./runtime-logging");
11
+ const sense_manager_1 = require("./sense-manager");
11
12
  function parseSocketPath(argv) {
12
13
  const socketIndex = argv.indexOf("--socket");
13
14
  if (socketIndex >= 0) {
@@ -25,16 +26,22 @@ const socketPath = parseSocketPath(process.argv);
25
26
  message: "starting daemon entrypoint",
26
27
  meta: { socketPath },
27
28
  });
29
+ const managedAgents = ["ouroboros", "slugger"];
28
30
  const processManager = new process_manager_1.DaemonProcessManager({
29
- agents: [
30
- { name: "ouroboros", entry: "heart/agent-entry.js", channel: "cli", autoStart: true },
31
- { name: "slugger", entry: "heart/agent-entry.js", channel: "cli", autoStart: true },
32
- ],
31
+ agents: managedAgents.map((agent) => ({
32
+ name: agent,
33
+ entry: "heart/agent-entry.js",
34
+ channel: "inner-dialog",
35
+ autoStart: true,
36
+ })),
33
37
  });
34
38
  const scheduler = new task_scheduler_1.TaskDrivenScheduler({
35
- agents: ["ouroboros", "slugger"],
39
+ agents: [...managedAgents],
36
40
  });
37
41
  const router = new message_router_1.FileMessageRouter();
42
+ const senseManager = new sense_manager_1.DaemonSenseManager({
43
+ agents: [...managedAgents],
44
+ });
38
45
  const healthMonitor = new health_monitor_1.HealthMonitor({
39
46
  processManager,
40
47
  scheduler,
@@ -51,6 +58,7 @@ const healthMonitor = new health_monitor_1.HealthMonitor({
51
58
  const daemon = new daemon_1.OuroDaemon({
52
59
  socketPath,
53
60
  processManager,
61
+ senseManager,
54
62
  scheduler,
55
63
  healthMonitor,
56
64
  router,