@orchestrator-claude/definitions 3.30.2 → 3.31.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.
Files changed (32) hide show
  1. package/agents/adversarial-reviewer.md +217 -0
  2. package/agents/azure-devops-engineer.md +805 -0
  3. package/agents/methodology-researcher.md +190 -0
  4. package/agents/operational-artifact-builder.md +173 -0
  5. package/agents/presentation-engineer.md +605 -0
  6. package/dist/scripts/generate-manifest.js +3 -3
  7. package/dist/scripts/generate-manifest.js.map +1 -1
  8. package/hooks/mailbox-listener.ts +246 -0
  9. package/hooks/pre-agent-dispatch.ts +57 -0
  10. package/hooks/sprint-registry.ts +85 -0
  11. package/hooks/verification-before-completion.ts +97 -0
  12. package/manifest.json +276 -3
  13. package/package.json +1 -1
  14. package/skills/artifact-generator/SKILL.md +385 -0
  15. package/skills/brainstorm-formalize/SKILL.md +272 -0
  16. package/skills/brainstorming/SKILL.md +167 -0
  17. package/skills/brainstorming/scripts/frame-template.html +214 -0
  18. package/skills/brainstorming/scripts/helper.js +88 -0
  19. package/skills/brainstorming/scripts/server.cjs +354 -0
  20. package/skills/brainstorming/scripts/start-server.sh +148 -0
  21. package/skills/brainstorming/scripts/stop-server.sh +56 -0
  22. package/skills/brainstorming/visual-companion.md +139 -0
  23. package/skills/compositional-intelligence/SKILL.md +93 -0
  24. package/skills/design-system-validated-loop/SKILL.md +366 -0
  25. package/skills/finishing-branch/SKILL.md +184 -0
  26. package/skills/orchestrator/SKILL.md +141 -0
  27. package/skills/persona-library/SKILL.md +365 -0
  28. package/skills/project-management-enterprise/SKILL.md +304 -0
  29. package/skills/receiving-code-review/SKILL.md +186 -0
  30. package/skills/sprint-launch/SKILL.md +176 -0
  31. package/skills/sprint-teammate/sprint-teammate.md +107 -0
  32. package/skills/systematic-debugging/SKILL.md +262 -0
