zelari-code 0.2.0 → 0.2.1

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.
@@ -1321,8 +1321,29 @@ var CODING_SKILL_CATALOG = [];
1321
1321
  function listCodingSkills() {
1322
1322
  return [...CODING_SKILL_CATALOG];
1323
1323
  }
1324
+ function validateCodingSkillRequires(skill) {
1325
+ const errors = [];
1326
+ for (const reqId of skill.requires) {
1327
+ if (!CODING_SKILL_CATALOG.some((s) => s.id === reqId)) {
1328
+ errors.push(`Skill "${skill.id}" requires unknown skill "${reqId}"`);
1329
+ }
1330
+ }
1331
+ return errors;
1332
+ }
1333
+ function registerCodingSkill(skill) {
1334
+ const errors = validateCodingSkillRequires(skill);
1335
+ if (errors.length > 0) {
1336
+ throw new Error(`Cannot register coding skill: ${errors.join("; ")}`);
1337
+ }
1338
+ const idx = CODING_SKILL_CATALOG.findIndex((s) => s.id === skill.id);
1339
+ if (idx >= 0) {
1340
+ CODING_SKILL_CATALOG[idx] = skill;
1341
+ } else {
1342
+ CODING_SKILL_CATALOG.push(skill);
1343
+ }
1344
+ }
1324
1345
 
