@katyella/legio 0.1.0
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.md +422 -0
- package/LICENSE +21 -0
- package/README.md +555 -0
- package/agents/builder.md +141 -0
- package/agents/coordinator.md +351 -0
- package/agents/cto.md +196 -0
- package/agents/gateway.md +276 -0
- package/agents/lead.md +281 -0
- package/agents/merger.md +156 -0
- package/agents/monitor.md +212 -0
- package/agents/reviewer.md +142 -0
- package/agents/scout.md +131 -0
- package/agents/supervisor.md +416 -0
- package/bin/legio.mjs +38 -0
- package/package.json +77 -0
- package/src/agents/checkpoint.test.ts +88 -0
- package/src/agents/checkpoint.ts +102 -0
- package/src/agents/hooks-deployer.test.ts +1820 -0
- package/src/agents/hooks-deployer.ts +574 -0
- package/src/agents/identity.test.ts +614 -0
- package/src/agents/identity.ts +385 -0
- package/src/agents/lifecycle.test.ts +202 -0
- package/src/agents/lifecycle.ts +184 -0
- package/src/agents/manifest.test.ts +558 -0
- package/src/agents/manifest.ts +297 -0
- package/src/agents/overlay.test.ts +592 -0
- package/src/agents/overlay.ts +316 -0
- package/src/beads/client.test.ts +210 -0
- package/src/beads/client.ts +227 -0
- package/src/beads/molecules.test.ts +320 -0
- package/src/beads/molecules.ts +209 -0
- package/src/commands/agents.test.ts +325 -0
- package/src/commands/agents.ts +286 -0
- package/src/commands/clean.test.ts +730 -0
- package/src/commands/clean.ts +653 -0
- package/src/commands/completions.test.ts +346 -0
- package/src/commands/completions.ts +950 -0
- package/src/commands/coordinator.test.ts +1524 -0
- package/src/commands/coordinator.ts +880 -0
- package/src/commands/costs.test.ts +1015 -0
- package/src/commands/costs.ts +473 -0
- package/src/commands/dashboard.test.ts +94 -0
- package/src/commands/dashboard.ts +607 -0
- package/src/commands/doctor.test.ts +295 -0
- package/src/commands/doctor.ts +213 -0
- package/src/commands/down.test.ts +308 -0
- package/src/commands/down.ts +124 -0
- package/src/commands/errors.test.ts +648 -0
- package/src/commands/errors.ts +255 -0
- package/src/commands/feed.test.ts +579 -0
- package/src/commands/feed.ts +368 -0
- package/src/commands/gateway.test.ts +698 -0
- package/src/commands/gateway.ts +419 -0
- package/src/commands/group.test.ts +262 -0
- package/src/commands/group.ts +539 -0
- package/src/commands/hooks.test.ts +292 -0
- package/src/commands/hooks.ts +210 -0
- package/src/commands/init.test.ts +211 -0
- package/src/commands/init.ts +622 -0
- package/src/commands/inspect.test.ts +670 -0
- package/src/commands/inspect.ts +455 -0
- package/src/commands/log.test.ts +1556 -0
- package/src/commands/log.ts +752 -0
- package/src/commands/logs.test.ts +379 -0
- package/src/commands/logs.ts +544 -0
- package/src/commands/mail.test.ts +1726 -0
- package/src/commands/mail.ts +926 -0
- package/src/commands/merge.test.ts +676 -0
- package/src/commands/merge.ts +374 -0
- package/src/commands/metrics.test.ts +444 -0
- package/src/commands/metrics.ts +150 -0
- package/src/commands/monitor.test.ts +151 -0
- package/src/commands/monitor.ts +394 -0
- package/src/commands/nudge.test.ts +230 -0
- package/src/commands/nudge.ts +373 -0
- package/src/commands/prime.test.ts +467 -0
- package/src/commands/prime.ts +386 -0
- package/src/commands/replay.test.ts +742 -0
- package/src/commands/replay.ts +367 -0
- package/src/commands/run.test.ts +443 -0
- package/src/commands/run.ts +365 -0
- package/src/commands/server.test.ts +626 -0
- package/src/commands/server.ts +298 -0
- package/src/commands/sling.test.ts +810 -0
- package/src/commands/sling.ts +700 -0
- package/src/commands/spec.test.ts +206 -0
- package/src/commands/spec.ts +171 -0
- package/src/commands/status.test.ts +276 -0
- package/src/commands/status.ts +339 -0
- package/src/commands/stop.test.ts +357 -0
- package/src/commands/stop.ts +119 -0
- package/src/commands/supervisor.test.ts +186 -0
- package/src/commands/supervisor.ts +544 -0
- package/src/commands/trace.test.ts +746 -0
- package/src/commands/trace.ts +332 -0
- package/src/commands/up.test.ts +597 -0
- package/src/commands/up.ts +275 -0
- package/src/commands/watch.test.ts +152 -0
- package/src/commands/watch.ts +238 -0
- package/src/commands/worktree.test.ts +648 -0
- package/src/commands/worktree.ts +266 -0
- package/src/config.test.ts +496 -0
- package/src/config.ts +616 -0
- package/src/doctor/agents.test.ts +448 -0
- package/src/doctor/agents.ts +396 -0
- package/src/doctor/config-check.test.ts +184 -0
- package/src/doctor/config-check.ts +185 -0
- package/src/doctor/consistency.test.ts +645 -0
- package/src/doctor/consistency.ts +294 -0
- package/src/doctor/databases.test.ts +284 -0
- package/src/doctor/databases.ts +211 -0
- package/src/doctor/dependencies.test.ts +150 -0
- package/src/doctor/dependencies.ts +179 -0
- package/src/doctor/logs.test.ts +244 -0
- package/src/doctor/logs.ts +295 -0
- package/src/doctor/merge-queue.test.ts +210 -0
- package/src/doctor/merge-queue.ts +144 -0
- package/src/doctor/structure.test.ts +285 -0
- package/src/doctor/structure.ts +195 -0
- package/src/doctor/types.ts +37 -0
- package/src/doctor/version.test.ts +130 -0
- package/src/doctor/version.ts +131 -0
- package/src/e2e/chat-flow.test.ts +346 -0
- package/src/e2e/init-sling-lifecycle.test.ts +288 -0
- package/src/errors.test.ts +21 -0
- package/src/errors.ts +246 -0
- package/src/events/store.test.ts +660 -0
- package/src/events/store.ts +344 -0
- package/src/events/tool-filter.test.ts +330 -0
- package/src/events/tool-filter.ts +126 -0
- package/src/global-setup.ts +14 -0
- package/src/index.ts +339 -0
- package/src/insights/analyzer.test.ts +466 -0
- package/src/insights/analyzer.ts +203 -0
- package/src/logging/color.test.ts +118 -0
- package/src/logging/color.ts +71 -0
- package/src/logging/logger.test.ts +812 -0
- package/src/logging/logger.ts +266 -0
- package/src/logging/reporter.test.ts +258 -0
- package/src/logging/reporter.ts +109 -0
- package/src/logging/sanitizer.test.ts +190 -0
- package/src/logging/sanitizer.ts +57 -0
- package/src/mail/broadcast.test.ts +203 -0
- package/src/mail/broadcast.ts +92 -0
- package/src/mail/client.test.ts +873 -0
- package/src/mail/client.ts +236 -0
- package/src/mail/store.test.ts +815 -0
- package/src/mail/store.ts +402 -0
- package/src/merge/queue.test.ts +449 -0
- package/src/merge/queue.ts +262 -0
- package/src/merge/resolver.test.ts +1453 -0
- package/src/merge/resolver.ts +759 -0
- package/src/metrics/store.test.ts +1167 -0
- package/src/metrics/store.ts +511 -0
- package/src/metrics/summary.test.ts +397 -0
- package/src/metrics/summary.ts +178 -0
- package/src/metrics/transcript.test.ts +643 -0
- package/src/metrics/transcript.ts +351 -0
- package/src/mulch/client.test.ts +547 -0
- package/src/mulch/client.ts +416 -0
- package/src/server/audit-store.test.ts +384 -0
- package/src/server/audit-store.ts +257 -0
- package/src/server/headless.test.ts +180 -0
- package/src/server/headless.ts +151 -0
- package/src/server/index.test.ts +241 -0
- package/src/server/index.ts +317 -0
- package/src/server/public/app.js +187 -0
- package/src/server/public/apple-touch-icon.png +0 -0
- package/src/server/public/components/agent-badge.js +37 -0
- package/src/server/public/components/data-table.js +114 -0
- package/src/server/public/components/gateway-chat.js +256 -0
- package/src/server/public/components/issue-card.js +96 -0
- package/src/server/public/components/layout.js +88 -0
- package/src/server/public/components/message-bubble.js +120 -0
- package/src/server/public/components/stat-card.js +26 -0
- package/src/server/public/components/terminal-panel.js +140 -0
- package/src/server/public/favicon-16.png +0 -0
- package/src/server/public/favicon-32.png +0 -0
- package/src/server/public/favicon.ico +0 -0
- package/src/server/public/favicon.png +0 -0
- package/src/server/public/index.html +64 -0
- package/src/server/public/lib/api.js +35 -0
- package/src/server/public/lib/markdown.js +8 -0
- package/src/server/public/lib/preact-setup.js +8 -0
- package/src/server/public/lib/state.js +99 -0
- package/src/server/public/lib/utils.js +309 -0
- package/src/server/public/lib/ws.js +79 -0
- package/src/server/public/views/chat.js +983 -0
- package/src/server/public/views/costs.js +692 -0
- package/src/server/public/views/dashboard.js +781 -0
- package/src/server/public/views/gateway-chat.js +622 -0
- package/src/server/public/views/inspect.js +399 -0
- package/src/server/public/views/issues.js +470 -0
- package/src/server/public/views/setup.js +94 -0
- package/src/server/public/views/task-detail.js +422 -0
- package/src/server/routes.test.ts +3816 -0
- package/src/server/routes.ts +1964 -0
- package/src/server/websocket.test.ts +288 -0
- package/src/server/websocket.ts +196 -0
- package/src/sessions/compat.test.ts +109 -0
- package/src/sessions/compat.ts +17 -0
- package/src/sessions/store.test.ts +969 -0
- package/src/sessions/store.ts +480 -0
- package/src/test-helpers.test.ts +97 -0
- package/src/test-helpers.ts +143 -0
- package/src/types.ts +708 -0
- package/src/watchdog/daemon.test.ts +1233 -0
- package/src/watchdog/daemon.ts +533 -0
- package/src/watchdog/health.test.ts +371 -0
- package/src/watchdog/health.ts +248 -0
- package/src/watchdog/triage.test.ts +162 -0
- package/src/watchdog/triage.ts +193 -0
- package/src/worktree/manager.test.ts +444 -0
- package/src/worktree/manager.ts +224 -0
- package/src/worktree/tmux.test.ts +1238 -0
- package/src/worktree/tmux.ts +644 -0
- package/templates/CLAUDE.md.tmpl +89 -0
- package/templates/hooks.json.tmpl +132 -0
- package/templates/overlay.md.tmpl +79 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `legio prime` command.
|
|
3
|
+
*
|
|
4
|
+
* Loads context for the orchestrator or a specific agent and outputs it
|
|
5
|
+
* to stdout for injection into Claude Code's context via hooks.
|
|
6
|
+
*
|
|
7
|
+
* Called by the SessionStart hook.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { spawn } from "node:child_process";
|
|
11
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { loadCheckpoint } from "../agents/checkpoint.ts";
|
|
14
|
+
import { loadIdentity } from "../agents/identity.ts";
|
|
15
|
+
import { createManifestLoader } from "../agents/manifest.ts";
|
|
16
|
+
import { loadConfig } from "../config.ts";
|
|
17
|
+
import { AgentError } from "../errors.ts";
|
|
18
|
+
import { createMetricsStore } from "../metrics/store.ts";
|
|
19
|
+
import { createMulchClient } from "../mulch/client.ts";
|
|
20
|
+
import { openSessionStore } from "../sessions/compat.ts";
|
|
21
|
+
import type { AgentIdentity, AgentManifest, SessionCheckpoint, SessionMetrics } from "../types.ts";
|
|
22
|
+
import { getCurrentSessionName } from "../worktree/tmux.ts";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Gitignore content for .legio/.gitignore.
|
|
26
|
+
* TODO: Import from init.ts once it's exported (parallel branch change).
|
|
27
|
+
* Wildcard+whitelist pattern: ignore everything except tracked config files.
|
|
28
|
+
*/
|
|
29
|
+
const LEGIO_GITIGNORE = `# Wildcard+whitelist: ignore everything, whitelist tracked files
|
|
30
|
+
# Auto-healed by legio prime on each session start
|
|
31
|
+
*
|
|
32
|
+
!.gitignore
|
|
33
|
+
!config.yaml
|
|
34
|
+
!agent-manifest.json
|
|
35
|
+
!hooks.json
|
|
36
|
+
!groups.json
|
|
37
|
+
!agent-defs/
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Parse CLI flags from the args array.
|
|
42
|
+
*
|
|
43
|
+
* Supports:
|
|
44
|
+
* - `--agent <name>` — Prime for a specific agent
|
|
45
|
+
* - `--compact` — Output reduced context
|
|
46
|
+
*/
|
|
47
|
+
function parseArgs(args: string[]): { agentName: string | null; compact: boolean } {
|
|
48
|
+
let agentName: string | null = null;
|
|
49
|
+
let compact = false;
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < args.length; i++) {
|
|
52
|
+
const arg = args[i];
|
|
53
|
+
if (arg === "--agent") {
|
|
54
|
+
const next = args[i + 1];
|
|
55
|
+
if (next === undefined || next.startsWith("--")) {
|
|
56
|
+
throw new AgentError("--agent requires a name argument");
|
|
57
|
+
}
|
|
58
|
+
agentName = next;
|
|
59
|
+
i++; // Skip the value
|
|
60
|
+
} else if (arg === "--compact") {
|
|
61
|
+
compact = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { agentName, compact };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Format the agent manifest section for output.
|
|
70
|
+
*/
|
|
71
|
+
function formatManifest(manifest: AgentManifest): string {
|
|
72
|
+
const lines: string[] = [];
|
|
73
|
+
for (const [name, def] of Object.entries(manifest.agents)) {
|
|
74
|
+
const caps = def.capabilities.join(", ");
|
|
75
|
+
const spawn = def.canSpawn ? " (can spawn)" : "";
|
|
76
|
+
lines.push(`- **${name}** [${def.model}]: ${caps}${spawn}`);
|
|
77
|
+
}
|
|
78
|
+
return lines.length > 0 ? lines.join("\n") : "No agents registered.";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Format recent session metrics for output.
|
|
83
|
+
*/
|
|
84
|
+
function formatMetrics(sessions: SessionMetrics[]): string {
|
|
85
|
+
if (sessions.length === 0) {
|
|
86
|
+
return "No recent sessions.";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const lines: string[] = [];
|
|
90
|
+
for (const s of sessions) {
|
|
91
|
+
const status = s.completedAt !== null ? "completed" : "in-progress";
|
|
92
|
+
const duration = s.durationMs > 0 ? ` (${Math.round(s.durationMs / 1000)}s)` : "";
|
|
93
|
+
const merge = s.mergeResult !== null ? ` [${s.mergeResult}]` : "";
|
|
94
|
+
lines.push(`- ${s.agentName} (${s.capability}): ${s.beadId} — ${status}${duration}${merge}`);
|
|
95
|
+
}
|
|
96
|
+
return lines.join("\n");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Format agent identity for output.
|
|
101
|
+
*/
|
|
102
|
+
function formatIdentity(identity: AgentIdentity): string {
|
|
103
|
+
const lines: string[] = [];
|
|
104
|
+
lines.push(`Name: ${identity.name}`);
|
|
105
|
+
lines.push(`Capability: ${identity.capability}`);
|
|
106
|
+
lines.push(`Sessions completed: ${identity.sessionsCompleted}`);
|
|
107
|
+
|
|
108
|
+
if (identity.expertiseDomains.length > 0) {
|
|
109
|
+
lines.push(`Expertise: ${identity.expertiseDomains.join(", ")}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (identity.recentTasks.length > 0) {
|
|
113
|
+
lines.push("Recent tasks:");
|
|
114
|
+
for (const task of identity.recentTasks) {
|
|
115
|
+
lines.push(` - ${task.beadId}: ${task.summary} (${task.completedAt})`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return lines.join("\n");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Format checkpoint recovery section for compact priming.
|
|
124
|
+
*/
|
|
125
|
+
function formatCheckpointRecovery(checkpoint: SessionCheckpoint): string {
|
|
126
|
+
const lines: string[] = [];
|
|
127
|
+
lines.push("\n## Session Recovery");
|
|
128
|
+
lines.push("");
|
|
129
|
+
lines.push("You are resuming from a previous session that was compacted.");
|
|
130
|
+
lines.push("");
|
|
131
|
+
lines.push(`**Progress so far:** ${checkpoint.progressSummary}`);
|
|
132
|
+
lines.push(`**Files modified:** ${checkpoint.filesModified.join(", ") || "none"}`);
|
|
133
|
+
lines.push(`**Pending work:** ${checkpoint.pendingWork}`);
|
|
134
|
+
lines.push(`**Branch:** ${checkpoint.currentBranch}`);
|
|
135
|
+
return lines.join("\n");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Auto-heal .legio/.gitignore if its content differs from the template.
|
|
140
|
+
* Ensures existing projects get updated gitignore on session start.
|
|
141
|
+
*/
|
|
142
|
+
async function healGitignore(legioDir: string): Promise<void> {
|
|
143
|
+
const gitignorePath = join(legioDir, ".gitignore");
|
|
144
|
+
try {
|
|
145
|
+
const current = await readFile(gitignorePath, "utf-8");
|
|
146
|
+
if (current === LEGIO_GITIGNORE) {
|
|
147
|
+
return; // Already up to date
|
|
148
|
+
}
|
|
149
|
+
} catch {
|
|
150
|
+
// File does not exist — write it fresh
|
|
151
|
+
}
|
|
152
|
+
await writeFile(gitignorePath, LEGIO_GITIGNORE);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Prime command entry point.
|
|
157
|
+
*
|
|
158
|
+
* Gathers project state and outputs context to stdout for injection
|
|
159
|
+
* into Claude Code's context.
|
|
160
|
+
*
|
|
161
|
+
* @param args - CLI arguments after "prime" subcommand
|
|
162
|
+
*/
|
|
163
|
+
const PRIME_HELP = `legio prime — Load context for orchestrator/agent
|
|
164
|
+
|
|
165
|
+
Usage: legio prime [--agent <name>] [--compact]
|
|
166
|
+
|
|
167
|
+
Options:
|
|
168
|
+
--agent <name> Prime for a specific agent (default: orchestrator)
|
|
169
|
+
--compact Output reduced context (for PreCompact hook)
|
|
170
|
+
--help, -h Show this help`;
|
|
171
|
+
|
|
172
|
+
export async function primeCommand(args: string[]): Promise<void> {
|
|
173
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
174
|
+
process.stdout.write(`${PRIME_HELP}\n`);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const { agentName, compact } = parseArgs(args);
|
|
179
|
+
|
|
180
|
+
// 1. Load config
|
|
181
|
+
const config = await loadConfig(process.cwd());
|
|
182
|
+
|
|
183
|
+
// 2. Auto-heal .legio/.gitignore
|
|
184
|
+
const legioDir = join(config.project.root, ".legio");
|
|
185
|
+
await healGitignore(legioDir);
|
|
186
|
+
|
|
187
|
+
// 3. Load mulch expertise (optional — skip on failure)
|
|
188
|
+
let expertiseOutput: string | null = null;
|
|
189
|
+
if (!compact && config.mulch.enabled) {
|
|
190
|
+
try {
|
|
191
|
+
const mulch = createMulchClient(config.project.root);
|
|
192
|
+
const domains = config.mulch.domains.length > 0 ? config.mulch.domains : undefined;
|
|
193
|
+
expertiseOutput = await mulch.prime(domains, config.mulch.primeFormat);
|
|
194
|
+
} catch {
|
|
195
|
+
// Mulch is optional — silently skip if it fails
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 4. Output context (orchestrator or agent)
|
|
200
|
+
if (agentName !== null) {
|
|
201
|
+
// === Agent priming ===
|
|
202
|
+
await outputAgentContext(config, agentName, compact, expertiseOutput);
|
|
203
|
+
} else {
|
|
204
|
+
// === Orchestrator priming ===
|
|
205
|
+
await outputOrchestratorContext(config, compact, expertiseOutput);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Output context for a specific agent.
|
|
211
|
+
*/
|
|
212
|
+
async function outputAgentContext(
|
|
213
|
+
config: Awaited<ReturnType<typeof loadConfig>>,
|
|
214
|
+
agentName: string,
|
|
215
|
+
compact: boolean,
|
|
216
|
+
expertiseOutput: string | null,
|
|
217
|
+
): Promise<void> {
|
|
218
|
+
const sections: string[] = [];
|
|
219
|
+
|
|
220
|
+
sections.push(`# Agent Context: ${agentName}`);
|
|
221
|
+
|
|
222
|
+
// Check if the agent exists in the SessionStore or has an identity file
|
|
223
|
+
const legioDir = join(config.project.root, ".legio");
|
|
224
|
+
const { store } = openSessionStore(legioDir);
|
|
225
|
+
let sessionExists = false;
|
|
226
|
+
let boundSession: { beadId: string } | null = null;
|
|
227
|
+
try {
|
|
228
|
+
const agentSession = store.getByName(agentName);
|
|
229
|
+
sessionExists = agentSession !== null;
|
|
230
|
+
if (
|
|
231
|
+
agentSession &&
|
|
232
|
+
agentSession.state !== "completed" &&
|
|
233
|
+
agentSession.state !== "zombie" &&
|
|
234
|
+
agentSession.beadId
|
|
235
|
+
) {
|
|
236
|
+
boundSession = { beadId: agentSession.beadId };
|
|
237
|
+
}
|
|
238
|
+
} finally {
|
|
239
|
+
store.close();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Identity section
|
|
243
|
+
let identity: AgentIdentity | null = null;
|
|
244
|
+
try {
|
|
245
|
+
const baseDir = join(config.project.root, ".legio", "agents");
|
|
246
|
+
identity = await loadIdentity(baseDir, agentName);
|
|
247
|
+
} catch {
|
|
248
|
+
// Identity may not exist yet
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Warn if agent is completely unknown (no session and no identity)
|
|
252
|
+
if (!sessionExists && identity === null) {
|
|
253
|
+
process.stderr.write(
|
|
254
|
+
`Warning: agent "${agentName}" not found in sessions or identity store.\n`,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
sections.push("\n## Identity");
|
|
259
|
+
if (identity !== null) {
|
|
260
|
+
sections.push(formatIdentity(identity));
|
|
261
|
+
} else {
|
|
262
|
+
sections.push("New agent - no prior sessions");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Activation context: if agent has a bound task, inject it
|
|
266
|
+
if (boundSession) {
|
|
267
|
+
sections.push("\n## Activation");
|
|
268
|
+
sections.push(`You have a bound task: **${boundSession.beadId}**`);
|
|
269
|
+
sections.push("Read your overlay at `.claude/CLAUDE.md` and begin working immediately.");
|
|
270
|
+
sections.push("Do not wait for dispatch mail. Your assignment was bound at spawn time.");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// In compact mode, check for checkpoint recovery
|
|
274
|
+
if (compact) {
|
|
275
|
+
const baseDir = join(config.project.root, ".legio", "agents");
|
|
276
|
+
const checkpoint = await loadCheckpoint(baseDir, agentName);
|
|
277
|
+
if (checkpoint !== null) {
|
|
278
|
+
sections.push(formatCheckpointRecovery(checkpoint));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// In compact mode, skip expertise
|
|
283
|
+
if (!compact && expertiseOutput !== null) {
|
|
284
|
+
sections.push("\n## Expertise");
|
|
285
|
+
sections.push(expertiseOutput.trim());
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
process.stdout.write(`${sections.join("\n")}\n`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Output context for the orchestrator.
|
|
293
|
+
*/
|
|
294
|
+
async function outputOrchestratorContext(
|
|
295
|
+
config: Awaited<ReturnType<typeof loadConfig>>,
|
|
296
|
+
compact: boolean,
|
|
297
|
+
expertiseOutput: string | null,
|
|
298
|
+
): Promise<void> {
|
|
299
|
+
// Register orchestrator tmux session for reverse-nudge (agents → orchestrator)
|
|
300
|
+
try {
|
|
301
|
+
const tmuxSession = await getCurrentSessionName();
|
|
302
|
+
if (tmuxSession) {
|
|
303
|
+
const regPath = join(config.project.root, ".legio", "orchestrator-tmux.json");
|
|
304
|
+
await writeFile(
|
|
305
|
+
regPath,
|
|
306
|
+
`${JSON.stringify({ tmuxSession, registeredAt: new Date().toISOString() }, null, "\t")}\n`,
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
} catch {
|
|
310
|
+
// Tmux detection is optional — silently skip
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Record the orchestrator's current branch for merge targeting
|
|
314
|
+
let sessionBranch: string | null = null;
|
|
315
|
+
try {
|
|
316
|
+
const branch = await new Promise<string>((res) => {
|
|
317
|
+
let out = "";
|
|
318
|
+
const p = spawn("git", ["symbolic-ref", "--short", "HEAD"], {
|
|
319
|
+
cwd: config.project.root,
|
|
320
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
321
|
+
});
|
|
322
|
+
p.stdout.on("data", (d: Buffer) => {
|
|
323
|
+
out += d.toString();
|
|
324
|
+
});
|
|
325
|
+
p.on("close", () => res(out.trim()));
|
|
326
|
+
p.on("error", () => res(""));
|
|
327
|
+
});
|
|
328
|
+
if (branch) {
|
|
329
|
+
sessionBranch = branch;
|
|
330
|
+
const sessionBranchPath = join(config.project.root, ".legio", "session-branch.txt");
|
|
331
|
+
await writeFile(sessionBranchPath, `${branch}\n`);
|
|
332
|
+
}
|
|
333
|
+
} catch {
|
|
334
|
+
// Branch detection is optional — silently skip
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const sections: string[] = [];
|
|
338
|
+
|
|
339
|
+
// Project section
|
|
340
|
+
sections.push("# Legio Context");
|
|
341
|
+
sections.push(`\n## Project: ${config.project.name}`);
|
|
342
|
+
sections.push(`Canonical branch: ${config.project.canonicalBranch}`);
|
|
343
|
+
if (sessionBranch && sessionBranch !== config.project.canonicalBranch) {
|
|
344
|
+
sections.push(`Session branch: ${sessionBranch} (merge target)`);
|
|
345
|
+
}
|
|
346
|
+
sections.push(`Max concurrent agents: ${config.agents.maxConcurrent}`);
|
|
347
|
+
sections.push(`Max depth: ${config.agents.maxDepth}`);
|
|
348
|
+
|
|
349
|
+
// Agent manifest section
|
|
350
|
+
sections.push("\n## Agent Manifest");
|
|
351
|
+
try {
|
|
352
|
+
const manifestPath = join(config.project.root, config.agents.manifestPath);
|
|
353
|
+
const baseDir = join(config.project.root, config.agents.baseDir);
|
|
354
|
+
const loader = createManifestLoader(manifestPath, baseDir);
|
|
355
|
+
const manifest = await loader.load();
|
|
356
|
+
sections.push(formatManifest(manifest));
|
|
357
|
+
} catch {
|
|
358
|
+
sections.push("No agent manifest found.");
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// In compact mode, skip metrics and expertise
|
|
362
|
+
if (!compact) {
|
|
363
|
+
// Recent activity section
|
|
364
|
+
sections.push("\n## Recent Activity");
|
|
365
|
+
try {
|
|
366
|
+
const metricsPath = join(config.project.root, ".legio", "metrics.db");
|
|
367
|
+
const store = createMetricsStore(metricsPath);
|
|
368
|
+
try {
|
|
369
|
+
const sessions = store.getRecentSessions(5);
|
|
370
|
+
sections.push(formatMetrics(sessions));
|
|
371
|
+
} finally {
|
|
372
|
+
store.close();
|
|
373
|
+
}
|
|
374
|
+
} catch {
|
|
375
|
+
sections.push("No metrics available.");
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Expertise section
|
|
379
|
+
if (expertiseOutput !== null) {
|
|
380
|
+
sections.push("\n## Expertise");
|
|
381
|
+
sections.push(expertiseOutput.trim());
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
process.stdout.write(`${sections.join("\n")}\n`);
|
|
386
|
+
}
|