chapterhouse 0.3.0 → 0.3.2
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 +72 -0
- package/dist/api/auth.js +4 -3
- package/dist/api/auth.test.js +27 -39
- package/dist/api/server.js +3 -0
- package/dist/api/team.js +4 -2
- package/dist/cli.js +8 -2
- package/dist/copilot/episode-writer.js +4 -2
- package/dist/copilot/orchestrator.js +312 -356
- package/dist/copilot/orchestrator.test.js +40 -1
- package/dist/copilot/session-manager.js +307 -0
- package/dist/copilot/session-manager.test.js +292 -0
- package/dist/copilot/system-message.js +2 -1
- package/dist/copilot/system-message.test.js +8 -0
- package/dist/daemon-install.js +62 -3
- package/dist/daemon-install.test.js +35 -0
- package/dist/daemon.js +21 -3
- package/dist/integrations/teams-notify.js +3 -1
- package/dist/squad/index.js +1 -0
- package/dist/squad/init-cli.js +109 -0
- package/dist/squad/init.js +395 -0
- package/dist/squad/init.test.js +351 -0
- package/dist/squad/mirror.js +4 -2
- package/dist/squad/mirror.scheduler.js +9 -7
- package/dist/version.js +7 -0
- package/dist/wiki/team-sync.js +3 -1
- package/package.json +2 -3
- package/web/dist/assets/index-Dpt-MCe8.css +10 -0
- package/web/dist/assets/index-NmxVWGY1.js +205 -0
- package/web/dist/assets/index-NmxVWGY1.js.map +1 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-CxT9905O.css +0 -10
- package/web/dist/assets/index-DI3rnGm-.js +0 -142
- package/web/dist/assets/index-DI3rnGm-.js.map +0 -1
package/dist/daemon-install.js
CHANGED
|
@@ -53,9 +53,35 @@ export function resolveChapterhouseBin() {
|
|
|
53
53
|
return argv1;
|
|
54
54
|
return "chapterhouse";
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Compose a rich PATH for launchd-spawned children (macOS).
|
|
58
|
+
* Prepends the installing shell's $PATH so any tool already on the user's PATH
|
|
59
|
+
* is available to sub-agents, then adds Homebrew and language-toolchain dirs.
|
|
60
|
+
*/
|
|
61
|
+
function composeMacOSPath(binDir, shellPath) {
|
|
62
|
+
const home = homedir();
|
|
63
|
+
return [
|
|
64
|
+
shellPath,
|
|
65
|
+
binDir,
|
|
66
|
+
"/opt/homebrew/bin",
|
|
67
|
+
"/usr/local/bin",
|
|
68
|
+
`${home}/.cargo/bin`,
|
|
69
|
+
`${home}/.bun/bin`,
|
|
70
|
+
`${home}/.volta/bin`,
|
|
71
|
+
`${home}/.local/bin`,
|
|
72
|
+
"/usr/bin",
|
|
73
|
+
"/bin",
|
|
74
|
+
"/usr/sbin",
|
|
75
|
+
"/sbin",
|
|
76
|
+
]
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.join(":");
|
|
79
|
+
}
|
|
56
80
|
/** Generate the launchd plist XML string. */
|
|
57
81
|
export function generatePlist(options) {
|
|
58
82
|
const label = options.label ?? DAEMON_LABEL;
|
|
83
|
+
const shellPath = options.shellPath ?? process.env.PATH ?? "";
|
|
84
|
+
const richPath = composeMacOSPath(dirname(options.binPath), shellPath);
|
|
59
85
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
60
86
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
61
87
|
<plist version="1.0">
|
|
@@ -78,16 +104,39 @@ export function generatePlist(options) {
|
|
|
78
104
|
<key>EnvironmentVariables</key>
|
|
79
105
|
<dict>
|
|
80
106
|
<key>PATH</key>
|
|
81
|
-
<string>${
|
|
107
|
+
<string>${richPath}</string>
|
|
82
108
|
</dict>
|
|
83
109
|
</dict>
|
|
84
110
|
</plist>
|
|
85
111
|
`;
|
|
86
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Compose a rich PATH for systemd-spawned children (Linux).
|
|
115
|
+
* Uses systemd's %h specifier for the home directory so the unit stays portable
|
|
116
|
+
* across uid changes. Prepends the installing shell's $PATH for robustness.
|
|
117
|
+
*/
|
|
118
|
+
function composeSystemdPath(binDir, shellPath) {
|
|
119
|
+
return [
|
|
120
|
+
shellPath,
|
|
121
|
+
binDir,
|
|
122
|
+
"/home/linuxbrew/.linuxbrew/bin",
|
|
123
|
+
"%h/.cargo/bin",
|
|
124
|
+
"%h/.bun/bin",
|
|
125
|
+
"%h/.volta/bin",
|
|
126
|
+
"%h/.local/bin",
|
|
127
|
+
"/opt/homebrew/bin",
|
|
128
|
+
"/usr/local/bin",
|
|
129
|
+
"/usr/bin",
|
|
130
|
+
"/bin",
|
|
131
|
+
]
|
|
132
|
+
.filter(Boolean)
|
|
133
|
+
.join(":");
|
|
134
|
+
}
|
|
87
135
|
/** Generate the systemd user unit file string. */
|
|
88
136
|
export function generateSystemdUnit(options) {
|
|
89
137
|
const description = options.description ?? "Chapterhouse AI assistant daemon";
|
|
90
|
-
const
|
|
138
|
+
const shellPath = options.shellPath ?? process.env.PATH ?? "";
|
|
139
|
+
const richPath = composeSystemdPath(dirname(options.binPath), shellPath);
|
|
91
140
|
return `[Unit]
|
|
92
141
|
Description=${description}
|
|
93
142
|
After=network.target
|
|
@@ -97,7 +146,17 @@ Type=simple
|
|
|
97
146
|
ExecStart=${options.binPath} start
|
|
98
147
|
Restart=on-failure
|
|
99
148
|
RestartSec=5s
|
|
100
|
-
|
|
149
|
+
# Rich PATH ensures spawned sub-agents and tools can find CLI dependencies
|
|
150
|
+
# (Linuxbrew, Homebrew, Cargo, Bun, Volta). %h is expanded to $HOME by systemd.
|
|
151
|
+
Environment="PATH=${richPath}"
|
|
152
|
+
# ── Timing contract (three layers) ─────────────────────────────────────────────
|
|
153
|
+
# Layer 1 │ Orchestrator turn timeout │ CHAPTERHOUSE_ORCHESTRATOR_TIMEOUT_MS (default 1800 s)
|
|
154
|
+
# Layer 2 │ Daemon graceful shutdown │ CHAPTERHOUSE_SHUTDOWN_TIMEOUT_MS (default 60 s)
|
|
155
|
+
# Layer 3 │ systemd force-kill window │ TimeoutStopSec = 90 s (must exceed layer 2)
|
|
156
|
+
# Each layer must exceed the one above it. Do NOT tighten TimeoutStopSec without
|
|
157
|
+
# first reducing CHAPTERHOUSE_SHUTDOWN_TIMEOUT_MS, or in-flight LLM streams will
|
|
158
|
+
# be killed mid-response.
|
|
159
|
+
TimeoutStopSec=90
|
|
101
160
|
|
|
102
161
|
[Install]
|
|
103
162
|
WantedBy=default.target
|
|
@@ -41,6 +41,23 @@ test("generatePlist includes bin directory in PATH environment", () => {
|
|
|
41
41
|
const plist = generatePlist({ binPath: MOCK_BIN_DARWIN, logPath: MOCK_LOG_DARWIN });
|
|
42
42
|
assert.ok(plist.includes(dirname(MOCK_BIN_DARWIN)), "plist PATH should include bin directory");
|
|
43
43
|
});
|
|
44
|
+
test("generatePlist PATH includes Homebrew and toolchain directories", () => {
|
|
45
|
+
const plist = generatePlist({ binPath: MOCK_BIN_DARWIN, logPath: MOCK_LOG_DARWIN, shellPath: "" });
|
|
46
|
+
const home = homedir();
|
|
47
|
+
assert.ok(plist.includes("/opt/homebrew/bin"), "plist PATH should include /opt/homebrew/bin");
|
|
48
|
+
assert.ok(plist.includes(`${home}/.cargo/bin`), "plist PATH should include ~/.cargo/bin");
|
|
49
|
+
assert.ok(plist.includes(`${home}/.bun/bin`), "plist PATH should include ~/.bun/bin");
|
|
50
|
+
assert.ok(plist.includes(`${home}/.volta/bin`), "plist PATH should include ~/.volta/bin");
|
|
51
|
+
assert.ok(plist.includes(`${home}/.local/bin`), "plist PATH should include ~/.local/bin");
|
|
52
|
+
});
|
|
53
|
+
test("generatePlist PATH prepends installing shell PATH", () => {
|
|
54
|
+
const shellPath = "/custom/shell/bin:/another/dir";
|
|
55
|
+
const plist = generatePlist({ binPath: MOCK_BIN_DARWIN, logPath: MOCK_LOG_DARWIN, shellPath });
|
|
56
|
+
assert.ok(plist.includes(shellPath), "plist PATH should include the installing shell PATH");
|
|
57
|
+
const pathStart = plist.indexOf(shellPath);
|
|
58
|
+
const binDirStart = plist.indexOf(dirname(MOCK_BIN_DARWIN), plist.indexOf("<key>PATH</key>"));
|
|
59
|
+
assert.ok(pathStart < binDirStart, "shell PATH should appear before bin dir in composed PATH");
|
|
60
|
+
});
|
|
44
61
|
test("generatePlist respects a custom label override", () => {
|
|
45
62
|
const customLabel = "com.example.test";
|
|
46
63
|
const plist = generatePlist({ binPath: MOCK_BIN_DARWIN, logPath: MOCK_LOG_DARWIN, label: customLabel });
|
|
@@ -78,6 +95,24 @@ test("generateSystemdUnit includes bin directory in PATH", () => {
|
|
|
78
95
|
const unit = generateSystemdUnit({ binPath: MOCK_BIN_LINUX });
|
|
79
96
|
assert.ok(unit.includes(dirname(MOCK_BIN_LINUX)), "unit PATH should include bin directory");
|
|
80
97
|
});
|
|
98
|
+
test("generateSystemdUnit PATH includes Linuxbrew and toolchain directories", () => {
|
|
99
|
+
const unit = generateSystemdUnit({ binPath: MOCK_BIN_LINUX, shellPath: "" });
|
|
100
|
+
assert.ok(unit.includes("/home/linuxbrew/.linuxbrew/bin"), "unit PATH should include Linuxbrew");
|
|
101
|
+
assert.ok(unit.includes("%h/.cargo/bin"), "unit PATH should include %h/.cargo/bin");
|
|
102
|
+
assert.ok(unit.includes("%h/.bun/bin"), "unit PATH should include %h/.bun/bin");
|
|
103
|
+
assert.ok(unit.includes("%h/.volta/bin"), "unit PATH should include %h/.volta/bin");
|
|
104
|
+
assert.ok(unit.includes("%h/.local/bin"), "unit PATH should include %h/.local/bin");
|
|
105
|
+
assert.ok(unit.includes("/opt/homebrew/bin"), "unit PATH should include /opt/homebrew/bin");
|
|
106
|
+
});
|
|
107
|
+
test("generateSystemdUnit PATH prepends installing shell PATH", () => {
|
|
108
|
+
const shellPath = "/custom/shell/bin";
|
|
109
|
+
const unit = generateSystemdUnit({ binPath: MOCK_BIN_LINUX, shellPath });
|
|
110
|
+
assert.ok(unit.includes(shellPath), "unit PATH should include the installing shell PATH");
|
|
111
|
+
});
|
|
112
|
+
test("generateSystemdUnit includes TimeoutStopSec=90", () => {
|
|
113
|
+
const unit = generateSystemdUnit({ binPath: MOCK_BIN_LINUX });
|
|
114
|
+
assert.ok(unit.includes("TimeoutStopSec=90"), "unit should set TimeoutStopSec=90 for timing contract");
|
|
115
|
+
});
|
|
81
116
|
test("generateSystemdUnit has all three sections", () => {
|
|
82
117
|
const unit = generateSystemdUnit({ binPath: MOCK_BIN_LINUX });
|
|
83
118
|
assert.ok(unit.includes("[Unit]"), "unit should have [Unit] section");
|
package/dist/daemon.js
CHANGED
|
@@ -17,8 +17,26 @@ import { StandupScheduler } from "./copilot/standup.js";
|
|
|
17
17
|
import { DecisionsSyncScheduler } from "./squad/mirror.scheduler.js";
|
|
18
18
|
import { registerShutdownSignals } from "./shutdown-signals.js";
|
|
19
19
|
import { logger } from "./util/logger.js";
|
|
20
|
+
import { CHAPTERHOUSE_VERSION } from "./version.js";
|
|
20
21
|
const log = logger.child({ module: "daemon" });
|
|
21
22
|
const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
23
|
+
/**
|
|
24
|
+
* How long the daemon waits for in-flight work to finish before forcing an exit.
|
|
25
|
+
* Layer 2 of the 3-layer timing contract:
|
|
26
|
+
* Layer 1 — orchestrator turn: CHAPTERHOUSE_ORCHESTRATOR_TIMEOUT_MS (default 1800 s)
|
|
27
|
+
* Layer 2 — daemon shutdown: CHAPTERHOUSE_SHUTDOWN_TIMEOUT_MS (default 60 s) ← this
|
|
28
|
+
* Layer 3 — systemd kill: TimeoutStopSec=90 s (must exceed layer 2)
|
|
29
|
+
* Allows in-flight LLM streams to complete before the process is torn down.
|
|
30
|
+
*/
|
|
31
|
+
const SHUTDOWN_TIMEOUT_MS = (() => {
|
|
32
|
+
const env = process.env.CHAPTERHOUSE_SHUTDOWN_TIMEOUT_MS;
|
|
33
|
+
if (env) {
|
|
34
|
+
const parsed = parseInt(env, 10);
|
|
35
|
+
if (!isNaN(parsed) && parsed > 0)
|
|
36
|
+
return parsed;
|
|
37
|
+
}
|
|
38
|
+
return 60_000;
|
|
39
|
+
})();
|
|
22
40
|
/** Remove orphaned session folders older than 7 days, preserving the current session. */
|
|
23
41
|
function pruneOldSessions() {
|
|
24
42
|
try {
|
|
@@ -61,7 +79,7 @@ function truncate(text, max = 200) {
|
|
|
61
79
|
return oneLine.length > max ? oneLine.slice(0, max) + "…" : oneLine;
|
|
62
80
|
}
|
|
63
81
|
async function main() {
|
|
64
|
-
log.info("Starting Chapterhouse daemon");
|
|
82
|
+
log.info({ version: CHAPTERHOUSE_VERSION }, "Starting Chapterhouse daemon");
|
|
65
83
|
if (config.selfEditEnabled) {
|
|
66
84
|
log.warn("Self-edit mode enabled — Chapterhouse can modify his own source code");
|
|
67
85
|
}
|
|
@@ -165,11 +183,11 @@ async function shutdown() {
|
|
|
165
183
|
}
|
|
166
184
|
shutdownState = "shutting_down";
|
|
167
185
|
log.info("Shutting down (Ctrl+C again to force)");
|
|
168
|
-
// Force exit after
|
|
186
|
+
// Force exit after the configured grace period no matter what
|
|
169
187
|
const forceTimer = setTimeout(() => {
|
|
170
188
|
log.warn("Shutdown timed out — forcing exit");
|
|
171
189
|
process.exit(1);
|
|
172
|
-
},
|
|
190
|
+
}, SHUTDOWN_TIMEOUT_MS);
|
|
173
191
|
forceTimer.unref();
|
|
174
192
|
// Destroy all active agent sessions
|
|
175
193
|
await shutdownAgents();
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { config } from "../config.js";
|
|
2
|
+
import { childLogger } from "../util/logger.js";
|
|
3
|
+
const log = childLogger("teams-notify");
|
|
2
4
|
export const TEAMS_MILESTONE_THRESHOLDS = [25, 50, 75, 100];
|
|
3
5
|
const DEFAULT_COLOR = "0076D7";
|
|
4
6
|
export class TeamsNotifier {
|
|
@@ -10,7 +12,7 @@ export class TeamsNotifier {
|
|
|
10
12
|
this.webhookUrl = (options.webhookUrl ?? config.teamsWebhookUrl).trim();
|
|
11
13
|
this.enabled = options.enabled ?? config.teamsNotificationsEnabled;
|
|
12
14
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
13
|
-
this.warn = options.warn ?? ((message) =>
|
|
15
|
+
this.warn = options.warn ?? ((message) => log.warn(message));
|
|
14
16
|
}
|
|
15
17
|
async sendMessage(title, body, color = DEFAULT_COLOR) {
|
|
16
18
|
return await this.postCard({
|
package/dist/squad/index.js
CHANGED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/squad/init-cli.ts
|
|
3
|
+
*
|
|
4
|
+
* Interactive CLI for `chapterhouse squad init`.
|
|
5
|
+
* Asks the user 5 questions, proposes a roster, then scaffolds `.squad/`.
|
|
6
|
+
*
|
|
7
|
+
* Flags:
|
|
8
|
+
* --force Re-scaffold even if .squad/ already exists (clobbers existing)
|
|
9
|
+
* --yes Skip confirmation prompt (non-interactive / CI mode)
|
|
10
|
+
* --universe <name> Override the naming universe (default: Dune)
|
|
11
|
+
*/
|
|
12
|
+
import { createInterface } from 'node:readline';
|
|
13
|
+
import { execSync } from 'node:child_process';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { scaffoldSquad, isSquadInitialized, allocateCast, resolveRoles, UNIVERSE_CHARACTERS, DEFAULT_UNIVERSE, } from './init.js';
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Entry point
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
export async function runInitCli(argv) {
|
|
20
|
+
const force = argv.includes('--force');
|
|
21
|
+
const yes = argv.includes('--yes');
|
|
22
|
+
// --universe <name>
|
|
23
|
+
const univIdx = argv.indexOf('--universe');
|
|
24
|
+
const univArg = univIdx !== -1 ? argv[univIdx + 1] : undefined;
|
|
25
|
+
const projectRoot = process.cwd();
|
|
26
|
+
if (!force && isSquadInitialized(projectRoot)) {
|
|
27
|
+
console.error('⚠️ Squad is already initialized in this directory.\n' +
|
|
28
|
+
' Run with --force to re-scaffold (existing files will be overwritten).');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
console.log('\n🚀 chapterhouse squad init — guided setup\n');
|
|
32
|
+
// Try to pre-fill the human name from git
|
|
33
|
+
let defaultHuman = 'the team';
|
|
34
|
+
try {
|
|
35
|
+
defaultHuman = execSync('git config user.name', { encoding: 'utf-8' }).trim() || defaultHuman;
|
|
36
|
+
}
|
|
37
|
+
catch { /* not a git repo or git not available */ }
|
|
38
|
+
const availableUniverses = Object.keys(UNIVERSE_CHARACTERS);
|
|
39
|
+
const universeList = availableUniverses.join(', ');
|
|
40
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
41
|
+
const ask = (prompt) => new Promise(resolve => rl.question(prompt, answer => resolve(answer.trim())));
|
|
42
|
+
try {
|
|
43
|
+
// ── Q1: project name ───────────────────────────────────────────────────
|
|
44
|
+
const projectName = (await ask('Project name: ')) || 'My Project';
|
|
45
|
+
// ── Q2: language / stack ───────────────────────────────────────────────
|
|
46
|
+
const stack = (await ask('Language / stack (e.g. TypeScript, React, Node): ')) || 'TypeScript';
|
|
47
|
+
// ── Q3: primary goal ──────────────────────────────────────────────────
|
|
48
|
+
const goal = (await ask('Primary goal — what does it do?: ')) || 'A software project';
|
|
49
|
+
// ── Q4: team size ─────────────────────────────────────────────────────
|
|
50
|
+
const teamSizeRaw = await ask('Team size (3–5, default 4): ');
|
|
51
|
+
const teamSize = Math.min(Math.max(parseInt(teamSizeRaw || '4', 10) || 4, 3), 5);
|
|
52
|
+
// ── Q5: universe ──────────────────────────────────────────────────────
|
|
53
|
+
const defaultUniverse = univArg && availableUniverses.includes(univArg) ? univArg : DEFAULT_UNIVERSE;
|
|
54
|
+
const universeInput = await ask(`Naming universe [${universeList}]\n (default: ${defaultUniverse}): `);
|
|
55
|
+
const universe = availableUniverses.find(u => u.toLowerCase() === universeInput.toLowerCase()) ?? defaultUniverse;
|
|
56
|
+
rl.close();
|
|
57
|
+
// ── Propose roster ────────────────────────────────────────────────────
|
|
58
|
+
const roles = resolveRoles(teamSize);
|
|
59
|
+
const castMap = allocateCast(roles, universe);
|
|
60
|
+
console.log(`\n📋 Proposed team (${universe} universe):\n`);
|
|
61
|
+
for (const role of roles) {
|
|
62
|
+
const castName = castMap[role.slug];
|
|
63
|
+
console.log(` ${role.icon} ${castName.padEnd(12)} — ${role.role.padEnd(14)} ${role.description}`);
|
|
64
|
+
}
|
|
65
|
+
console.log(' 📋 Scribe — Scribe Memory, decisions, session logs');
|
|
66
|
+
console.log(' 🔄 Ralph — Monitor Work queue, backlog, keep-alive\n');
|
|
67
|
+
// ── Confirm ───────────────────────────────────────────────────────────
|
|
68
|
+
if (!yes) {
|
|
69
|
+
const rl2 = createInterface({ input: process.stdin, output: process.stdout });
|
|
70
|
+
const confirm = await new Promise(resolve => rl2.question('Hire this team? [Y/n]: ', ans => { rl2.close(); resolve(ans.trim()); }));
|
|
71
|
+
if (confirm && confirm.toLowerCase() !== 'y' && confirm.toLowerCase() !== 'yes') {
|
|
72
|
+
console.log('Aborted. No files written.');
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// ── Scaffold ──────────────────────────────────────────────────────────
|
|
77
|
+
const result = scaffoldSquad(projectRoot, { projectName, stack, goal, teamSize, universe, humanName: defaultHuman }, { force });
|
|
78
|
+
if (!result) {
|
|
79
|
+
// Should not happen (we checked above) — safety fallback
|
|
80
|
+
console.error('⚠️ Squad already initialized. Use --force to overwrite.');
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
console.log(`\n✅ Squad initialized at ${join(projectRoot, '.squad')}/\n`);
|
|
84
|
+
console.log('Files written:');
|
|
85
|
+
console.log(' .squad/team.md');
|
|
86
|
+
console.log(' .squad/routing.md');
|
|
87
|
+
console.log(' .squad/ceremonies.md');
|
|
88
|
+
console.log(' .squad/decisions.md');
|
|
89
|
+
for (const agent of result.agents) {
|
|
90
|
+
console.log(` .squad/agents/${agent.slug}/charter.md`);
|
|
91
|
+
console.log(` .squad/agents/${agent.slug}/history.md`);
|
|
92
|
+
}
|
|
93
|
+
console.log(' .squad/agents/scribe/charter.md');
|
|
94
|
+
console.log(' .squad/agents/ralph/charter.md');
|
|
95
|
+
console.log(' .squad/casting/policy.json');
|
|
96
|
+
console.log(' .squad/casting/registry.json');
|
|
97
|
+
console.log(' .squad/casting/history.json');
|
|
98
|
+
console.log(' .gitattributes (updated with union merge rules)\n');
|
|
99
|
+
const firstAgent = result.agents[0];
|
|
100
|
+
if (firstAgent) {
|
|
101
|
+
console.log(`💡 Try: "@${firstAgent.slug}, set up the project structure"\n`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
rl.close();
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=init-cli.js.map
|