@@ -0,0 +1,246 @@
1
+ /**
2
+ * mailbox-listener.ts — PostToolUse + SessionStart hook (RFC-025 v3.1)
3
+ *
4
+ * PostToolUse: Checks the agent's mailbox inbox after every tool call.
5
+ * SessionStart: Consumes inbox at session boot so agents start with context.
6
+ *
7
+ * Environment variables (set by WorktreeIsolator in sprint worktrees):
8
+ * SPRINT_ID — tmux session name (e.g., "sprint-cd213cab")
9
+ * PANE_ROLE — agent's mailbox address (e.g., "implementer-0")
10
+ *
11
+ * When NOT in a sprint (no env vars), exits silently with zero overhead.
12
+ *
13
+ * Message format: RFC-025 MessageEnvelope (Zod-validated JSON files).
14
+ * Consume semantics: read → validate → delete (FIFO order).
15
+ * Invalid files are moved to quarantine/ (not lost, not injected).
16
+ *
17
+ * SessionStart branch:
18
+ * - Reads hook payload from stdin (JSON with hook_event_name)
19
+ * - If hook_event_name === "SessionStart" → consume inbox and outputContext("SessionStart", ...)
20
+ * - Backward compat: if process.stdin.isTTY → skip stdin read (PostToolUse path)
21
+ */
22
+
23
+ import { readdirSync, readFileSync, unlinkSync, renameSync, existsSync, mkdirSync } from "node:fs";
24
+ import { join } from "node:path";
25
+ import { log, outputContext } from "./lib/api-client.js";
26
+
27
+ const HOOK = "MAILBOX-LISTENER";
28
+
29
+ // --- Types (minimal, no Zod dependency in hooks) ---
30
+
31
+ interface MailboxMessage {
32
+ id: string;
33
+ from: { role: string; index: number };
34
+ to: { role: string; index: number };
35
+ timestamp: string;
36
+ body: {
37
+ type: string;
38
+ taskId?: string;
39
+ title?: string;
40
+ description?: string;
41
+ summary?: string;
42
+ progress?: number;
43
+ message?: string;
44
+ errorCode?: string;
45
+ acceptanceCriteria?: string[];
46
+ };
47
+ }
48
+
49
+ // --- Stdin reader (same pattern as sprint-registry.ts) ---
50
+
51
+ async function readStdinPayload(): Promise<Record<string, unknown>> {
52
+ const chunks: Buffer[] = [];
53
+ for await (const chunk of process.stdin) {
54
+ chunks.push(chunk as Buffer);
55
+ }
56
+ const raw = Buffer.concat(chunks).toString("utf-8").trim();
57
+ if (!raw) return {};
58
+ try {
59
+ return JSON.parse(raw) as Record<string, unknown>;
60
+ } catch {
61
+ return {};
62
+ }
63
+ }
64
+
65
+ // --- Core inbox consumption logic (shared between SessionStart and PostToolUse) ---
66
+
67
+ function consumeInbox(
68
+ inboxPath: string,
69
+ quarantinePath: string,
70
+ paneRole: string,
71
+ ): { consumed: MailboxMessage[]; errors: string[] } {
72
+ // List .json files (exclude .tmp- in-flight writes)
73
+ let files: string[];
74
+ try {
75
+ files = readdirSync(inboxPath)
76
+ .filter((f) => f.endsWith(".json") && !f.startsWith(".tmp-"))
77
+ .sort(); // FIFO by filename (UUID-based, but sorted for determinism)
78
+ } catch {
79
+ return { consumed: [], errors: [] }; // Directory read error — fail silently
80
+ }
81
+
82
+ if (files.length === 0) {
83
+ return { consumed: [], errors: [] };
84
+ }
85
+
86
+ log(HOOK, `${files.length} message(s) in ${paneRole} inbox`);
87
+
88
+ const consumed: MailboxMessage[] = [];
89
+ const errors: string[] = [];
90
+
91
+ for (const file of files) {
92
+ const filePath = join(inboxPath, file);
93
+ try {
94
+ const raw = readFileSync(filePath, "utf-8");
95
+ const parsed = JSON.parse(raw) as MailboxMessage;
96
+
97
+ // Basic validation (no Zod in hooks — keep deps minimal)
98
+ if (!parsed.id || !parsed.from || !parsed.to || !parsed.body?.type) {
99
+ throw new Error(`Invalid envelope: missing required fields`);
100
+ }
101
+
102
+ // Consume: delete after successful parse
103
+ unlinkSync(filePath);
104
+ consumed.push(parsed);
105
+ log(HOOK, `Consumed: ${parsed.id} (${parsed.body.type} from ${parsed.from.role}-${parsed.from.index})`);
106
+ } catch (err) {
107
+ // Quarantine malformed message
108
+ const errMsg = err instanceof Error ? err.message : String(err);
109
+ errors.push(`${file}: ${errMsg}`);
110
+ log(HOOK, `Quarantine: ${file} — ${errMsg}`);
111
+ try {
112
+ mkdirSync(quarantinePath, { recursive: true });
113
+ renameSync(filePath, join(quarantinePath, file));
114
+ } catch {
115
+ // Best-effort quarantine
116
+ }
117
+ }
118
+ }
119
+
120
+ return { consumed, errors };
121
+ }
122
+
123
+ // --- Context builder (shared between SessionStart and PostToolUse) ---
124
+
125
+ function buildContext(
126
+ consumed: MailboxMessage[],
127
+ errors: string[],
128
+ paneRole: string,
129
+ ): string | null {
130
+ if (consumed.length === 0 && errors.length === 0) {
131
+ return null;
132
+ }
133
+
134
+ const parts: string[] = [
135
+ `[MAILBOX] ${consumed.length} new message(s) for ${paneRole}:`,
136
+ ];
137
+
138
+ for (const msg of consumed) {
139
+ const from = `${msg.from.role}-${msg.from.index}`;
140
+ parts.push("");
141
+ parts.push(`--- Message from ${from} (${msg.body.type}) ---`);
142
+
143
+ switch (msg.body.type) {
144
+ case "task_assignment":
145
+ parts.push(`Task: ${msg.body.taskId}`);
146
+ parts.push(`Title: ${msg.body.title}`);
147
+ if (msg.body.description) parts.push(`Description: ${msg.body.description}`);
148
+ if (msg.body.acceptanceCriteria?.length) {
149
+ parts.push(`Acceptance Criteria:`);
150
+ for (const ac of msg.body.acceptanceCriteria) {
151
+ parts.push(` - ${ac}`);
152
+ }
153
+ }
154
+ break;
155
+
156
+ case "task_complete":
157
+ parts.push(`Task: ${msg.body.taskId}`);
158
+ parts.push(`Summary: ${msg.body.summary}`);
159
+ break;
160
+
161
+ case "status_update":
162
+ parts.push(`Task: ${msg.body.taskId}`);
163
+ parts.push(`Progress: ${msg.body.progress}%`);
164
+ parts.push(`Status: ${msg.body.message}`);
165
+ break;
166
+
167
+ case "error_report":
168
+ parts.push(`Error: ${msg.body.errorCode}`);
169
+ parts.push(`Message: ${msg.body.message}`);
170
+ if (msg.body.taskId) parts.push(`Task: ${msg.body.taskId}`);
171
+ break;
172
+
173
+ case "ping":
174
+ parts.push(`Ping received — respond with pong if appropriate.`);
175
+ break;
176
+
177
+ case "pong":
178
+ parts.push(`Pong received — heartbeat confirmed.`);
179
+ break;
180
+
181
+ default:
182
+ parts.push(`Type: ${msg.body.type}`);
183
+ parts.push(`Raw: ${JSON.stringify(msg.body)}`);
184
+ }
185
+ }
186
+
187
+ if (errors.length > 0) {
188
+ parts.push("");
189
+ parts.push(`[MAILBOX-WARN] ${errors.length} malformed message(s) quarantined:`);
190
+ for (const e of errors) {
191
+ parts.push(` - ${e}`);
192
+ }
193
+ }
194
+
195
+ return parts.join("\n");
196
+ }
197
+
198
+ // --- Main ---
199
+
200
+ async function main(): Promise<void> {
201
+ const sprintId = process.env.SPRINT_ID;
202
+ const paneRole = process.env.PANE_ROLE;
203
+
204
+ // Not in a sprint — exit silently (zero overhead path)
205
+ if (!sprintId || !paneRole) {
206
+ return;
207
+ }
208
+
209
+ const inboxPath = `/tmp/${sprintId}/mailbox/${paneRole}/inbox`;
210
+ const quarantinePath = `/tmp/${sprintId}/mailbox/${paneRole}/quarantine`;
211
+
212
+ // No inbox directory — sprint may not have mailbox enabled
213
+ if (!existsSync(inboxPath)) {
214
+ return;
215
+ }
216
+
217
+ // Determine hook event: read stdin unless this is an interactive TTY (PostToolUse path)
218
+ let hookEventName: string | undefined;
219
+ if (!process.stdin.isTTY) {
220
+ try {
221
+ const payload = await readStdinPayload();
222
+ hookEventName = payload.hook_event_name as string | undefined;
223
+ } catch {
224
+ // Stdin read failure — fall through to PostToolUse behaviour
225
+ }
226
+ }
227
+
228
+ // Consume inbox
229
+ const { consumed, errors } = consumeInbox(inboxPath, quarantinePath, paneRole);
230
+
231
+ if (consumed.length === 0 && errors.length === 0) {
232
+ return;
233
+ }
234
+
235
+ // Build context string
236
+ const context = buildContext(consumed, errors, paneRole);
237
+ if (!context) {
238
+ return;
239
+ }
240
+
241
+ // Emit context with the appropriate hook event name
242
+ const effectiveEvent = hookEventName === "SessionStart" ? "SessionStart" : "PostToolUse";
243
+ outputContext(effectiveEvent, context);
244
+ }
245
+
246
+ void main();
@@ -0,0 +1,57 @@
1
+ /**
2
+ * pre-agent-dispatch.ts — PreToolUse hook for Agent tool (RFC-027 Sprint 4)
3
+ *
4
+ * Warns when dispatching known agents without subagent_type.
5
+ * Non-blocking: always exits 0.
6
+ */
7
+
8
+ import { log, readStdin, getField, outputContext } from "./lib/api-client.js";
9
+
10
+ const HOOK = "PRE-AGENT-DISPATCH";
11
+
12
+ const KNOWN_AGENTS = [
13
+ "implementer",
14
+ "planner",
15
+ "specifier",
16
+ "reviewer",
17
+ "task-generator",
18
+ "researcher",
19
+ ];
20
+
21
+ const KNOWN_AGENTS_REGEX = new RegExp(
22
+ `\\b(${KNOWN_AGENTS.join("|")})\\b`,
23
+ "i"
24
+ );
25
+
26
+ async function main(): Promise<void> {
27
+ const stdin = await readStdin();
28
+ const toolInput = getField(stdin, "tool_input") || "";
29
+ const subagentType =
30
+ getField(stdin, "tool_input.subagent_type") ||
31
+ getField(stdin, "tool_input.subagentType") ||
32
+ "";
33
+
34
+ // If subagent_type is present, no warning needed
35
+ if (subagentType) {
36
+ log(HOOK, `Agent dispatched with subagent_type="${subagentType}", OK`);
37
+ return;
38
+ }
39
+
40
+ // Check if prompt mentions a known agent
41
+ const prompt =
42
+ getField(stdin, "tool_input.prompt") ||
43
+ getField(stdin, "tool_input.description") ||
44
+ (typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput));
45
+
46
+ const match = prompt.match(KNOWN_AGENTS_REGEX);
47
+ if (match) {
48
+ const detected = match[1].toLowerCase();
49
+ outputContext(
50
+ HOOK,
51
+ `[WARN] Agent dispatched without subagent_type. Known agent detected: "${detected}". Add subagent_type for proper frontmatter loading.`
52
+ );
53
+ log(HOOK, `WARN: known agent "${detected}" without subagent_type`);
54
+ }
55
+ }
56
+
57
+ main().catch(() => process.exit(0));
@@ -0,0 +1,85 @@
1
+ /**
2
+ * sprint-registry.ts — SessionStart self-registration hook (RFC-025 M4)
3
+ *
4
+ * Reads SPRINT_ID and PANE_ROLE from environment, then writes a registry
5
+ * entry to /tmp/sprint-<SPRINT_ID>/registry/<PANE_ROLE>.json.
6
+ *
7
+ * IMPORTANT: No imports from scripts/lib/ — uses raw Node.js fs only.
8
+ * This hook runs in isolated worktree environments where scripts/lib may
9
+ * not be available on the module resolution path.
10
+ *
11
+ * Hook input (stdin): Claude Code SessionStart JSON payload (ignored).
12
+ * Exit 0 always — registration failure must not block the session.
13
+ */
14
+
15
+ import { mkdirSync, writeFileSync } from 'node:fs';
16
+ import { join } from 'node:path';
17
+
18
+ function log(msg: string): void {
19
+ process.stderr.write(`[sprint-registry] ${msg}\n`);
20
+ }
21
+
22
+ /** Read hook JSON payload from stdin (async, same pattern as api-client.ts). */
23
+ async function readStdin(): Promise<Record<string, unknown>> {
24
+ const chunks: Buffer[] = [];
25
+ for await (const chunk of process.stdin) {
26
+ chunks.push(chunk as Buffer);
27
+ }
28
+ const raw = Buffer.concat(chunks).toString('utf-8').trim();
29
+ if (!raw) return {};
30
+ try {
31
+ return JSON.parse(raw) as Record<string, unknown>;
32
+ } catch {
33
+ return {};
34
+ }
35
+ }
36
+
37
+ async function main(): Promise<void> {
38
+ // Read SessionStart payload from stdin — contains session_id
39
+ const stdin = await readStdin();
40
+ const sessionId = (stdin.session_id as string) ?? undefined;
41
+
42
+
43
+ // Read required env vars
44
+ const sprintId = process.env['SPRINT_ID'];
45
+ const paneRole = process.env['PANE_ROLE'];
46
+
47
+ if (!sprintId || !paneRole) {
48
+ log(`Skipping: SPRINT_ID=${sprintId ?? 'unset'} PANE_ROLE=${paneRole ?? 'unset'}`);
49
+ process.exit(0);
50
+ }
51
+
52
+ const registryDir = `/tmp/${sprintId}/registry`;
53
+ const entryPath = join(registryDir, `${paneRole}.json`);
54
+
55
+ const workflowId = process.env['WORKFLOW_ID'];
56
+
57
+ const entry: Record<string, unknown> = {
58
+ role: paneRole,
59
+ sprintId,
60
+ pid: process.pid,
61
+ registeredAt: new Date().toISOString(),
62
+ worktreePath: process.cwd(),
63
+ };
64
+
65
+ if (sessionId) {
66
+ entry.sessionId = sessionId;
67
+ }
68
+
69
+ if (workflowId) {
70
+ entry.workflowId = workflowId;
71
+ }
72
+
73
+ try {
74
+ mkdirSync(registryDir, { recursive: true });
75
+ writeFileSync(entryPath, JSON.stringify(entry, null, 2), 'utf-8');
76
+ log(`Registered ${paneRole} for sprint ${sprintId} (pid=${process.pid}, session=${sessionId ?? 'unknown'})`);
77
+ } catch (err) {
78
+ // Log but do not fail — registration is best-effort
79
+ log(`Registration failed: ${err}`);
80
+ }
81
+
82
+ process.exit(0);
83
+ }
84
+
85
+ main().catch(() => process.exit(0));
@@ -0,0 +1,97 @@
1
+ /**
2
+ * verification-before-completion.ts — PreToolUse[Bash] hook
3
+ *
4
+ * Injects a verification reminder when the model is about to run
5
+ * `git commit` or `git push`. Does NOT block — only adds context.
6
+ *
7
+ * Principle: Evidence before claims, always.
8
+ * Source: verification-before-completion skill (superpowers 5.0.7)
9
+ *
10
+ * Flow:
11
+ * 1. readStdin() → extract bash command
12
+ * 2. commandMatchesGitOp() → check for commit/push
13
+ * 3. If match → output allow + context reminder
14
+ * 4. If no match → silent pass-through (no output)
15
+ */
16
+
17
+ import { log, readStdin, getField, outputPreToolUse } from "./lib/api-client.js";
18
+
19
+ const HOOK = "VERIFICATION-REMINDER";
20
+
21
+ // ─────────────────────────────────────────────────────────────
22
+ // Dependency injection interface (enables testing without IO)
23
+ // ─────────────────────────────────────────────────────────────
24
+
25
+ export interface HookDeps {
26
+ readStdin: () => Promise<Record<string, unknown>>;
27
+ output: (opts: { decision: "allow" | "deny"; reason?: string; context?: string }) => void;
28
+ log: (prefix: string, msg: string) => void;
29
+ }
30
+
31
+ const defaultDeps: HookDeps = {
32
+ readStdin,
33
+ output: outputPreToolUse,
34
+ log,
35
+ };
36
+
37
+ // ─────────────────────────────────────────────────────────────
38
+ // Pure helpers (exported for testing)
39
+ // ─────────────────────────────────────────────────────────────
40
+
41
+ /**
42
+ * Returns true when the bash command string contains a git commit or git push
43
+ * operation. Handles common variations: `git commit`, `git push`, multi-command
44
+ * pipelines (`&&`, `;`), and quoted forms.
45
+ */
46
+ export function commandMatchesGitOp(command: string): boolean {
47
+ return /git\s+(commit|push)/.test(command);
48
+ }
49
+
50
+ // ─────────────────────────────────────────────────────────────
51
+ // Core logic (exported for testing)
52
+ // ─────────────────────────────────────────────────────────────
53
+
54
+ export async function mainWithDeps(deps: HookDeps): Promise<void> {
55
+ const { readStdin, output, log } = deps;
56
+
57
+ const stdin = await readStdin();
58
+
59
+ // Extract the bash command from the tool input envelope
60
+ const command =
61
+ (getField(stdin, "tool_input.command") as string | undefined) ||
62
+ (getField(stdin, "command") as string | undefined) ||
63
+ "";
64
+
65
+ if (!commandMatchesGitOp(command)) {
66
+ // Silent pass-through — not a git commit/push, nothing to do
67
+ log(HOOK, `PASS (no git op): ${command.slice(0, 60)}`);
68
+ return;
69
+ }
70
+
71
+ log(HOOK, `REMIND git op detected: ${command.slice(0, 80)}`);
72
+
73
+ // Allow the command but inject a verification reminder as context.
74
+ // This hook NEVER blocks — it only nudges the model to verify first.
75
+ output({
76
+ decision: "allow",
77
+ context: [
78
+ "[VERIFICATION-REMINDER] You are about to run a git commit/push.",
79
+ "Before proceeding, confirm:",
80
+ " 1. Tests pass — run the test command and paste the output.",
81
+ " 2. Lint clean — run the lint command and confirm 0 errors.",
82
+ " 3. Build succeeds — run the build command and confirm exit 0.",
83
+ " 4. All acceptance criteria have matching test evidence.",
84
+ "Evidence before claims. No shortcuts for verification.",
85
+ ].join("\n"),
86
+ });
87
+ }
88
+
89
+ // ─────────────────────────────────────────────────────────────
90
+ // Entry point (production use)
91
+ // ─────────────────────────────────────────────────────────────
92
+
93
+ async function main(): Promise<void> {
94
+ await mainWithDeps(defaultDeps);
95
+ }
96
+
97
+ main().catch(() => process.exit(0));