1325
- // src/agents/roles.ts
1346
+ // src/agents/skills/builtin/debugging.ts
1326
1347
  var CLARIFICATION_PROTOCOL = `
1327
1348
 
1328
1349
  WHEN TO ASK THE USER (clarification):
@@ -1332,6 +1353,1991 @@ If a single missing fact would materially change your output (target platform, s
1332
1353
  { "question": "One focused question", "choices": ["Option A", "Option B", "Option C"], "context": "Why this matters" }
1333
1354
  ---END---
1334
1355
 
1356
+ Rules for clarifications:
1357
+ - Ask AT MOST ONE question per turn, and only when genuinely blocked.
1358
+ - Prefer a small set of concrete "choices" (2-4). The user can still type a wave-in response.
1359
+ - Do NOT ask for information that could be reasonably assumed or already in shared context.
1360
+ - If you can proceed with a sound documented assumption, DO SO instead of asking.`;
1361
+ var reproduceBug = {
1362
+ id: "reproduce-bug",
1363
+ version: "1.0.0",
1364
+ name: "Reproduce Bug",
1365
+ description: "Convert a bug report into a MINIMAL failing test. The test must fail without any code changes, then pass after the fix.",
1366
+ category: "debug",
1367
+ requiredRoles: ["oracle", "atlas"],
1368
+ requiredTools: ["read_file", "write_file"],
1369
+ estimatedCost: "medium",
1370
+ enabledByDefault: true,
1371
+ builtin: true,
1372
+ triggers: [
1373
+ "Bug report without a minimal reproduction",
1374
+ "Bug that only happens in production (not in tests)",
1375
+ 'Bug that happens "sometimes" (intermittent)',
1376
+ "Bug reported via screenshot or video, not repro steps",
1377
+ "A complex multi-agent state race condition that is hard to verify without a test"
1378
+ ],
1379
+ antiPatterns: [
1380
+ "Bug is in production AND has minimal repro steps already \u2014 skip to fix",
1381
+ "Bug is in test code itself \u2014 fix the test, not the production code",
1382
+ "Bug requires special hardware/network state \u2014 use a mock instead"
1383
+ ],
1384
+ requires: [],
1385
+ relatedSkills: ["debug-with-rag", "root-cause-five-whys"],
1386
+ tags: ["debug", "reproduction", "test", "minimal-example"],
1387
+ examples: [
1388
+ {
1389
+ input: 'Bug report: "Council sometimes shows duplicate messages at the start of a session"',
1390
+ output: {
1391
+ minimalRepro: `// tests/regression/duplicate-messages.test.ts
1392
+ import { runCouncilPure } from '../agents/councilApi';
1393
+
1394
+ describe('council duplicate message bug', () => {
1395
+ it('does not emit duplicate user message when session resumes', async () => {
1396
+ const mockProvider = async function* () {
1397
+ yield { kind: 'text', delta: 'first' };
1398
+ yield { kind: 'finish', reason: 'stop' };
1399
+ };
1400
+ const harness = new AgentHarness({
1401
+ model: 'test', provider: 'test',
1402
+ messages: [
1403
+ { role: 'user', content: 'hi' },
1404
+ { role: 'user', content: 'hi' }, // duplicate from session resume
1405
+ ],
1406
+ tools: [],
1407
+ providerStream: mockProvider,
1408
+ });
1409
+ const events = [];
1410
+ for await (const e of harness.run()) events.push(e);
1411
+ const userMessages = events.filter(e => e.type === 'message_delta').length;
1412
+ expect(userMessages).toBeGreaterThan(0); // sanity
1413
+ // The actual bug: duplicate is rendered twice
1414
+ // After fix, expect exactly 1 user message rendered
1415
+ });
1416
+ });`,
1417
+ reproSteps: [
1418
+ "1. Set up session with duplicate user message in transcript",
1419
+ "2. Run runCouncilPure",
1420
+ "3. Observe duplicate message in output"
1421
+ ],
1422
+ whyMinimal: "Removed all agents except the first specialist (was: sisyphus+prometheus+hephaestus+oracle+chairman). Removed RAG context, workspace context. Kept only the duplication pattern."
1423
+ }
1424
+ }
1425
+ ],
1426
+ outputSchema: "{ minimalRepro: string; reproSteps: string[]; whyMinimal: string }",
1427
+ systemPromptFragment: `You are converting a bug report into a minimal failing test.
1428
+
1429
+ ## Methodology
1430
+ 1. **Read the bug report** carefully: what's the exact symptom? When does it happen?
1431
+ 2. **Strip to essentials**: remove every agent, tool, env var, user state that isn't strictly required.
1432
+ 3. **Identify the trigger**: what ONE input causes the bug?
1433
+ 4. **Write the failing test**: it must FAIL on the current (buggy) code.
1434
+ 5. **Verify it fails**: the test must fail without any code changes.
1435
+ 6. **After the fix lands**, the test must PASS.
1436
+
1437
+ ## Output format (JSON-typed)
1438
+ - minimalRepro: string (the test code, runnable as-is)
1439
+ - reproSteps: string[] (3-7 numbered steps to reproduce manually)
1440
+ - whyMinimal: string (what you stripped + why)
1441
+
1442
+ ## Minimal-repro principles
1443
+ - **One assertion per test** \u2014 easier to debug when the test fails
1444
+ - **Deterministic** \u2014 no flaky timing, no random data
1445
+ - **No external dependencies** \u2014 use mocks for network/DB
1446
+ - **Fast** \u2014 under 100ms if possible
1447
+ - **Independent** \u2014 doesn't depend on other tests' state
1448
+
1449
+ Stay under 300 words.${CLARIFICATION_PROTOCOL}`
1450
+ };
1451
+ var debugWithRag = {
1452
+ id: "debug-with-rag",
1453
+ version: "1.0.0",
1454
+ name: "Debug With RAG",
1455
+ description: "Search the knowledge base for similar past bugs, read the stack trace, propose the most likely root cause and a concrete fix.",
1456
+ category: "debug",
1457
+ requiredRoles: ["oracle", "atlas"],
1458
+ requiredTools: ["searchRAG", "grep_content", "read_file"],
1459
+ estimatedCost: "medium",
1460
+ enabledByDefault: true,
1461
+ builtin: true,
1462
+ triggers: [
1463
+ "A bug report with a stack trace or error message",
1464
+ "A regression: feature worked before, now broken",
1465
+ "A flaky test that fails intermittently",
1466
+ "Performance regression: response time > 5s where it used to be < 500ms",
1467
+ "A memory leak causing out-of-memory crashes"
1468
+ ],
1469
+ antiPatterns: [
1470
+ "Bug is obvious from the error message (e.g. TypeError: cannot read property of undefined \u2014 fix directly)",
1471
+ "No stack trace or repro steps \u2014 first ask the user for a minimal repro",
1472
+ "A test is failing because the test itself is wrong \u2014 debug the test, not the code"
1473
+ ],
1474
+ requires: ["reproduce-bug"],
1475
+ relatedSkills: ["root-cause-five-whys", "reproduce-bug"],
1476
+ tags: ["debug", "rag", "stack-trace", "root-cause", "regression"],
1477
+ examples: [
1478
+ {
1479
+ input: 'TypeError: Cannot read properties of undefined (reading "map") at Council.tsx:847',
1480
+ output: {
1481
+ ragSearch: 'searchRAG(query="Council.tsx undefined map error") returned 2 prior incidents: similar bug fixed in commit a3f9d by adding optional chaining; another similar issue in Council.tsx:612.',
1482
+ stackTraceAnalysis: "Line 847 is inside `renderMessages()`. The most likely cause: `messages` is undefined when `runCouncilPure()` returns early on the first iteration before any agent has produced output.",
1483
+ rootCause: "race condition: messages array is set after the first agent completes, but renderMessages() is called during the loading state when messages is still undefined.",
1484
+ proposedFix: `function renderMessages() {
1485
+ const messages = session.messages ?? [];
1486
+ return messages.map(m => <Message key={m.id} {...m} />);
1487
+ }`,
1488
+ verification: "Add a unit test that calls renderMessages() with messages=undefined and expects [] (not crash). Manual: trigger the bug scenario, confirm no crash."
1489
+ }
1490
+ }
1491
+ ],
1492
+ outputSchema: "{ ragSearch: string; stackTraceAnalysis: string; rootCause: string; proposedFix: string; verification: string }",
1493
+ systemPromptFragment: `You are debugging a bug using the knowledge base.
1494
+
1495
+ ## Methodology
1496
+ 1. **Extract key terms** from the error message + stack trace (file names, function names, error type).
1497
+ 2. **Search the knowledge base**: searchRAG(query="<key terms>")
1498
+ 3. **Read the relevant files**: use read_file with line ranges from the stack trace.
1499
+ 4. **Identify the root cause**: what's the actual logic error? Don't just fix the symptom.
1500
+ 5. **Propose a minimal fix**: the SMALLEST change that addresses the root cause.
1501
+ 6. **Specify verification**: how to confirm the fix works (test case + manual steps).
1502
+
1503
+ ## Output format (JSON-typed)
1504
+ - ragSearch: string (what you searched + what you found)
1505
+ - stackTraceAnalysis: string (which line + what's likely happening)
1506
+ - rootCause: string (the actual bug, not the symptom)
1507
+ - proposedFix: string (code snippet)
1508
+ - verification: string (test + manual steps)
1509
+
1510
+ ## Debugging principles
1511
+ - **Fix the cause, not the symptom** (e.g. don't add a try/catch to silence an error)
1512
+ - **Minimal change** \u2014 don't refactor surrounding code while debugging
1513
+ - **One hypothesis at a time** \u2014 don't list 5 possible causes
1514
+ - **Verify the fix actually fixes** \u2014 write a failing test BEFORE the fix
1515
+
1516
+ Stay under 400 words.${CLARIFICATION_PROTOCOL}`
1517
+ };
1518
+ var rootCauseFiveWhys = {
1519
+ id: "root-cause-five-whys",
1520
+ version: "1.0.0",
1521
+ name: "Root Cause Five Whys",
1522
+ description: 'Apply iterative 5-whys analysis with council consensus. Drill down through 5 levels of "why" to find the systemic root cause, not just the proximate trigger.',
1523
+ category: "debug",
1524
+ requiredRoles: ["sisyphus", "oracle", "chairman"],
1525
+ requiredTools: ["searchRAG", "read_file"],
1526
+ estimatedCost: "high",
1527
+ enabledByDefault: true,
1528
+ builtin: true,
1529
+ triggers: [
1530
+ 'A bug that keeps recurring after fixes ("whack-a-mole" symptom)',
1531
+ "A systemic issue affecting multiple files/modules",
1532
+ "A post-mortem on a production incident",
1533
+ "Process issues (not just code) \u2014 why does the team keep making the same mistake?",
1534
+ "Frequent merge failures or regression patterns in a single week"
1535
+ ],
1536
+ antiPatterns: [
1537
+ "A trivial bug with obvious cause \u2014 just fix it directly",
1538
+ "A user error (bad input) \u2014 the system should validate, not the user change behavior",
1539
+ "A flaky test \u2014 fix the test, not the underlying system"
1540
+ ],
1541
+ requires: ["debug-with-rag"],
1542
+ relatedSkills: ["debug-with-rag", "reproduce-bug"],
1543
+ tags: ["debug", "root-cause", "systemic", "postmortem", "five-whys"],
1544
+ examples: [
1545
+ {
1546
+ input: "Production incident: 3 outages in 2 weeks, all caused by the same API rate limit hit",
1547
+ output: {
1548
+ whys: [
1549
+ { level: 1, question: "Why did the service go down?", answer: "The MiniMax API returned 429 Too Many Requests at peak load." },
1550
+ { level: 2, question: "Why did we hit the rate limit?", answer: "We made 200 requests/minute; the limit is 100/minute." },
1551
+ { level: 3, question: "Why did we exceed the rate limit?", answer: "A single user request triggered 200 sub-requests for parallel tool calls." },
1552
+ { level: 4, question: "Why did one request trigger 200 sub-requests?", answer: "The agent loop calls all tools in parallel without batching." },
1553
+ { level: 5, question: "Why don't we batch tool calls?", answer: "No rate limiter in the agent loop. The team assumed the API limit was high enough." }
1554
+ ],
1555
+ rootCause: "No client-side rate limiting. The agent loop fires parallel tool calls without backoff or batching.",
1556
+ systemicFix: "Add a token-bucket rate limiter (100 req/min) before the API client. Implement batching for independent tool calls. Add a circuit breaker that pauses on 429 responses.",
1557
+ processFix: "Add a pre-deploy load test that runs 1000 concurrent requests and verifies rate-limit handling.",
1558
+ councilConsensus: 'Chairman: "All three council members agree on the systemic root cause. The process fix is as important as the code fix \u2014 load tests should be CI-required."'
1559
+ }
1560
+ }
1561
+ ],
1562
+ outputSchema: "{ whys: Array<{ level: number; question: string; answer: string }>; rootCause: string; systemicFix: string; processFix: string; councilConsensus: string }",
1563
+ systemPromptFragment: `You are applying the 5-whys technique with council consensus.
1564
+
1565
+ ## Methodology
1566
+ 1. **State the symptom** as the starting point for "why?"
1567
+ 2. **Ask "why?" 5 times**, drilling down through layers of cause
1568
+ 3. **Each answer must be FACTUAL**, not speculative
1569
+ 4. **At the 5th why**, you've reached the systemic root cause
1570
+ 5. **Propose TWO fixes**: systemic (code/architecture) + process (team/workflow)
1571
+ 6. **Get council consensus**: chairman summarizes agreement from sisyphus (orchestrator) and oracle (analyst)
1572
+
1573
+ ## Output format (JSON-typed)
1574
+ - whys: Array<{ level: number; question: string; answer: string }>
1575
+ - rootCause: string (the deepest why)
1576
+ - systemicFix: string (code/architecture change)
1577
+ - processFix: string (team/workflow change)
1578
+ - councilConsensus: string (chairman's summary of agreement)
1579
+
1580
+ ## Anti-patterns to avoid
1581
+ - **"Human error"** is NEVER the root cause \u2014 the system should make the error impossible
1582
+ - **"Lack of training"** is NEVER the root cause \u2014 automation + checklists > training
1583
+ - **"Not enough time"** is NEVER the root cause \u2014 find the constraint, not the excuse
1584
+ - Stop at the FIRST level if it's a trivial bug \u2014 don't overthink simple cases
1585
+
1586
+ Stay under 500 words.${CLARIFICATION_PROTOCOL}`
1587
+ };
1588
+ var registerCodingSkill2 = registerCodingSkill;
1589
+ registerCodingSkill2(reproduceBug);
1590
+ registerCodingSkill2(debugWithRag);
1591
+ registerCodingSkill2(rootCauseFiveWhys);
1592
+
1593
+ // src/agents/skills/builtin/docs.ts
1594
+ var CLARIFICATION_PROTOCOL2 = `
1595
+
1596
+ WHEN TO ASK THE USER (clarification):
1597
+ If a single missing fact would materially change your output (target platform, scope, a binary design choice with significant trade-offs, a constraint you cannot safely assume), pause and ask the user by appending EXACTLY this block at the end of your message:
1598
+
1599
+ ---QUESTION---
1600
+ { "question": "One focused question", "choices": ["Option A", "Option B", "Option C"], "context": "Why this matters" }
1601
+ ---END---
1602
+
1603
+ Rules for clarifications:
1604
+ - Ask AT MOST ONE question per turn, and only when genuinely blocked.
1605
+ - Prefer a small set of concrete "choices" (2-4). The user can still type a custom answer.
1606
+ - Do NOT ask for information that could be reasonably assumed or already in shared context.
1607
+ - If you can proceed with a sound documented assumption, DO SO instead of asking.`;
1608
+ var writeReadme = {
1609
+ id: "write-readme",
1610
+ version: "1.0.0",
1611
+ name: "Write README",
1612
+ description: "Generate a project README from the codebase. Sections: Title, Badges, Description, Quick Start, Architecture, Development, Testing, Deployment, Contributing, License.",
1613
+ category: "docs",
1614
+ requiredRoles: ["prometheus", "atlas"],
1615
+ requiredTools: ["read_file", "grep_content"],
1616
+ estimatedCost: "medium",
1617
+ enabledByDefault: true,
1618
+ builtin: true,
1619
+ triggers: [
1620
+ "A new project is being created",
1621
+ "Existing README is missing or stale",
1622
+ "A project is being open-sourced",
1623
+ "Onboarding new team members (they read the README first)"
1624
+ ],
1625
+ antiPatterns: [
1626
+ "README exists and is recent \u2014 update instead of regenerate",
1627
+ "Personal/internal project (README rarely matters)",
1628
+ "Library with auto-generated docs from source code comments"
1629
+ ],
1630
+ requires: [],
1631
+ relatedSkills: ["write-tsdoc", "write-changelog"],
1632
+ tags: ["docs", "readme", "onboarding", "open-source"],
1633
+ examples: [
1634
+ {
1635
+ input: "Generate README for AnathemaBrain (Electron + React + TypeScript AI agent platform)",
1636
+ output: {
1637
+ readme: `# AnathemaBrain
1638
+
1639
+ > An educational Python project for learning how coding agents are built.
1640
+
1641
+ AnathemaBrain is an Electron desktop app... (truncated)
1642
+
1643
+ ## Quick Start
1644
+
1645
+ \`\`\`bash
1646
+ npm install
1647
+ npm run dev
1648
+ \`\`\`
1649
+
1650
+ ## Architecture
1651
+
1652
+ - **Main process** (electron/main/): Node.js, IPC handlers, LLM streaming
1653
+ - **Renderer** (src/): React 19 UI, Zustand stores
1654
+ - **Agent system** (src/agents/): Council multi-agent orchestration
1655
+
1656
+ ## Development
1657
+
1658
+ \`\`\`bash
1659
+ npm run typecheck
1660
+ npm run test
1661
+ npm run lint
1662
+ \`\`\`
1663
+
1664
+ ## Testing
1665
+
1666
+ 96+ tests via Vitest + jsdom.
1667
+
1668
+ ## Deployment
1669
+
1670
+ \`\`\`bash
1671
+ npm run build
1672
+ npm run electron:build
1673
+ \`\`\`
1674
+
1675
+ ## Contributing
1676
+
1677
+ See docs/plans/ for active roadmaps. Pull requests welcome.
1678
+
1679
+ ## License
1680
+
1681
+ Apache 2.0
1682
+ `,
1683
+ sections: ["Title + tagline", "Badges", "Description", "Quick Start", "Architecture diagram", "Development commands", "Testing", "Deployment", "Contributing link", "License"]
1684
+ }
1685
+ }
1686
+ ],
1687
+ outputSchema: "{ readme: string; sections: string[] }",
1688
+ systemPromptFragment: `You are writing a project README.md.
1689
+
1690
+ ## Required sections (in order)
1691
+ 1. **Title + tagline** (1 sentence): what is this project?
1692
+ 2. **Badges** (optional): CI status, npm version, license
1693
+ 3. **Description** (2-3 paragraphs): what does it do, who is it for
1694
+ 4. **Quick Start** (5-10 lines): install + run + see something working
1695
+ 5. **Architecture** (1-2 paragraphs + diagram): high-level structure
1696
+ 6. **Development** (commands): how to run tests, typecheck, lint
1697
+ 7. **Testing**: what kind of tests exist, how to run them
1698
+ 8. **Deployment** (if applicable): how to build + ship
1699
+ 9. **Contributing**: link to CONTRIBUTING.md or describe PR process
1700
+ 10. **License**: license type
1701
+
1702
+ ## Output format (JSON-typed)
1703
+ - readme: string (the full README content in markdown)
1704
+ - sections: string[] (the sections you included)
1705
+
1706
+ ## README principles
1707
+ - **Show, don't tell**: code examples for non-trivial concepts
1708
+ - **Up-to-date**: don't include commands that don't work
1709
+ - **Scannable**: use headers, bullets, code blocks
1710
+ - **One page max**: link to deeper docs rather than nesting everything
1711
+
1712
+ Stay under 600 words.${CLARIFICATION_PROTOCOL2}`
1713
+ };
1714
+ var writeTsdoc = {
1715
+ id: "write-tsdoc",
1716
+ version: "1.0.0",
1717
+ name: "Write TSDoc",
1718
+ description: "Generate TSDoc/JSDoc comments for undocumented exports. Output: docstrings with @param, @returns, @throws, @example tags following the TSDoc standard.",
1719
+ category: "docs",
1720
+ requiredRoles: ["atlas"],
1721
+ requiredTools: ["read_file"],
1722
+ estimatedCost: "low",
1723
+ enabledByDefault: true,
1724
+ builtin: true,
1725
+ triggers: [
1726
+ "A function or class lacks documentation",
1727
+ "A public API is exported without TSDoc",
1728
+ 'IDE intellisense shows "any" or missing info for a function',
1729
+ "A new utility is added to a shared library"
1730
+ ],
1731
+ antiPatterns: [
1732
+ "The function is private/internal \u2014 no need for TSDoc",
1733
+ "Auto-generated docs already exist (typedoc, etc.)",
1734
+ "The function is self-explanatory (getName \u2192 returns name)"
1735
+ ],
1736
+ requires: [],
1737
+ relatedSkills: ["write-readme"],
1738
+ tags: ["docs", "tsdoc", "jsdoc", "intellisense", "api-docs"],
1739
+ examples: [
1740
+ {
1741
+ input: "Add TSDoc to src/lib/formatDuration.ts",
1742
+ output: {
1743
+ documented: `/**
1744
+ * Formats a duration in milliseconds as a human-readable string.
1745
+ *
1746
+ * @param ms - Duration in milliseconds. Must be a non-negative finite number.
1747
+ * @param opts - Formatting options.
1748
+ * @param opts.style - 'compact' (1h 23m), 'full' (1:23:45), or 'minimal' (23m).
1749
+ * Defaults to 'compact'.
1750
+ * @returns The formatted duration string.
1751
+ * @throws {RangeError} If ms is negative.
1752
+ * @throws {TypeError} If ms is NaN or Infinity.
1753
+ *
1754
+ * @example
1755
+ * formatDuration(4980000); // '1h 23m'
1756
+ * formatDuration(4980000, { style: 'full' }); // '1:23:45'
1757
+ * formatDuration(60000, { style: 'minimal' }); // '1m'
1758
+ */
1759
+ export function formatDuration(ms: number, opts?: { style?: 'compact' | 'full' | 'minimal' }): string {
1760
+ // ... existing implementation
1761
+ }`,
1762
+ tagsAdded: ["@param", "@param (nested)", "@returns", "@throws x2", "@example"]
1763
+ }
1764
+ }
1765
+ ],
1766
+ outputSchema: "{ documented: string; tagsAdded: string[] }",
1767
+ systemPromptFragment: `You are writing TSDoc comments for a TypeScript function or class.
1768
+
1769
+ ## TSDoc tags (use these)
1770
+ - \`@param {type} name - description\` \u2014 for each parameter
1771
+ - \`@returns description\` (or \`@return\` legacy alias)
1772
+ - \`@throws {ErrorType} condition\` \u2014 for each throw path
1773
+ - \`@example\` followed by code block (for non-trivial usage)
1774
+ - \`@deprecated reason\` \u2014 if the function is being phased out
1775
+ - \`@see FunctionName\` \u2014 for cross-references
1776
+ - \`@remarks\` \u2014 additional context
1777
+ - \`@internal\` \u2014 for internal helpers (suppresses from public docs)
1778
+
1779
+ ## Output format (JSON-typed)
1780
+ - documented: string (the full code block with TSDoc added)
1781
+ - tagsAdded: string[] (which tags you included)
1782
+
1783
+ ## TSDoc principles
1784
+ - **First sentence is the summary**: ends with a period, capital letter
1785
+ - **One @param per parameter**: don't omit even if obvious
1786
+ - **@throws for EVERY throw path**: callers need to know
1787
+ - **@example for non-trivial usage**: best way to communicate intent
1788
+ - **No marketing language**: just describe what the function does
1789
+ - **No implementation details**: TSDoc is for the API contract, not internals
1790
+
1791
+ Stay under 400 words.${CLARIFICATION_PROTOCOL2}`
1792
+ };
1793
+ var writeChangelog = {
1794
+ id: "write-changelog",
1795
+ version: "1.0.0",
1796
+ name: "Write Changelog",
1797
+ description: "Generate a CHANGELOG.md entry from git history. Follows Keep a Changelog format with sections: Added, Changed, Deprecated, Removed, Fixed, Security. Groups commits by category, deduplicates, writes user-facing descriptions.",
1798
+ category: "docs",
1799
+ requiredRoles: ["atlas"],
1800
+ requiredTools: [],
1801
+ estimatedCost: "low",
1802
+ enabledByDefault: true,
1803
+ builtin: true,
1804
+ triggers: [
1805
+ "A release is being prepared",
1806
+ "A sprint/iteration ends",
1807
+ "Significant changes have accumulated since the last entry",
1808
+ "A user-visible breaking change happened"
1809
+ ],
1810
+ antiPatterns: [
1811
+ "No commits since the last changelog entry \u2014 skip",
1812
+ "Internal refactors with no user impact \u2014 exclude",
1813
+ 'Doc-only changes \u2014 mention briefly in "Changed" or skip'
1814
+ ],
1815
+ requires: [],
1816
+ relatedSkills: ["write-readme", "write-tsdoc"],
1817
+ tags: ["docs", "changelog", "release-notes", "keep-a-changelog"],
1818
+ examples: [
1819
+ {
1820
+ input: "Generate changelog for v0.2.0 from commits since v0.1.0 (last 30 commits)",
1821
+ output: {
1822
+ changelog: `# Changelog
1823
+
1824
+ All notable changes to AnathemaBrain are documented in this file.
1825
+
1826
+ ## [0.2.0] - 2026-06-28
1827
+
1828
+ ### Added
1829
+ - **BrainEvent provider-neutral types** (Phase 11): 12 discriminated union events for agent lifecycle, message streaming, tool execution, errors
1830
+ - **EventBus**: typed in-memory pub/sub with error isolation
1831
+ - **AgentHarness**: pure agent loop yielding AsyncIterable<BrainEvent>
1832
+ - **runCouncilPure()**: council orchestration without window.electronAPI dependency
1833
+ - **5 built-in coding tools**: read_file, write_file, edit_file, bash, grep_content
1834
+ - **14 senior-grade coding skills** across 4 categories (Planning, Refactoring, Debugging, Review)
1835
+
1836
+ ### Changed
1837
+ - LLM streaming now emits BrainEvents alongside the IPC bridge (renderer unchanged)
1838
+ - Agent system refactored to use AgentHarness as the loop driver
1839
+
1840
+ ### Fixed
1841
+ - Council event timing: agent_start/agent_end now emit at correct boundaries
1842
+ - Tool execution start events fire when provider yields tool_call deltas
1843
+
1844
+ ### Security
1845
+ - Grok OAuth token refresh now respects rate limits (fixed race condition)
1846
+
1847
+ [0.1.0] - 2026-06-01
1848
+ ... (previous entries)
1849
+ `,
1850
+ sectionsUsed: ["Added", "Changed", "Fixed", "Security"],
1851
+ commitsProcessed: 30
1852
+ }
1853
+ }
1854
+ ],
1855
+ outputSchema: "{ changelog: string; sectionsUsed: string[]; commitsProcessed: number }",
1856
+ systemPromptFragment: `You are writing a CHANGELOG.md entry from git history.
1857
+
1858
+ ## Keep a Changelog format
1859
+ Sections (use only those with content):
1860
+ - **Added** \u2014 new features
1861
+ - **Changed** \u2014 changes in existing functionality
1862
+ - **Deprecated** \u2014 soon-to-be removed features
1863
+ - **Removed** \u2014 now removed features
1864
+ - **Fixed** \u2014 bug fixes
1865
+ - **Security** \u2014 vulnerability fixes
1866
+
1867
+ ## Methodology
1868
+ 1. **List commits since the last release** (use git log).
1869
+ 2. **Categorize each commit** into the right section.
1870
+ 3. **Translate to user-facing language**: "feat: add event bus" \u2192 "EventBus: typed in-memory pub/sub".
1871
+ 4. **Group related commits**: if 5 commits all touch the event system, write ONE entry.
1872
+ 5. **Skip internal-only changes**: refactors that don't change behavior.
1873
+ 6. **Add version + date**: top of the entry.
1874
+
1875
+ ## Output format (JSON-typed)
1876
+ - changelog: string (the full CHANGELOG.md content)
1877
+ - sectionsUsed: string[] (which sections you wrote)
1878
+ - commitsProcessed: number
1879
+
1880
+ ## Changelog principles
1881
+ - **User-facing language**: write for end-users, not developers
1882
+ - **Group, don't list**: 1 entry per feature, not 1 per commit
1883
+ - **Highlight breaking changes**: bold or callout
1884
+ - **Link to issues/PRs**: reference #123, fixes #456
1885
+ - **Date format**: ISO 8601 (YYYY-MM-DD)
1886
+
1887
+ Stay under 500 words.${CLARIFICATION_PROTOCOL2}`
1888
+ };
1889
+ registerCodingSkill(writeReadme);
1890
+ registerCodingSkill(writeTsdoc);
1891
+ registerCodingSkill(writeChangelog);
1892
+
1893
+ // src/agents/skills/builtin/git-ops.ts
1894
+ var CLARIFICATION_PROTOCOL3 = `
1895
+
1896
+ WHEN TO ASK THE USER (clarification):
1897
+ If a single missing fact would materially change your output (target platform, scope, a binary design choice with significant trade-offs, a constraint you cannot safely assume), pause and ask the user by appending EXACTLY this block at the end of your message:
1898
+
1899
+ ---QUESTION---
1900
+ { "question": "One focused question", "choices": ["Option A", "Option B", "Option C"], "context": "Why this matters" }
1901
+ ---END---
1902
+
1903
+ Rules for clarifications:
1904
+ - Ask AT MOST ONE question per turn, and only when genuinely blocked.
1905
+ - Prefer a small set of concrete "choices" (2-4). The user can still type a custom answer.
1906
+ - Do NOT ask for information that could be reasonably assumed or already in shared context.
1907
+ - If you can proceed with a sound documented assumption, DO SO instead of asking.`;
1908
+ var commitMessage = {
1909
+ id: "commit-message",
1910
+ version: "1.0.0",
1911
+ name: "Commit Message",
1912
+ description: "Generate a Conventional Commits message from a staged git diff. Format: <type>(<scope>): <subject>. Body explains WHY. Footer references issues.",
1913
+ category: "ops",
1914
+ requiredRoles: ["atlas"],
1915
+ requiredTools: [],
1916
+ estimatedCost: "low",
1917
+ enabledByDefault: true,
1918
+ builtin: true,
1919
+ triggers: [
1920
+ "A developer has staged changes and needs a commit message",
1921
+ "A PR description requires a list of conventional commits",
1922
+ "A CHANGELOG is generated from commit history"
1923
+ ],
1924
+ antiPatterns: [
1925
+ "Empty diff (nothing staged) \u2014 fail fast",
1926
+ "Merge commits or reverts \u2014 different format",
1927
+ "Multi-purpose commits (refactor + feat + fix in one) \u2014 split first"
1928
+ ],
1929
+ requires: [],
1930
+ relatedSkills: ["pr-description", "write-changelog"],
1931
+ tags: ["git", "commit", "conventional-commits", "changelog"],
1932
+ examples: [
1933
+ {
1934
+ input: "Staged diff: 3 files changed, +45/-12. Adds brain event types and EventBus class.",
1935
+ output: {
1936
+ subject: "feat(events): add BrainEvent types + EventBus (Phase 11)",
1937
+ body: `Adds the provider-neutral event layer:
1938
+ - 12 BrainEvent discriminated union types
1939
+ - EventBus class with typed subscribers + error isolation
1940
+ - emit/subscribe/subscribeAll API
1941
+
1942
+ Foundation for Phase 12 (AgentHarness extraction).`,
1943
+ footer: "Refs: docs/plans/2026-06-28-zelari-code.md",
1944
+ type: "feat",
1945
+ scope: "events"
1946
+ }
1947
+ }
1948
+ ],
1949
+ outputSchema: '{ subject: string; body: string; footer: string; type: "feat" | "fix" | "refactor" | "docs" | "test" | "chore" | "perf" | "style"; scope: string }',
1950
+ systemPromptFragment: `You are writing a Conventional Commits message.
1951
+
1952
+ ## Format
1953
+ <type>(<scope>): <subject>
1954
+
1955
+ <body \u2014 explain WHY, not WHAT>
1956
+
1957
+ <footer \u2014 issue refs, breaking changes>
1958
+
1959
+ ## Types
1960
+ - feat: new feature
1961
+ - fix: bug fix
1962
+ - refactor: code change that neither fixes a bug nor adds a feature
1963
+ - docs: documentation only
1964
+ - test: adding/correcting tests
1965
+ - chore: build/tooling changes (deps, CI, config)
1966
+ - perf: performance improvement
1967
+ - style: formatting, missing semicolons, etc. (no logic change)
1968
+
1969
+ ## Subject rules
1970
+ - Imperative mood ("add" not "added")
1971
+ - Lowercase (except proper nouns)
1972
+ - Max 72 characters
1973
+ - No trailing period
1974
+
1975
+ ## Body rules
1976
+ - Wrap at 72 characters
1977
+ - Explain WHY the change was made
1978
+ - Bullet points for multiple distinct changes
1979
+
1980
+ ## Footer rules
1981
+ - \`Refs: #123\` for related issues
1982
+ - \`BREAKING CHANGE: <description>\` for breaking changes
1983
+ - \`Co-authored-by: Name <email>\` for pair work
1984
+
1985
+ ## Output format (JSON-typed)
1986
+ - subject: string
1987
+ - body: string
1988
+ - footer: string
1989
+ - type: ConventionalCommitType
1990
+ - scope: string (optional, lowercase module name)
1991
+
1992
+ Stay under 300 words.${CLARIFICATION_PROTOCOL3}`
1993
+ };
1994
+ var prDescription = {
1995
+ id: "pr-description",
1996
+ version: "1.0.0",
1997
+ name: "PR Description",
1998
+ description: "Generate a pull request description with sections: Summary, Motivation, Changes, Testing, Screenshots, Risks. Synthesizes from commits + diff + related issues.",
1999
+ category: "ops",
2000
+ requiredRoles: ["prometheus", "atlas"],
2001
+ requiredTools: [],
2002
+ estimatedCost: "medium",
2003
+ enabledByDefault: true,
2004
+ builtin: true,
2005
+ triggers: [
2006
+ "A PR is ready for review and needs a description",
2007
+ "A PR template requires filling in structured sections",
2008
+ "A large change (>200 LOC) needs careful framing for reviewers"
2009
+ ],
2010
+ antiPatterns: [
2011
+ "A 1-line typo fix \u2014 title only, no body needed",
2012
+ "A WIP / draft PR \u2014 defer until ready",
2013
+ "Generated code (codegen output) \u2014 link to the generator instead"
2014
+ ],
2015
+ requires: ["commit-message"],
2016
+ relatedSkills: ["commit-message", "write-changelog"],
2017
+ tags: ["git", "pr", "pull-request", "review", "documentation"],
2018
+ examples: [
2019
+ {
2020
+ input: "PR for Phase 11 of AnathemaCoder: BrainEvent types + EventBus + LLM streaming wiring",
2021
+ output: {
2022
+ title: "Phase 11: Provider-neutral events foundation",
2023
+ summary: "Introduces the BrainEvent discriminated union and EventBus \u2014 the provider-neutral contract between LLM providers and any frontend (Electron renderer today, Ink CLI in Phase 14).",
2024
+ motivation: "The current IPC bridge (`emitChunk`/`emitDone`) couples every frontend to provider-specific chunks. Any new frontend (CLI, mobile, web) would need to duplicate SSE parsing per provider. This is the foundation that lets Phase 12 extract an AgentHarness usable from any context.",
2025
+ changes: [
2026
+ "Added `electron/shared/events.ts` \u2014 12 discriminated union types (agent_start, message_delta, tool_execution_start, etc.)",
2027
+ "Added `electron/shared/eventBus.ts` \u2014 typed pub/sub with error isolation",
2028
+ "Wired 5 local emit helpers in `electron/main/ipc/llm.ts` (emitAgentStart, emitMessageStart, emitMessageDelta, emitMessageEnd, emitBrainError)",
2029
+ "Tool execution emits `BrainToolExecutionStartEvent` when provider yields tool_call",
2030
+ "Council emits `agent_start` / `agent_end` at run boundaries",
2031
+ "6 new vitest tests for EventBus (typed delivery, wildcard, error isolation)"
2032
+ ],
2033
+ testing: "96/96 tests passing. New: 6 EventBus tests in tests/unit/eventBus.test.ts. Manual: existing Electron renderer unchanged (verified Council chat still streams correctly).",
2034
+ risks: [
2035
+ "Adding event emission alongside IPC is purely additive \u2014 no migration needed for existing renderer code.",
2036
+ "Council event timing verified manually: agent_start fires before any message events, agent_end fires after."
2037
+ ]
2038
+ }
2039
+ }
2040
+ ],
2041
+ outputSchema: "{ title: string; summary: string; motivation: string; changes: string[]; testing: string; risks: string[] }",
2042
+ systemPromptFragment: `You are writing a Pull Request description.
2043
+
2044
+ ## Sections (use all)
2045
+ 1. **Title** (max 72 chars, imperative mood)
2046
+ 2. **Summary** (1 paragraph): what does this PR do?
2047
+ 3. **Motivation** (1-2 paragraphs): WHY? What problem does it solve?
2048
+ 4. **Changes** (bullet list, 5-15 items): specific files/concepts changed
2049
+ 5. **Testing** (1 paragraph): how was it verified? Test counts, manual steps
2050
+ 6. **Risks** (bullet list, 2-5 items): what could go wrong? Migration needed?
2051
+
2052
+ ## Output format (JSON-typed)
2053
+ - title: string
2054
+ - summary: string
2055
+ - motivation: string
2056
+ - changes: string[]
2057
+ - testing: string
2058
+ - risks: string[]
2059
+
2060
+ ## PR description principles
2061
+ - **Reviewer-first**: optimize for someone who has NEVER seen this code
2062
+ - **Concrete not abstract**: cite file paths, line numbers, function names
2063
+ - **Risk-honest**: don't hide breaking changes or migration steps
2064
+ - **Self-contained**: PR description should make sense without clicking through to issues
2065
+
2066
+ Stay under 500 words.${CLARIFICATION_PROTOCOL3}`
2067
+ };
2068
+ var ciPipeline = {
2069
+ id: "ci-pipeline",
2070
+ version: "1.0.0",
2071
+ name: "CI Pipeline",
2072
+ description: "Generate a GitHub Actions workflow YAML that runs typecheck + lint + test + build on every push and PR. Configures Node version, caching, and failure annotations.",
2073
+ category: "ops",
2074
+ requiredRoles: ["atlas"],
2075
+ requiredTools: [],
2076
+ estimatedCost: "medium",
2077
+ enabledByDefault: true,
2078
+ builtin: true,
2079
+ triggers: [
2080
+ "A project is being open-sourced and needs CI",
2081
+ "Existing CI is missing a step (e.g. no typecheck)",
2082
+ "A new test framework is added",
2083
+ "A release pipeline is being automated"
2084
+ ],
2085
+ antiPatterns: [
2086
+ "The project has no tests yet \u2014 write tests first, then CI",
2087
+ "A different CI system is in use (CircleCI, GitLab CI) \u2014 use that one",
2088
+ "The project is a personal/internal tool (CI rarely matters)"
2089
+ ],
2090
+ requires: [],
2091
+ relatedSkills: ["write-changelog"],
2092
+ tags: ["ci", "github-actions", "automation", "pipeline"],
2093
+ examples: [
2094
+ {
2095
+ input: "Generate GitHub Actions for AnathemaBrain (Electron + React + TS + Vitest)",
2096
+ output: {
2097
+ workflowFile: ".github/workflows/ci.yml",
2098
+ content: `name: CI
2099
+
2100
+ on:
2101
+ push:
2102
+ branches: [main]
2103
+ pull_request:
2104
+ branches: [main]
2105
+
2106
+ jobs:
2107
+ test:
2108
+ runs-on: ubuntu-latest
2109
+ timeout-minutes: 15
2110
+ strategy:
2111
+ matrix:
2112
+ node-version: [20.x]
2113
+
2114
+ steps:
2115
+ - uses: actions/checkout@v4
2116
+
2117
+ - name: Setup Node.js \${{ matrix.node-version }}
2118
+ uses: actions/setup-node@v4
2119
+ with:
2120
+ node-version: \${{ matrix.node-version }}
2121
+ cache: 'npm'
2122
+
2123
+ - name: Install dependencies
2124
+ run: npm ci
2125
+
2126
+ - name: Typecheck (node config)
2127
+ run: npm run typecheck:node
2128
+
2129
+ - name: Typecheck (web config)
2130
+ run: npm run typecheck:web
2131
+
2132
+ - name: Lint
2133
+ run: npm run lint
2134
+
2135
+ - name: Test
2136
+ run: npm run test
2137
+
2138
+ - name: Build (Electron renderer + main)
2139
+ run: npm run build
2140
+
2141
+ - name: Upload coverage
2142
+ if: success()
2143
+ uses: actions/upload-artifact@v4
2144
+ with:
2145
+ name: coverage-\${{ matrix.node-version }}
2146
+ path: coverage/
2147
+ `,
2148
+ jobs: ["test"],
2149
+ triggers: ["push to main", "pull_request to main"],
2150
+ nodeVersion: "20.x"
2151
+ }
2152
+ }
2153
+ ],
2154
+ outputSchema: "{ workflowFile: string; content: string; jobs: string[]; triggers: string[]; nodeVersion: string }",
2155
+ systemPromptFragment: `You are writing a GitHub Actions workflow YAML.
2156
+
2157
+ ## Required structure
2158
+ 1. **name**: workflow display name
2159
+ 2. **on**: triggers (push to main, pull_request, manual workflow_dispatch)
2160
+ 3. **jobs**: at least one job with steps
2161
+ 4. **runs-on**: ubuntu-latest (default)
2162
+ 5. **timeout-minutes**: 15 (default, increase for slower builds)
2163
+ 6. **matrix.node-version**: pin to a specific major version (e.g. 20.x)
2164
+
2165
+ ## Standard steps (in order)
2166
+ 1. actions/checkout@v4
2167
+ 2. actions/setup-node@v4 with cache: 'npm'
2168
+ 3. npm ci (NOT npm install \u2014 faster in CI)
2169
+ 4. typecheck (run the project's typecheck script)
2170
+ 5. lint (run the project's lint script)
2171
+ 6. test (run the project's test script)
2172
+ 7. build (run the project's build script, if applicable)
2173
+
2174
+ ## Output format (JSON-typed)
2175
+ - workflowFile: string (path like .github/workflows/ci.yml)
2176
+ - content: string (the full YAML)
2177
+ - jobs: string[] (job names)
2178
+ - triggers: string[] (trigger events)
2179
+ - nodeVersion: string (pinned Node version)
2180
+
2181
+ ## CI principles
2182
+ - **Fail fast**: order steps from fastest \u2192 slowest (typecheck before build)
2183
+ - **Cache dependencies**: cache: 'npm' saves 30s+ on cold builds
2184
+ - **Pin versions**: use @v4 not @latest for reproducibility
2185
+ - **Don't cache build artifacts**: cache only deps
2186
+ - **Separate jobs for parallel**: typecheck, lint, test can be parallel jobs
2187
+
2188
+ Stay under 500 words.${CLARIFICATION_PROTOCOL3}`
2189
+ };
2190
+ registerCodingSkill(commitMessage);
2191
+ registerCodingSkill(prDescription);
2192
+ registerCodingSkill(ciPipeline);
2193
+
2194
+ // src/agents/skills/builtin/planning.ts
2195
+ var CLARIFICATION_PROTOCOL4 = `
2196
+
2197
+ WHEN TO ASK THE USER (clarification):
2198
+ If a single missing fact would materially change your output (target platform, scope, a binary design choice with significant trade-offs, a constraint you cannot safely assume), pause and ask the user by appending EXACTLY this block at the end of your message:
2199
+
2200
+ ---QUESTION---
2201
+ { "question": "One focused question", "choices": ["Option A", "Option B", "Option C"], "context": "Why this matters" }
2202
+ ---END---
2203
+
2204
+ Rules for clarifications:
2205
+ - Ask AT MOST ONE question per turn, and only when genuinely blocked.
2206
+ - Prefer a small set of concrete "choices" (2-4). The user can still type a custom answer.
2207
+ - Do NOT ask for information that could be reasonably assumed or already in shared context.
2208
+ - If you can proceed with a sound documented assumption, DO SO instead of asking.`;
2209
+ var architectFeature = {
2210
+ id: "architect-feature",
2211
+ version: "1.0.0",
2212
+ name: "Architect Feature",
2213
+ description: "Multi-perspective feature design: explore 3-5 alternatives, pick the strongest, generate an ADR, list concrete risks and mitigations.",
2214
+ category: "planning",
2215
+ requiredRoles: ["sisyphus", "prometheus", "oracle"],
2216
+ requiredTools: ["createTask", "searchRAG"],
2217
+ estimatedCost: "high",
2218
+ enabledByDefault: true,
2219
+ builtin: true,
2220
+ triggers: [
2221
+ "New feature spanning multiple modules",
2222
+ "Architectural decision with long-term impact",
2223
+ "Public API design (libraries, services, CLI commands)",
2224
+ "Schema or data model changes affecting persistence",
2225
+ "Cross-cutting concern (auth, logging, caching) added to existing system"
2226
+ ],
2227
+ antiPatterns: [
2228
+ "Simple bug fix \u2014 use debug-with-rag instead",
2229
+ "Single-file refactor under 500 LOC \u2014 use refactor-monolith only if > 1000 LOC",
2230
+ "Documentation update \u2014 use write-tsdoc or write-readme",
2231
+ "Adding a one-line config or env var \u2014 direct edit is faster",
2232
+ "Pure refactor with no design choice \u2014 use refactor-monolith or extract-reusable"
2233
+ ],
2234
+ requires: ["architect-decision-record"],
2235
+ relatedSkills: ["scope-check", "migrate-stack", "code-review"],
2236
+ tags: ["planning", "architecture", "design", "multi-role", "adr"],
2237
+ examples: [
2238
+ {
2239
+ input: "Add OAuth login via Google to the Electron desktop app",
2240
+ output: {
2241
+ goal: "Enable users to sign in to AnathemaBrain with their Google account, replacing the current email+password flow.",
2242
+ alternatives: [
2243
+ { approach: "OAuth library (Passport.js)", score: "F:4 R:3 M:5", rationale: "Battle-tested, large ecosystem, easy revocation, ~50KB bundle." },
2244
+ { approach: "Custom PKCE flow with raw HTTP", score: "F:3 R:4 M:2", rationale: "No deps, but high surface area (token refresh, CSRF, error states)." },
2245
+ { approach: "Use Electron built-in OAuth via deep link", score: "F:4 R:2 M:4", rationale: "Native to Electron, no redirect_uri issues, but desktop-specific." }
2246
+ ],
2247
+ chosen: { approach: "Passport.js with Google strategy", rationale: "Lowest maintenance burden given the OAuth library ecosystem; revocation handled by the library." },
2248
+ adrPath: "docs/adr/0014-google-oauth.md",
2249
+ risks: [
2250
+ { risk: "Token refresh edge cases when offline", mitigation: "Persist refresh token in keychain (already implemented for Grok OAuth)." },
2251
+ { risk: "CSRF on deep-link redirect", mitigation: "Use OAuth state parameter with cryptographic random nonce." }
2252
+ ]
2253
+ }
2254
+ },
2255
+ {
2256
+ input: "Add a /skill command to the CLI that expands a skill template",
2257
+ output: {
2258
+ goal: "Allow the CLI to invoke a skill by name, expanding the template and dispatching to the council.",
2259
+ alternatives: [
2260
+ { approach: "Slash command handler in cli/main.ts", score: "F:5 R:1 M:5", rationale: "Direct, simple." },
2261
+ { approach: "Plugin system with skill loader", score: "F:2 R:4 M:2", rationale: "Over-engineered for v1." }
2262
+ ],
2263
+ chosen: { approach: "Slash command handler", rationale: "YAGNI \u2014 defer plugin system to v2 when users request it." },
2264
+ adrPath: "docs/adr/0015-cli-skill-commands.md",
2265
+ risks: [
2266
+ { risk: "Skill name collisions across categories", mitigation: "Namespace as `<category>:<name>` (e.g. `planning:architect-feature`)." }
2267
+ ]
2268
+ }
2269
+ }
2270
+ ],
2271
+ outputSchema: "{ goal: string; alternatives: Array<{ approach: string; score: string; rationale: string }>; chosen: { approach: string; rationale: string }; adrPath: string; risks: Array<{ risk: string; mitigation: string }> }",
2272
+ systemPromptFragment: `You are designing a feature with multi-perspective analysis.
2273
+
2274
+ ## Methodology
2275
+ 1. Restate the goal in ONE sentence: what user need does this serve?
2276
+ 2. Search the knowledge base for prior decisions: searchRAG(query="<feature-keyword>")
2277
+ 3. Generate 3-5 DISTINCT approaches (vary the axis: library vs custom, sync vs async, desktop vs server, etc.).
2278
+ 4. Score each approach on three dimensions (1-5 each):
2279
+ - Feasibility (F): how easy to implement given the current stack
2280
+ - Risk (R, INVERTED \u2014 lower is better): likelihood of bugs, security holes, scope creep
2281
+ - Maintenance (M): long-term cost (bus factor, doc quality, ecosystem health)
2282
+ 5. Pick the top approach and justify in 1-2 sentences.
2283
+ 6. Generate an ADR (link to architect-decision-record skill).
2284
+ 7. List 2-5 concrete risks + mitigations.
2285
+
2286
+ ## Output format (JSON-typed)
2287
+ - goal: string
2288
+ - alternatives: Array<{ approach: string; score: string; rationale: string }>
2289
+ - chosen: { approach: string; rationale: string }
2290
+ - adrPath: string
2291
+ - risks: Array<{ risk: string; mitigation: string }>
2292
+
2293
+ Stay under 400 words. Be decisive: pick ONE approach, do not list 3 equally good options.${CLARIFICATION_PROTOCOL4}`
2294
+ };
2295
+ var architectDecisionRecord = {
2296
+ id: "architect-decision-record",
2297
+ version: "1.0.0",
2298
+ name: "Architect Decision Record",
2299
+ description: "Generate an ADR (Architecture Decision Record) from a decision rationale. Format: Context, Decision, Consequences, Alternatives Considered.",
2300
+ category: "planning",
2301
+ requiredRoles: ["prometheus", "atlas"],
2302
+ requiredTools: ["createDocument"],
2303
+ estimatedCost: "low",
2304
+ enabledByDefault: true,
2305
+ builtin: true,
2306
+ triggers: [
2307
+ "A non-trivial architectural decision has been made",
2308
+ "Reviewing a past decision and want to capture WHY it was made",
2309
+ "Onboarding a new team member to the rationale behind existing choices",
2310
+ "After running architect-feature, to formalize the chosen approach"
2311
+ ],
2312
+ antiPatterns: [
2313
+ "No real decision has been made yet \u2014 first run architect-feature or scope-check",
2314
+ "Trivial choice (file naming, color scheme) \u2014 use a code comment",
2315
+ "Decision is reversible in under 1 hour \u2014 defer the ADR"
2316
+ ],
2317
+ requires: [],
2318
+ relatedSkills: ["architect-feature", "scope-check"],
2319
+ tags: ["planning", "documentation", "adr", "decision-log"],
2320
+ examples: [
2321
+ {
2322
+ input: "Document why we use SQLite over Postgres for local storage",
2323
+ output: {
2324
+ path: "docs/adr/0007-sqlite-over-postgres.md",
2325
+ content: `# 7. SQLite over Postgres for local storage
2326
+
2327
+ ## Context
2328
+ AnathemaBrain is an Electron desktop app that runs entirely on the user's machine. We need persistent storage for vault documents, RAG vectors, settings, and council sessions.
2329
+
2330
+ ## Decision
2331
+ Use SQLite (via better-sqlite3) for all local storage.
2332
+
2333
+ ## Consequences
2334
+ - Zero-config: works out of the box, no separate DB server to manage
2335
+ - Single-file backups: copy ~/.anathemabrain/db.sqlite
2336
+ - FTS5 + JSON1 support built-in (used by RAG and vault search)
2337
+ - Cross-platform: tested on macOS, Linux, Windows
2338
+
2339
+ ### Positive
2340
+ - No network round-trip \u2014 sub-millisecond reads
2341
+ - Embedded = no auth/connection-string management
2342
+ - Mature ecosystem (better-sqlite3, sqlite-vss)
2343
+
2344
+ ### Negative
2345
+ - Single-writer: concurrent writes are serialized (acceptable for desktop)
2346
+ - No horizontal scaling (irrelevant for desktop)
2347
+ - Migration tooling requires custom scripts (we have db/migrations.ts)
2348
+
2349
+ ## Alternatives Considered
2350
+ - **Postgres**: rejected \u2014 requires a server process, overkill for desktop
2351
+ - **LevelDB / RocksDB**: rejected \u2014 no SQL, harder to query vector data
2352
+ - **JSON files**: rejected \u2014 no concurrent access, no FTS
2353
+ `
2354
+ }
2355
+ }
2356
+ ],
2357
+ outputSchema: "{ path: string; content: string }",
2358
+ systemPromptFragment: `You are writing an Architecture Decision Record (ADR).
2359
+
2360
+ ## Required sections
2361
+ 1. **# N. <Title>** \u2014 short title in present tense ("Use X for Y")
2362
+ 2. **## Context** \u2014 what is the situation? What forces are at play? (2-3 paragraphs)
2363
+ 3. **## Decision** \u2014 what did we decide? State it clearly in 1-2 sentences.
2364
+ 4. **## Consequences** \u2014 what becomes easier (+) and harder (-) as a result?
2365
+ 5. **## Alternatives Considered** \u2014 2-4 alternatives with one-line rejection reasons.
2366
+
2367
+ ## Numbering
2368
+ - Check docs/adr/ for the next available number (NNN-title.md format)
2369
+ - ADR numbers are permanent \u2014 never reuse, even if an ADR is superseded (mark superseded in the body instead)
2370
+
2371
+ ## Tone
2372
+ - Factual, not promotional
2373
+ - Acknowledge trade-offs honestly
2374
+ - Link to relevant code/PRs/files using relative paths
2375
+
2376
+ Stay under 800 words.${CLARIFICATION_PROTOCOL4}`
2377
+ };
2378
+ var migrateStack = {
2379
+ id: "migrate-stack",
2380
+ version: "1.0.0",
2381
+ name: "Migrate Stack",
2382
+ description: "Plan a migration (framework, language, database, build tool) with phased rollout and a concrete rollback strategy. Identifies big-bang risks and proposes incremental steps.",
2383
+ category: "planning",
2384
+ requiredRoles: ["sisyphus", "prometheus", "hephaestus"],
2385
+ requiredTools: ["createTask", "searchRAG"],
2386
+ estimatedCost: "high",
2387
+ enabledByDefault: true,
2388
+ builtin: true,
2389
+ triggers: [
2390
+ "Replacing a framework (e.g. CRA \u2192 Vite, Express \u2192 Fastify)",
2391
+ "Upgrading a major version with breaking changes (React 17 \u2192 18)",
2392
+ "Switching databases (Postgres \u2192 SQLite, JSON files \u2192 Postgres)",
2393
+ "Migrating build tooling (webpack \u2192 esbuild, npm \u2192 pnpm)",
2394
+ "Replacing a key library with a competitor (moment \u2192 date-fns)"
2395
+ ],
2396
+ antiPatterns: [
2397
+ "Greenfield project \u2014 use architect-feature instead",
2398
+ "A/B test of two stacks \u2014 use a spike/benchmark first",
2399
+ "Migration can be done by automated codemods \u2014 skip the planning skill"
2400
+ ],
2401
+ requires: ["architect-decision-record", "scope-check"],
2402
+ relatedSkills: ["architect-feature", "refactor-monolith"],
2403
+ tags: ["planning", "migration", "rollout", "rollback", "risk-management"],
2404
+ examples: [
2405
+ {
2406
+ input: "Migrate AnathemaBrain from Create React App (CRA) to Vite",
2407
+ output: {
2408
+ goal: "Reduce dev server startup from 30s to <2s, reduce prod build from 60s to <10s.",
2409
+ phases: [
2410
+ { name: "Phase 1: Setup parallel build", exitCriterion: "npm run dev:vite works for a hello-world", durationDays: 2 },
2411
+ { name: "Phase 2: Migrate entry points", exitCriterion: "Both Webpack and Vite builds succeed; renderer loads in both", durationDays: 5 },
2412
+ { name: "Phase 3: Migrate loaders (CSS, assets)", exitCriterion: "All static assets load correctly under Vite", durationDays: 3 },
2413
+ { name: "Phase 4: Cut over", exitCriterion: "Webpack config deleted; CI runs only Vite", durationDays: 2 }
2414
+ ],
2415
+ rollbackStrategy: "Keep Webpack config in git history (tag: pre-vite-migration). Revert the single commit that switches the default dev script.",
2416
+ risks: [
2417
+ { risk: "Asset paths differ between Webpack and Vite (~/ aliases, ?url imports)", mitigation: "Phase 3 includes a path-mapping smoke test." },
2418
+ { risk: "electron-vite specific electron features not yet stable", mitigation: "Phase 1 spike verifies electron-vite can compile the existing Electron main process." }
2419
+ ]
2420
+ }
2421
+ }
2422
+ ],
2423
+ outputSchema: "{ goal: string; phases: Array<{ name: string; exitCriterion: string; durationDays: number }>; rollbackStrategy: string; risks: Array<{ risk: string; mitigation: string }> }",
2424
+ systemPromptFragment: `You are planning a stack migration.
2425
+
2426
+ ## Methodology
2427
+ 1. State the GOAL in measurable terms (e.g. "reduce build time from 60s to 10s").
2428
+ 2. Decompose into 3-6 phases. Each phase has a CLEAR exit criterion (testable condition).
2429
+ 3. Each phase should be DEPLOYABLE on its own (no big-bang steps).
2430
+ 4. Plan a ROLLBACK strategy: which single commit reverts to the previous state?
2431
+ 5. List 3-7 concrete risks + mitigations per phase.
2432
+
2433
+ ## Migration principles
2434
+ - **Strangler Fig**: new system grows alongside old, traffic migrates incrementally
2435
+ - **No big-bang rewrites**: every phase must be independently shippable
2436
+ - **Reversibility first**: every phase has a tested rollback path
2437
+ - **Data migration last**: schema changes follow code changes (dual-write during transition)
2438
+
2439
+ ## Output format (JSON-typed)
2440
+ - goal: string (measurable)
2441
+ - phases: Array<{ name: string; exitCriterion: string; durationDays: number }>
2442
+ - rollbackStrategy: string (the single command or commit that reverts)
2443
+ - risks: Array<{ risk: string; mitigation: string }>
2444
+
2445
+ Stay under 500 words.${CLARIFICATION_PROTOCOL4}`
2446
+ };
2447
+ var scopeCheck = {
2448
+ id: "scope-check",
2449
+ version: "1.0.0",
2450
+ name: "Scope Check",
2451
+ description: "Validate feasibility of a proposed feature or change. Identify hidden costs, dependencies, risks. Propose a reduced scope that fits the available budget.",
2452
+ category: "planning",
2453
+ requiredRoles: ["sisyphus", "oracle", "chairman"],
2454
+ requiredTools: ["searchRAG"],
2455
+ estimatedCost: "medium",
2456
+ enabledByDefault: true,
2457
+ builtin: true,
2458
+ triggers: [
2459
+ "User proposes an ambitious feature without a clear MVP boundary",
2460
+ "Estimates are unclear or contested",
2461
+ "Hidden dependencies suspected (auth, billing, third-party APIs)",
2462
+ "Before committing to a multi-week project",
2463
+ 'When a stakeholder says "this should be easy" and you suspect it is not'
2464
+ ],
2465
+ antiPatterns: [
2466
+ "The task is already small and well-scoped \u2014 just do it",
2467
+ "Pure research / exploration \u2014 no commitment to implement",
2468
+ "Bug fix \u2014 use debug-with-rag instead"
2469
+ ],
2470
+ requires: [],
2471
+ relatedSkills: ["architect-feature", "migrate-stack"],
2472
+ tags: ["planning", "feasibility", "risk-assessment", "mvp", "scoping"],
2473
+ examples: [
2474
+ {
2475
+ input: 'Build a "smart inbox" that categorizes emails using the local LLM council',
2476
+ output: {
2477
+ feasibility: "partial",
2478
+ estimatedEffortDays: 14,
2479
+ hiddenCosts: [
2480
+ { cost: "IMAP polling + auth setup (3-5 days)", mitigation: "Use existing Gmail API OAuth2 flow" },
2481
+ { cost: "Email parsing (HTML, attachments) - 2 days", mitigation: "Use mailparser library" },
2482
+ { cost: "Local LLM inference at scale - 1 day setup + per-email ~3s", mitigation: "Batch categorization, use smaller model (gemini-2.5-flash)" },
2483
+ { cost: "Council orchestration - already have it", mitigation: "Reuse runCouncilPure" }
2484
+ ],
2485
+ reducedScope: {
2486
+ phase1: "Single Gmail account, read-only, top-20 inbox categorization (3 days)",
2487
+ phase2: "Multi-account + auto-archive (5 days)",
2488
+ phase3: "Reply drafting suggestions (6 days)"
2489
+ },
2490
+ risks: [
2491
+ "Council API costs: 5 emails \xD7 3 council members \xD7 4 providers = 60 LLM calls per batch",
2492
+ "User privacy: emails sent to cloud providers unless restricted to local-only model"
2493
+ ]
2494
+ }
2495
+ }
2496
+ ],
2497
+ outputSchema: '{ feasibility: "yes" | "partial" | "no"; estimatedEffortDays: number; hiddenCosts: Array<{ cost: string; mitigation: string }>; reducedScope: { phase1: string; phase2: string; phase3: string }; risks: string[] }',
2498
+ systemPromptFragment: `You are validating the scope of a proposed feature or change.
2499
+
2500
+ ## Methodology
2501
+ 1. Restate the proposal in ONE sentence.
2502
+ 2. Search the knowledge base: searchRAG(query="<proposed-keyword>")
2503
+ 3. Estimate effort realistically (in person-days, with calibration from prior projects).
2504
+ 4. List HIDDEN costs: auth flows, data migration, third-party APIs, deployment, monitoring.
2505
+ 5. Propose a REDUCED SCOPE that delivers 80% of the value in 20% of the time.
2506
+ 6. Flag risks that could block the project.
2507
+
2508
+ ## Output format (JSON-typed)
2509
+ - feasibility: 'yes' | 'partial' | 'no'
2510
+ - estimatedEffortDays: number
2511
+ - hiddenCosts: Array<{ cost: string; mitigation: string }>
2512
+ - reducedScope: { phase1: string; phase2: string; phase3: string } (3 phases, each independently shippable)
2513
+ - risks: string[] (3-5 risks)
2514
+
2515
+ ## When to be skeptical
2516
+ - "Just add X" usually hides auth + persistence + UI work (3x multiplier)
2517
+ - "Use the existing system" usually means new integration code
2518
+ - Estimates from optimistic people \u2014 multiply by 2-3x for calendar time
2519
+
2520
+ Stay under 400 words. Be honest about feasibility \u2014 say NO when the answer is NO.${CLARIFICATION_PROTOCOL4}`
2521
+ };
2522
+ registerCodingSkill(architectDecisionRecord);
2523
+ registerCodingSkill(scopeCheck);
2524
+ registerCodingSkill(architectFeature);
2525
+ registerCodingSkill(migrateStack);
2526
+
2527
+ // src/agents/skills/builtin/refactoring.ts
2528
+ var CLARIFICATION_PROTOCOL5 = `
2529
+
2530
+ WHEN TO ASK THE USER (clarification):
2531
+ If a single missing fact would materially change your output (target platform, scope, a binary design choice with significant trade-offs, a constraint you cannot safely assume), pause and ask the user by appending EXACTLY this block at the end of your message:
2532
+
2533
+ ---QUESTION---
2534
+ { "question": "One focused question", "choices": ["Option A", "Option B", "Option C"], "context": "Why this matters" }
2535
+ ---END---
2536
+
2537
+ Rules for clarifications:
2538
+ - Ask AT MOST ONE question per turn, and only when genuinely blocked.
2539
+ - Prefer a small set of concrete "choices" (2-4). The user can still type a custom answer.
2540
+ - Do NOT ask for information that could be reasonably assumed or already in shared context.
2541
+ - If you can proceed with a sound documented assumption, DO SO instead of asking.`;
2542
+ var extractReusable = {
2543
+ id: "extract-reusable",
2544
+ version: "1.0.0",
2545
+ name: "Extract Reusable Module",
2546
+ description: "Find duplication across files via semantic search. Identify shared logic, propose a new reusable module with a clean API, plan the migration of all call sites.",
2547
+ category: "refactor",
2548
+ requiredRoles: ["atlas", "oracle"],
2549
+ requiredTools: ["searchRAG"],
2550
+ estimatedCost: "medium",
2551
+ enabledByDefault: true,
2552
+ builtin: true,
2553
+ triggers: [
2554
+ "Same logic copy-pasted across 3+ files",
2555
+ "Two near-identical utility functions (e.g. formatDate in 2 different files)",
2556
+ "A bug fix needs to be applied in 5 places",
2557
+ "New project copies code from old project (opportunity to extract)"
2558
+ ],
2559
+ antiPatterns: [
2560
+ "Duplication is INTENTIONAL (different domains, different evolution paths)",
2561
+ "Shared logic is so trivial (< 5 lines) that a function call adds more noise than it removes",
2562
+ "API is unstable \u2014 extracting now would force awkward premature abstractions"
2563
+ ],
2564
+ requires: [],
2565
+ relatedSkills: ["refactor-monolith", "simplify-conditionals"],
2566
+ tags: ["refactor", "deduplication", "reuse", "shared-module"],
2567
+ examples: [
2568
+ {
2569
+ input: 'There are 3 different "formatDuration" implementations in src/ \u2014 slightly different in each',
2570
+ output: {
2571
+ duplication: {
2572
+ callSites: [
2573
+ { file: "src/components/Timer.tsx", function: "formatDuration(ms: number): string", line: 23 },
2574
+ { file: "src/lib/utils.ts", function: "formatDuration(ms: number): string", line: 47 },
2575
+ { file: "src/pages/Settings.tsx", function: "formatDurationText(ms: number): string", line: 156 }
2576
+ ],
2577
+ differences: [
2578
+ 'Timer.tsx: shows "1h 23m" for >1h, "23m 45s" for >1m, else "45s"',
2579
+ 'utils.ts: shows "1:23:45" always (HH:MM:SS)',
2580
+ 'Settings.tsx: shows "23m" for >1m, "45s" else'
2581
+ ]
2582
+ },
2583
+ proposedModule: {
2584
+ path: "src/lib/format/duration.ts",
2585
+ api: `export function formatDuration(ms: number, opts?: { style?: 'compact' | 'full' | 'minimal' }): string`,
2586
+ variants: [
2587
+ { style: "compact", example: "1h 23m" },
2588
+ { style: "full", example: "1:23:45" },
2589
+ { style: "minimal", example: "23m" }
2590
+ ]
2591
+ },
2592
+ migration: [
2593
+ { step: "Create src/lib/format/duration.ts with the 3-style API", linesToAdd: 30 },
2594
+ { step: 'Update Timer.tsx to call formatDuration(ms, {style:"compact"})', linesToChange: 2 },
2595
+ { step: 'Update utils.ts to call formatDuration(ms, {style:"full"})', linesToChange: 2 },
2596
+ { step: 'Update Settings.tsx to call formatDuration(ms, {style:"minimal"})', linesToChange: 2 }
2597
+ ],
2598
+ benefits: ["Single source of truth for duration formatting", "Easy to add new styles (e.g. ISO 8601)", "Tests in one place"]
2599
+ }
2600
+ }
2601
+ ],
2602
+ outputSchema: "{ duplication: { callSites: Array<{ file: string; function: string; line: number }>; differences: string[] }; proposedModule: { path: string; api: string; variants: Array<{ style: string; example: string }> }; migration: Array<{ step: string; linesToChange: number }>; benefits: string[] }",
2603
+ systemPromptFragment: `You are finding duplicated logic and extracting a reusable module.
2604
+
2605
+ ## Methodology
2606
+ 1. Use grep/semantic search to find candidate duplication (3+ similar implementations).
2607
+ 2. Read each call site to understand the VARIATIONS (not just the common pattern).
2608
+ 3. Design an API that accommodates ALL variations (use options pattern, not multiple functions).
2609
+ 4. Specify the new module path + function signature + behavior table.
2610
+ 5. List migration steps: which file + which lines change to use the new module.
2611
+
2612
+ ## Extraction principles
2613
+ - **Variations via options, not forks**: prefer formatDuration(ms, opts) over formatDurationCompact()/Full()
2614
+ - **Pure functions**: extracted modules should have no hidden state, no I/O
2615
+ - **Tests first**: write the test suite for the new module BEFORE migrating call sites
2616
+ - **Single responsibility**: extracted module does ONE thing well
2617
+
2618
+ ## Output format (JSON-typed)
2619
+ - duplication: { callSites: [{file, function, line}], differences[] }
2620
+ - proposedModule: { path, api (TS signature), variants: [{style, example}] }
2621
+ - migration: [{step, linesToChange}]
2622
+ - benefits: string[] (3-5 bullet points)
2623
+
2624
+ Stay under 500 words.${CLARIFICATION_PROTOCOL5}`
2625
+ };
2626
+ var simplifyConditionals = {
2627
+ id: "simplify-conditionals",
2628
+ version: "1.0.0",
2629
+ name: "Simplify Conditionals",
2630
+ description: "Reduce cyclomatic complexity of boolean/conditional code. Extract guard clauses, replace nested if-else with lookup tables, simplify boolean expressions using De Morgan's laws.",
2631
+ category: "refactor",
2632
+ requiredRoles: ["atlas"],
2633
+ requiredTools: [],
2634
+ estimatedCost: "low",
2635
+ enabledByDefault: true,
2636
+ builtin: true,
2637
+ triggers: [
2638
+ "Cyclomatic complexity > 10 in a single function",
2639
+ "Nested if-else > 3 levels deep",
2640
+ "Boolean expressions with > 4 terms",
2641
+ "Multiple early returns that could be guard clauses",
2642
+ "Switch statements with > 8 cases that share structure"
2643
+ ],
2644
+ antiPatterns: [
2645
+ "Performance-critical branches (compiler will optimize nested ifs better than lookups)",
2646
+ "Complex domain logic that IS the conditional structure (e.g. tax brackets)",
2647
+ "Code under active development where the structure is still in flux"
2648
+ ],
2649
+ requires: [],
2650
+ relatedSkills: ["extract-reusable", "refactor-monolith"],
2651
+ tags: ["refactor", "simplification", "cyclomatic-complexity", "clean-code"],
2652
+ examples: [
2653
+ {
2654
+ input: "function canUserEdit(user, doc) { if (user.isAdmin) { return true; } else { if (doc.ownerId === user.id) { return true; } else { if (doc.collaborators.includes(user.id) && !doc.locked) { return true; } else { return false; } } } }",
2655
+ output: {
2656
+ before: { lines: 9, cyclomaticComplexity: 5 },
2657
+ after: { lines: 6, cyclomaticComplexity: 1 },
2658
+ refactored: `function canUserEdit(user, doc) {
2659
+ if (user.isAdmin) return true;
2660
+ if (doc.ownerId === user.id) return true;
2661
+ if (doc.collaborators.includes(user.id) && !doc.locked) return true;
2662
+ return false;
2663
+ }`,
2664
+ explanation: "Extracted guard clauses (early returns). Eliminated 3 levels of nesting. Cyclomatic complexity dropped from 5 to 1. Logic preserved exactly."
2665
+ }
2666
+ },
2667
+ {
2668
+ input: 'function getDiscount(tier) { if (tier === "bronze") return 0.05; else if (tier === "silver") return 0.10; else if (tier === "gold") return 0.15; else if (tier === "platinum") return 0.20; else return 0; }',
2669
+ output: {
2670
+ before: { lines: 7, cyclomaticComplexity: 5 },
2671
+ after: { lines: 7, cyclomaticComplexity: 1 },
2672
+ refactored: `const DISCOUNT_BY_TIER = { bronze: 0.05, silver: 0.10, gold: 0.15, platinum: 0.20 } as const;
2673
+ function getDiscount(tier: DiscountTier): number {
2674
+ return DISCOUNT_BY_TIER[tier] ?? 0;
2675
+ }`,
2676
+ explanation: "Replaced if-else chain with const lookup table. Adding a new tier = one line in the table, no function edit. Type-safe via `as const`. Default 0 via nullish coalescing."
2677
+ }
2678
+ }
2679
+ ],
2680
+ outputSchema: "{ before: { lines: number; cyclomaticComplexity: number }; after: { lines: number; cyclomaticComplexity: number }; refactored: string; explanation: string }",
2681
+ systemPromptFragment: `You are reducing the complexity of conditional code.
2682
+
2683
+ ## Common patterns to apply
2684
+ 1. **Guard clauses**: replace \`if (cond) { ...big block... } else { return false; }\` with \`if (!cond) return false; ...big block...\`
2685
+ 2. **Lookup tables**: replace \`if/else if/else if/...\` chains with \`const TABLE = { ... } as const; return TABLE[key] ?? default\`
2686
+ 3. **Boolean simplification**: apply De Morgan's laws (\`!(A && B)\` \u2192 \`!A || !B\`), extract complex predicates into named booleans
2687
+ 4. **Polymorphism**: replace type-checking conditionals (\`if (type === 'A') ... else if (type === 'B') ...\`) with method dispatch
2688
+
2689
+ ## Output format (JSON-typed)
2690
+ - before: { lines, cyclomaticComplexity }
2691
+ - after: { lines, cyclomaticComplexity }
2692
+ - refactored: string (the new code)
2693
+ - explanation: string (1-2 sentences on what changed and why)
2694
+
2695
+ ## What NOT to do
2696
+ - Don't change behavior \u2014 only structure
2697
+ - Don't introduce new abstractions for one-off conditionals
2698
+ - Don't sacrifice readability for fewer lines (e.g. overly clever ternary chains)
2699
+
2700
+ Stay under 400 words.${CLARIFICATION_PROTOCOL5}`
2701
+ };
2702
+ var refactorMonolith = {
2703
+ id: "refactor-monolith",
2704
+ version: "1.0.0",
2705
+ name: "Refactor Monolith",
2706
+ description: "Multi-perspective decomposition of large files (>1000 LOC) into focused modules. Identifies natural responsibility boundaries, proposes a module map with line numbers, validates no circular deps, generates a per-phase migration plan.",
2707
+ category: "refactor",
2708
+ requiredRoles: ["hephaestus", "atlas", "oracle"],
2709
+ requiredTools: ["searchRAG", "createTask"],
2710
+ estimatedCost: "high",
2711
+ enabledByDefault: true,
2712
+ builtin: true,
2713
+ triggers: [
2714
+ "Single file > 1000 LOC with mixed responsibilities",
2715
+ "Cyclomatic complexity > 50 in a single function",
2716
+ "Multiple unrelated imports (>15 distinct packages) in one file",
2717
+ "Onboarding new devs is hard because no one file fits in their head",
2718
+ "Build/test times are slow because one big file recompiles on every change"
2719
+ ],
2720
+ antiPatterns: [
2721
+ "File under 500 LOC \u2014 defer; not worth the migration cost",
2722
+ "Truly cohesive file even if large (e.g. a state machine FSM)",
2723
+ "Quick fix needed urgently \u2014 refactor first, fix second",
2724
+ "No tests exist \u2014 write characterization tests first, refactor second"
2725
+ ],
2726
+ requires: ["extract-reusable"],
2727
+ relatedSkills: ["simplify-conditionals", "architect-feature"],
2728
+ tags: ["refactor", "modularization", "god-module", "split", "architecture"],
2729
+ examples: [
2730
+ {
2731
+ input: "src/Council.tsx is 1822 LOC with mixed concerns: chat rendering, tool dispatch, voice input, slash commands, file attachments",
2732
+ output: {
2733
+ currentState: {
2734
+ file: "src/Council.tsx",
2735
+ totalLines: 1822,
2736
+ responsibilities: ["chat render", "tool dispatch", "voice input", "slash commands", "file attachments", "settings panel", "token counter"]
2737
+ },
2738
+ proposedModules: [
2739
+ { name: "ChatStream.tsx", responsibility: "render assistant/user messages", estimatedLines: 350, dependsOn: ["useChatStream hook"] },
2740
+ { name: "ToolDispatch.tsx", responsibility: "show tool execution + results", estimatedLines: 280, dependsOn: ["brain/tool types"] },
2741
+ { name: "VoiceInput.tsx", responsibility: "microphone + transcription UI", estimatedLines: 150, dependsOn: ["useVoiceRecorder hook"] },
2742
+ { name: "SlashCommands.tsx", responsibility: "command palette + autocomplete", estimatedLines: 200, dependsOn: ["useSlashCommands hook"] },
2743
+ { name: "useCouncil.ts (hook)", responsibility: "state + council orchestration logic", estimatedLines: 400, dependsOn: [] }
2744
+ ],
2745
+ migrationPhases: [
2746
+ { phase: 1, name: "Extract useCouncil hook", exitCriterion: "Council.tsx < 1500 LOC, all behavior unchanged, all tests pass", durationDays: 2 },
2747
+ { phase: 2, name: "Extract ChatStream + ToolDispatch", exitCriterion: "Council.tsx < 1000 LOC", durationDays: 3 },
2748
+ { phase: 3, name: "Extract VoiceInput + SlashCommands", exitCriterion: "Council.tsx < 500 LOC OR deleted entirely", durationDays: 2 }
2749
+ ],
2750
+ risks: [
2751
+ { risk: "Hidden circular imports between new modules", mitigation: "Run `madge --circular src/` after each phase; CI fails on circular deps." },
2752
+ { risk: "Lost re-render optimization (inline JSX vs memoized components)", mitigation: "Phase 1 establishes React.memo + useMemo conventions before extracting." }
2753
+ ]
2754
+ }
2755
+ }
2756
+ ],
2757
+ outputSchema: "{ currentState: { file: string; totalLines: number; responsibilities: string[] }; proposedModules: Array<{ name: string; responsibility: string; estimatedLines: number; dependsOn: string[] }>; migrationPhases: Array<{ phase: number; name: string; exitCriterion: string; durationDays: number }>; risks: Array<{ risk: string; mitigation: string }> }",
2758
+ systemPromptFragment: `You are planning a multi-perspective decomposition of a large file.
2759
+
2760
+ ## Methodology
2761
+ 1. Identify the file's CURRENT responsibilities (read the source if needed).
2762
+ 2. Search the KB for prior split decisions: searchRAG(query="<file-name-keyword>")
2763
+ 3. Propose 4-8 NEW modules, each with ONE clear responsibility.
2764
+ 4. Map existing functions/sections to new modules (with line numbers).
2765
+ 5. Validate NO circular dependencies between proposed modules.
2766
+ 6. Plan 3-5 migration phases, each independently shippable with all tests passing.
2767
+
2768
+ ## Module split principles
2769
+ - **One job per module**: if you can't describe a module's purpose in ONE sentence, split further
2770
+ - **No circular deps**: A imports B, B imports C is fine; A imports B, B imports A is not
2771
+ - **Stable abstractions**: the new module boundaries should match NATURAL responsibility seams (state vs UI vs I/O), not arbitrary line counts
2772
+ - **Testability first**: each module should be unit-testable in isolation
2773
+
2774
+ ## Output format (JSON-typed)
2775
+ - currentState: { file, totalLines, responsibilities[] }
2776
+ - proposedModules: Array<{ name, responsibility, estimatedLines, dependsOn[] }>
2777
+ - migrationPhases: Array<{ phase, name, exitCriterion, durationDays }>
2778
+ - risks: Array<{ risk, mitigation }>
2779
+
2780
+ Stay under 600 words. Be decisive about module boundaries \u2014 pick ONE natural split, don't present 3 equally valid options.${CLARIFICATION_PROTOCOL5}`
2781
+ };
2782
+ registerCodingSkill(extractReusable);
2783
+ registerCodingSkill(simplifyConditionals);
2784
+ registerCodingSkill(refactorMonolith);
2785
+
2786
+ // src/agents/skills/builtin/review.ts
2787
+ var CLARIFICATION_PROTOCOL6 = `
2788
+
2789
+ WHEN TO ASK THE USER (clarification):
2790
+ If a single missing fact would materially change your output (target platform, scope, a binary design choice with significant trade-offs, a constraint you cannot safely assume), pause and ask the user by appending EXACTLY this block at the end of your message:
2791
+
2792
+ ---QUESTION---
2793
+ { "question": "One focused question", "choices": ["Option A", "Option B", "Option C"], "context": "Why this matters" }
2794
+ ---END---
2795
+
2796
+ Rules for clarifications:
2797
+ - Ask AT MOST ONE question per turn, and only when genuinely blocked.
2798
+ - Prefer a small set of concrete "choices" (2-4). The user can still type a wave-in response.
2799
+ - Do NOT ask for information that could be reasonably assumed or already in shared context.
2800
+ - If you can proceed with a sound documented assumption, DO SO instead of asking.`;
2801
+ var securityAudit = {
2802
+ id: "security-audit",
2803
+ version: "1.0.0",
2804
+ name: "Security Audit",
2805
+ description: "OWASP Top 10 + CVE check on dependencies + secrets scan. Identifies injection vectors, auth flaws, unsafe deserialization, hardcoded credentials.",
2806
+ category: "review",
2807
+ requiredRoles: ["oracle"],
2808
+ requiredTools: ["grep_content", "read_file"],
2809
+ estimatedCost: "medium",
2810
+ enabledByDefault: true,
2811
+ builtin: true,
2812
+ triggers: [
2813
+ "Any code touching auth, session, password, token, or credential storage",
2814
+ "Pre-release audit of the codebase",
2815
+ "After a security incident or CVE disclosure",
2816
+ "Quarterly compliance check",
2817
+ "Integrating a new third-party npm dependency that handles user data"
2818
+ ],
2819
+ antiPatterns: [
2820
+ "Pure UI code with no I/O \u2014 defer to code-review skill",
2821
+ "A unit test file \u2014 no production risk",
2822
+ "Mock/seed data \u2014 those credentials are intentionally fake"
2823
+ ],
2824
+ requires: [],
2825
+ relatedSkills: ["code-review"],
2826
+ tags: ["security", "owasp", "audit", "cve", "secrets", "compliance"],
2827
+ examples: [
2828
+ {
2829
+ input: "Audit src/auth/ directory for security issues",
2830
+ output: {
2831
+ owaspFindings: [
2832
+ { category: "A01:2021 Broken Access Control", severity: "HIGH", file: "src/auth/middleware.ts:34", issue: "Missing role check on /admin/* routes \u2014 any authenticated user can access admin endpoints" },
2833
+ { category: "A02:2021 Cryptographic Failures", severity: "CRITICAL", file: "src/auth/token.ts:12", issue: "Tokens stored with SHA-256(password) instead of bcrypt/argon2 \u2014 vulnerable to rainbow tables" },
2834
+ { category: "A03:2021 Injection", severity: "MEDIUM", file: "src/auth/login.ts:45", issue: "Email field not validated; potential XSS if rendered unsafed downstream" }
2835
+ ],
2836
+ cveCheck: "npm audit: 3 vulnerabilities (1 high, 2 moderate) in jsonwebtoken@8.5.1 (CVE-2022-23529) \u2014 upgrade to 9.0.0+",
2837
+ secretsScan: "No hardcoded API keys found in src/. .env files are gitignored.",
2838
+ priorityOrder: [
2839
+ "1. Fix SHA-256 \u2192 bcrypt (CRITICAL)",
2840
+ "2. Add role check middleware (HIGH)",
2841
+ "3. Upgrade jsonwebtoken (CVE)",
2842
+ "4. Add email validation (MEDIUM)"
2843
+ ]
2844
+ }
2845
+ }
2846
+ ],
2847
+ outputSchema: "{ owaspFindings: Array<{ category: string; severity: string; file: string; line: number; issue: string }>; cveCheck: string; secretsScan: string; priorityOrder: string[] }",
2848
+ systemPromptFragment: `You are auditing the codebase for security vulnerabilities.
2849
+
2850
+ ## OWASP Top 10 (2021) \u2014 check each
2851
+ 1. **A01 Broken Access Control**: missing role checks, IDOR vulnerabilities
2852
+ 2. **A02 Cryptographic Failures**: weak hashing (MD5/SHA-1/SHA-256 for passwords), missing TLS
2853
+ 3. **A03 Injection**: SQL injection, command injection, XSS (especially via unsafe dangerouslySetInnerHTML)
2854
+ 4. **A04 Insecure Design**: missing rate limiting on auth endpoints
2855
+ 5. **A05 Security Misconfiguration**: default credentials, exposed debug endpoints
2856
+ 6. **A06 Vulnerable Components**: outdated dependencies with known CVEs (run npm audit)
2857
+ 7. **A07 Identification & Auth Failures**: missing MFA, weak password policies
2858
+ 8. **A08 Software & Data Integrity Failures**: missing signature verification on updates
2859
+ 9. **A09 Logging & Monitoring Failures**: no audit log on sensitive operations
2860
+ 10. **A10 SSRF**: user-controlled URLs fetched server-side without allowlist
2861
+
2862
+ ## CVE check
2863
+ - Run npm audit (or pip-audit, cargo audit, etc. depending on the stack)
2864
+ - Cross-reference with the GitHub Advisory Database
2865
+
2866
+ ## Secrets scan
2867
+ - grep for common patterns: API_KEY, SECRET, TOKEN, PASSWORD, BEGIN PRIVATE KEY
2868
+ - Check .env files are gitignored
2869
+
2870
+ ## Output format (JSON-typed)
2871
+ - owaspFindings: Array<{ category, severity, file, line, issue }>
2872
+ - cveCheck: string (results of dependency scan)
2873
+ - secretsScan: string (results of hardcoded secrets scan)
2874
+ - priorityOrder: string[] (numbered list, CRITICAL first)
2875
+
2876
+ Stay under 500 words.${CLARIFICATION_PROTOCOL6}`
2877
+ };
2878
+ var performanceReview = {
2879
+ id: "performance-review",
2880
+ version: "1.0.0",
2881
+ name: "Performance Review",
2882
+ description: "Identify algorithmic complexity issues (O(n\xB2), O(n\xB3)), N+1 queries, unnecessary allocations, missing caching opportunities, I/O blocking patterns.",
2883
+ category: "review",
2884
+ requiredRoles: ["atlas"],
2885
+ requiredTools: ["read_file", "grep_content"],
2886
+ estimatedCost: "medium",
2887
+ enabledByDefault: true,
2888
+ builtin: true,
2889
+ triggers: [
2890
+ "Performance regression report from users",
2891
+ "Response time > 1s where it used to be < 100ms",
2892
+ "CPU profiling shows a hot function",
2893
+ "Memory growth over time (potential leak)",
2894
+ "Database query log shows slow queries"
2895
+ ],
2896
+ antiPatterns: [
2897
+ "UI-only code with no I/O or loops \u2014 defer",
2898
+ "Code that handles < 100 items (premature optimization)",
2899
+ "One-time scripts \u2014 performance rarely matters"
2900
+ ],
2901
+ requires: [],
2902
+ relatedSkills: ["code-review"],
2903
+ tags: ["performance", "optimization", "big-o", "caching", "memory", "profiling"],
2904
+ examples: [
2905
+ {
2906
+ input: "Review src/lib/search.ts (filters 10,000 documents)",
2907
+ output: {
2908
+ findings: [
2909
+ { type: "N+1 query", severity: "HIGH", file: "src/lib/search.ts:34", issue: "For each of 10,000 docs, fires a separate DB query to fetch tags. Total: 10,001 queries per search.", fix: "Use a single JOIN query to fetch all tags upfront. Reduces to 1 query." },
2910
+ { type: "Allocation in hot loop", severity: "MEDIUM", file: "src/lib/search.ts:67", issue: "Creates a new Date object inside the filter loop (10,000 allocations per search).", fix: "Hoist the comparison date outside the loop." },
2911
+ { type: "Missing index", severity: "MEDIUM", file: "src/lib/search.ts:45", issue: "SQL query filters by `created_at` but the table has no index on that column. Full table scan on every search.", fix: "CREATE INDEX idx_documents_created_at ON documents(created_at);" },
2912
+ { type: "Caching opportunity", severity: "LOW", file: "src/lib/search.ts:89", issue: "Results identical for the same query within 60s window; no caching layer.", fix: "Add LRU cache keyed on query hash, TTL 60s." }
2913
+ ],
2914
+ bigO: "O(n) becomes O(1) with the JOIN fix + O(1) with the cache",
2915
+ expectedSpeedup: "10-100x for typical queries"
2916
+ }
2917
+ }
2918
+ ],
2919
+ outputSchema: "{ findings: Array<{ type: string; severity: string; file: string; line: number; issue: string; fix: string }>; bigO: string; expectedSpeedup: string }",
2920
+ systemPromptFragment: `You are reviewing code for performance issues.
2921
+
2922
+ ## Common patterns to detect
2923
+ 1. **N+1 queries**: loop with a DB call inside (fetch all data upfront with a JOIN)
2924
+ 2. **Allocation in hot loops**: \`new SomeClass()\` inside \`for (...)\` (hoist outside)
2925
+ 3. **Missing index**: SQL filter on unindexed column (add index, EXPLAIN ANALYZE)
2926
+ 4. **No caching**: identical computation repeated (LRU cache, memoize)
2927
+ 5. **Blocking I/O in async**: \`await fs.readFileSync(...)\` (use async readFile)
2928
+ 6. **Quadratic loops**: nested loops over the same array (use hash map for O(n) lookup)
2929
+ 7. **Synchronous XHR/fetch**: blocking the main thread (use async/await or worker)
2930
+
2931
+ ## Output format (JSON-typed)
2932
+ - findings: Array<{ type, severity, file, line, issue, fix }>
2933
+ - bigO: string (the complexity change after fixes)
2934
+ - expectedSpeedup: string (estimated speedup magnitude)
2935
+
2936
+ ## Anti-patterns to avoid
2937
+ - **Don't propose premature optimizations** (caching for a function called once)
2938
+ - **Don't change behavior for performance** unless explicitly asked
2939
+ - **Profile first** \u2014 don't guess where the hot path is
2940
+
2941
+ Stay under 500 words.${CLARIFICATION_PROTOCOL6}`
2942
+ };
2943
+ var testCoverageAnalysis = {
2944
+ id: "test-coverage-analysis",
2945
+ version: "1.0.0",
2946
+ name: "Test Coverage Analysis",
2947
+ description: "Identify untested code branches. Suggest specific test cases that would close the coverage gap, prioritizing edge cases and error paths.",
2948
+ category: "review",
2949
+ requiredRoles: ["oracle", "atlas"],
2950
+ requiredTools: ["read_file"],
2951
+ estimatedCost: "medium",
2952
+ enabledByDefault: true,
2953
+ builtin: true,
2954
+ triggers: [
2955
+ "Coverage report shows < 80% line coverage",
2956
+ "A new feature was added without tests",
2957
+ "A bug was found that should have been caught by tests",
2958
+ "Quarterly coverage audit",
2959
+ "Refactoring a high-churn module where regression risk is high"
2960
+ ],
2961
+ antiPatterns: [
2962
+ "Tests exist but are tautologies (assertions that always pass)",
2963
+ "Coverage targets > 95% (often forces meaningless tests)",
2964
+ "Auto-generated code (skip \u2014 the generator has its own tests)"
2965
+ ],
2966
+ requires: [],
2967
+ relatedSkills: ["code-review"],
2968
+ tags: ["review", "testing", "coverage", "edge-cases"],
2969
+ examples: [
2970
+ {
2971
+ input: "src/lib/formatDuration.ts has 6 branches; coverage report shows only 3 are tested",
2972
+ output: {
2973
+ untestedBranches: [
2974
+ { branch: "ms < 0 (negative duration)", file: "src/lib/formatDuration.ts:12", suggestedTest: "expect(formatDuration(-1000)).toThrow('Negative duration')" },
2975
+ { branch: "ms === 0", file: "src/lib/formatDuration.ts:14", suggestedTest: "expect(formatDuration(0)).toBe('0s')" },
2976
+ { branch: "ms > 1 day (very large)", file: "src/lib/formatDuration.ts:22", suggestedTest: "expect(formatDuration(86400000 * 2)).toBe('2d')" }
2977
+ ],
2978
+ edgeCases: [
2979
+ "ms is NaN (e.g. from a corrupt timestamp) \u2014 should throw",
2980
+ "ms is Infinity (e.g. from setTimeout overflow) \u2014 should cap at a max",
2981
+ 'style option is invalid \u2014 should default to "compact"'
2982
+ ],
2983
+ priorityOrder: ["1. NaN/Infinity handling (data integrity)", "2. ms === 0 boundary (off-by-one)", "3. style validation (UX)"]
2984
+ }
2985
+ }
2986
+ ],
2987
+ outputSchema: "{ untestedBranches: Array<{ branch: string; file: string; line: number; suggestedTest: string }>; edgeCases: string[]; priorityOrder: string[] }",
2988
+ systemPromptFragment: `You are identifying untested code branches.
2989
+
2990
+ ## Methodology
2991
+ 1. **Read each branch** in the source file (if/else, switch cases, try/catch).
2992
+ 2. **Identify untested branches** by reading the corresponding test file.
2993
+ 3. **Suggest a SPECIFIC test** (not "add a test for X" \u2014 write the actual assertion).
2994
+ 4. **Prioritize edge cases**: NaN, null, undefined, empty array, max int, negative numbers.
2995
+ 5. **Prioritize error paths**: what happens when validation fails, network drops, etc.?
2996
+
2997
+ ## Output format (JSON-typed)
2998
+ - untestedBranches: Array<{ branch, file, line, suggestedTest }>
2999
+ - edgeCases: string[] (3-7 cases)
3000
+ - priorityOrder: string[] (numbered list)
3001
+
3002
+ ## Test quality principles
3003
+ - **One assertion per test** \u2014 easier to debug
3004
+ - **Specific assertion** \u2014 not \`expect(result).toBeTruthy()\` but \`expect(result).toBe('1h 23m')\`
3005
+ - **Test the boundary** \u2014 \`n=0\`, \`n=1\`, \`n=MAX_INT\`
3006
+ - **Test the failure path** \u2014 invalid input, network errors, partial data
3007
+
3008
+ Stay under 400 words.${CLARIFICATION_PROTOCOL6}`
3009
+ };
3010
+ var codeReview = {
3011
+ id: "code-review",
3012
+ version: "1.0.0",
3013
+ name: "Code Review",
3014
+ description: "Multi-role review covering correctness, style, performance, security, and accessibility. Each role emits findings with severity (CRITICAL/HIGH/MEDIUM/LOW). Chairman synthesizes consensus.",
3015
+ category: "review",
3016
+ requiredRoles: ["oracle", "atlas", "chairman"],
3017
+ requiredTools: ["read_file", "grep_content", "searchRAG"],
3018
+ estimatedCost: "high",
3019
+ enabledByDefault: true,
3020
+ builtin: true,
3021
+ triggers: [
3022
+ "A pull request is ready for review",
3023
+ "A code change affects > 100 lines",
3024
+ "Code touches auth, payments, or other security-critical paths",
3025
+ "Before merging to main",
3026
+ "A pre-merge sanity check on critical files"
3027
+ ],
3028
+ antiPatterns: [
3029
+ "Single-line change (typo, comment fix) \u2014 direct merge",
3030
+ "Trivial style fix (whitespace, import order) \u2014 direct merge",
3031
+ "WIP / draft PR \u2014 review when ready",
3032
+ "Generated code (e.g. codegen output) \u2014 review the upstream generator instead"
3033
+ ],
3034
+ requires: ["security-audit", "performance-review", "test-coverage-analysis"],
3035
+ relatedSkills: ["security-audit", "performance-review", "test-coverage-analysis"],
3036
+ tags: ["review", "multi-role", "pr-review", "correctness", "security"],
3037
+ examples: [
3038
+ {
3039
+ input: "Review src/components/AuthForm.tsx (180 LOC) \u2014 adds passwordless email login",
3040
+ output: {
3041
+ findings: [
3042
+ { role: "oracle", severity: "CRITICAL", line: 67, issue: "No CSRF token on the form submit", file: "src/components/AuthForm.tsx" },
3043
+ { role: "atlas", severity: "HIGH", line: 23, issue: "N+1 query: user lookup fires on every keystroke instead of debounced", file: "src/components/AuthForm.tsx" },
3044
+ { role: "oracle", severity: "MEDIUM", line: 102, issue: "Error message leaks server response body (XSS risk)", file: "src/components/AuthForm.tsx" },
3045
+ { role: "chairman", severity: "LOW", line: 45, issue: "Variable name `usrEml` should be `userEmail` (style)", file: "src/components/AuthForm.tsx" }
3046
+ ],
3047
+ consensus: "2 CRITICAL/HIGH blockers must be fixed before merge. CSRF is the most critical. After CSRF fix, N+1 query, then error message.",
3048
+ mergeVerdict: "BLOCK"
3049
+ }
3050
+ }
3051
+ ],
3052
+ outputSchema: '{ findings: Array<{ role: string; severity: "CRITICAL" | "HIGH" | "MEDIUM" | "LOW"; line: number; issue: string; file: string }>; consensus: string; mergeVerdict: "APPROVE" | "REQUEST_CHANGES" | "BLOCK" }',
3053
+ systemPromptFragment: `You are conducting a multi-role code review.
3054
+
3055
+ ## Review roles
3056
+ - **oracle (correctness)**: bugs, race conditions, edge cases, error handling
3057
+ - **atlas (performance)**: O(n\xB2) algorithms, N+1 queries, unnecessary allocations, missing caching
3058
+ - **oracle (security)**: injection, auth bypass, secrets in code, unsafe deserialization (overlaps with security-audit skill \u2014 run that first)
3059
+ - **atlas (accessibility)**: ARIA labels, keyboard nav, color contrast
3060
+ - **chairman (synthesis)**: aggregates all findings, emits verdict (APPROVE / REQUEST_CHANGES / BLOCK)
3061
+
3062
+ ## Severity levels
3063
+ - **CRITICAL**: must fix before merge (security, data loss, crash)
3064
+ - **HIGH**: should fix before merge (correctness bug, performance regression)
3065
+ - **MEDIUM**: nice to fix (code smell, minor perf)
3066
+ - **LOW**: nitpick (style, naming)
3067
+
3068
+ ## Output format (JSON-typed)
3069
+ - findings: Array<{ role, severity, line, issue, file }>
3070
+ - consensus: string (chairman's synthesis)
3071
+ - mergeVerdict: 'APPROVE' | 'REQUEST_CHANGES' | 'BLOCK'
3072
+
3073
+ ## Review principles
3074
+ - **Be specific**: cite file + line numbers
3075
+ - **Be actionable**: each finding has a concrete fix
3076
+ - **Don't bikeshed style** \u2014 note as LOW or skip
3077
+ - **Trust the author** \u2014 don't require changes that are preference, not correctness
3078
+
3079
+ Stay under 600 words.${CLARIFICATION_PROTOCOL6}`
3080
+ };
3081
+ var registerCodingSkill3 = registerCodingSkill;
3082
+ registerCodingSkill3(securityAudit);
3083
+ registerCodingSkill3(performanceReview);
3084
+ registerCodingSkill3(testCoverageAnalysis);
3085
+ registerCodingSkill3(codeReview);
3086
+
3087
+ // src/agents/skills/builtin/testing.ts
3088
+ var CLARIFICATION_PROTOCOL7 = `
3089
+
3090
+ WHEN TO ASK THE USER (clarification):
3091
+ If a single missing fact would materially change your output (target platform, scope, a binary design choice with significant trade-offs, a constraint you cannot safely assume), pause and ask the user by appending EXACTLY this block at the end of your message:
3092
+
3093
+ ---QUESTION---
3094
+ { "question": "One focused question", "choices": ["Option A", "Option B", "Option C"], "context": "Why this matters" }
3095
+ ---END---
3096
+
3097
+ Rules for clarifications:
3098
+ - Ask AT MOST ONE question per turn, and only when genuinely blocked.
3099
+ - Prefer a small set of concrete "choices" (2-4). The user can still type a custom answer.
3100
+ - Do NOT ask for information that could be reasonably assumed or already in shared context.
3101
+ - If you can proceed with a sound documented assumption, DO SO instead of asking.`;
3102
+ var writeUnitTests = {
3103
+ id: "write-unit-tests",
3104
+ version: "1.0.0",
3105
+ name: "Write Unit Tests",
3106
+ description: "TDD-style test generation for a function or module. Output: 5-10 focused unit tests covering happy paths, edge cases (empty/null/boundary), error paths. Each test has one assertion. Uses Vitest patterns (the project standard).",
3107
+ category: "test",
3108
+ requiredRoles: ["oracle", "atlas"],
3109
+ requiredTools: ["read_file"],
3110
+ estimatedCost: "medium",
3111
+ enabledByDefault: true,
3112
+ builtin: true,
3113
+ triggers: [
3114
+ "A function or module has no tests",
3115
+ "Coverage report shows a specific function is untested",
3116
+ "A new utility is added (helper, formatter, validator)",
3117
+ "A bug is fixed and a regression test is needed"
3118
+ ],
3119
+ antiPatterns: [
3120
+ "Integration scenarios (multi-component) \u2014 use write-integration-tests",
3121
+ "A bug report exists \u2014 use regression-test instead (more focused)",
3122
+ "Performance benchmarks \u2014 those need write-bench-test, not unit tests"
3123
+ ],
3124
+ requires: [],
3125
+ relatedSkills: ["regression-test", "test-coverage-analysis"],
3126
+ tags: ["test", "unit-test", "tdd", "vitest", "coverage"],
3127
+ examples: [
3128
+ {
3129
+ input: "Write unit tests for src/lib/formatDuration.ts",
3130
+ output: {
3131
+ testFile: "tests/unit/formatDuration.test.ts",
3132
+ tests: [
3133
+ { name: 'formats 0 ms as "0s"', input: 0, expected: "0s" },
3134
+ { name: 'formats 45 seconds as "45s"', input: 45e3, expected: "45s" },
3135
+ { name: 'formats 1 minute as "1m"', input: 6e4, expected: "1m" },
3136
+ { name: "formats 1h 23m for long duration", input: 498e4, expected: "1h 23m" },
3137
+ { name: "formats 2d for >24h", input: 1728e5, expected: "2d" },
3138
+ { name: "throws on negative duration", input: -1e3, expectedThrows: "Negative duration" },
3139
+ { name: "throws on NaN", input: NaN, expectedThrows: "NaN duration" },
3140
+ { name: "respects compact style option", input: 6e4, options: { style: "compact" }, expected: "1m" }
3141
+ ]
3142
+ }
3143
+ }
3144
+ ],
3145
+ outputSchema: "{ testFile: string; tests: Array<{ name: string; input: unknown; expected?: unknown; options?: Record<string, unknown>; expectedThrows?: string }> }",
3146
+ systemPromptFragment: `You are writing Vitest unit tests for a function or module.
3147
+
3148
+ ## Methodology
3149
+ 1. **Read the source**: understand the function signature, branches, error paths.
3150
+ 2. **Identify happy paths**: at least 2 tests for typical inputs.
3151
+ 3. **Identify edge cases** (boundaries): 0, 1, max, empty array, empty string.
3152
+ 4. **Identify error paths**: invalid input, network errors, partial data.
3153
+ 5. **Write one test per assertion** \u2014 easier to debug when a test fails.
3154
+ 6. **Use Vitest patterns**: describe/it/expect, vi.fn() for mocks, vi.spyOn for stubs.
3155
+
3156
+ ## Test naming
3157
+ - Use \`describe('moduleName', () => { it('does X when Y', ...) })\`.
3158
+ - Test name describes behavior, not implementation.
3159
+ - Group related tests in the same describe block.
3160
+
3161
+ ## Output format (JSON-typed)
3162
+ - testFile: string (the file path)
3163
+ - tests: Array<{ name, input, expected, options?, expectedThrows? }>
3164
+
3165
+ ## Anti-patterns to avoid
3166
+ - Tests that always pass (tautologies)
3167
+ - Tests with multiple assertions (split into separate tests)
3168
+ - Tests that depend on test execution order
3169
+ - Snapshot tests for non-deterministic output
3170
+
3171
+ Stay under 500 words.${CLARIFICATION_PROTOCOL7}`
3172
+ };
3173
+ var writeIntegrationTests = {
3174
+ id: "write-integration-tests",
3175
+ version: "1.0.0",
3176
+ name: "Write Integration Tests",
3177
+ description: "Generate end-to-end scenario tests that span multiple components. Tests verify the integration boundary, not individual units. Uses real subsystems (or close mocks) to catch contract mismatches.",
3178
+ category: "test",
3179
+ requiredRoles: ["oracle", "hephaestus"],
3180
+ requiredTools: ["read_file", "grep_content"],
3181
+ estimatedCost: "medium",
3182
+ enabledByDefault: true,
3183
+ builtin: true,
3184
+ triggers: [
3185
+ "A new feature spans 2+ modules",
3186
+ "A new API endpoint needs end-to-end coverage",
3187
+ "A refactor changes module boundaries \u2014 verify behavior preserved",
3188
+ "A bug was caused by a contract mismatch between two modules"
3189
+ ],
3190
+ antiPatterns: [
3191
+ "Single-component logic \u2014 use write-unit-tests",
3192
+ "Performance benchmarks \u2014 use a dedicated perf test",
3193
+ "Visual regression (UI snapshot) \u2014 needs screenshot diff tooling"
3194
+ ],
3195
+ requires: ["write-unit-tests"],
3196
+ relatedSkills: ["write-unit-tests", "regression-test"],
3197
+ tags: ["test", "integration", "e2e", "contract", "multi-component"],
3198
+ examples: [
3199
+ {
3200
+ input: 'Integration test for "user uploads file \u2192 RAG indexes \u2192 user queries"',
3201
+ output: {
3202
+ testFile: "tests/integration/upload-query.test.ts",
3203
+ scenarios: [
3204
+ { name: "uploaded file appears in search results within 5s", steps: ["create test vault", "upload doc.md", "wait 1s for indexer", 'query "doc.md"', "expect result"] },
3205
+ { name: "deleted file removed from search results", steps: ["upload + query (sanity)", "delete file", "wait 1s", "query", "expect no result"] },
3206
+ { name: "concurrent uploads maintain ordering", steps: ["upload 10 files in parallel", "query each", "expect all 10 found"] },
3207
+ { name: "corrupt file (binary garbage) does not crash indexer", steps: ["upload binary garbage", "wait 5s", "expect vault accessible (no crash)"] }
3208
+ ],
3209
+ contracts: [
3210
+ { between: "vault + indexer", contract: "indexer subscribes to vault:created events and processes new files within 5s" },
3211
+ { between: "indexer + search", contract: "search returns docs only after indexer has flushed their embeddings to SQLite" }
3212
+ ]
3213
+ }
3214
+ }
3215
+ ],
3216
+ outputSchema: "{ testFile: string; scenarios: Array<{ name: string; steps: string[] }>; contracts: Array<{ between: string; contract: string }> }",
3217
+ systemPromptFragment: `You are writing integration tests that span multiple components.
3218
+
3219
+ ## Methodology
3220
+ 1. **Identify the integration boundary**: which 2+ components interact?
3221
+ 2. **Document the contract**: what does component A promise to component B?
3222
+ 3. **Test the contract, not the internals**: verify the OBSERVABLE behavior, not the implementation.
3223
+ 4. **Use real subsystems where possible**: in-memory SQLite instead of mocks; fake HTTP server instead of stubbed client.
3224
+ 5. **Each scenario is a complete user story**: setup \u2192 action \u2192 verify.
3225
+
3226
+ ## Output format (JSON-typed)
3227
+ - testFile: string
3228
+ - scenarios: Array<{ name, steps[] }>
3229
+ - contracts: Array<{ between, contract }>
3230
+
3231
+ ## Integration test principles
3232
+ - **Minimal mocks**: mock only the OUTSIDE world (network, disk, time), not your own code
3233
+ - **Real data**: use realistic test data, not "foo" / "bar"
3234
+ - **Deterministic**: avoid timing-based assertions (use polling with timeout)
3235
+ - **Cleanup**: each scenario should leave the system in a clean state
3236
+
3237
+ Stay under 500 words.${CLARIFICATION_PROTOCOL7}`
3238
+ };
3239
+ var regressionTest = {
3240
+ id: "regression-test",
3241
+ version: "1.0.0",
3242
+ name: "Regression Test",
3243
+ description: "Convert a bug report into a permanent regression test. The test must fail on the BUGGY code (pre-fix), pass on the FIXED code (post-fix), and stay in the suite forever to prevent the bug from coming back.",
3244
+ category: "test",
3245
+ requiredRoles: ["oracle"],
3246
+ requiredTools: ["write_file"],
3247
+ estimatedCost: "low",
3248
+ enabledByDefault: true,
3249
+ builtin: true,
3250
+ triggers: [
3251
+ "A bug is fixed and a regression test is needed",
3252
+ 'A user reports a recurring bug ("this used to work")',
3253
+ "A flaky test that intermittently fails",
3254
+ "A bug found in code review"
3255
+ ],
3256
+ antiPatterns: [
3257
+ "The bug has not been fixed yet \u2014 first fix, then add regression test",
3258
+ "The bug is in test code itself \u2014 fix the test",
3259
+ "A bug that requires impossible-to-mock state (real network, real browser)"
3260
+ ],
3261
+ requires: [],
3262
+ relatedSkills: ["write-unit-tests", "reproduce-bug"],
3263
+ tags: ["test", "regression", "bug-fix", "permanent-test"],
3264
+ examples: [
3265
+ {
3266
+ input: 'Bug: "Council shows duplicate user message at session resume" was just fixed in commit a3f9d',
3267
+ output: {
3268
+ testFile: "tests/regression/duplicate-user-message.test.ts",
3269
+ test: `import { describe, it, expect } from 'vitest';
3270
+ import { runCouncilPure } from '../agents/councilApi';
3271
+
3272
+ describe('regression: duplicate user message at session resume', () => {
3273
+ it('does not render duplicate user message when session resumes with duplicate input', async () => {
3274
+ const messages = [
3275
+ { role: 'user' as const, content: 'hi' },
3276
+ { role: 'user' as const, content: 'hi' }, // duplicate from session resume
3277
+ ];
3278
+ const mockProvider = async function* () {
3279
+ yield { kind: 'text' as const, delta: 'response' };
3280
+ yield { kind: 'finish' as const, reason: 'stop' };
3281
+ };
3282
+ const events = [];
3283
+ for await (const e of runCouncilPure('hi', {
3284
+ model: 'test', provider: 'test',
3285
+ messages,
3286
+ tools: [],
3287
+ councilSize: 1,
3288
+ debateMode: false,
3289
+ ragContext: '',
3290
+ workspaceContext: '',
3291
+ providerStream: mockProvider,
3292
+ }, {})) {
3293
+ events.push(e);
3294
+ }
3295
+ const userMessageDeltas = events.filter(e => e.type === 'message_delta').length;
3296
+ expect(userMessageDeltas).toBe(1); // exactly 1, not 2
3297
+ });
3298
+ });`,
3299
+ whyRegression: "Bug was reported 3 times in the last month. The fix (dedup user messages on session resume) was in commit a3f9d. This test guards against future refactors breaking the dedup logic.",
3300
+ bugCommit: "a3f9d"
3301
+ }
3302
+ }
3303
+ ],
3304
+ outputSchema: "{ testFile: string; test: string; whyRegression: string; bugCommit: string }",
3305
+ systemPromptFragment: `You are converting a fixed bug into a permanent regression test.
3306
+
3307
+ ## Methodology
3308
+ 1. **Confirm the bug is FIXED**: check git log / recent commits for the fix.
3309
+ 2. **Write the test**: it should FAIL if you revert the fix (verify this mentally).
3310
+ 3. **Add a comment** in the test file linking to the bug commit.
3311
+ 4. **Place in tests/regression/**: separate from unit tests for visibility.
3312
+
3313
+ ## Output format (JSON-typed)
3314
+ - testFile: string
3315
+ - test: string (the full test code, runnable as-is)
3316
+ - whyRegression: string (why this test should stay forever)
3317
+ - bugCommit: string (the commit hash that fixed the bug)
3318
+
3319
+ ## Regression test principles
3320
+ - **One test per bug** \u2014 easier to identify which regression fired
3321
+ - **Reference the bug commit** in a comment so future devs understand the history
3322
+ - **Minimal repro** \u2014 strip to the essential trigger, don't include unrelated setup
3323
+ - **Never delete a regression test** without a written justification (the bug came back once, it'll come back again)
3324
+
3325
+ Stay under 300 words.${CLARIFICATION_PROTOCOL7}`
3326
+ };
3327
+ registerCodingSkill(writeUnitTests);
3328
+ registerCodingSkill(writeIntegrationTests);
3329
+ registerCodingSkill(regressionTest);
3330
+
3331
+ // src/agents/roles.ts
3332
+ var CLARIFICATION_PROTOCOL8 = `
3333
+
3334
+ WHEN TO ASK THE USER (clarification):
3335
+ If a single missing fact would materially change your output (target platform, scope, a binary design choice with significant trade-offs, a constraint you cannot safely assume), pause and ask the user by appending EXACTLY this block at the end of your message:
3336
+
3337
+ ---QUESTION---
3338
+ { "question": "One focused question", "choices": ["Option A", "Option B", "Option C"], "context": "Why this matters" }
3339
+ ---END---
3340
+
1335
3341
  Rules for clarifications:
1336
3342
  - Ask AT MOST ONE question per turn, and only when genuinely blocked.
1337
3343
  - Prefer a small set of concrete "choices" (2-4). The user can still type a custom answer.
@@ -1361,7 +3367,7 @@ var AGENT_ROLES = [
1361
3367
  - You run first and set context for everyone downstream; make it count.
1362
3368
 
1363
3369
  ## Output format
1364
- A short "Analysis" section (the goal + constraints), then a "Delegation Plan" with one bullet per specialist naming what they should produce. Keep it tight \u2014 under 150 words total.${CLARIFICATION_PROTOCOL}`,
3370
+ A short "Analysis" section (the goal + constraints), then a "Delegation Plan" with one bullet per specialist naming what they should produce. Keep it tight \u2014 under 150 words total.${CLARIFICATION_PROTOCOL8}`,
1365
3371
  tools: ["createTask", "createPhase"],
1366
3372
  skills: ["project-planner", "research-analyst"]
1367
3373
  },
@@ -1397,7 +3403,7 @@ Example format:
1397
3403
  - Make dependencies explicit (task B depends on task A).
1398
3404
  - If the stack/platform is unknown, ask ONCE (see clarification protocol) rather than guessing.
1399
3405
 
1400
- Keep plans hierarchical and practical. Stay under 250 words.${CLARIFICATION_PROTOCOL}`,
3406
+ Keep plans hierarchical and practical. Stay under 250 words.${CLARIFICATION_PROTOCOL8}`,
1401
3407
  tools: ["createTask", "createPhase"],
1402
3408
  skills: ["project-planner", "vault-manager"]
1403
3409
  },
@@ -1427,7 +3433,7 @@ Keep plans hierarchical and practical. Stay under 250 words.${CLARIFICATION_PROT
1427
3433
  ## Themes
1428
3434
  ## Top picks (with feasibility/novelty + de-risk note)
1429
3435
 
1430
- Stay under 200 words.${CLARIFICATION_PROTOCOL}`,
3436
+ Stay under 200 words.${CLARIFICATION_PROTOCOL8}`,
1431
3437
  tools: ["addIdea"],
1432
3438
  skills: ["idea-synthesizer", "mind-mapper", "document-writer"]
1433
3439
  },
@@ -1454,7 +3460,7 @@ Stay under 200 words.${CLARIFICATION_PROTOCOL}`,
1454
3460
  - Keep the graph comprehensible: prune redundant or duplicate nodes.
