@ouro.bot/cli 0.0.1-alpha.0 → 0.1.0-alpha.10

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.
Files changed (129) hide show
  1. package/AdoptionSpecialist.ouro/agent.json +20 -0
  2. package/AdoptionSpecialist.ouro/psyche/SOUL.md +24 -0
  3. package/AdoptionSpecialist.ouro/psyche/identities/basilisk.md +31 -0
  4. package/AdoptionSpecialist.ouro/psyche/identities/jafar.md +31 -0
  5. package/AdoptionSpecialist.ouro/psyche/identities/jormungandr.md +31 -0
  6. package/AdoptionSpecialist.ouro/psyche/identities/kaa.md +31 -0
  7. package/AdoptionSpecialist.ouro/psyche/identities/medusa.md +31 -0
  8. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +31 -0
  9. package/AdoptionSpecialist.ouro/psyche/identities/nagini.md +31 -0
  10. package/AdoptionSpecialist.ouro/psyche/identities/ouroboros.md +31 -0
  11. package/AdoptionSpecialist.ouro/psyche/identities/python.md +31 -0
  12. package/AdoptionSpecialist.ouro/psyche/identities/quetzalcoatl.md +31 -0
  13. package/AdoptionSpecialist.ouro/psyche/identities/sir-hiss.md +31 -0
  14. package/AdoptionSpecialist.ouro/psyche/identities/the-serpent.md +31 -0
  15. package/AdoptionSpecialist.ouro/psyche/identities/the-snake.md +31 -0
  16. package/README.md +224 -6
  17. package/dist/heart/agent-entry.js +17 -0
  18. package/dist/heart/api-error.js +34 -0
  19. package/dist/heart/config.js +330 -0
  20. package/dist/heart/core.js +524 -0
  21. package/dist/heart/daemon/daemon-cli.js +884 -0
  22. package/dist/heart/daemon/daemon-entry.js +74 -0
  23. package/dist/heart/daemon/daemon.js +313 -0
  24. package/dist/heart/daemon/hatch-animation.js +28 -0
  25. package/dist/heart/daemon/hatch-flow.js +286 -0
  26. package/dist/heart/daemon/hatch-specialist.js +112 -0
  27. package/dist/heart/daemon/health-monitor.js +79 -0
  28. package/dist/heart/daemon/log-tailer.js +146 -0
  29. package/dist/heart/daemon/message-router.js +98 -0
  30. package/dist/heart/daemon/os-cron.js +260 -0
  31. package/dist/heart/daemon/ouro-bot-entry.js +23 -0
  32. package/dist/heart/daemon/ouro-bot-wrapper.js +91 -0
  33. package/dist/heart/daemon/ouro-entry.js +23 -0
  34. package/dist/heart/daemon/ouro-uti.js +212 -0
  35. package/dist/heart/daemon/process-manager.js +237 -0
  36. package/dist/heart/daemon/runtime-logging.js +102 -0
  37. package/dist/heart/daemon/specialist-orchestrator.js +161 -0
  38. package/dist/heart/daemon/specialist-prompt.js +56 -0
  39. package/dist/heart/daemon/specialist-session.js +150 -0
  40. package/dist/heart/daemon/specialist-tools.js +132 -0
  41. package/dist/heart/daemon/subagent-installer.js +125 -0
  42. package/dist/heart/daemon/task-scheduler.js +240 -0
  43. package/dist/heart/harness.js +26 -0
  44. package/dist/heart/identity.js +295 -0
  45. package/dist/heart/kicks.js +144 -0
  46. package/dist/heart/primitives.js +4 -0
  47. package/dist/heart/providers/anthropic.js +332 -0
  48. package/dist/heart/providers/azure.js +66 -0
  49. package/dist/heart/providers/minimax.js +53 -0
  50. package/dist/heart/providers/openai-codex.js +162 -0
  51. package/dist/heart/streaming.js +415 -0
  52. package/dist/heart/turn-coordinator.js +62 -0
  53. package/dist/inner-worker-entry.js +4 -0
  54. package/dist/mind/associative-recall.js +197 -0
  55. package/dist/mind/bundle-manifest.js +118 -0
  56. package/dist/mind/context.js +302 -0
  57. package/dist/mind/first-impressions.js +43 -0
  58. package/dist/mind/format.js +56 -0
  59. package/dist/mind/friends/channel.js +49 -0
  60. package/dist/mind/friends/resolver.js +84 -0
  61. package/dist/mind/friends/store-file.js +171 -0
  62. package/dist/mind/friends/store.js +4 -0
  63. package/dist/mind/friends/tokens.js +26 -0
  64. package/dist/mind/friends/types.js +21 -0
  65. package/dist/mind/memory.js +388 -0
  66. package/dist/mind/pending.js +93 -0
  67. package/dist/mind/phrases.js +43 -0
  68. package/dist/mind/prompt-refresh.js +20 -0
  69. package/dist/mind/prompt.js +355 -0
  70. package/dist/mind/token-estimate.js +119 -0
  71. package/dist/nerves/cli-logging.js +31 -0
  72. package/dist/nerves/coverage/audit-rules.js +81 -0
  73. package/dist/nerves/coverage/audit.js +200 -0
  74. package/dist/nerves/coverage/cli-main.js +5 -0
  75. package/dist/nerves/coverage/cli.js +51 -0
  76. package/dist/nerves/coverage/contract.js +23 -0
  77. package/dist/nerves/coverage/file-completeness.js +56 -0
  78. package/dist/nerves/coverage/run-artifacts.js +77 -0
  79. package/dist/nerves/coverage/source-scanner.js +34 -0
  80. package/dist/nerves/index.js +152 -0
  81. package/dist/nerves/runtime.js +38 -0
  82. package/dist/repertoire/ado-client.js +211 -0
  83. package/dist/repertoire/ado-context.js +73 -0
  84. package/dist/repertoire/ado-semantic.js +841 -0
  85. package/dist/repertoire/ado-templates.js +146 -0
  86. package/dist/repertoire/coding/index.js +36 -0
  87. package/dist/repertoire/coding/manager.js +489 -0
  88. package/dist/repertoire/coding/monitor.js +60 -0
  89. package/dist/repertoire/coding/reporter.js +45 -0
  90. package/dist/repertoire/coding/spawner.js +102 -0
  91. package/dist/repertoire/coding/tools.js +167 -0
  92. package/dist/repertoire/coding/types.js +2 -0
  93. package/dist/repertoire/data/ado-endpoints.json +122 -0
  94. package/dist/repertoire/data/graph-endpoints.json +212 -0
  95. package/dist/repertoire/github-client.js +64 -0
  96. package/dist/repertoire/graph-client.js +118 -0
  97. package/dist/repertoire/skills.js +156 -0
  98. package/dist/repertoire/tasks/board.js +122 -0
  99. package/dist/repertoire/tasks/index.js +210 -0
  100. package/dist/repertoire/tasks/lifecycle.js +80 -0
  101. package/dist/repertoire/tasks/middleware.js +65 -0
  102. package/dist/repertoire/tasks/parser.js +173 -0
  103. package/dist/repertoire/tasks/scanner.js +132 -0
  104. package/dist/repertoire/tasks/transitions.js +145 -0
  105. package/dist/repertoire/tasks/types.js +2 -0
  106. package/dist/repertoire/tools-base.js +714 -0
  107. package/dist/repertoire/tools-github.js +53 -0
  108. package/dist/repertoire/tools-teams.js +308 -0
  109. package/dist/repertoire/tools.js +199 -0
  110. package/dist/senses/bluebubbles-client.js +279 -0
  111. package/dist/senses/bluebubbles-entry.js +11 -0
  112. package/dist/senses/bluebubbles-model.js +253 -0
  113. package/dist/senses/bluebubbles-mutation-log.js +76 -0
  114. package/dist/senses/bluebubbles.js +332 -0
  115. package/dist/senses/cli-entry.js +15 -0
  116. package/dist/senses/cli.js +604 -0
  117. package/dist/senses/commands.js +98 -0
  118. package/dist/senses/inner-dialog-worker.js +61 -0
  119. package/dist/senses/inner-dialog.js +231 -0
  120. package/dist/senses/session-lock.js +119 -0
  121. package/dist/senses/teams-entry.js +15 -0
  122. package/dist/senses/teams.js +696 -0
  123. package/dist/senses/trust-gate.js +150 -0
  124. package/package.json +35 -11
  125. package/subagents/README.md +73 -0
  126. package/subagents/work-doer.md +233 -0
  127. package/subagents/work-merger.md +624 -0
  128. package/subagents/work-planner.md +373 -0
  129. package/bin/ouro.js +0 -6
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const process_manager_1 = require("./process-manager");
5
+ const daemon_1 = require("./daemon");
6
+ const runtime_1 = require("../../nerves/runtime");
7
+ const message_router_1 = require("./message-router");
8
+ const health_monitor_1 = require("./health-monitor");
9
+ const task_scheduler_1 = require("./task-scheduler");
10
+ const runtime_logging_1 = require("./runtime-logging");
11
+ function parseSocketPath(argv) {
12
+ const socketIndex = argv.indexOf("--socket");
13
+ if (socketIndex >= 0) {
14
+ const value = argv[socketIndex + 1];
15
+ if (value && value.trim().length > 0)
16
+ return value;
17
+ }
18
+ return "/tmp/ouroboros-daemon.sock";
19
+ }
20
+ const socketPath = parseSocketPath(process.argv);
21
+ (0, runtime_logging_1.configureDaemonRuntimeLogger)("daemon");
22
+ (0, runtime_1.emitNervesEvent)({
23
+ component: "daemon",
24
+ event: "daemon.entry_start",
25
+ message: "starting daemon entrypoint",
26
+ meta: { socketPath },
27
+ });
28
+ 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
+ ],
33
+ });
34
+ const scheduler = new task_scheduler_1.TaskDrivenScheduler({
35
+ agents: ["ouroboros", "slugger"],
36
+ });
37
+ const router = new message_router_1.FileMessageRouter();
38
+ const healthMonitor = new health_monitor_1.HealthMonitor({
39
+ processManager,
40
+ scheduler,
41
+ alertSink: (message) => {
42
+ (0, runtime_1.emitNervesEvent)({
43
+ level: "error",
44
+ component: "daemon",
45
+ event: "daemon.health_alert",
46
+ message: "health monitor produced critical alert",
47
+ meta: { message },
48
+ });
49
+ },
50
+ });
51
+ const daemon = new daemon_1.OuroDaemon({
52
+ socketPath,
53
+ processManager,
54
+ scheduler,
55
+ healthMonitor,
56
+ router,
57
+ });
58
+ void daemon.start().catch(async () => {
59
+ (0, runtime_1.emitNervesEvent)({
60
+ level: "error",
61
+ component: "daemon",
62
+ event: "daemon.entry_error",
63
+ message: "daemon entrypoint failed",
64
+ meta: {},
65
+ });
66
+ await daemon.stop();
67
+ process.exit(1);
68
+ });
69
+ process.on("SIGINT", () => {
70
+ void daemon.stop().then(() => process.exit(0));
71
+ });
72
+ process.on("SIGTERM", () => {
73
+ void daemon.stop().then(() => process.exit(0));
74
+ });
@@ -0,0 +1,313 @@
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.OuroDaemon = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const net = __importStar(require("net"));
39
+ const path = __importStar(require("path"));
40
+ const identity_1 = require("../identity");
41
+ const runtime_1 = require("../../nerves/runtime");
42
+ function formatStatusSummary(snapshots) {
43
+ if (snapshots.length === 0)
44
+ return "no managed agents";
45
+ return snapshots
46
+ .map((snapshot) => {
47
+ return `${snapshot.name}\t${snapshot.channel}\t${snapshot.status}\tpid=${snapshot.pid ?? "none"}\trestarts=${snapshot.restartCount}`;
48
+ })
49
+ .join("\n");
50
+ }
51
+ function parseIncomingCommand(raw) {
52
+ let parsed;
53
+ try {
54
+ parsed = JSON.parse(raw);
55
+ }
56
+ catch {
57
+ throw new Error("Invalid daemon command payload: expected JSON object.");
58
+ }
59
+ if (!parsed || typeof parsed !== "object" || !("kind" in parsed)) {
60
+ throw new Error("Invalid daemon command payload: missing kind.");
61
+ }
62
+ const kind = parsed.kind;
63
+ if (typeof kind !== "string") {
64
+ throw new Error("Invalid daemon command payload: kind must be a string.");
65
+ }
66
+ return parsed;
67
+ }
68
+ class OuroDaemon {
69
+ socketPath;
70
+ processManager;
71
+ scheduler;
72
+ healthMonitor;
73
+ router;
74
+ bundlesRoot;
75
+ server = null;
76
+ constructor(options) {
77
+ this.socketPath = options.socketPath;
78
+ this.processManager = options.processManager;
79
+ this.scheduler = options.scheduler;
80
+ this.healthMonitor = options.healthMonitor;
81
+ this.router = options.router;
82
+ this.bundlesRoot = options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
83
+ }
84
+ async start() {
85
+ if (this.server)
86
+ return;
87
+ (0, runtime_1.emitNervesEvent)({
88
+ component: "daemon",
89
+ event: "daemon.server_start",
90
+ message: "starting daemon server",
91
+ meta: { socketPath: this.socketPath },
92
+ });
93
+ await this.processManager.startAutoStartAgents();
94
+ this.scheduler.start?.();
95
+ await this.scheduler.reconcile?.();
96
+ await this.drainPendingBundleMessages();
97
+ if (fs.existsSync(this.socketPath)) {
98
+ fs.unlinkSync(this.socketPath);
99
+ }
100
+ this.server = net.createServer((connection) => {
101
+ let raw = "";
102
+ let responded = false;
103
+ const flushResponse = async () => {
104
+ if (responded)
105
+ return;
106
+ responded = true;
107
+ const response = await this.handleRawPayload(raw);
108
+ connection.end(response);
109
+ };
110
+ connection.on("data", (chunk) => {
111
+ raw += chunk.toString("utf-8");
112
+ void flushResponse();
113
+ });
114
+ connection.on("end", () => {
115
+ void flushResponse();
116
+ });
117
+ });
118
+ const server = this.server;
119
+ await new Promise((resolve, reject) => {
120
+ server.once("error", reject);
121
+ server.listen(this.socketPath, () => resolve());
122
+ });
123
+ }
124
+ async drainPendingBundleMessages() {
125
+ if (!fs.existsSync(this.bundlesRoot))
126
+ return;
127
+ let bundleDirs;
128
+ try {
129
+ bundleDirs = fs.readdirSync(this.bundlesRoot, { withFileTypes: true });
130
+ }
131
+ catch {
132
+ return;
133
+ }
134
+ for (const bundleDir of bundleDirs) {
135
+ if (!bundleDir.isDirectory() || !bundleDir.name.endsWith(".ouro"))
136
+ continue;
137
+ const pendingPath = path.join(this.bundlesRoot, bundleDir.name, "inbox", "pending.jsonl");
138
+ if (!fs.existsSync(pendingPath))
139
+ continue;
140
+ const raw = fs.readFileSync(pendingPath, "utf-8");
141
+ const lines = raw
142
+ .split("\n")
143
+ .map((line) => line.trim())
144
+ .filter((line) => line.length > 0);
145
+ const retained = [];
146
+ for (const line of lines) {
147
+ try {
148
+ const parsed = JSON.parse(line);
149
+ if (typeof parsed.from !== "string" ||
150
+ typeof parsed.to !== "string" ||
151
+ typeof parsed.content !== "string") {
152
+ retained.push(line);
153
+ continue;
154
+ }
155
+ await this.router.send({
156
+ from: parsed.from,
157
+ to: parsed.to,
158
+ content: parsed.content,
159
+ priority: typeof parsed.priority === "string" ? parsed.priority : undefined,
160
+ sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : undefined,
161
+ taskRef: typeof parsed.taskRef === "string" ? parsed.taskRef : undefined,
162
+ });
163
+ }
164
+ catch {
165
+ retained.push(line);
166
+ }
167
+ }
168
+ const next = retained.length > 0 ? `${retained.join("\n")}\n` : "";
169
+ fs.writeFileSync(pendingPath, next, "utf-8");
170
+ }
171
+ }
172
+ async stop() {
173
+ (0, runtime_1.emitNervesEvent)({
174
+ component: "daemon",
175
+ event: "daemon.server_stop",
176
+ message: "stopping daemon server",
177
+ meta: { socketPath: this.socketPath },
178
+ });
179
+ this.scheduler.stop?.();
180
+ await this.processManager.stopAll();
181
+ if (this.server) {
182
+ await new Promise((resolve) => {
183
+ this.server?.close(() => resolve());
184
+ });
185
+ this.server = null;
186
+ }
187
+ if (fs.existsSync(this.socketPath)) {
188
+ fs.unlinkSync(this.socketPath);
189
+ }
190
+ }
191
+ async handleRawPayload(raw) {
192
+ try {
193
+ const command = parseIncomingCommand(raw);
194
+ const response = await this.handleCommand(command);
195
+ return JSON.stringify(response);
196
+ }
197
+ catch (error) {
198
+ return JSON.stringify({
199
+ ok: false,
200
+ error: error instanceof Error ? error.message : String(error),
201
+ });
202
+ }
203
+ }
204
+ async handleCommand(command) {
205
+ (0, runtime_1.emitNervesEvent)({
206
+ component: "daemon",
207
+ event: "daemon.command_received",
208
+ message: "handling daemon command",
209
+ meta: { kind: command.kind },
210
+ });
211
+ switch (command.kind) {
212
+ case "daemon.start":
213
+ await this.start();
214
+ return { ok: true, message: "daemon started" };
215
+ case "daemon.stop":
216
+ await this.stop();
217
+ return { ok: true, message: "daemon stopped" };
218
+ case "daemon.status": {
219
+ const snapshots = this.processManager.listAgentSnapshots();
220
+ return {
221
+ ok: true,
222
+ summary: formatStatusSummary(snapshots),
223
+ data: snapshots,
224
+ };
225
+ }
226
+ case "daemon.health": {
227
+ const checks = await this.healthMonitor.runChecks();
228
+ const summary = checks.map((check) => `${check.name}:${check.status}:${check.message}`).join("\n");
229
+ return { ok: true, summary, data: checks };
230
+ }
231
+ case "daemon.logs":
232
+ return {
233
+ ok: true,
234
+ summary: "logs: use `ouro logs` to tail daemon and agent output",
235
+ message: "log streaming available via ouro logs",
236
+ data: { logDir: "~/.agentstate/daemon/logs" },
237
+ };
238
+ case "agent.start":
239
+ await this.processManager.startAgent(command.agent);
240
+ return { ok: true, message: `started ${command.agent}` };
241
+ case "agent.stop":
242
+ await this.processManager.stopAgent?.(command.agent);
243
+ return { ok: true, message: `stopped ${command.agent}` };
244
+ case "agent.restart":
245
+ await this.processManager.restartAgent?.(command.agent);
246
+ return { ok: true, message: `restarted ${command.agent}` };
247
+ case "cron.list": {
248
+ const jobs = this.scheduler.listJobs();
249
+ const summary = jobs.length === 0
250
+ ? "no cron jobs"
251
+ : jobs.map((job) => `${job.id}\t${job.schedule}\tlast=${job.lastRun ?? "never"}`).join("\n");
252
+ return { ok: true, summary, data: jobs };
253
+ }
254
+ case "cron.trigger": {
255
+ const result = await this.scheduler.triggerJob(command.jobId);
256
+ return { ok: result.ok, message: result.message };
257
+ }
258
+ case "message.send": {
259
+ const receipt = await this.router.send({
260
+ from: command.from,
261
+ to: command.to,
262
+ content: command.content,
263
+ priority: command.priority,
264
+ sessionId: command.sessionId,
265
+ taskRef: command.taskRef,
266
+ });
267
+ this.processManager.sendToAgent?.(command.to, { type: "message" });
268
+ return { ok: true, message: `queued message ${receipt.id}`, data: receipt };
269
+ }
270
+ case "message.poll": {
271
+ const messages = this.router.pollInbox(command.agent);
272
+ return {
273
+ ok: true,
274
+ summary: `${messages.length} messages`,
275
+ data: messages,
276
+ };
277
+ }
278
+ case "chat.connect":
279
+ await this.processManager.startAgent(command.agent);
280
+ return {
281
+ ok: true,
282
+ message: `connected to ${command.agent}`,
283
+ };
284
+ case "task.poke": {
285
+ const receipt = await this.router.send({
286
+ from: "ouro-poke",
287
+ to: command.agent,
288
+ content: `poke ${command.taskId}`,
289
+ priority: "high",
290
+ taskRef: command.taskId,
291
+ });
292
+ await this.scheduler.recordTaskRun?.(command.agent, command.taskId);
293
+ this.processManager.sendToAgent?.(command.agent, { type: "poke", taskId: command.taskId });
294
+ return {
295
+ ok: true,
296
+ message: `queued poke ${receipt.id}`,
297
+ data: receipt,
298
+ };
299
+ }
300
+ case "hatch.start":
301
+ return {
302
+ ok: true,
303
+ message: "hatch flow is stubbed in Gate 3 and completed in Gate 6",
304
+ };
305
+ default:
306
+ return {
307
+ ok: false,
308
+ error: `Unknown daemon command kind '${command.kind}'.`,
309
+ };
310
+ }
311
+ }
312
+ }
313
+ exports.OuroDaemon = OuroDaemon;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.playHatchAnimation = playHatchAnimation;
4
+ const runtime_1 = require("../../nerves/runtime");
5
+ const EGG = "\uD83E\uDD5A";
6
+ const SNAKE = "\uD83D\uDC0D";
7
+ const DOTS = " . . . ";
8
+ function wait(ms) {
9
+ return new Promise((resolve) => setTimeout(resolve, ms));
10
+ }
11
+ /**
12
+ * Play the hatch animation: egg -> dots -> snake + name.
13
+ * The writer function receives each chunk. Default writer is process.stderr.write.
14
+ */
15
+ async function playHatchAnimation(hatchlingName, writer) {
16
+ (0, runtime_1.emitNervesEvent)({
17
+ component: "daemon",
18
+ event: "daemon.hatch_animation_start",
19
+ message: "playing hatch animation",
20
+ meta: { hatchlingName },
21
+ });
22
+ const write = writer ?? ((text) => process.stderr.write(text));
23
+ write(`\n ${EGG}`);
24
+ await wait(400);
25
+ write(DOTS);
26
+ await wait(400);
27
+ write(`${SNAKE} \x1b[1m${hatchlingName}\x1b[0m\n\n`);
28
+ }