@orchestrator-claude/cli 3.25.1 → 3.26.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/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/base/claude/agents/debug-sidecar.md +133 -0
- package/dist/templates/base/claude/agents/doc-sidecar.md +60 -0
- package/dist/templates/base/claude/agents/implementer.md +162 -31
- package/dist/templates/base/claude/agents/test-sidecar.md +106 -0
- package/dist/templates/base/claude/hooks/mailbox-listener.ts +246 -0
- package/dist/templates/base/claude/hooks/sprint-registry.ts +85 -0
- package/dist/templates/base/claude/settings.json +19 -0
- package/dist/templates/base/claude/skills/sprint-launch/SKILL.md +176 -0
- package/dist/templates/base/claude/skills/sprint-teammate/sprint-teammate.md +79 -0
- package/dist/templates/base/scripts/lib/SprintLauncher.ts +325 -0
- package/dist/templates/base/scripts/lib/TmuxManager.ts +296 -0
- package/dist/templates/base/scripts/lib/WorktreeIsolator.ts +165 -0
- package/dist/templates/base/scripts/lib/WorktreeManager.ts +106 -0
- package/dist/templates/base/scripts/lib/mailbox/types.ts +175 -0
- package/dist/templates/base/scripts/lib/sidecar/SidecarWatcher.ts +249 -0
- package/dist/templates/base/scripts/lib/sidecar/run.ts +90 -0
- package/dist/templates/base/scripts/sprint-launch.ts +285 -0
- package/package.json +1 -1
- package/templates/base/claude/agents/debug-sidecar.md +133 -0
- package/templates/base/claude/agents/doc-sidecar.md +60 -0
- package/templates/base/claude/agents/implementer.md +162 -31
- package/templates/base/claude/agents/test-sidecar.md +106 -0
- package/templates/base/claude/hooks/mailbox-listener.ts +246 -0
- package/templates/base/claude/hooks/sprint-registry.ts +85 -0
- package/templates/base/claude/settings.json +19 -0
- package/templates/base/claude/skills/sprint-launch/SKILL.md +176 -0
- package/templates/base/claude/skills/sprint-teammate/sprint-teammate.md +79 -0
- package/templates/base/scripts/lib/SprintLauncher.ts +325 -0
- package/templates/base/scripts/lib/TmuxManager.ts +296 -0
- package/templates/base/scripts/lib/WorktreeIsolator.ts +165 -0
- package/templates/base/scripts/lib/WorktreeManager.ts +106 -0
- package/templates/base/scripts/lib/mailbox/types.ts +175 -0
- package/templates/base/scripts/lib/sidecar/SidecarWatcher.ts +249 -0
- package/templates/base/scripts/lib/sidecar/run.ts +90 -0
- package/templates/base/scripts/sprint-launch.ts +285 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* run.ts — CLI entry point for the reactive sidecar watcher (RFC-025 v3.3)
|
|
4
|
+
*
|
|
5
|
+
* Launched by TmuxManager in sidecar panes. Parses CLI args, creates a
|
|
6
|
+
* SidecarWatcher, and runs until SIGTERM/SIGINT.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx tsx scripts/lib/sidecar/run.ts --inbox /path/to/inbox --worktree /path/to/wt --agent doc-sidecar
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { createSidecarWatcher } from './SidecarWatcher.js';
|
|
13
|
+
import { parseArgs } from 'node:util';
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// CLI argument parsing
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
const { values } = parseArgs({
|
|
20
|
+
options: {
|
|
21
|
+
inbox: { type: 'string' },
|
|
22
|
+
worktree: { type: 'string' },
|
|
23
|
+
agent: { type: 'string' },
|
|
24
|
+
},
|
|
25
|
+
strict: true,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!values.inbox || !values.worktree || !values.agent) {
|
|
29
|
+
// eslint-disable-next-line no-console
|
|
30
|
+
console.error('Usage: npx tsx run.ts --inbox <path> --worktree <path> --agent <slug>');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Force OAuth/Claude Max — strip API keys from process.env BEFORE any SDK use.
|
|
36
|
+
// The SDK reads process.env internally; passing `env` in query() options alone
|
|
37
|
+
// is not sufficient. This mirrors `env -u ANTHROPIC_API_KEY` from TmuxManager.
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
const API_KEY_VARS = ['ANTHROPIC_API_KEY', 'CLAUDE_API_KEY'] as const;
|
|
41
|
+
for (const key of API_KEY_VARS) {
|
|
42
|
+
if (process.env[key]) {
|
|
43
|
+
// eslint-disable-next-line no-console
|
|
44
|
+
console.log(`[sidecar:${values.agent}] Stripping ${key} from env to force OAuth`);
|
|
45
|
+
delete process.env[key];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Resilience: prevent unhandled rejections from killing the watcher.
|
|
51
|
+
// The SDK's internal chokidar/inotify errors (ENOSPC) are non-fatal —
|
|
52
|
+
// they surface as unhandled rejections and must not crash the process.
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
process.on('unhandledRejection', (reason) => {
|
|
56
|
+
// eslint-disable-next-line no-console
|
|
57
|
+
console.error(
|
|
58
|
+
`[sidecar:${values.agent}] Unhandled rejection (non-fatal):`,
|
|
59
|
+
reason instanceof Error ? reason.message : String(reason),
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Start watcher
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
// eslint-disable-next-line no-console
|
|
68
|
+
console.log(`[sidecar:${values.agent}] Starting reactive watcher on ${values.inbox}`);
|
|
69
|
+
|
|
70
|
+
const watcher = createSidecarWatcher({
|
|
71
|
+
inboxPath: values.inbox,
|
|
72
|
+
worktreePath: values.worktree,
|
|
73
|
+
agentSlug: values.agent,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
watcher.start();
|
|
77
|
+
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// Graceful shutdown
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
async function shutdown(signal: string): Promise<void> {
|
|
83
|
+
// eslint-disable-next-line no-console
|
|
84
|
+
console.log(`[sidecar:${values.agent}] Received ${signal}, shutting down...`);
|
|
85
|
+
await watcher.stop();
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
process.on('SIGTERM', () => void shutdown('SIGTERM'));
|
|
90
|
+
process.on('SIGINT', () => void shutdown('SIGINT'));
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* sprint-launch.ts — TypeScript CLI entry point for RFC-025 Sprint Launcher
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx tsx scripts/sprint-launch.ts <preset> [workflowId]
|
|
7
|
+
*
|
|
8
|
+
* Presets:
|
|
9
|
+
* duo — 1 implementer pane
|
|
10
|
+
* duo-doc — 1 implementer + 1 doc-sidecar pane
|
|
11
|
+
* squad — 2 implementers + 1 debug-sidecar pane
|
|
12
|
+
* squad-review — 1 implementer + 1 debug-sidecar + 1 doc-sidecar pane
|
|
13
|
+
* platoon — 3 implementers + 1 reviewer + 1 debug-sidecar pane
|
|
14
|
+
* platoon-full — 2 implementers + 2 doc-sidecars + 1 debug-sidecar + 1 test-sidecar + 1 reviewer pane
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import * as childProcess from 'child_process';
|
|
18
|
+
import * as crypto from 'crypto';
|
|
19
|
+
import { createWorktreeManager } from './lib/WorktreeManager.js';
|
|
20
|
+
import { createWorktreeIsolator } from './lib/WorktreeIsolator.js';
|
|
21
|
+
import { createTmuxManager } from './lib/TmuxManager.js';
|
|
22
|
+
import { createSprintLauncher } from './lib/SprintLauncher.js';
|
|
23
|
+
import type { SprintTask } from './lib/SprintLauncher.js';
|
|
24
|
+
import { createMailboxManager } from './lib/mailbox/MailboxManager.js';
|
|
25
|
+
import { generateLayout } from '../src/domain/value-objects/generateLayout.js';
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Constants
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
const VERSION = '1.0.0';
|
|
32
|
+
const VALID_PRESETS = [
|
|
33
|
+
'duo',
|
|
34
|
+
'duo-doc',
|
|
35
|
+
'squad',
|
|
36
|
+
'squad-review',
|
|
37
|
+
'platoon',
|
|
38
|
+
'platoon-full',
|
|
39
|
+
] as const;
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Helpers
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* parseTaskFlags — Extract SprintTask objects from CLI args.
|
|
47
|
+
*
|
|
48
|
+
* Supports:
|
|
49
|
+
* --task "TASK-001: Title text" (separate value)
|
|
50
|
+
* --task=TASK-001: Title text (equals-sign form)
|
|
51
|
+
*
|
|
52
|
+
* When the value has a colon, everything before the first colon is the taskId
|
|
53
|
+
* (trimmed) and everything after is the title (trimmed).
|
|
54
|
+
* When there is no colon, a generated taskId is used and the full string is the title.
|
|
55
|
+
*/
|
|
56
|
+
export function parseTaskFlags(args: string[]): SprintTask[] {
|
|
57
|
+
const tasks: SprintTask[] = [];
|
|
58
|
+
let taskCounter = 0;
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < args.length; i++) {
|
|
61
|
+
const arg = args[i];
|
|
62
|
+
let value: string | undefined;
|
|
63
|
+
|
|
64
|
+
if (arg === '--task' && i + 1 < args.length) {
|
|
65
|
+
value = args[i + 1];
|
|
66
|
+
i++; // consume next arg
|
|
67
|
+
} else if (arg.startsWith('--task=')) {
|
|
68
|
+
value = arg.slice('--task='.length);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (value !== undefined) {
|
|
72
|
+
const colonIdx = value.indexOf(':');
|
|
73
|
+
let taskId: string;
|
|
74
|
+
let title: string;
|
|
75
|
+
if (colonIdx !== -1) {
|
|
76
|
+
taskId = value.slice(0, colonIdx).trim();
|
|
77
|
+
title = value.slice(colonIdx + 1).trim();
|
|
78
|
+
} else {
|
|
79
|
+
taskCounter++;
|
|
80
|
+
taskId = `task-${taskCounter}`;
|
|
81
|
+
title = value.trim();
|
|
82
|
+
}
|
|
83
|
+
tasks.push({ taskId, title, description: '', acceptanceCriteria: [] });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return tasks;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function printUsage(): void {
|
|
91
|
+
console.log(`sprint-launch.ts v${VERSION} — RFC-025 Sprint Launcher
|
|
92
|
+
|
|
93
|
+
Usage:
|
|
94
|
+
npx tsx scripts/sprint-launch.ts <preset> [workflowId] [--mailbox] [--task "..."]
|
|
95
|
+
|
|
96
|
+
Presets:
|
|
97
|
+
duo 1 implementer pane (orchestrator = calling session)
|
|
98
|
+
duo-doc 1 implementer + 1 doc-sidecar pane (orchestrator = calling session)
|
|
99
|
+
squad 2 implementers + 1 debug-sidecar pane
|
|
100
|
+
squad-review 1 implementer + 1 debug-sidecar + 1 doc-sidecar pane
|
|
101
|
+
platoon 3 implementers + 1 reviewer + 1 debug-sidecar pane
|
|
102
|
+
platoon-full 2 implementers + 2 doc-sidecars + 1 debug-sidecar + 1 test-sidecar + 1 reviewer pane
|
|
103
|
+
|
|
104
|
+
Arguments:
|
|
105
|
+
preset Required. One of: ${VALID_PRESETS.join(', ')}
|
|
106
|
+
workflowId Optional. UUID of the active workflow (default: generated random UUID)
|
|
107
|
+
|
|
108
|
+
Flags:
|
|
109
|
+
--mailbox Enable mailbox protocol: creates /tmp/<session>/mailbox/<role>/inbox/ for each pane.
|
|
110
|
+
--task "ID: Title" Assign a task to the next implementer pane (repeatable, round-robin).
|
|
111
|
+
Example: --task "TASK-001: Implement auth" --task "TASK-002: Write tests"
|
|
112
|
+
--fetch-tasks Fetch tasks from the REST API (graceful fallback if unavailable).
|
|
113
|
+
|
|
114
|
+
Task Distribution:
|
|
115
|
+
Tasks are distributed round-robin to implementer panes only.
|
|
116
|
+
Sidecar agents (doc-sidecar, debug-sidecar, test-sidecar) receive tasks via mailbox reactions,
|
|
117
|
+
not via direct assignment. Each implementer pane gets one task per round.
|
|
118
|
+
Reference: sprint-teammate skill for agent-side protocol.
|
|
119
|
+
|
|
120
|
+
Examples:
|
|
121
|
+
npx tsx scripts/sprint-launch.ts duo
|
|
122
|
+
npx tsx scripts/sprint-launch.ts duo-doc adaaa9a4-ef49-4e26-af14-2b239f62b40c
|
|
123
|
+
npx tsx scripts/sprint-launch.ts squad --mailbox
|
|
124
|
+
npx tsx scripts/sprint-launch.ts squad --mailbox --task "TASK-001: Implement feature X" --task "TASK-002: Write docs"
|
|
125
|
+
|
|
126
|
+
Notes:
|
|
127
|
+
- Agent panes use 'claude --agent <role>' — compatible with Claude Max (OAuth) and API key.
|
|
128
|
+
- The calling session (orchestrator) is NOT included in tmux — you continue orchestrating here.
|
|
129
|
+
- Worktrees are created at ../wt-{session}-{index} (0-indexed).
|
|
130
|
+
- The tmux session is named sprint-{workflowId_prefix}.
|
|
131
|
+
- Bash MVP removed (v2) — TypeScript is the only launcher
|
|
132
|
+
`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// Main
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
async function main(): Promise<void> {
|
|
140
|
+
const args = process.argv.slice(2);
|
|
141
|
+
|
|
142
|
+
// Help flag
|
|
143
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
144
|
+
printUsage();
|
|
145
|
+
process.exit(0);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const mailboxFlag = args.includes('--mailbox');
|
|
149
|
+
const fetchTasksFlag = args.includes('--fetch-tasks');
|
|
150
|
+
const positional = args.filter((a) => !a.startsWith('--'));
|
|
151
|
+
const presetArg = positional[0];
|
|
152
|
+
const workflowIdArg = positional[1];
|
|
153
|
+
|
|
154
|
+
// Require preset
|
|
155
|
+
if (!presetArg) {
|
|
156
|
+
console.error('Error: preset argument is required.\n');
|
|
157
|
+
printUsage();
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Validate preset early (before creating deps) to give a clean error
|
|
162
|
+
if (!VALID_PRESETS.includes(presetArg as (typeof VALID_PRESETS)[number])) {
|
|
163
|
+
console.error(
|
|
164
|
+
`Error: invalid preset '${presetArg}'. Valid presets: ${VALID_PRESETS.join(', ')}`,
|
|
165
|
+
);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// SEC-002: Validate workflowId format if provided by the caller
|
|
170
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
171
|
+
if (workflowIdArg && !UUID_RE.test(workflowIdArg)) {
|
|
172
|
+
console.error('Error: workflowId must be a valid UUID');
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Default workflowId to a fresh UUID
|
|
177
|
+
const workflowId = workflowIdArg ?? crypto.randomUUID();
|
|
178
|
+
if (!workflowIdArg) {
|
|
179
|
+
console.warn(`[sprint-launch] No workflowId provided — using generated: ${workflowId}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Resolve project root (one level up from scripts/)
|
|
183
|
+
const projectRoot = new URL('..', import.meta.url).pathname.replace(/\/$/, '');
|
|
184
|
+
|
|
185
|
+
// Build real dependencies
|
|
186
|
+
const worktreeManager = createWorktreeManager(projectRoot, {
|
|
187
|
+
execSync: childProcess.execSync,
|
|
188
|
+
});
|
|
189
|
+
const worktreeIsolator = createWorktreeIsolator(projectRoot);
|
|
190
|
+
const tmuxManager = createTmuxManager({ execSync: childProcess.execSync });
|
|
191
|
+
|
|
192
|
+
const sessionName = `sprint-${workflowId.slice(0, 8)}`;
|
|
193
|
+
const mailboxBaseDir = `/tmp/${sessionName}/mailbox`;
|
|
194
|
+
const mailboxManager = mailboxFlag ? createMailboxManager(mailboxBaseDir) : undefined;
|
|
195
|
+
if (mailboxFlag) {
|
|
196
|
+
console.log(`[sprint-launch] Mailbox enabled — inbox directories at ${mailboxBaseDir}/`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Collect tasks from --task flags
|
|
200
|
+
let tasks = parseTaskFlags(args);
|
|
201
|
+
|
|
202
|
+
// Fetch tasks from REST API if --fetch-tasks flag provided
|
|
203
|
+
if (fetchTasksFlag) {
|
|
204
|
+
try {
|
|
205
|
+
const apiUrl = process.env['ORCHESTRATOR_API_URL'] ?? 'http://localhost:3000';
|
|
206
|
+
const response = await fetch(`${apiUrl}/api/workflows/${workflowId}/tasks`);
|
|
207
|
+
if (!response.ok) {
|
|
208
|
+
console.warn(`[sprint-launch] --fetch-tasks: API returned ${response.status} — continuing with ${tasks.length} CLI tasks`);
|
|
209
|
+
} else {
|
|
210
|
+
const data = await response.json() as { tasks?: SprintTask[] };
|
|
211
|
+
const fetchedTasks = data.tasks ?? [];
|
|
212
|
+
tasks = [...tasks, ...fetchedTasks];
|
|
213
|
+
console.log(`[sprint-launch] --fetch-tasks: loaded ${fetchedTasks.length} tasks from API`);
|
|
214
|
+
}
|
|
215
|
+
} catch (err) {
|
|
216
|
+
console.warn(`[sprint-launch] --fetch-tasks: failed to reach API (${err instanceof Error ? err.message : String(err)}) — continuing with ${tasks.length} CLI tasks`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (tasks.length > 0) {
|
|
221
|
+
console.log(`[sprint-launch] Task distribution: ${tasks.length} task(s) → round-robin to implementer panes`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const launcher = createSprintLauncher({
|
|
225
|
+
worktreeManager,
|
|
226
|
+
worktreeIsolator,
|
|
227
|
+
tmuxManager,
|
|
228
|
+
generateLayout,
|
|
229
|
+
execSync: childProcess.execSync,
|
|
230
|
+
projectRoot,
|
|
231
|
+
mailboxManager,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Register cleanup handlers for SIGINT / SIGTERM
|
|
235
|
+
let launched = false;
|
|
236
|
+
|
|
237
|
+
function onSignal(): void {
|
|
238
|
+
if (launched) {
|
|
239
|
+
console.warn('\n[sprint-launch] Interrupted — cleaning up...');
|
|
240
|
+
launcher.cleanup(sessionName);
|
|
241
|
+
}
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
process.on('SIGINT', onSignal);
|
|
246
|
+
process.on('SIGTERM', onSignal);
|
|
247
|
+
|
|
248
|
+
// Launch
|
|
249
|
+
let result: Awaited<ReturnType<typeof launcher.launch>>;
|
|
250
|
+
try {
|
|
251
|
+
result = await launcher.launch(presetArg, workflowId, tasks.length > 0 ? tasks : undefined);
|
|
252
|
+
launched = true;
|
|
253
|
+
} catch (err) {
|
|
254
|
+
launcher.cleanup(sessionName);
|
|
255
|
+
throw err;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Print summary
|
|
259
|
+
console.log('');
|
|
260
|
+
console.log(`[sprint-launch] Sprint session launched: ${result.sessionName}`);
|
|
261
|
+
console.log('');
|
|
262
|
+
console.log(` Preset: ${result.preset}`);
|
|
263
|
+
console.log(` WorkflowId: ${result.workflowId}`);
|
|
264
|
+
console.log(` Session: ${result.sessionName}`);
|
|
265
|
+
console.log(` Worktrees:`);
|
|
266
|
+
for (const wt of result.worktreePaths) {
|
|
267
|
+
console.log(` ${wt}`);
|
|
268
|
+
}
|
|
269
|
+
console.log('');
|
|
270
|
+
console.log('Attach to agent panes with:');
|
|
271
|
+
console.log(` tmux attach -t ${result.sessionName}`);
|
|
272
|
+
console.log('');
|
|
273
|
+
console.log('You (orchestrator) continue in this session.');
|
|
274
|
+
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Guard: only run main() when this file is the entry point (not when imported for tests)
|
|
278
|
+
const isMain = process.argv[1]?.endsWith('sprint-launch.ts') ||
|
|
279
|
+
process.argv[1]?.endsWith('sprint-launch.js');
|
|
280
|
+
if (isMain) {
|
|
281
|
+
main().catch((err: unknown) => {
|
|
282
|
+
console.error('[sprint-launch] Error:', err instanceof Error ? err.message : String(err));
|
|
283
|
+
process.exit(1);
|
|
284
|
+
});
|
|
285
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: debug-sidecar
|
|
3
|
+
description: Code review sidecar for sprint sessions. Continuously reviews implementer commits for bugs, SOLID violations, and security issues. Use in squad-review or platoon-full preset sprints alongside implementer panes.
|
|
4
|
+
tools: Read, Glob, Grep, Bash
|
|
5
|
+
model: claude-sonnet-4-5
|
|
6
|
+
color: red
|
|
7
|
+
permissionMode: default
|
|
8
|
+
skills:
|
|
9
|
+
- project-conventions
|
|
10
|
+
- sprint-teammate
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Debug-Sidecar Agent
|
|
14
|
+
|
|
15
|
+
## Identity
|
|
16
|
+
|
|
17
|
+
You are the **Debug-Sidecar Agent** in a sprint session.
|
|
18
|
+
You are NOT the orchestrator. You do NOT manage workflows, greet users, or make architectural decisions.
|
|
19
|
+
You are a focused code review sidecar running alongside implementer panes, catching issues in real-time.
|
|
20
|
+
|
|
21
|
+
## Responsibilities
|
|
22
|
+
|
|
23
|
+
1. **Continuous Code Review**: Monitor commits and diffs from implementer worktrees for logic errors, edge cases, and bugs.
|
|
24
|
+
2. **SOLID Principle Enforcement**: Flag Single Responsibility, Open-Closed, Liskov, Interface Segregation, and Dependency Inversion violations.
|
|
25
|
+
3. **Security Analysis**: Detect injection vulnerabilities, XSS patterns, unsafe type assertions, hardcoded secrets, and unvalidated inputs.
|
|
26
|
+
4. **Clean Architecture Compliance**: Verify dependency direction (domain must NOT depend on application or infrastructure layers).
|
|
27
|
+
5. **Test Coverage Gaps**: Identify new code paths that lack corresponding test coverage.
|
|
28
|
+
|
|
29
|
+
## Severity Classification
|
|
30
|
+
|
|
31
|
+
Use this standard classification for ALL findings:
|
|
32
|
+
|
|
33
|
+
| Severity | Meaning | Action |
|
|
34
|
+
|----------|---------|--------|
|
|
35
|
+
| **CRITICAL** | Bugs, security vulnerabilities, architecture violations, failing tests | Report to orchestrator immediately |
|
|
36
|
+
| **HIGH** | SOLID/Clean Code violations, insufficient coverage, unhandled errors | Report to orchestrator |
|
|
37
|
+
| **MEDIUM** | Readability issues, minor optimizations, tech debt | Report to implementer (fix when convenient) |
|
|
38
|
+
| **LOW** | Style improvements, optional docs, cosmetic refactoring | Batch summary at end of sprint |
|
|
39
|
+
|
|
40
|
+
## Source of Truth: Git Commits
|
|
41
|
+
|
|
42
|
+
**Git commits are the source of truth.** Task files become stale during a sprint and MUST NOT be used to infer what work was done. Always derive context from `git log` and `git diff`.
|
|
43
|
+
|
|
44
|
+
- Use `git log --oneline -5` to see the most recent commits.
|
|
45
|
+
- Use `git diff HEAD~1` to inspect the latest commit's changes.
|
|
46
|
+
- Use `git show <sha>` for deeper inspection of a specific commit when needed.
|
|
47
|
+
|
|
48
|
+
## Severity Rule: Task File Mismatches
|
|
49
|
+
|
|
50
|
+
**NEVER flag task file mismatches or stale task file content as CRITICAL or HIGH.**
|
|
51
|
+
Task files are planning artifacts — they are expected to drift from reality as the sprint progresses.
|
|
52
|
+
If you notice a task file discrepancy, classify it as **LOW** at most, and only include it if it provides actionable signal. In most cases, omit it entirely.
|
|
53
|
+
|
|
54
|
+
## Collaboration with Implementer
|
|
55
|
+
|
|
56
|
+
- You and the implementer run in **separate worktrees on separate branches**. The implementer's changes are NOT automatically visible to you.
|
|
57
|
+
- Use `git log --oneline -5` and `git diff HEAD~1` via Bash as the primary discovery mechanism.
|
|
58
|
+
- Do NOT use task files to infer implementer progress — they go stale immediately.
|
|
59
|
+
- Do NOT modify code. Only analyze and report findings.
|
|
60
|
+
- Structure findings clearly with file path, line number, severity, and suggested fix.
|
|
61
|
+
|
|
62
|
+
## Polling Protocol (Last-Reviewed-SHA)
|
|
63
|
+
|
|
64
|
+
Poll approximately every **5 minutes**. Use a "last reviewed SHA" to avoid re-reviewing already-seen commits:
|
|
65
|
+
|
|
66
|
+
1. Run `git log --oneline -1` to get the current HEAD SHA.
|
|
67
|
+
2. Compare to your `last_reviewed_sha` (stored in memory for this session).
|
|
68
|
+
3. If HEAD SHA == `last_reviewed_sha`: **no new commits — skip this cycle** and wait for the next poll.
|
|
69
|
+
4. If HEAD SHA != `last_reviewed_sha`: run `git log --oneline <last_reviewed_sha>..HEAD` to see new commits, then `git diff <last_reviewed_sha>..HEAD` for the full diff.
|
|
70
|
+
5. After reviewing, update `last_reviewed_sha` to the current HEAD SHA.
|
|
71
|
+
|
|
72
|
+
On session start, set `last_reviewed_sha` to the current HEAD before the first review cycle.
|
|
73
|
+
|
|
74
|
+
## What You Are NOT
|
|
75
|
+
|
|
76
|
+
- NOT the orchestrator — do not invoke workflow tools or AskUserQuestion.
|
|
77
|
+
- NOT a documentation writer — do not write or update docs, READMEs, or changelogs.
|
|
78
|
+
- NOT a tester — do not write or run tests (only identify missing coverage).
|
|
79
|
+
- NOT a release manager — do not bump versions or trigger CI.
|
|
80
|
+
- NOT an implementer — do NOT fix the issues you find. Report them.
|
|
81
|
+
- NOT a task tracker — do not read or validate task files for sprint progress.
|
|
82
|
+
|
|
83
|
+
## Workflow
|
|
84
|
+
|
|
85
|
+
1. At session start, record current HEAD SHA as `last_reviewed_sha` via `git log --oneline -1`.
|
|
86
|
+
2. Run an initial review: `git log --oneline -5` + `git diff HEAD~1` to establish baseline context.
|
|
87
|
+
3. Analyze each changed file for the 5 responsibility areas above.
|
|
88
|
+
4. Produce a findings report grouped by severity.
|
|
89
|
+
5. Every ~5 minutes: check if HEAD SHA has changed since `last_reviewed_sha`.
|
|
90
|
+
- If no new commits: output "No new commits since last review (SHA: {sha}). Waiting..." and skip.
|
|
91
|
+
- If new commits: review only the new commits (`git diff <last_reviewed_sha>..HEAD`), then update `last_reviewed_sha`.
|
|
92
|
+
|
|
93
|
+
## Output Format
|
|
94
|
+
|
|
95
|
+
```markdown
|
|
96
|
+
# Debug Sidecar Review — Cycle {N}
|
|
97
|
+
|
|
98
|
+
## CRITICAL
|
|
99
|
+
- **BUG-001**: [Description]
|
|
100
|
+
- Location: `src/path/file.ts:42`
|
|
101
|
+
- Impact: [Why this is critical]
|
|
102
|
+
- Suggested fix: [How to fix]
|
|
103
|
+
|
|
104
|
+
## HIGH
|
|
105
|
+
- **ARCH-001**: [Description]
|
|
106
|
+
- Location: `src/path/file.ts:15`
|
|
107
|
+
- Impact: [Consequence]
|
|
108
|
+
- Suggested fix: [Recommendation]
|
|
109
|
+
|
|
110
|
+
## MEDIUM
|
|
111
|
+
- **QUAL-001**: [Description]
|
|
112
|
+
- Location: `src/path/file.ts:88`
|
|
113
|
+
- Suggested fix: [Improvement]
|
|
114
|
+
|
|
115
|
+
## LOW
|
|
116
|
+
- **STYLE-001**: [Description]
|
|
117
|
+
|
|
118
|
+
## Summary
|
|
119
|
+
- Files reviewed: {N}
|
|
120
|
+
- Findings: {critical} CRITICAL, {high} HIGH, {medium} MEDIUM, {low} LOW
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Quality Standards
|
|
124
|
+
|
|
125
|
+
- Every finding MUST include file path and line number.
|
|
126
|
+
- Every CRITICAL/HIGH finding MUST include a suggested fix.
|
|
127
|
+
- Never report style preferences as HIGH or CRITICAL.
|
|
128
|
+
- Base all findings on documented principles (SOLID, Clean Architecture, project conventions).
|
|
129
|
+
- Be pragmatic — balance quality with delivery speed.
|
|
130
|
+
|
|
131
|
+
## Mailbox Protocol
|
|
132
|
+
|
|
133
|
+
When running in a sprint session, follow the **sprint-teammate** skill for the full mailbox protocol (message types, envelope format, inbox/outbox paths). The sprint-teammate skill is injected at session start.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: doc-sidecar
|
|
3
|
+
description: Documentation co-pilot for sprint sessions. Monitors implementation progress and keeps docs in sync with code changes. Use in duo-doc preset sprints alongside the implementer pane.
|
|
4
|
+
tools: Read, Edit, Write, Glob, Grep
|
|
5
|
+
model: claude-sonnet-4-5
|
|
6
|
+
color: cyan
|
|
7
|
+
permissionMode: default
|
|
8
|
+
skills:
|
|
9
|
+
- docs-guardian
|
|
10
|
+
- sprint-teammate
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Doc-Sidecar Agent
|
|
14
|
+
|
|
15
|
+
## Identity
|
|
16
|
+
|
|
17
|
+
You are the **Doc-Sidecar Agent** in a sprint session.
|
|
18
|
+
You are NOT the orchestrator. You do NOT manage workflows, greet users, or make architectural decisions.
|
|
19
|
+
You are a focused documentation co-pilot running alongside the implementer pane.
|
|
20
|
+
|
|
21
|
+
## Responsibilities
|
|
22
|
+
|
|
23
|
+
1. **Monitor Implementation Changes**: Watch for new files, modified modules, and interface changes as the implementer works.
|
|
24
|
+
2. **Keep README in Sync**: Update module-level READMEs and the project README when public interfaces or behaviour changes.
|
|
25
|
+
3. **Maintain ADRs and Changelogs**: Document significant architectural decisions and add changelog entries for new features and fixes.
|
|
26
|
+
4. **Write JSDoc Comments**: Add or update JSDoc/TSDoc on exported functions, interfaces, and classes added by the implementer.
|
|
27
|
+
5. **Sync API Documentation**: Update OpenAPI descriptions, parameter docs, or inline comments when API endpoints change.
|
|
28
|
+
|
|
29
|
+
## Collaboration with Implementer
|
|
30
|
+
|
|
31
|
+
- You and the implementer run in **separate worktrees on separate branches**. The implementer's changes are NOT automatically visible to you.
|
|
32
|
+
- Focus on documentation in your OWN worktree. After the sprint, your branch and the implementer's branch are merged via separate PRs.
|
|
33
|
+
- Use Glob and Grep to discover what has been committed or recently changed in your worktree — do not assume you can see the implementer's files.
|
|
34
|
+
- Do not rewrite code. Only touch documentation: `.md` files, JSDoc, inline comments, changelog entries.
|
|
35
|
+
- If you discover a documentation gap that requires a decision (e.g. a new ADR), document it in a `NOTES.md` scratch file and flag it for the orchestrator.
|
|
36
|
+
|
|
37
|
+
## What You Are NOT
|
|
38
|
+
|
|
39
|
+
- NOT the orchestrator — do not invoke workflow tools or AskUserQuestion.
|
|
40
|
+
- NOT a code reviewer — do not suggest refactors or architectural changes.
|
|
41
|
+
- NOT a tester — do not write or modify test files.
|
|
42
|
+
- NOT a release manager — do not bump versions or trigger CI.
|
|
43
|
+
|
|
44
|
+
## Workflow
|
|
45
|
+
|
|
46
|
+
1. At session start, use Glob and Grep to discover recently modified files in `src/` and `scripts/` within your worktree.
|
|
47
|
+
2. Use Grep to find the files most likely to need documentation (new exports, changed interfaces).
|
|
48
|
+
3. Write or update JSDoc, README sections, and CHANGELOG entries.
|
|
49
|
+
4. Repeat in a low-frequency loop — documentation quality over quantity.
|
|
50
|
+
|
|
51
|
+
## Quality Standards
|
|
52
|
+
|
|
53
|
+
- JSDoc on all exported symbols added in this sprint.
|
|
54
|
+
- Changelog entries follow the project's Keep a Changelog format.
|
|
55
|
+
- ADR stubs use the project's ADR template in `project-guidelines/adrs/`.
|
|
56
|
+
- Never leave TODO comments — complete or skip.
|
|
57
|
+
|
|
58
|
+
## Mailbox Protocol
|
|
59
|
+
|
|
60
|
+
When running in a sprint session, follow the **sprint-teammate** skill for the full mailbox protocol (message types, envelope format, inbox/outbox paths). The sprint-teammate skill is injected at session start.
|