1455
3461
 
1456
3462
  ## Output format
1457
- Describe the proposed structure (root \u2192 branches \u2192 leaves) in text and, when building via tool, emit a buildMindMap payload. Stay under 200 words.${CLARIFICATION_PROTOCOL}`,
3463
+ Describe the proposed structure (root \u2192 branches \u2192 leaves) in text and, when building via tool, emit a buildMindMap payload. Stay under 200 words.${CLARIFICATION_PROTOCOL8}`,
1458
3464
  tools: ["buildMindMap", "addNode", "linkNodes"],
1459
3465
  skills: ["mind-mapper", "research-analyst"]
1460
3466
  },
@@ -1485,7 +3491,7 @@ Describe the proposed structure (root \u2192 branches \u2192 leaves) in text and
1485
3491
  ## Contradictions
1486
3492
  ## Top improvements
1487
3493
 
1488
- Stay under 200 words. (You do not create workspace artifacts, so you do not emit a tools block \u2014 but you MAY ask the user a clarifying question if a core ambiguity blocks judgment.)${CLARIFICATION_PROTOCOL}`,
3494
+ Stay under 200 words. (You do not create workspace artifacts, so you do not emit a tools block \u2014 but you MAY ask the user a clarifying question if a core ambiguity blocks judgment.)${CLARIFICATION_PROTOCOL8}`,
1489
3495
  tools: [],
1490
3496
  skills: ["research-analyst"]
1491
3497
  },
@@ -1519,7 +3525,7 @@ Append EXACTLY this block at the very end of your message when you must create w
1519
3525
  ]
1520
3526
  ---END---
1521
3527
  Available tool names: createTask, addIdea, createPhase, buildMindMap, addNode, linkNodes, createDocument.
1522
- Only emit this block at the very end and only if actions are needed. Otherwise output plain text.${CLARIFICATION_PROTOCOL}`,
3528
+ Only emit this block at the very end and only if actions are needed. Otherwise output plain text.${CLARIFICATION_PROTOCOL8}`,
1523
3529
  tools: ["createTask", "addIdea", "createPhase", "buildMindMap", "addNode", "linkNodes", "createDocument"],
1524
3530
  skills: ["vault-manager", "project-planner", "idea-synthesizer"]
1525
3531
  }
@@ -20061,7 +22067,7 @@ function App() {
20061
22067
  fallback: failoverResolution.fallback
20062
22068
  });
20063
22069
  const cwd = process.cwd();
20064
- const toolList = toolRegistry.toOpenAITools().map((t) => `- ${t.name}: ${t.description}`).join("\n");
22070
+ const toolList = toolRegistry.toOpenAITools().map((t) => `- ${t.function.name}: ${t.function.description}`).join("\n");
20065
22071
  const systemPrompt = [
20066
22072
  "You are Zelari Code, an interactive AI coding agent operating directly in the user's terminal.",
20067
22073
  "",
@@ -20088,7 +22094,11 @@ function App() {
20088
22094
  { role: "system", content: systemPrompt },
20089
22095
  { role: "user", content: userText }
20090
22096
  ],
20091
- tools: toolRegistry.toOpenAITools(),
22097
+ tools: toolRegistry.toOpenAITools().map((t) => ({
22098
+ name: t.function.name,
22099
+ description: t.function.description,
22100
+ parameters: t.function.parameters
22101
+ })),
20092
22102
  toolRegistry,
20093
22103
  providerStream,
20094
22104
  cwd
@@ -21433,7 +23443,7 @@ ${lines.join("\n")}`,
21433
23443
  }
21434
23444
 
21435
23445
  // src/cli/main.ts
21436
- var VERSION = "0.1.0";
23446
+ var VERSION = "0.2.1";
21437
23447
  async function backgroundUpdateCheck() {
21438
23448
  if (process.env.ANATHEMA_DEV === "1") return;
21439
23449
  await new Promise((resolve) => setTimeout(resolve, 3e3));