oh-my-opencode 2.8.3 → 2.9.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.
package/dist/index.js CHANGED
@@ -1479,9 +1479,256 @@ function isGptModel(model) {
1479
1479
  return model.startsWith("openai/") || model.startsWith("github-copilot/gpt-");
1480
1480
  }
1481
1481
 
1482
+ // src/agents/sisyphus-prompt-builder.ts
1483
+ function categorizeTools(toolNames) {
1484
+ return toolNames.map((name) => {
1485
+ let category = "other";
1486
+ if (name.startsWith("lsp_")) {
1487
+ category = "lsp";
1488
+ } else if (name.startsWith("ast_grep")) {
1489
+ category = "ast";
1490
+ } else if (name === "grep" || name === "glob") {
1491
+ category = "search";
1492
+ } else if (name.startsWith("session_")) {
1493
+ category = "session";
1494
+ } else if (name === "slashcommand") {
1495
+ category = "command";
1496
+ }
1497
+ return { name, category };
1498
+ });
1499
+ }
1500
+ function formatToolsForPrompt(tools) {
1501
+ const lspTools = tools.filter((t) => t.category === "lsp");
1502
+ const astTools = tools.filter((t) => t.category === "ast");
1503
+ const searchTools = tools.filter((t) => t.category === "search");
1504
+ const parts = [];
1505
+ if (searchTools.length > 0) {
1506
+ parts.push(...searchTools.map((t) => `\`${t.name}\``));
1507
+ }
1508
+ if (lspTools.length > 0) {
1509
+ parts.push("`lsp_*`");
1510
+ }
1511
+ if (astTools.length > 0) {
1512
+ parts.push("`ast_grep`");
1513
+ }
1514
+ return parts.join(", ");
1515
+ }
1516
+ function buildKeyTriggersSection(agents, skills = []) {
1517
+ const keyTriggers = agents.filter((a) => a.metadata.keyTrigger).map((a) => `- ${a.metadata.keyTrigger}`);
1518
+ const skillTriggers = skills.filter((s) => s.description).map((s) => `- **Skill \`${s.name}\`**: ${extractTriggerFromDescription(s.description)}`);
1519
+ const allTriggers = [...keyTriggers, ...skillTriggers];
1520
+ if (allTriggers.length === 0)
1521
+ return "";
1522
+ return `### Key Triggers (check BEFORE classification):
1523
+
1524
+ **BLOCKING: Check skills FIRST before any action.**
1525
+ If a skill matches, invoke it IMMEDIATELY via \`skill\` tool.
1526
+
1527
+ ${allTriggers.join(`
1528
+ `)}
1529
+ - **GitHub mention (@mention in issue/PR)** \u2192 This is a WORK REQUEST. Plan full cycle: investigate \u2192 implement \u2192 create PR
1530
+ - **"Look into" + "create PR"** \u2192 Not just research. Full implementation cycle expected.`;
1531
+ }
1532
+ function extractTriggerFromDescription(description) {
1533
+ const triggerMatch = description.match(/Trigger[s]?[:\s]+([^.]+)/i);
1534
+ if (triggerMatch)
1535
+ return triggerMatch[1].trim();
1536
+ const activateMatch = description.match(/Activate when[:\s]+([^.]+)/i);
1537
+ if (activateMatch)
1538
+ return activateMatch[1].trim();
1539
+ const useWhenMatch = description.match(/Use (?:this )?when[:\s]+([^.]+)/i);
1540
+ if (useWhenMatch)
1541
+ return useWhenMatch[1].trim();
1542
+ return description.split(".")[0] || description;
1543
+ }
1544
+ function buildToolSelectionTable(agents, tools = [], skills = []) {
1545
+ const rows = [
1546
+ "### Tool & Skill Selection:",
1547
+ "",
1548
+ "**Priority Order**: Skills \u2192 Direct Tools \u2192 Agents",
1549
+ ""
1550
+ ];
1551
+ if (skills.length > 0) {
1552
+ rows.push("#### Skills (INVOKE FIRST if matching)");
1553
+ rows.push("");
1554
+ rows.push("| Skill | When to Use |");
1555
+ rows.push("|-------|-------------|");
1556
+ for (const skill of skills) {
1557
+ const shortDesc = extractTriggerFromDescription(skill.description);
1558
+ rows.push(`| \`${skill.name}\` | ${shortDesc} |`);
1559
+ }
1560
+ rows.push("");
1561
+ }
1562
+ rows.push("#### Tools & Agents");
1563
+ rows.push("");
1564
+ rows.push("| Resource | Cost | When to Use |");
1565
+ rows.push("|----------|------|-------------|");
1566
+ if (tools.length > 0) {
1567
+ const toolsDisplay = formatToolsForPrompt(tools);
1568
+ rows.push(`| ${toolsDisplay} | FREE | Not Complex, Scope Clear, No Implicit Assumptions |`);
1569
+ }
1570
+ const costOrder = { FREE: 0, CHEAP: 1, EXPENSIVE: 2 };
1571
+ const sortedAgents = [...agents].filter((a) => a.metadata.category !== "utility").sort((a, b) => costOrder[a.metadata.cost] - costOrder[b.metadata.cost]);
1572
+ for (const agent of sortedAgents) {
1573
+ const shortDesc = agent.description.split(".")[0] || agent.description;
1574
+ rows.push(`| \`${agent.name}\` agent | ${agent.metadata.cost} | ${shortDesc} |`);
1575
+ }
1576
+ rows.push("");
1577
+ rows.push("**Default flow**: skill (if match) \u2192 explore/librarian (background) + tools \u2192 oracle (if required)");
1578
+ return rows.join(`
1579
+ `);
1580
+ }
1581
+ function buildExploreSection(agents) {
1582
+ const exploreAgent = agents.find((a) => a.name === "explore");
1583
+ if (!exploreAgent)
1584
+ return "";
1585
+ const useWhen = exploreAgent.metadata.useWhen || [];
1586
+ const avoidWhen = exploreAgent.metadata.avoidWhen || [];
1587
+ return `### Explore Agent = Contextual Grep
1588
+
1589
+ Use it as a **peer tool**, not a fallback. Fire liberally.
1590
+
1591
+ | Use Direct Tools | Use Explore Agent |
1592
+ |------------------|-------------------|
1593
+ ${avoidWhen.map((w) => `| ${w} | |`).join(`
1594
+ `)}
1595
+ ${useWhen.map((w) => `| | ${w} |`).join(`
1596
+ `)}`;
1597
+ }
1598
+ function buildLibrarianSection(agents) {
1599
+ const librarianAgent = agents.find((a) => a.name === "librarian");
1600
+ if (!librarianAgent)
1601
+ return "";
1602
+ const useWhen = librarianAgent.metadata.useWhen || [];
1603
+ return `### Librarian Agent = Reference Grep
1604
+
1605
+ Search **external references** (docs, OSS, web). Fire proactively when unfamiliar libraries are involved.
1606
+
1607
+ | Contextual Grep (Internal) | Reference Grep (External) |
1608
+ |----------------------------|---------------------------|
1609
+ | Search OUR codebase | Search EXTERNAL resources |
1610
+ | Find patterns in THIS repo | Find examples in OTHER repos |
1611
+ | How does our code work? | How does this library work? |
1612
+ | Project-specific logic | Official API documentation |
1613
+ | | Library best practices & quirks |
1614
+ | | OSS implementation examples |
1615
+
1616
+ **Trigger phrases** (fire librarian immediately):
1617
+ ${useWhen.map((w) => `- "${w}"`).join(`
1618
+ `)}`;
1619
+ }
1620
+ function buildDelegationTable(agents) {
1621
+ const rows = [
1622
+ "### Delegation Table:",
1623
+ "",
1624
+ "| Domain | Delegate To | Trigger |",
1625
+ "|--------|-------------|---------|"
1626
+ ];
1627
+ for (const agent of agents) {
1628
+ for (const trigger of agent.metadata.triggers) {
1629
+ rows.push(`| ${trigger.domain} | \`${agent.name}\` | ${trigger.trigger} |`);
1630
+ }
1631
+ }
1632
+ return rows.join(`
1633
+ `);
1634
+ }
1635
+ function buildFrontendSection(agents) {
1636
+ const frontendAgent = agents.find((a) => a.name === "frontend-ui-ux-engineer");
1637
+ if (!frontendAgent)
1638
+ return "";
1639
+ return `### Frontend Files: Decision Gate (NOT a blind block)
1640
+
1641
+ Frontend files (.tsx, .jsx, .vue, .svelte, .css, etc.) require **classification before action**.
1642
+
1643
+ #### Step 1: Classify the Change Type
1644
+
1645
+ | Change Type | Examples | Action |
1646
+ |-------------|----------|--------|
1647
+ | **Visual/UI/UX** | Color, spacing, layout, typography, animation, responsive breakpoints, hover states, shadows, borders, icons, images | **DELEGATE** to \`frontend-ui-ux-engineer\` |
1648
+ | **Pure Logic** | API calls, data fetching, state management, event handlers (non-visual), type definitions, utility functions, business logic | **CAN handle directly** |
1649
+ | **Mixed** | Component changes both visual AND logic | **Split**: handle logic yourself, delegate visual to \`frontend-ui-ux-engineer\` |
1650
+
1651
+ #### Step 2: Ask Yourself
1652
+
1653
+ Before touching any frontend file, think:
1654
+ > "Is this change about **how it LOOKS** or **how it WORKS**?"
1655
+
1656
+ - **LOOKS** (colors, sizes, positions, animations) \u2192 DELEGATE
1657
+ - **WORKS** (data flow, API integration, state) \u2192 Handle directly
1658
+
1659
+ #### When in Doubt \u2192 DELEGATE if ANY of these keywords involved:
1660
+ style, className, tailwind, color, background, border, shadow, margin, padding, width, height, flex, grid, animation, transition, hover, responsive, font-size, icon, svg`;
1661
+ }
1662
+ function buildOracleSection(agents) {
1663
+ const oracleAgent = agents.find((a) => a.name === "oracle");
1664
+ if (!oracleAgent)
1665
+ return "";
1666
+ const useWhen = oracleAgent.metadata.useWhen || [];
1667
+ const avoidWhen = oracleAgent.metadata.avoidWhen || [];
1668
+ return `<Oracle_Usage>
1669
+ ## Oracle \u2014 Your Senior Engineering Advisor (GPT-5.2)
1670
+
1671
+ Oracle is an expensive, high-quality reasoning model. Use it wisely.
1672
+
1673
+ ### WHEN to Consult:
1674
+
1675
+ | Trigger | Action |
1676
+ |---------|--------|
1677
+ ${useWhen.map((w) => `| ${w} | Oracle FIRST, then implement |`).join(`
1678
+ `)}
1679
+
1680
+ ### WHEN NOT to Consult:
1681
+
1682
+ ${avoidWhen.map((w) => `- ${w}`).join(`
1683
+ `)}
1684
+
1685
+ ### Usage Pattern:
1686
+ Briefly announce "Consulting Oracle for [reason]" before invocation.
1687
+
1688
+ **Exception**: This is the ONLY case where you announce before acting. For all other work, start immediately without status updates.
1689
+ </Oracle_Usage>`;
1690
+ }
1691
+ function buildHardBlocksSection(agents) {
1692
+ const frontendAgent = agents.find((a) => a.name === "frontend-ui-ux-engineer");
1693
+ const blocks = [
1694
+ "| Type error suppression (`as any`, `@ts-ignore`) | Never |",
1695
+ "| Commit without explicit request | Never |",
1696
+ "| Speculate about unread code | Never |",
1697
+ "| Leave code in broken state after failures | Never |"
1698
+ ];
1699
+ if (frontendAgent) {
1700
+ blocks.unshift("| Frontend VISUAL changes (styling, layout, animation) | Always delegate to `frontend-ui-ux-engineer` |");
1701
+ }
1702
+ return `## Hard Blocks (NEVER violate)
1703
+
1704
+ | Constraint | No Exceptions |
1705
+ |------------|---------------|
1706
+ ${blocks.join(`
1707
+ `)}`;
1708
+ }
1709
+ function buildAntiPatternsSection(agents) {
1710
+ const frontendAgent = agents.find((a) => a.name === "frontend-ui-ux-engineer");
1711
+ const patterns = [
1712
+ "| **Type Safety** | `as any`, `@ts-ignore`, `@ts-expect-error` |",
1713
+ "| **Error Handling** | Empty catch blocks `catch(e) {}` |",
1714
+ '| **Testing** | Deleting failing tests to "pass" |',
1715
+ "| **Search** | Firing agents for single-line typos or obvious syntax errors |",
1716
+ "| **Debugging** | Shotgun debugging, random changes |"
1717
+ ];
1718
+ if (frontendAgent) {
1719
+ patterns.splice(4, 0, "| **Frontend** | Direct edit to visual/styling code (logic changes OK) |");
1720
+ }
1721
+ return `## Anti-Patterns (BLOCKING violations)
1722
+
1723
+ | Category | Forbidden |
1724
+ |----------|-----------|
1725
+ ${patterns.join(`
1726
+ `)}`;
1727
+ }
1728
+
1482
1729
  // src/agents/sisyphus.ts
1483
1730
  var DEFAULT_MODEL = "anthropic/claude-opus-4-5";
1484
- var SISYPHUS_SYSTEM_PROMPT = `<Role>
1731
+ var SISYPHUS_ROLE_SECTION = `<Role>
1485
1732
  You are "Sisyphus" - Powerful AI Agent with orchestration capabilities from OhMyOpenCode.
1486
1733
  Named by [YeonGyu Kim](https://github.com/code-yeongyu).
1487
1734
 
@@ -1499,22 +1746,26 @@ Named by [YeonGyu Kim](https://github.com/code-yeongyu).
1499
1746
 
1500
1747
  **Operating Mode**: You NEVER work alone when specialists are available. Frontend work \u2192 delegate. Deep research \u2192 parallel background agents (async subagents). Complex architecture \u2192 consult Oracle.
1501
1748
 
1502
- </Role>
1749
+ </Role>`;
1750
+ var SISYPHUS_PHASE0_STEP1_3 = `### Step 0: Check Skills FIRST (BLOCKING)
1503
1751
 
1504
- <Behavior_Instructions>
1752
+ **Before ANY classification or action, scan for matching skills.**
1505
1753
 
1506
- ## Phase 0 - Intent Gate (EVERY message)
1754
+ \`\`\`
1755
+ IF request matches a skill trigger:
1756
+ \u2192 INVOKE skill tool IMMEDIATELY
1757
+ \u2192 Do NOT proceed to Step 1 until skill is invoked
1758
+ \`\`\`
1507
1759
 
1508
- ### Key Triggers (check BEFORE classification):
1509
- - External library/source mentioned \u2192 fire \`librarian\` background
1510
- - 2+ modules involved \u2192 fire \`explore\` background
1511
- - **GitHub mention (@mention in issue/PR)** \u2192 This is a WORK REQUEST. Plan full cycle: investigate \u2192 implement \u2192 create PR
1512
- - **"Look into" + "create PR"** \u2192 Not just research. Full implementation cycle expected.
1760
+ Skills are specialized workflows. When relevant, they handle the task better than manual orchestration.
1761
+
1762
+ ---
1513
1763
 
1514
1764
  ### Step 1: Classify Request Type
1515
1765
 
1516
1766
  | Type | Signal | Action |
1517
1767
  |------|--------|--------|
1768
+ | **Skill Match** | Matches skill trigger phrase | **INVOKE skill FIRST** via \`skill\` tool |
1518
1769
  | **Trivial** | Single file, known location, direct answer | Direct tools only (UNLESS Key Trigger applies) |
1519
1770
  | **Explicit** | Specific file/line, clear command | Execute directly |
1520
1771
  | **Exploratory** | "How does X work?", "Find Y" | Fire explore (1-3) + tools in parallel |
@@ -1556,11 +1807,8 @@ Then: Raise your concern concisely. Propose an alternative. Ask if they want to
1556
1807
  I notice [observation]. This might cause [problem] because [reason].
1557
1808
  Alternative: [your suggestion].
1558
1809
  Should I proceed with your original request, or try the alternative?
1559
- \`\`\`
1560
-
1561
- ---
1562
-
1563
- ## Phase 1 - Codebase Assessment (for Open-ended tasks)
1810
+ \`\`\``;
1811
+ var SISYPHUS_PHASE1 = `## Phase 1 - Codebase Assessment (for Open-ended tasks)
1564
1812
 
1565
1813
  Before following existing patterns, assess whether they're worth following.
1566
1814
 
@@ -1581,54 +1829,8 @@ Before following existing patterns, assess whether they're worth following.
1581
1829
  IMPORTANT: If codebase appears undisciplined, verify before assuming:
1582
1830
  - Different patterns may serve different purposes (intentional)
1583
1831
  - Migration might be in progress
1584
- - You might be looking at the wrong reference files
1585
-
1586
- ---
1587
-
1588
- ## Phase 2A - Exploration & Research
1589
-
1590
- ### Tool Selection:
1591
-
1592
- | Tool | Cost | When to Use |
1593
- |------|------|-------------|
1594
- | \`grep\`, \`glob\`, \`lsp_*\`, \`ast_grep\` | FREE | Not Complex, Scope Clear, No Implicit Assumptions |
1595
- | \`explore\` agent | FREE | Multiple search angles, unfamiliar modules, cross-layer patterns |
1596
- | \`librarian\` agent | CHEAP | External docs, GitHub examples, OpenSource Implementations, OSS reference |
1597
- | \`oracle\` agent | EXPENSIVE | Architecture, review, debugging after 2+ failures |
1598
-
1599
- **Default flow**: explore/librarian (background) + tools \u2192 oracle (if required)
1600
-
1601
- ### Explore Agent = Contextual Grep
1602
-
1603
- Use it as a **peer tool**, not a fallback. Fire liberally.
1604
-
1605
- | Use Direct Tools | Use Explore Agent |
1606
- |------------------|-------------------|
1607
- | You know exactly what to search | Multiple search angles needed |
1608
- | Single keyword/pattern suffices | Unfamiliar module structure |
1609
- | Known file location | Cross-layer pattern discovery |
1610
-
1611
- ### Librarian Agent = Reference Grep
1612
-
1613
- Search **external references** (docs, OSS, web). Fire proactively when unfamiliar libraries are involved.
1614
-
1615
- | Contextual Grep (Internal) | Reference Grep (External) |
1616
- |----------------------------|---------------------------|
1617
- | Search OUR codebase | Search EXTERNAL resources |
1618
- | Find patterns in THIS repo | Find examples in OTHER repos |
1619
- | How does our code work? | How does this library work? |
1620
- | Project-specific logic | Official API documentation |
1621
- | | Library best practices & quirks |
1622
- | | OSS implementation examples |
1623
-
1624
- **Trigger phrases** (fire librarian immediately):
1625
- - "How do I use [library]?"
1626
- - "What's the best practice for [framework feature]?"
1627
- - "Why does [external dependency] behave this way?"
1628
- - "Find examples of [library] usage"
1629
- - Working with unfamiliar npm/pip/cargo packages
1630
-
1631
- ### Parallel Execution (DEFAULT behavior)
1832
+ - You might be looking at the wrong reference files`;
1833
+ var SISYPHUS_PARALLEL_EXECUTION = `### Parallel Execution (DEFAULT behavior)
1632
1834
 
1633
1835
  **Explore/Librarian = Grep, not consultants.
1634
1836
 
@@ -1660,64 +1862,14 @@ STOP searching when:
1660
1862
  - 2 search iterations yielded no new useful data
1661
1863
  - Direct answer found
1662
1864
 
1663
- **DO NOT over-explore. Time is precious.**
1664
-
1665
- ---
1666
-
1667
- ## Phase 2B - Implementation
1865
+ **DO NOT over-explore. Time is precious.**`;
1866
+ var SISYPHUS_PHASE2B_PRE_IMPLEMENTATION = `## Phase 2B - Implementation
1668
1867
 
1669
1868
  ### Pre-Implementation:
1670
1869
  1. If task has 2+ steps \u2192 Create todo list IMMEDIATELY, IN SUPER DETAIL. No announcements\u2014just create it.
1671
1870
  2. Mark current task \`in_progress\` before starting
1672
- 3. Mark \`completed\` as soon as done (don't batch) - OBSESSIVELY TRACK YOUR WORK USING TODO TOOLS
1673
-
1674
- ### Frontend Files: Decision Gate (NOT a blind block)
1675
-
1676
- Frontend files (.tsx, .jsx, .vue, .svelte, .css, etc.) require **classification before action**.
1677
-
1678
- #### Step 1: Classify the Change Type
1679
-
1680
- | Change Type | Examples | Action |
1681
- |-------------|----------|--------|
1682
- | **Visual/UI/UX** | Color, spacing, layout, typography, animation, responsive breakpoints, hover states, shadows, borders, icons, images | **DELEGATE** to \`frontend-ui-ux-engineer\` |
1683
- | **Pure Logic** | API calls, data fetching, state management, event handlers (non-visual), type definitions, utility functions, business logic | **CAN handle directly** |
1684
- | **Mixed** | Component changes both visual AND logic | **Split**: handle logic yourself, delegate visual to \`frontend-ui-ux-engineer\` |
1685
-
1686
- #### Step 2: Ask Yourself
1687
-
1688
- Before touching any frontend file, think:
1689
- > "Is this change about **how it LOOKS** or **how it WORKS**?"
1690
-
1691
- - **LOOKS** (colors, sizes, positions, animations) \u2192 DELEGATE
1692
- - **WORKS** (data flow, API integration, state) \u2192 Handle directly
1693
-
1694
- #### Quick Reference Examples
1695
-
1696
- | File | Change | Type | Action |
1697
- |------|--------|------|--------|
1698
- | \`Button.tsx\` | Change color blue\u2192green | Visual | DELEGATE |
1699
- | \`Button.tsx\` | Add onClick API call | Logic | Direct |
1700
- | \`UserList.tsx\` | Add loading spinner animation | Visual | DELEGATE |
1701
- | \`UserList.tsx\` | Fix pagination logic bug | Logic | Direct |
1702
- | \`Modal.tsx\` | Make responsive for mobile | Visual | DELEGATE |
1703
- | \`Modal.tsx\` | Add form validation logic | Logic | Direct |
1704
-
1705
- #### When in Doubt \u2192 DELEGATE if ANY of these keywords involved:
1706
- style, className, tailwind, color, background, border, shadow, margin, padding, width, height, flex, grid, animation, transition, hover, responsive, font-size, icon, svg
1707
-
1708
- ### Delegation Table:
1709
-
1710
- | Domain | Delegate To | Trigger |
1711
- |--------|-------------|---------|
1712
- | Explore | \`explore\` | Find existing codebase structure, patterns and styles |
1713
- | Frontend UI/UX | \`frontend-ui-ux-engineer\` | Visual changes only (styling, layout, animation). Pure logic changes in frontend files \u2192 handle directly |
1714
- | Librarian | \`librarian\` | Unfamiliar packages / libraries, struggles at weird behaviour (to find existing implementation of opensource) |
1715
- | Documentation | \`document-writer\` | README, API docs, guides |
1716
- | Architecture decisions | \`oracle\` | Multi-system tradeoffs, unfamiliar patterns |
1717
- | Self-review | \`oracle\` | After completing significant implementation |
1718
- | Hard debugging | \`oracle\` | After 2+ failed fix attempts |
1719
-
1720
- ### Delegation Prompt Structure (MANDATORY - ALL 7 sections):
1871
+ 3. Mark \`completed\` as soon as done (don't batch) - OBSESSIVELY TRACK YOUR WORK USING TODO TOOLS`;
1872
+ var SISYPHUS_DELEGATION_PROMPT_STRUCTURE = `### Delegation Prompt Structure (MANDATORY - ALL 7 sections):
1721
1873
 
1722
1874
  When delegating, your prompt MUST include:
1723
1875
 
@@ -1737,9 +1889,8 @@ AFTER THE WORK YOU DELEGATED SEEMS DONE, ALWAYS VERIFY THE RESULTS AS FOLLOWING:
1737
1889
  - EXPECTED RESULT CAME OUT?
1738
1890
  - DID THE AGENT FOLLOWED "MUST DO" AND "MUST NOT DO" REQUIREMENTS?
1739
1891
 
1740
- **Vague prompts = rejected. Be exhaustive.**
1741
-
1742
- ### GitHub Workflow (CRITICAL - When mentioned in issues/PRs):
1892
+ **Vague prompts = rejected. Be exhaustive.**`;
1893
+ var SISYPHUS_GITHUB_WORKFLOW = `### GitHub Workflow (CRITICAL - When mentioned in issues/PRs):
1743
1894
 
1744
1895
  When you're mentioned in GitHub issues or asked to "look into" something and "create PR":
1745
1896
 
@@ -1772,9 +1923,8 @@ When you're mentioned in GitHub issues or asked to "look into" something and "cr
1772
1923
  **EMPHASIS**: "Look into" does NOT mean "just investigate and report back."
1773
1924
  It means "investigate, understand, implement a solution, and create a PR."
1774
1925
 
1775
- **If the user says "look into X and create PR", they expect a PR, not just analysis.**
1776
-
1777
- ### Code Changes:
1926
+ **If the user says "look into X and create PR", they expect a PR, not just analysis.**`;
1927
+ var SISYPHUS_CODE_CHANGES = `### Code Changes:
1778
1928
  - Match existing patterns (if codebase is disciplined)
1779
1929
  - Propose approach first (if codebase is chaotic)
1780
1930
  - Never suppress type errors with \`as any\`, \`@ts-ignore\`, \`@ts-expect-error\`
@@ -1800,11 +1950,8 @@ If project has build/test commands, run them at task completion.
1800
1950
  | Test run | Pass (or explicit note of pre-existing failures) |
1801
1951
  | Delegation | Agent result received and verified |
1802
1952
 
1803
- **NO EVIDENCE = NOT COMPLETE.**
1804
-
1805
- ---
1806
-
1807
- ## Phase 2C - Failure Recovery
1953
+ **NO EVIDENCE = NOT COMPLETE.**`;
1954
+ var SISYPHUS_PHASE2C = `## Phase 2C - Failure Recovery
1808
1955
 
1809
1956
  ### When Fixes Fail:
1810
1957
 
@@ -1820,11 +1967,8 @@ If project has build/test commands, run them at task completion.
1820
1967
  4. **CONSULT** Oracle with full failure context
1821
1968
  5. If Oracle cannot resolve \u2192 **ASK USER** before proceeding
1822
1969
 
1823
- **Never**: Leave code in broken state, continue hoping it'll work, delete failing tests to "pass"
1824
-
1825
- ---
1826
-
1827
- ## Phase 3 - Completion
1970
+ **Never**: Leave code in broken state, continue hoping it'll work, delete failing tests to "pass"`;
1971
+ var SISYPHUS_PHASE3 = `## Phase 3 - Completion
1828
1972
 
1829
1973
  A task is complete when:
1830
1974
  - [ ] All planned todo items marked done
@@ -1839,41 +1983,8 @@ If verification fails:
1839
1983
 
1840
1984
  ### Before Delivering Final Answer:
1841
1985
  - Cancel ALL running background tasks: \`background_cancel(all=true)\`
1842
- - This conserves resources and ensures clean workflow completion
1843
-
1844
- </Behavior_Instructions>
1845
-
1846
- <Oracle_Usage>
1847
- ## Oracle \u2014 Your Senior Engineering Advisor (GPT-5.2)
1848
-
1849
- Oracle is an expensive, high-quality reasoning model. Use it wisely.
1850
-
1851
- ### WHEN to Consult:
1852
-
1853
- | Trigger | Action |
1854
- |---------|--------|
1855
- | Complex architecture design | Oracle FIRST, then implement |
1856
- | After completing significant work | Oracle review before marking complete |
1857
- | 2+ failed fix attempts | Oracle for debugging guidance |
1858
- | Unfamiliar code patterns | Oracle to explain behavior |
1859
- | Security/performance concerns | Oracle for analysis |
1860
- | Multi-system tradeoffs | Oracle for architectural decision |
1861
-
1862
- ### WHEN NOT to Consult:
1863
-
1864
- - Simple file operations (use direct tools)
1865
- - First attempt at any fix (try yourself first)
1866
- - Questions answerable from code you've read
1867
- - Trivial decisions (variable names, formatting)
1868
- - Things you can infer from existing code patterns
1869
-
1870
- ### Usage Pattern:
1871
- Briefly announce "Consulting Oracle for [reason]" before invocation.
1872
-
1873
- **Exception**: This is the ONLY case where you announce before acting. For all other work, start immediately without status updates.
1874
- </Oracle_Usage>
1875
-
1876
- <Task_Management>
1986
+ - This conserves resources and ensures clean workflow completion`;
1987
+ var SISYPHUS_TASK_MANAGEMENT = `<Task_Management>
1877
1988
  ## Todo Management (CRITICAL)
1878
1989
 
1879
1990
  **DEFAULT BEHAVIOR**: Create todos BEFORE starting any non-trivial task. This is your PRIMARY coordination mechanism.
@@ -1928,9 +2039,8 @@ I want to make sure I understand correctly.
1928
2039
 
1929
2040
  Should I proceed with [recommendation], or would you prefer differently?
1930
2041
  \`\`\`
1931
- </Task_Management>
1932
-
1933
- <Tone_and_Style>
2042
+ </Task_Management>`;
2043
+ var SISYPHUS_TONE_AND_STYLE = `<Tone_and_Style>
1934
2044
  ## Communication Style
1935
2045
 
1936
2046
  ### Be Concise
@@ -1970,31 +2080,8 @@ If the user's approach seems problematic:
1970
2080
  - If user is terse, be terse
1971
2081
  - If user wants detail, provide detail
1972
2082
  - Adapt to their communication preference
1973
- </Tone_and_Style>
1974
-
1975
- <Constraints>
1976
- ## Hard Blocks (NEVER violate)
1977
-
1978
- | Constraint | No Exceptions |
1979
- |------------|---------------|
1980
- | Frontend VISUAL changes (styling, layout, animation) | Always delegate to \`frontend-ui-ux-engineer\` |
1981
- | Type error suppression (\`as any\`, \`@ts-ignore\`) | Never |
1982
- | Commit without explicit request | Never |
1983
- | Speculate about unread code | Never |
1984
- | Leave code in broken state after failures | Never |
1985
-
1986
- ## Anti-Patterns (BLOCKING violations)
1987
-
1988
- | Category | Forbidden |
1989
- |----------|-----------|
1990
- | **Type Safety** | \`as any\`, \`@ts-ignore\`, \`@ts-expect-error\` |
1991
- | **Error Handling** | Empty catch blocks \`catch(e) {}\` |
1992
- | **Testing** | Deleting failing tests to "pass" |
1993
- | **Search** | Firing agents for single-line typos or obvious syntax errors |
1994
- | **Frontend** | Direct edit to visual/styling code (logic changes OK) |
1995
- | **Debugging** | Shotgun debugging, random changes |
1996
-
1997
- ## Soft Guidelines
2083
+ </Tone_and_Style>`;
2084
+ var SISYPHUS_SOFT_GUIDELINES = `## Soft Guidelines
1998
2085
 
1999
2086
  - Prefer existing libraries over new dependencies
2000
2087
  - Prefer small, focused changes over large refactors
@@ -2002,13 +2089,92 @@ If the user's approach seems problematic:
2002
2089
  </Constraints>
2003
2090
 
2004
2091
  `;
2005
- function createSisyphusAgent(model = DEFAULT_MODEL) {
2092
+ function buildDynamicSisyphusPrompt(availableAgents, availableTools = [], availableSkills = []) {
2093
+ const keyTriggers = buildKeyTriggersSection(availableAgents, availableSkills);
2094
+ const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills);
2095
+ const exploreSection = buildExploreSection(availableAgents);
2096
+ const librarianSection = buildLibrarianSection(availableAgents);
2097
+ const frontendSection = buildFrontendSection(availableAgents);
2098
+ const delegationTable = buildDelegationTable(availableAgents);
2099
+ const oracleSection = buildOracleSection(availableAgents);
2100
+ const hardBlocks = buildHardBlocksSection(availableAgents);
2101
+ const antiPatterns = buildAntiPatternsSection(availableAgents);
2102
+ const sections = [
2103
+ SISYPHUS_ROLE_SECTION,
2104
+ "<Behavior_Instructions>",
2105
+ "",
2106
+ "## Phase 0 - Intent Gate (EVERY message)",
2107
+ "",
2108
+ keyTriggers,
2109
+ "",
2110
+ SISYPHUS_PHASE0_STEP1_3,
2111
+ "",
2112
+ "---",
2113
+ "",
2114
+ SISYPHUS_PHASE1,
2115
+ "",
2116
+ "---",
2117
+ "",
2118
+ "## Phase 2A - Exploration & Research",
2119
+ "",
2120
+ toolSelection,
2121
+ "",
2122
+ exploreSection,
2123
+ "",
2124
+ librarianSection,
2125
+ "",
2126
+ SISYPHUS_PARALLEL_EXECUTION,
2127
+ "",
2128
+ "---",
2129
+ "",
2130
+ SISYPHUS_PHASE2B_PRE_IMPLEMENTATION,
2131
+ "",
2132
+ frontendSection,
2133
+ "",
2134
+ delegationTable,
2135
+ "",
2136
+ SISYPHUS_DELEGATION_PROMPT_STRUCTURE,
2137
+ "",
2138
+ SISYPHUS_GITHUB_WORKFLOW,
2139
+ "",
2140
+ SISYPHUS_CODE_CHANGES,
2141
+ "",
2142
+ "---",
2143
+ "",
2144
+ SISYPHUS_PHASE2C,
2145
+ "",
2146
+ "---",
2147
+ "",
2148
+ SISYPHUS_PHASE3,
2149
+ "",
2150
+ "</Behavior_Instructions>",
2151
+ "",
2152
+ oracleSection,
2153
+ "",
2154
+ SISYPHUS_TASK_MANAGEMENT,
2155
+ "",
2156
+ SISYPHUS_TONE_AND_STYLE,
2157
+ "",
2158
+ "<Constraints>",
2159
+ hardBlocks,
2160
+ "",
2161
+ antiPatterns,
2162
+ "",
2163
+ SISYPHUS_SOFT_GUIDELINES
2164
+ ];
2165
+ return sections.filter((s) => s !== "").join(`
2166
+ `);
2167
+ }
2168
+ function createSisyphusAgent(model = DEFAULT_MODEL, availableAgents, availableToolNames, availableSkills) {
2169
+ const tools = availableToolNames ? categorizeTools(availableToolNames) : [];
2170
+ const skills = availableSkills ?? [];
2171
+ const prompt = availableAgents ? buildDynamicSisyphusPrompt(availableAgents, tools, skills) : buildDynamicSisyphusPrompt([], tools, skills);
2006
2172
  const base = {
2007
2173
  description: "Sisyphus - Powerful AI orchestrator from OhMyOpenCode. Plans obsessively with todos, assesses search complexity before exploration, delegates strategically to specialized agents. Uses explore for internal code (parallel-friendly), librarian only for external docs, and always delegates UI work to frontend engineer.",
2008
2174
  mode: "primary",
2009
2175
  model,
2010
2176
  maxTokens: 64000,
2011
- prompt: SISYPHUS_SYSTEM_PROMPT,
2177
+ prompt,
2012
2178
  color: "#00CED1"
2013
2179
  };
2014
2180
  if (isGptModel(model)) {
@@ -4170,6 +4336,82 @@ function detectConfigFile(basePath) {
4170
4336
  }
4171
4337
  return { format: "none", path: jsonPath };
4172
4338
  }
4339
+ // src/shared/migration.ts
4340
+ import * as fs3 from "fs";
4341
+ var AGENT_NAME_MAP = {
4342
+ omo: "Sisyphus",
4343
+ OmO: "Sisyphus",
4344
+ "OmO-Plan": "Planner-Sisyphus",
4345
+ "omo-plan": "Planner-Sisyphus",
4346
+ sisyphus: "Sisyphus",
4347
+ "planner-sisyphus": "Planner-Sisyphus",
4348
+ build: "build",
4349
+ oracle: "oracle",
4350
+ librarian: "librarian",
4351
+ explore: "explore",
4352
+ "frontend-ui-ux-engineer": "frontend-ui-ux-engineer",
4353
+ "document-writer": "document-writer",
4354
+ "multimodal-looker": "multimodal-looker"
4355
+ };
4356
+ var HOOK_NAME_MAP = {
4357
+ "anthropic-auto-compact": "anthropic-context-window-limit-recovery"
4358
+ };
4359
+ function migrateAgentNames(agents) {
4360
+ const migrated = {};
4361
+ let changed = false;
4362
+ for (const [key, value] of Object.entries(agents)) {
4363
+ const newKey = AGENT_NAME_MAP[key.toLowerCase()] ?? AGENT_NAME_MAP[key] ?? key;
4364
+ if (newKey !== key) {
4365
+ changed = true;
4366
+ }
4367
+ migrated[newKey] = value;
4368
+ }
4369
+ return { migrated, changed };
4370
+ }
4371
+ function migrateHookNames(hooks) {
4372
+ const migrated = [];
4373
+ let changed = false;
4374
+ for (const hook of hooks) {
4375
+ const newHook = HOOK_NAME_MAP[hook] ?? hook;
4376
+ if (newHook !== hook) {
4377
+ changed = true;
4378
+ }
4379
+ migrated.push(newHook);
4380
+ }
4381
+ return { migrated, changed };
4382
+ }
4383
+ function migrateConfigFile(configPath, rawConfig) {
4384
+ let needsWrite = false;
4385
+ if (rawConfig.agents && typeof rawConfig.agents === "object") {
4386
+ const { migrated, changed } = migrateAgentNames(rawConfig.agents);
4387
+ if (changed) {
4388
+ rawConfig.agents = migrated;
4389
+ needsWrite = true;
4390
+ }
4391
+ }
4392
+ if (rawConfig.omo_agent) {
4393
+ rawConfig.sisyphus_agent = rawConfig.omo_agent;
4394
+ delete rawConfig.omo_agent;
4395
+ needsWrite = true;
4396
+ }
4397
+ if (rawConfig.disabled_hooks && Array.isArray(rawConfig.disabled_hooks)) {
4398
+ const { migrated, changed } = migrateHookNames(rawConfig.disabled_hooks);
4399
+ if (changed) {
4400
+ rawConfig.disabled_hooks = migrated;
4401
+ needsWrite = true;
4402
+ }
4403
+ }
4404
+ if (needsWrite) {
4405
+ try {
4406
+ fs3.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + `
4407
+ `, "utf-8");
4408
+ log(`Migrated config file: ${configPath}`);
4409
+ } catch (err) {
4410
+ log(`Failed to write migrated config to ${configPath}:`, err);
4411
+ }
4412
+ }
4413
+ return needsWrite;
4414
+ }
4173
4415
  // src/agents/utils.ts
4174
4416
  var agentSources = {
4175
4417
  Sisyphus: createSisyphusAgent,
@@ -4258,7 +4500,7 @@ function getMainSessionID() {
4258
4500
  return mainSessionID;
4259
4501
  }
4260
4502
  // src/features/hook-message-injector/injector.ts
4261
- import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync3, readdirSync, writeFileSync } from "fs";
4503
+ import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync3, readdirSync, writeFileSync as writeFileSync2 } from "fs";
4262
4504
  import { join as join7 } from "path";
4263
4505
 
4264
4506
  // src/features/hook-message-injector/constants.ts
@@ -4360,12 +4602,12 @@ function injectHookMessage(sessionID, hookContent, originalMessage) {
4360
4602
  sessionID
4361
4603
  };
4362
4604
  try {
4363
- writeFileSync(join7(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
4605
+ writeFileSync2(join7(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
4364
4606
  const partDir = join7(PART_STORAGE, messageID);
4365
4607
  if (!existsSync5(partDir)) {
4366
4608
  mkdirSync(partDir, { recursive: true });
4367
4609
  }
4368
- writeFileSync(join7(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
4610
+ writeFileSync2(join7(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
4369
4611
  return true;
4370
4612
  } catch {
4371
4613
  return false;
@@ -5097,7 +5339,7 @@ function createSessionNotification(ctx, config = {}) {
5097
5339
  };
5098
5340
  }
5099
5341
  // src/hooks/session-recovery/storage.ts
5100
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
5342
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
5101
5343
  import { join as join10 } from "path";
5102
5344
 
5103
5345
  // src/hooks/session-recovery/constants.ts
@@ -5206,7 +5448,7 @@ function injectTextPart(sessionID, messageID, text) {
5206
5448
  synthetic: true
5207
5449
  };
5208
5450
  try {
5209
- writeFileSync2(join10(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
5451
+ writeFileSync3(join10(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
5210
5452
  return true;
5211
5453
  } catch {
5212
5454
  return false;
@@ -5316,7 +5558,7 @@ function prependThinkingPart(sessionID, messageID) {
5316
5558
  synthetic: true
5317
5559
  };
5318
5560
  try {
5319
- writeFileSync2(join10(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
5561
+ writeFileSync3(join10(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
5320
5562
  return true;
5321
5563
  } catch {
5322
5564
  return false;
@@ -5361,7 +5603,7 @@ function replaceEmptyTextParts(messageID, replacementText) {
5361
5603
  if (!textPart.text?.trim()) {
5362
5604
  textPart.text = replacementText;
5363
5605
  textPart.synthetic = true;
5364
- writeFileSync2(filePath, JSON.stringify(textPart, null, 2));
5606
+ writeFileSync3(filePath, JSON.stringify(textPart, null, 2));
5365
5607
  anyReplaced = true;
5366
5608
  }
5367
5609
  }
@@ -5632,7 +5874,7 @@ var {spawn: spawn4 } = globalThis.Bun;
5632
5874
  import { createRequire as createRequire2 } from "module";
5633
5875
  import { dirname, join as join12 } from "path";
5634
5876
  import { existsSync as existsSync9 } from "fs";
5635
- import * as fs3 from "fs";
5877
+ import * as fs4 from "fs";
5636
5878
  import { tmpdir as tmpdir3 } from "os";
5637
5879
 
5638
5880
  // src/hooks/comment-checker/downloader.ts
@@ -5780,7 +6022,7 @@ function debugLog2(...args) {
5780
6022
  if (DEBUG2) {
5781
6023
  const msg = `[${new Date().toISOString()}] [comment-checker:cli] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
5782
6024
  `;
5783
- fs3.appendFileSync(DEBUG_FILE2, msg);
6025
+ fs4.appendFileSync(DEBUG_FILE2, msg);
5784
6026
  }
5785
6027
  }
5786
6028
  function getBinaryName2() {
@@ -5894,7 +6136,7 @@ async function runCommentChecker(input, cliPath, customPrompt) {
5894
6136
  }
5895
6137
 
5896
6138
  // src/hooks/comment-checker/index.ts
5897
- import * as fs4 from "fs";
6139
+ import * as fs5 from "fs";
5898
6140
  import { existsSync as existsSync10 } from "fs";
5899
6141
  import { tmpdir as tmpdir4 } from "os";
5900
6142
  import { join as join13 } from "path";
@@ -5904,7 +6146,7 @@ function debugLog3(...args) {
5904
6146
  if (DEBUG3) {
5905
6147
  const msg = `[${new Date().toISOString()}] [comment-checker:hook] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
5906
6148
  `;
5907
- fs4.appendFileSync(DEBUG_FILE3, msg);
6149
+ fs5.appendFileSync(DEBUG_FILE3, msg);
5908
6150
  }
5909
6151
  }
5910
6152
  var pendingCalls = new Map;
@@ -6059,7 +6301,7 @@ import {
6059
6301
  existsSync as existsSync11,
6060
6302
  mkdirSync as mkdirSync4,
6061
6303
  readFileSync as readFileSync5,
6062
- writeFileSync as writeFileSync3,
6304
+ writeFileSync as writeFileSync4,
6063
6305
  unlinkSync as unlinkSync3
6064
6306
  } from "fs";
6065
6307
  import { join as join15 } from "path";
@@ -6095,7 +6337,7 @@ function saveInjectedPaths(sessionID, paths) {
6095
6337
  injectedPaths: [...paths],
6096
6338
  updatedAt: Date.now()
6097
6339
  };
6098
- writeFileSync3(getStoragePath(sessionID), JSON.stringify(data, null, 2));
6340
+ writeFileSync4(getStoragePath(sessionID), JSON.stringify(data, null, 2));
6099
6341
  }
6100
6342
  function clearInjectedPaths(sessionID) {
6101
6343
  const filePath = getStoragePath(sessionID);
@@ -6231,7 +6473,7 @@ import {
6231
6473
  existsSync as existsSync13,
6232
6474
  mkdirSync as mkdirSync5,
6233
6475
  readFileSync as readFileSync7,
6234
- writeFileSync as writeFileSync4,
6476
+ writeFileSync as writeFileSync5,
6235
6477
  unlinkSync as unlinkSync4
6236
6478
  } from "fs";
6237
6479
  import { join as join18 } from "path";
@@ -6267,7 +6509,7 @@ function saveInjectedPaths2(sessionID, paths) {
6267
6509
  injectedPaths: [...paths],
6268
6510
  updatedAt: Date.now()
6269
6511
  };
6270
- writeFileSync4(getStoragePath2(sessionID), JSON.stringify(data, null, 2));
6512
+ writeFileSync5(getStoragePath2(sessionID), JSON.stringify(data, null, 2));
6271
6513
  }
6272
6514
  function clearInjectedPaths2(sessionID) {
6273
6515
  const filePath = getStoragePath2(sessionID);
@@ -6587,10 +6829,6 @@ var RETRY_CONFIG = {
6587
6829
  backoffFactor: 2,
6588
6830
  maxDelayMs: 30000
6589
6831
  };
6590
- var FALLBACK_CONFIG = {
6591
- maxRevertAttempts: 3,
6592
- minMessagesRequired: 2
6593
- };
6594
6832
  var TRUNCATE_CONFIG = {
6595
6833
  maxTruncateAttempts: 20,
6596
6834
  minOutputSizeToTruncate: 500,
@@ -7011,7 +7249,7 @@ function executePurgeErrors(sessionID, state2, config, protectedTools) {
7011
7249
  }
7012
7250
 
7013
7251
  // src/hooks/anthropic-context-window-limit-recovery/pruning-storage.ts
7014
- import { existsSync as existsSync18, readdirSync as readdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync5 } from "fs";
7252
+ import { existsSync as existsSync18, readdirSync as readdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
7015
7253
  import { join as join23 } from "path";
7016
7254
  function getMessageDir6(sessionID) {
7017
7255
  if (!existsSync18(MESSAGE_STORAGE))
@@ -7061,7 +7299,7 @@ async function applyPruning(sessionID, state2) {
7061
7299
  }
7062
7300
  }
7063
7301
  if (modified) {
7064
- writeFileSync5(filePath, JSON.stringify(data, null, 2), "utf-8");
7302
+ writeFileSync6(filePath, JSON.stringify(data, null, 2), "utf-8");
7065
7303
  filesModified++;
7066
7304
  }
7067
7305
  }
@@ -7161,7 +7399,7 @@ async function executeDynamicContextPruning(sessionID, config, client) {
7161
7399
  }
7162
7400
 
7163
7401
  // src/hooks/anthropic-context-window-limit-recovery/storage.ts
7164
- import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "fs";
7402
+ import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
7165
7403
  import { join as join24 } from "path";
7166
7404
  var OPENCODE_STORAGE5 = getOpenCodeStorageDir();
7167
7405
  var MESSAGE_STORAGE3 = join24(OPENCODE_STORAGE5, "message");
@@ -7245,7 +7483,7 @@ function truncateToolResult(partPath) {
7245
7483
  part.state.time = { start: Date.now() };
7246
7484
  }
7247
7485
  part.state.time.compacted = Date.now();
7248
- writeFileSync6(partPath, JSON.stringify(part, null, 2));
7486
+ writeFileSync7(partPath, JSON.stringify(part, null, 2));
7249
7487
  return { success: true, toolName, originalSize };
7250
7488
  } catch {
7251
7489
  return { success: false };
@@ -7312,14 +7550,6 @@ function getOrCreateRetryState(autoCompactState, sessionID) {
7312
7550
  }
7313
7551
  return state2;
7314
7552
  }
7315
- function getOrCreateFallbackState(autoCompactState, sessionID) {
7316
- let state2 = autoCompactState.fallbackStateBySession.get(sessionID);
7317
- if (!state2) {
7318
- state2 = { revertAttempt: 0 };
7319
- autoCompactState.fallbackStateBySession.set(sessionID, state2);
7320
- }
7321
- return state2;
7322
- }
7323
7553
  function getOrCreateTruncateState(autoCompactState, sessionID) {
7324
7554
  let state2 = autoCompactState.truncateStateBySession.get(sessionID);
7325
7555
  if (!state2) {
@@ -7362,43 +7592,6 @@ function sanitizeEmptyMessagesBeforeSummarize(sessionID) {
7362
7592
  }
7363
7593
  return fixedCount;
7364
7594
  }
7365
- async function getLastMessagePair(sessionID, client, directory) {
7366
- try {
7367
- const resp = await client.session.messages({
7368
- path: { id: sessionID },
7369
- query: { directory }
7370
- });
7371
- const data = resp.data;
7372
- if (!Array.isArray(data) || data.length < FALLBACK_CONFIG.minMessagesRequired) {
7373
- return null;
7374
- }
7375
- const reversed = [...data].reverse();
7376
- const lastAssistant = reversed.find((m) => {
7377
- const msg = m;
7378
- const info = msg.info;
7379
- return info?.role === "assistant";
7380
- });
7381
- const lastUser = reversed.find((m) => {
7382
- const msg = m;
7383
- const info = msg.info;
7384
- return info?.role === "user";
7385
- });
7386
- if (!lastUser)
7387
- return null;
7388
- const userInfo = lastUser.info;
7389
- const userMessageID = userInfo?.id;
7390
- if (!userMessageID)
7391
- return null;
7392
- let assistantMessageID;
7393
- if (lastAssistant) {
7394
- const assistantInfo = lastAssistant.info;
7395
- assistantMessageID = assistantInfo?.id;
7396
- }
7397
- return { userMessageID, assistantMessageID };
7398
- } catch {
7399
- return null;
7400
- }
7401
- }
7402
7595
  function formatBytes(bytes) {
7403
7596
  if (bytes < 1024)
7404
7597
  return `${bytes}B`;
@@ -7432,7 +7625,6 @@ function clearSessionState(autoCompactState, sessionID) {
7432
7625
  autoCompactState.pendingCompact.delete(sessionID);
7433
7626
  autoCompactState.errorDataBySession.delete(sessionID);
7434
7627
  autoCompactState.retryStateBySession.delete(sessionID);
7435
- autoCompactState.fallbackStateBySession.delete(sessionID);
7436
7628
  autoCompactState.truncateStateBySession.delete(sessionID);
7437
7629
  autoCompactState.dcpStateBySession.delete(sessionID);
7438
7630
  autoCompactState.emptyContentAttemptBySession.delete(sessionID);
@@ -7704,7 +7896,6 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
7704
7896
  }
7705
7897
  if (Date.now() - retryState.lastAttemptTime > 300000) {
7706
7898
  retryState.attempt = 0;
7707
- autoCompactState.fallbackStateBySession.delete(sessionID);
7708
7899
  autoCompactState.truncateStateBySession.delete(sessionID);
7709
7900
  }
7710
7901
  if (!skipSummarize && retryState.attempt < RETRY_CONFIG.maxAttempts) {
@@ -7750,57 +7941,7 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
7750
7941
  await client.tui.showToast({
7751
7942
  body: {
7752
7943
  title: "Summarize Skipped",
7753
- message: "Missing providerID or modelID. Skipping to revert...",
7754
- variant: "warning",
7755
- duration: 3000
7756
- }
7757
- }).catch(() => {});
7758
- }
7759
- }
7760
- const fallbackState = getOrCreateFallbackState(autoCompactState, sessionID);
7761
- if (fallbackState.revertAttempt < FALLBACK_CONFIG.maxRevertAttempts) {
7762
- const pair = await getLastMessagePair(sessionID, client, directory);
7763
- if (pair) {
7764
- try {
7765
- await client.tui.showToast({
7766
- body: {
7767
- title: "Emergency Recovery",
7768
- message: "Removing last message pair...",
7769
- variant: "warning",
7770
- duration: 3000
7771
- }
7772
- }).catch(() => {});
7773
- if (pair.assistantMessageID) {
7774
- await client.session.revert({
7775
- path: { id: sessionID },
7776
- body: { messageID: pair.assistantMessageID },
7777
- query: { directory }
7778
- });
7779
- }
7780
- await client.session.revert({
7781
- path: { id: sessionID },
7782
- body: { messageID: pair.userMessageID },
7783
- query: { directory }
7784
- });
7785
- fallbackState.revertAttempt++;
7786
- fallbackState.lastRevertedMessageID = pair.userMessageID;
7787
- clearSessionState(autoCompactState, sessionID);
7788
- setTimeout(async () => {
7789
- try {
7790
- await client.session.prompt_async({
7791
- path: { sessionID },
7792
- body: { parts: [{ type: "text", text: "Continue" }] },
7793
- query: { directory }
7794
- });
7795
- } catch {}
7796
- }, 500);
7797
- return;
7798
- } catch {}
7799
- } else {
7800
- await client.tui.showToast({
7801
- body: {
7802
- title: "Revert Skipped",
7803
- message: "Could not find last message pair to revert.",
7944
+ message: "Missing providerID or modelID.",
7804
7945
  variant: "warning",
7805
7946
  duration: 3000
7806
7947
  }
@@ -7827,7 +7968,6 @@ function createRecoveryState() {
7827
7968
  pendingCompact: new Set,
7828
7969
  errorDataBySession: new Map,
7829
7970
  retryStateBySession: new Map,
7830
- fallbackStateBySession: new Map,
7831
7971
  truncateStateBySession: new Map,
7832
7972
  dcpStateBySession: new Map,
7833
7973
  emptyContentAttemptBySession: new Map,
@@ -7846,7 +7986,6 @@ function createAnthropicContextWindowLimitRecoveryHook(ctx, options) {
7846
7986
  autoCompactState.pendingCompact.delete(sessionInfo.id);
7847
7987
  autoCompactState.errorDataBySession.delete(sessionInfo.id);
7848
7988
  autoCompactState.retryStateBySession.delete(sessionInfo.id);
7849
- autoCompactState.fallbackStateBySession.delete(sessionInfo.id);
7850
7989
  autoCompactState.truncateStateBySession.delete(sessionInfo.id);
7851
7990
  autoCompactState.dcpStateBySession.delete(sessionInfo.id);
7852
7991
  autoCompactState.emptyContentAttemptBySession.delete(sessionInfo.id);
@@ -7967,9 +8106,9 @@ function createPreemptiveCompactionHook(ctx, options) {
7967
8106
  const experimental = options?.experimental;
7968
8107
  const onBeforeSummarize = options?.onBeforeSummarize;
7969
8108
  const getModelLimit = options?.getModelLimit;
7970
- const enabled = experimental?.preemptive_compaction === true;
8109
+ const explicitlyDisabled = experimental?.preemptive_compaction === false;
7971
8110
  const threshold = experimental?.preemptive_compaction_threshold ?? DEFAULT_THRESHOLD;
7972
- if (!enabled) {
8111
+ if (explicitlyDisabled) {
7973
8112
  return { event: async () => {} };
7974
8113
  }
7975
8114
  const state2 = createState();
@@ -8711,7 +8850,7 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
8711
8850
 
8712
8851
  // src/hooks/claude-code-hooks/transcript.ts
8713
8852
  import { join as join28 } from "path";
8714
- import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync23, writeFileSync as writeFileSync7, unlinkSync as unlinkSync5 } from "fs";
8853
+ import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync23, writeFileSync as writeFileSync8, unlinkSync as unlinkSync5 } from "fs";
8715
8854
  import { tmpdir as tmpdir5 } from "os";
8716
8855
  import { randomUUID } from "crypto";
8717
8856
  var TRANSCRIPT_DIR = join28(getClaudeConfigDir(), "transcripts");
@@ -8807,7 +8946,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
8807
8946
  };
8808
8947
  entries.push(JSON.stringify(currentEntry));
8809
8948
  const tempPath = join28(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
8810
- writeFileSync7(tempPath, entries.join(`
8949
+ writeFileSync8(tempPath, entries.join(`
8811
8950
  `) + `
8812
8951
  `);
8813
8952
  return tempPath;
@@ -8827,7 +8966,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
8827
8966
  }
8828
8967
  };
8829
8968
  const tempPath = join28(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
8830
- writeFileSync7(tempPath, JSON.stringify(currentEntry) + `
8969
+ writeFileSync8(tempPath, JSON.stringify(currentEntry) + `
8831
8970
  `);
8832
8971
  return tempPath;
8833
8972
  } catch {
@@ -9768,7 +9907,7 @@ import {
9768
9907
  existsSync as existsSync25,
9769
9908
  mkdirSync as mkdirSync7,
9770
9909
  readFileSync as readFileSync14,
9771
- writeFileSync as writeFileSync8,
9910
+ writeFileSync as writeFileSync9,
9772
9911
  unlinkSync as unlinkSync6
9773
9912
  } from "fs";
9774
9913
  import { join as join32 } from "path";
@@ -9800,7 +9939,7 @@ function saveInjectedRules(sessionID, data) {
9800
9939
  injectedRealPaths: [...data.realPaths],
9801
9940
  updatedAt: Date.now()
9802
9941
  };
9803
- writeFileSync8(getStoragePath3(sessionID), JSON.stringify(storageData, null, 2));
9942
+ writeFileSync9(getStoragePath3(sessionID), JSON.stringify(storageData, null, 2));
9804
9943
  }
9805
9944
  function clearInjectedRules(sessionID) {
9806
9945
  const filePath = getStoragePath3(sessionID);
@@ -9948,7 +10087,7 @@ function createBackgroundNotificationHook(manager) {
9948
10087
  };
9949
10088
  }
9950
10089
  // src/hooks/auto-update-checker/checker.ts
9951
- import * as fs5 from "fs";
10090
+ import * as fs6 from "fs";
9952
10091
  import * as path5 from "path";
9953
10092
  import { fileURLToPath } from "url";
9954
10093
 
@@ -9992,9 +10131,9 @@ function getConfigPaths(directory) {
9992
10131
  function getLocalDevPath(directory) {
9993
10132
  for (const configPath of getConfigPaths(directory)) {
9994
10133
  try {
9995
- if (!fs5.existsSync(configPath))
10134
+ if (!fs6.existsSync(configPath))
9996
10135
  continue;
9997
- const content = fs5.readFileSync(configPath, "utf-8");
10136
+ const content = fs6.readFileSync(configPath, "utf-8");
9998
10137
  const config = JSON.parse(stripJsonComments(content));
9999
10138
  const plugins = config.plugin ?? [];
10000
10139
  for (const entry of plugins) {
@@ -10014,13 +10153,13 @@ function getLocalDevPath(directory) {
10014
10153
  }
10015
10154
  function findPackageJsonUp(startPath) {
10016
10155
  try {
10017
- const stat = fs5.statSync(startPath);
10156
+ const stat = fs6.statSync(startPath);
10018
10157
  let dir = stat.isDirectory() ? startPath : path5.dirname(startPath);
10019
10158
  for (let i = 0;i < 10; i++) {
10020
10159
  const pkgPath = path5.join(dir, "package.json");
10021
- if (fs5.existsSync(pkgPath)) {
10160
+ if (fs6.existsSync(pkgPath)) {
10022
10161
  try {
10023
- const content = fs5.readFileSync(pkgPath, "utf-8");
10162
+ const content = fs6.readFileSync(pkgPath, "utf-8");
10024
10163
  const pkg = JSON.parse(content);
10025
10164
  if (pkg.name === PACKAGE_NAME)
10026
10165
  return pkgPath;
@@ -10042,7 +10181,7 @@ function getLocalDevVersion(directory) {
10042
10181
  const pkgPath = findPackageJsonUp(localPath);
10043
10182
  if (!pkgPath)
10044
10183
  return null;
10045
- const content = fs5.readFileSync(pkgPath, "utf-8");
10184
+ const content = fs6.readFileSync(pkgPath, "utf-8");
10046
10185
  const pkg = JSON.parse(content);
10047
10186
  return pkg.version ?? null;
10048
10187
  } catch {
@@ -10052,9 +10191,9 @@ function getLocalDevVersion(directory) {
10052
10191
  function findPluginEntry(directory) {
10053
10192
  for (const configPath of getConfigPaths(directory)) {
10054
10193
  try {
10055
- if (!fs5.existsSync(configPath))
10194
+ if (!fs6.existsSync(configPath))
10056
10195
  continue;
10057
- const content = fs5.readFileSync(configPath, "utf-8");
10196
+ const content = fs6.readFileSync(configPath, "utf-8");
10058
10197
  const config = JSON.parse(stripJsonComments(content));
10059
10198
  const plugins = config.plugin ?? [];
10060
10199
  for (const entry of plugins) {
@@ -10075,8 +10214,8 @@ function findPluginEntry(directory) {
10075
10214
  }
10076
10215
  function getCachedVersion() {
10077
10216
  try {
10078
- if (fs5.existsSync(INSTALLED_PACKAGE_JSON)) {
10079
- const content = fs5.readFileSync(INSTALLED_PACKAGE_JSON, "utf-8");
10217
+ if (fs6.existsSync(INSTALLED_PACKAGE_JSON)) {
10218
+ const content = fs6.readFileSync(INSTALLED_PACKAGE_JSON, "utf-8");
10080
10219
  const pkg = JSON.parse(content);
10081
10220
  if (pkg.version)
10082
10221
  return pkg.version;
@@ -10086,7 +10225,7 @@ function getCachedVersion() {
10086
10225
  const currentDir = path5.dirname(fileURLToPath(import.meta.url));
10087
10226
  const pkgPath = findPackageJsonUp(currentDir);
10088
10227
  if (pkgPath) {
10089
- const content = fs5.readFileSync(pkgPath, "utf-8");
10228
+ const content = fs6.readFileSync(pkgPath, "utf-8");
10090
10229
  const pkg = JSON.parse(content);
10091
10230
  if (pkg.version)
10092
10231
  return pkg.version;
@@ -10098,7 +10237,7 @@ function getCachedVersion() {
10098
10237
  }
10099
10238
  function updatePinnedVersion(configPath, oldEntry, newVersion) {
10100
10239
  try {
10101
- const content = fs5.readFileSync(configPath, "utf-8");
10240
+ const content = fs6.readFileSync(configPath, "utf-8");
10102
10241
  const newEntry = `${PACKAGE_NAME}@${newVersion}`;
10103
10242
  const pluginMatch = content.match(/"plugin"\s*:\s*\[/);
10104
10243
  if (!pluginMatch || pluginMatch.index === undefined) {
@@ -10130,7 +10269,7 @@ function updatePinnedVersion(configPath, oldEntry, newVersion) {
10130
10269
  log(`[auto-update-checker] No changes made to ${configPath}`);
10131
10270
  return false;
10132
10271
  }
10133
- fs5.writeFileSync(configPath, updatedContent, "utf-8");
10272
+ fs6.writeFileSync(configPath, updatedContent, "utf-8");
10134
10273
  log(`[auto-update-checker] Updated ${configPath}: ${oldEntry} \u2192 ${newEntry}`);
10135
10274
  return true;
10136
10275
  } catch (err) {
@@ -10158,17 +10297,17 @@ async function getLatestVersion() {
10158
10297
  }
10159
10298
 
10160
10299
  // src/hooks/auto-update-checker/cache.ts
10161
- import * as fs6 from "fs";
10300
+ import * as fs7 from "fs";
10162
10301
  import * as path6 from "path";
10163
10302
  function stripTrailingCommas(json) {
10164
10303
  return json.replace(/,(\s*[}\]])/g, "$1");
10165
10304
  }
10166
10305
  function removeFromBunLock(packageName) {
10167
10306
  const lockPath = path6.join(CACHE_DIR, "bun.lock");
10168
- if (!fs6.existsSync(lockPath))
10307
+ if (!fs7.existsSync(lockPath))
10169
10308
  return false;
10170
10309
  try {
10171
- const content = fs6.readFileSync(lockPath, "utf-8");
10310
+ const content = fs7.readFileSync(lockPath, "utf-8");
10172
10311
  const lock = JSON.parse(stripTrailingCommas(content));
10173
10312
  let modified = false;
10174
10313
  if (lock.workspaces?.[""]?.dependencies?.[packageName]) {
@@ -10180,7 +10319,7 @@ function removeFromBunLock(packageName) {
10180
10319
  modified = true;
10181
10320
  }
10182
10321
  if (modified) {
10183
- fs6.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
10322
+ fs7.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
10184
10323
  log(`[auto-update-checker] Removed from bun.lock: ${packageName}`);
10185
10324
  }
10186
10325
  return modified;
@@ -10195,17 +10334,17 @@ function invalidatePackage(packageName = PACKAGE_NAME) {
10195
10334
  let packageRemoved = false;
10196
10335
  let dependencyRemoved = false;
10197
10336
  let lockRemoved = false;
10198
- if (fs6.existsSync(pkgDir)) {
10199
- fs6.rmSync(pkgDir, { recursive: true, force: true });
10337
+ if (fs7.existsSync(pkgDir)) {
10338
+ fs7.rmSync(pkgDir, { recursive: true, force: true });
10200
10339
  log(`[auto-update-checker] Package removed: ${pkgDir}`);
10201
10340
  packageRemoved = true;
10202
10341
  }
10203
- if (fs6.existsSync(pkgJsonPath)) {
10204
- const content = fs6.readFileSync(pkgJsonPath, "utf-8");
10342
+ if (fs7.existsSync(pkgJsonPath)) {
10343
+ const content = fs7.readFileSync(pkgJsonPath, "utf-8");
10205
10344
  const pkgJson = JSON.parse(content);
10206
10345
  if (pkgJson.dependencies?.[packageName]) {
10207
10346
  delete pkgJson.dependencies[packageName];
10208
- fs6.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
10347
+ fs7.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
10209
10348
  log(`[auto-update-checker] Dependency removed from package.json: ${packageName}`);
10210
10349
  dependencyRemoved = true;
10211
10350
  }
@@ -10382,7 +10521,7 @@ import {
10382
10521
  existsSync as existsSync28,
10383
10522
  mkdirSync as mkdirSync8,
10384
10523
  readFileSync as readFileSync18,
10385
- writeFileSync as writeFileSync11,
10524
+ writeFileSync as writeFileSync12,
10386
10525
  unlinkSync as unlinkSync7
10387
10526
  } from "fs";
10388
10527
  import { join as join37 } from "path";
@@ -10453,7 +10592,7 @@ function saveAgentUsageState(state2) {
10453
10592
  mkdirSync8(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
10454
10593
  }
10455
10594
  const filePath = getStoragePath4(state2.sessionID);
10456
- writeFileSync11(filePath, JSON.stringify(state2, null, 2));
10595
+ writeFileSync12(filePath, JSON.stringify(state2, null, 2));
10457
10596
  }
10458
10597
  function clearAgentUsageState(sessionID) {
10459
10598
  const filePath = getStoragePath4(sessionID);
@@ -10766,7 +10905,7 @@ import {
10766
10905
  existsSync as existsSync29,
10767
10906
  mkdirSync as mkdirSync9,
10768
10907
  readFileSync as readFileSync19,
10769
- writeFileSync as writeFileSync12,
10908
+ writeFileSync as writeFileSync13,
10770
10909
  unlinkSync as unlinkSync8
10771
10910
  } from "fs";
10772
10911
  import { join as join39 } from "path";
@@ -10814,7 +10953,7 @@ function saveInteractiveBashSessionState(state2) {
10814
10953
  tmuxSessions: Array.from(state2.tmuxSessions),
10815
10954
  updatedAt: state2.updatedAt
10816
10955
  };
10817
- writeFileSync12(filePath, JSON.stringify(serialized, null, 2));
10956
+ writeFileSync13(filePath, JSON.stringify(serialized, null, 2));
10818
10957
  }
10819
10958
  function clearInteractiveBashSessionState(sessionID) {
10820
10959
  const filePath = getStoragePath5(sessionID);
@@ -11152,7 +11291,7 @@ function createThinkingBlockValidatorHook() {
11152
11291
  import { existsSync as existsSync31, readFileSync as readFileSync21 } from "fs";
11153
11292
 
11154
11293
  // src/hooks/ralph-loop/storage.ts
11155
- import { existsSync as existsSync30, readFileSync as readFileSync20, writeFileSync as writeFileSync13, unlinkSync as unlinkSync9, mkdirSync as mkdirSync10 } from "fs";
11294
+ import { existsSync as existsSync30, readFileSync as readFileSync20, writeFileSync as writeFileSync14, unlinkSync as unlinkSync9, mkdirSync as mkdirSync10 } from "fs";
11156
11295
  import { dirname as dirname6, join as join40 } from "path";
11157
11296
 
11158
11297
  // src/hooks/ralph-loop/constants.ts
@@ -11218,7 +11357,7 @@ started_at: "${state2.started_at}"
11218
11357
  ${sessionIdLine}---
11219
11358
  ${state2.prompt}
11220
11359
  `;
11221
- writeFileSync13(filePath, content, "utf-8");
11360
+ writeFileSync14(filePath, content, "utf-8");
11222
11361
  return true;
11223
11362
  } catch {
11224
11363
  return false;
@@ -13319,7 +13458,8 @@ $ARGUMENTS
13319
13458
  function commandsToRecord(commands) {
13320
13459
  const result = {};
13321
13460
  for (const cmd of commands) {
13322
- result[cmd.name] = cmd.definition;
13461
+ const { name: _name, argumentHint: _argumentHint, ...openCodeCompatible } = cmd.definition;
13462
+ result[cmd.name] = openCodeCompatible;
13323
13463
  }
13324
13464
  return result;
13325
13465
  }
@@ -13345,208 +13485,194 @@ function loadOpencodeProjectCommands() {
13345
13485
  return commandsToRecord(commands);
13346
13486
  }
13347
13487
  // src/features/builtin-commands/templates/init-deep.ts
13348
- var INIT_DEEP_TEMPLATE = `# Initialize Deep Knowledge Base
13488
+ var INIT_DEEP_TEMPLATE = `# /init-deep
13349
13489
 
13350
- Generate comprehensive AGENTS.md files across project hierarchy. Combines root-level project knowledge (gen-knowledge) with complexity-based subdirectory documentation (gen-knowledge-deep).
13490
+ Generate hierarchical AGENTS.md files. Root + complexity-scored subdirectories.
13351
13491
 
13352
13492
  ## Usage
13353
13493
 
13354
13494
  \`\`\`
13355
- /init-deep # Analyze and generate hierarchical AGENTS.md
13356
- /init-deep --create-new # Force create from scratch (ignore existing)
13357
- /init-deep --max-depth=2 # Limit to N directory levels (default: 3)
13495
+ /init-deep # Update mode: modify existing + create new where warranted
13496
+ /init-deep --create-new # Read existing \u2192 remove all \u2192 regenerate from scratch
13497
+ /init-deep --max-depth=2 # Limit directory depth (default: 3)
13358
13498
  \`\`\`
13359
13499
 
13360
13500
  ---
13361
13501
 
13362
- ## Core Principles
13363
-
13364
- - **Telegraphic Style**: Sacrifice grammar for concision ("Project uses React" \u2192 "React 18")
13365
- - **Predict-then-Compare**: Predict standard \u2192 find actual \u2192 document ONLY deviations
13366
- - **Hierarchy Aware**: Parent covers general, children cover specific
13367
- - **No Redundancy**: Child AGENTS.md NEVER repeats parent content
13368
- - **LSP-First**: Use LSP tools for accurate code intelligence when available (semantic > text search)
13369
-
13370
- ---
13502
+ ## Workflow (High-Level)
13371
13503
 
13372
- ## Process
13504
+ 1. **Discovery + Analysis** (concurrent)
13505
+ - Fire background explore agents immediately
13506
+ - Main session: bash structure + LSP codemap + read existing AGENTS.md
13507
+ 2. **Score & Decide** - Determine AGENTS.md locations from merged findings
13508
+ 3. **Generate** - Root first, then subdirs in parallel
13509
+ 4. **Review** - Deduplicate, trim, validate
13373
13510
 
13374
13511
  <critical>
13375
- **MANDATORY: TodoWrite for ALL phases. Mark in_progress \u2192 completed in real-time.**
13376
- </critical>
13377
-
13378
- ### Phase 0: Initialize
13379
-
13512
+ **TodoWrite ALL phases. Mark in_progress \u2192 completed in real-time.**
13380
13513
  \`\`\`
13381
13514
  TodoWrite([
13382
- { id: "p1-analysis", content: "Parallel project structure & complexity analysis", status: "pending", priority: "high" },
13383
- { id: "p2-scoring", content: "Score directories, determine AGENTS.md locations", status: "pending", priority: "high" },
13384
- { id: "p3-root", content: "Generate root AGENTS.md with Predict-then-Compare", status: "pending", priority: "high" },
13385
- { id: "p4-subdirs", content: "Generate subdirectory AGENTS.md files in parallel", status: "pending", priority: "high" },
13386
- { id: "p5-review", content: "Review, deduplicate, validate all files", status: "pending", priority: "medium" }
13515
+ { id: "discovery", content: "Fire explore agents + LSP codemap + read existing", status: "pending", priority: "high" },
13516
+ { id: "scoring", content: "Score directories, determine locations", status: "pending", priority: "high" },
13517
+ { id: "generate", content: "Generate AGENTS.md files (root + subdirs)", status: "pending", priority: "high" },
13518
+ { id: "review", content: "Deduplicate, validate, trim", status: "pending", priority: "medium" }
13387
13519
  ])
13388
13520
  \`\`\`
13521
+ </critical>
13389
13522
 
13390
13523
  ---
13391
13524
 
13392
- ## Phase 1: Parallel Project Analysis
13525
+ ## Phase 1: Discovery + Analysis (Concurrent)
13393
13526
 
13394
- **Mark "p1-analysis" as in_progress.**
13527
+ **Mark "discovery" as in_progress.**
13395
13528
 
13396
- Launch **ALL tasks simultaneously**:
13529
+ ### Fire Background Explore Agents IMMEDIATELY
13397
13530
 
13398
- <parallel-tasks>
13531
+ Don't wait\u2014these run async while main session works.
13399
13532
 
13400
- ### Structural Analysis (bash - run in parallel)
13401
- \`\`\`bash
13402
- # Task A: Directory depth analysis
13403
- find . -type d -not -path '*/\\.*' -not -path '*/node_modules/*' -not -path '*/venv/*' -not -path '*/__pycache__/*' -not -path '*/dist/*' -not -path '*/build/*' | awk -F/ '{print NF-1}' | sort -n | uniq -c
13533
+ \`\`\`
13534
+ // Fire all at once, collect results later
13535
+ background_task(agent="explore", prompt="Project structure: PREDICT standard patterns for detected language \u2192 REPORT deviations only")
13536
+ background_task(agent="explore", prompt="Entry points: FIND main files \u2192 REPORT non-standard organization")
13537
+ background_task(agent="explore", prompt="Conventions: FIND config files (.eslintrc, pyproject.toml, .editorconfig) \u2192 REPORT project-specific rules")
13538
+ background_task(agent="explore", prompt="Anti-patterns: FIND 'DO NOT', 'NEVER', 'ALWAYS', 'DEPRECATED' comments \u2192 LIST forbidden patterns")
13539
+ background_task(agent="explore", prompt="Build/CI: FIND .github/workflows, Makefile \u2192 REPORT non-standard patterns")
13540
+ background_task(agent="explore", prompt="Test patterns: FIND test configs, test structure \u2192 REPORT unique conventions")
13541
+ \`\`\`
13404
13542
 
13405
- # Task B: File count per directory
13406
- find . -type f -not -path '*/\\.*' -not -path '*/node_modules/*' -not -path '*/venv/*' -not -path '*/__pycache__/*' | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn | head -30
13543
+ <dynamic-agents>
13544
+ **DYNAMIC AGENT SPAWNING**: After bash analysis, spawn ADDITIONAL explore agents based on project scale:
13407
13545
 
13408
- # Task C: Code concentration
13409
- find . -type f \\( -name "*.py" -o -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.go" -o -name "*.rs" -o -name "*.java" \\) -not -path '*/node_modules/*' -not -path '*/venv/*' | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn | head -20
13546
+ | Factor | Threshold | Additional Agents |
13547
+ |--------|-----------|-------------------|
13548
+ | **Total files** | >100 | +1 per 100 files |
13549
+ | **Total lines** | >10k | +1 per 10k lines |
13550
+ | **Directory depth** | \u22654 | +2 for deep exploration |
13551
+ | **Large files (>500 lines)** | >10 files | +1 for complexity hotspots |
13552
+ | **Monorepo** | detected | +1 per package/workspace |
13553
+ | **Multiple languages** | >1 | +1 per language |
13410
13554
 
13411
- # Task D: Existing knowledge files
13412
- find . -type f \\( -name "AGENTS.md" -o -name "CLAUDE.md" \\) -not -path '*/node_modules/*' 2>/dev/null
13555
+ \`\`\`bash
13556
+ # Measure project scale first
13557
+ total_files=$(find . -type f -not -path '*/node_modules/*' -not -path '*/.git/*' | wc -l)
13558
+ total_lines=$(find . -type f \\( -name "*.ts" -o -name "*.py" -o -name "*.go" \\) -not -path '*/node_modules/*' -exec wc -l {} + 2>/dev/null | tail -1 | awk '{print $1}')
13559
+ large_files=$(find . -type f \\( -name "*.ts" -o -name "*.py" \\) -not -path '*/node_modules/*' -exec wc -l {} + 2>/dev/null | awk '$1 > 500 {count++} END {print count+0}')
13560
+ max_depth=$(find . -type d -not -path '*/node_modules/*' -not -path '*/.git/*' | awk -F/ '{print NF}' | sort -rn | head -1)
13413
13561
  \`\`\`
13414
13562
 
13415
- ### Context Gathering (Explore agents - background_task in parallel)
13416
-
13563
+ Example spawning:
13417
13564
  \`\`\`
13418
- background_task(agent="explore", prompt="Project structure: PREDICT standard {lang} patterns \u2192 FIND package.json/pyproject.toml/go.mod \u2192 REPORT deviations only")
13419
-
13420
- background_task(agent="explore", prompt="Entry points: PREDICT typical (main.py, index.ts) \u2192 FIND actual \u2192 REPORT non-standard organization")
13565
+ // 500 files, 50k lines, depth 6, 15 large files \u2192 spawn 5+5+2+1 = 13 additional agents
13566
+ background_task(agent="explore", prompt="Large file analysis: FIND files >500 lines, REPORT complexity hotspots")
13567
+ background_task(agent="explore", prompt="Deep modules at depth 4+: FIND hidden patterns, internal conventions")
13568
+ background_task(agent="explore", prompt="Cross-cutting concerns: FIND shared utilities across directories")
13569
+ // ... more based on calculation
13570
+ \`\`\`
13571
+ </dynamic-agents>
13421
13572
 
13422
- background_task(agent="explore", prompt="Conventions: FIND .cursor/rules, .cursorrules, eslintrc, pyproject.toml \u2192 REPORT project-specific rules DIFFERENT from defaults")
13573
+ ### Main Session: Concurrent Analysis
13423
13574
 
13424
- background_task(agent="explore", prompt="Anti-patterns: FIND comments with 'DO NOT', 'NEVER', 'ALWAYS', 'LEGACY', 'DEPRECATED' \u2192 REPORT forbidden patterns")
13575
+ **While background agents run**, main session does:
13425
13576
 
13426
- background_task(agent="explore", prompt="Build/CI: FIND .github/workflows, Makefile, justfile \u2192 REPORT non-standard build/deploy patterns")
13577
+ #### 1. Bash Structural Analysis
13578
+ \`\`\`bash
13579
+ # Directory depth + file counts
13580
+ find . -type d -not -path '*/\\.*' -not -path '*/node_modules/*' -not -path '*/venv/*' -not -path '*/dist/*' -not -path '*/build/*' | awk -F/ '{print NF-1}' | sort -n | uniq -c
13427
13581
 
13428
- background_task(agent="explore", prompt="Test patterns: FIND pytest.ini, jest.config, test structure \u2192 REPORT unique testing conventions")
13429
- \`\`\`
13582
+ # Files per directory (top 30)
13583
+ find . -type f -not -path '*/\\.*' -not -path '*/node_modules/*' | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn | head -30
13430
13584
 
13431
- ### Code Intelligence Analysis (LSP tools - run in parallel)
13585
+ # Code concentration by extension
13586
+ find . -type f \\( -name "*.py" -o -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.go" -o -name "*.rs" \\) -not -path '*/node_modules/*' | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn | head -20
13432
13587
 
13433
- LSP provides semantic understanding beyond text search. Use for accurate code mapping.
13588
+ # Existing AGENTS.md / CLAUDE.md
13589
+ find . -type f \\( -name "AGENTS.md" -o -name "CLAUDE.md" \\) -not -path '*/node_modules/*' 2>/dev/null
13590
+ \`\`\`
13434
13591
 
13592
+ #### 2. Read Existing AGENTS.md
13435
13593
  \`\`\`
13436
- # Step 1: Check LSP availability
13437
- lsp_servers() # Verify language server is available
13438
-
13439
- # Step 2: Analyze entry point files (run in parallel)
13440
- # Find entry points first, then analyze each with lsp_document_symbols
13441
- lsp_document_symbols(filePath="src/index.ts") # Main entry
13442
- lsp_document_symbols(filePath="src/main.py") # Python entry
13443
- lsp_document_symbols(filePath="cmd/main.go") # Go entry
13444
-
13445
- # Step 3: Discover key symbols across workspace (run in parallel)
13446
- lsp_workspace_symbols(filePath=".", query="class") # All classes
13447
- lsp_workspace_symbols(filePath=".", query="interface") # All interfaces
13448
- lsp_workspace_symbols(filePath=".", query="function") # Top-level functions
13449
- lsp_workspace_symbols(filePath=".", query="type") # Type definitions
13450
-
13451
- # Step 4: Analyze symbol centrality (for top 5-10 key symbols)
13452
- # High reference count = central/important concept
13453
- lsp_find_references(filePath="src/index.ts", line=X, character=Y) # Main export
13594
+ For each existing file found:
13595
+ Read(filePath=file)
13596
+ Extract: key insights, conventions, anti-patterns
13597
+ Store in EXISTING_AGENTS map
13454
13598
  \`\`\`
13455
13599
 
13456
- #### LSP Analysis Output Format
13600
+ If \`--create-new\`: Read all existing first (preserve context) \u2192 then delete all \u2192 regenerate.
13457
13601
 
13602
+ #### 3. LSP Codemap (if available)
13458
13603
  \`\`\`
13459
- CODE_INTELLIGENCE = {
13460
- entry_points: [
13461
- { file: "src/index.ts", exports: ["Plugin", "createHook"], symbol_count: 12 }
13462
- ],
13463
- key_symbols: [
13464
- { name: "Plugin", type: "class", file: "src/index.ts", refs: 45, role: "Central orchestrator" },
13465
- { name: "createHook", type: "function", file: "src/utils.ts", refs: 23, role: "Hook factory" }
13466
- ],
13467
- module_boundaries: [
13468
- { dir: "src/hooks", exports: 21, imports_from: ["shared/"] },
13469
- { dir: "src/tools", exports: 15, imports_from: ["shared/", "hooks/"] }
13470
- ]
13471
- }
13604
+ lsp_servers() # Check availability
13605
+
13606
+ # Entry points (parallel)
13607
+ lsp_document_symbols(filePath="src/index.ts")
13608
+ lsp_document_symbols(filePath="main.py")
13609
+
13610
+ # Key symbols (parallel)
13611
+ lsp_workspace_symbols(filePath=".", query="class")
13612
+ lsp_workspace_symbols(filePath=".", query="interface")
13613
+ lsp_workspace_symbols(filePath=".", query="function")
13614
+
13615
+ # Centrality for top exports
13616
+ lsp_find_references(filePath="...", line=X, character=Y)
13472
13617
  \`\`\`
13473
13618
 
13474
- <critical>
13475
- **LSP Fallback**: If LSP unavailable (no server installed), skip this section and rely on explore agents + AST-grep patterns.
13476
- </critical>
13619
+ **LSP Fallback**: If unavailable, rely on explore agents + AST-grep.
13477
13620
 
13478
- </parallel-tasks>
13621
+ ### Collect Background Results
13479
13622
 
13480
- **Collect all results. Mark "p1-analysis" as completed.**
13623
+ \`\`\`
13624
+ // After main session analysis done, collect all task results
13625
+ for each task_id: background_output(task_id="...")
13626
+ \`\`\`
13627
+
13628
+ **Merge: bash + LSP + existing + explore findings. Mark "discovery" as completed.**
13481
13629
 
13482
13630
  ---
13483
13631
 
13484
- ## Phase 2: Complexity Scoring & Location Decision
13632
+ ## Phase 2: Scoring & Location Decision
13485
13633
 
13486
- **Mark "p2-scoring" as in_progress.**
13634
+ **Mark "scoring" as in_progress.**
13487
13635
 
13488
13636
  ### Scoring Matrix
13489
13637
 
13490
- | Factor | Weight | Threshold | Source |
13491
- |--------|--------|-----------|--------|
13492
- | File count | 3x | >20 files = high | bash |
13493
- | Subdirectory count | 2x | >5 subdirs = high | bash |
13494
- | Code file ratio | 2x | >70% code = high | bash |
13638
+ | Factor | Weight | High Threshold | Source |
13639
+ |--------|--------|----------------|--------|
13640
+ | File count | 3x | >20 | bash |
13641
+ | Subdir count | 2x | >5 | bash |
13642
+ | Code ratio | 2x | >70% | bash |
13495
13643
  | Unique patterns | 1x | Has own config | explore |
13496
- | Module boundary | 2x | Has __init__.py/index.ts | bash |
13497
- | **Symbol density** | 2x | >30 symbols = high | LSP |
13498
- | **Export count** | 2x | >10 exports = high | LSP |
13499
- | **Reference centrality** | 3x | Symbols with >20 refs | LSP |
13500
-
13501
- <lsp-scoring>
13502
- **LSP-Enhanced Scoring** (if available):
13503
-
13504
- \`\`\`
13505
- For each directory in candidates:
13506
- symbols = lsp_document_symbols(dir/index.ts or dir/__init__.py)
13507
-
13508
- symbol_score = len(symbols) > 30 ? 6 : len(symbols) > 15 ? 3 : 0
13509
- export_score = count(exported symbols) > 10 ? 4 : 0
13510
-
13511
- # Check if this module is central (many things depend on it)
13512
- for each exported symbol:
13513
- refs = lsp_find_references(symbol)
13514
- if refs > 20: centrality_score += 3
13515
-
13516
- total_score += symbol_score + export_score + centrality_score
13517
- \`\`\`
13518
- </lsp-scoring>
13644
+ | Module boundary | 2x | Has index.ts/__init__.py | bash |
13645
+ | Symbol density | 2x | >30 symbols | LSP |
13646
+ | Export count | 2x | >10 exports | LSP |
13647
+ | Reference centrality | 3x | >20 refs | LSP |
13519
13648
 
13520
13649
  ### Decision Rules
13521
13650
 
13522
13651
  | Score | Action |
13523
13652
  |-------|--------|
13524
- | **Root (.)** | ALWAYS create AGENTS.md |
13525
- | **High (>15)** | Create dedicated AGENTS.md |
13526
- | **Medium (8-15)** | Create if distinct domain |
13527
- | **Low (<8)** | Skip, parent sufficient |
13528
-
13529
- ### Output Format
13653
+ | **Root (.)** | ALWAYS create |
13654
+ | **>15** | Create AGENTS.md |
13655
+ | **8-15** | Create if distinct domain |
13656
+ | **<8** | Skip (parent covers) |
13530
13657
 
13658
+ ### Output
13531
13659
  \`\`\`
13532
13660
  AGENTS_LOCATIONS = [
13533
13661
  { path: ".", type: "root" },
13534
- { path: "src/api", score: 18, reason: "high complexity, 45 files" },
13535
- { path: "src/hooks", score: 12, reason: "distinct domain, unique patterns" },
13662
+ { path: "src/hooks", score: 18, reason: "high complexity" },
13663
+ { path: "src/api", score: 12, reason: "distinct domain" }
13536
13664
  ]
13537
13665
  \`\`\`
13538
13666
 
13539
- **Mark "p2-scoring" as completed.**
13667
+ **Mark "scoring" as completed.**
13540
13668
 
13541
13669
  ---
13542
13670
 
13543
- ## Phase 3: Generate Root AGENTS.md
13544
-
13545
- **Mark "p3-root" as in_progress.**
13671
+ ## Phase 3: Generate AGENTS.md
13546
13672
 
13547
- Root AGENTS.md gets **full treatment** with Predict-then-Compare synthesis.
13673
+ **Mark "generate" as in_progress.**
13548
13674
 
13549
- ### Required Sections
13675
+ ### Root AGENTS.md (Full Treatment)
13550
13676
 
13551
13677
  \`\`\`markdown
13552
13678
  # PROJECT KNOWLEDGE BASE
@@ -13556,153 +13682,75 @@ Root AGENTS.md gets **full treatment** with Predict-then-Compare synthesis.
13556
13682
  **Branch:** {BRANCH}
13557
13683
 
13558
13684
  ## OVERVIEW
13559
-
13560
- {1-2 sentences: what project does, core tech stack}
13685
+ {1-2 sentences: what + core stack}
13561
13686
 
13562
13687
  ## STRUCTURE
13563
-
13564
13688
  \\\`\\\`\\\`
13565
- {project-root}/
13566
- \u251C\u2500\u2500 {dir}/ # {non-obvious purpose only}
13567
- \u2514\u2500\u2500 {entry} # entry point
13689
+ {root}/
13690
+ \u251C\u2500\u2500 {dir}/ # {non-obvious purpose only}
13691
+ \u2514\u2500\u2500 {entry}
13568
13692
  \\\`\\\`\\\`
13569
13693
 
13570
13694
  ## WHERE TO LOOK
13571
-
13572
13695
  | Task | Location | Notes |
13573
13696
  |------|----------|-------|
13574
- | Add feature X | \\\`src/x/\\\` | {pattern hint} |
13575
13697
 
13576
13698
  ## CODE MAP
13577
-
13578
- {Generated from LSP analysis - shows key symbols and their relationships}
13699
+ {From LSP - skip if unavailable or project <10 files}
13579
13700
 
13580
13701
  | Symbol | Type | Location | Refs | Role |
13581
13702
  |--------|------|----------|------|------|
13582
- | {MainClass} | Class | \\\`src/index.ts\\\` | {N} | {Central orchestrator} |
13583
- | {createX} | Function | \\\`src/utils.ts\\\` | {N} | {Factory pattern} |
13584
- | {Config} | Interface | \\\`src/types.ts\\\` | {N} | {Configuration contract} |
13585
-
13586
- ### Module Dependencies
13587
-
13588
- \\\`\\\`\\\`
13589
- {entry} \u2500\u2500imports\u2500\u2500> {core/}
13590
- \u2502 \u2502
13591
- \u2514\u2500\u2500imports\u2500\u2500> {utils/} <\u2500\u2500imports\u2500\u2500 {features/}
13592
- \\\`\\\`\\\`
13593
-
13594
- <code-map-note>
13595
- **Skip CODE MAP if**: LSP unavailable OR project too small (<10 files) OR no clear module boundaries.
13596
- </code-map-note>
13597
13703
 
13598
13704
  ## CONVENTIONS
13599
-
13600
- {ONLY deviations from standard - skip generic advice}
13601
-
13602
- - **{rule}**: {specific detail}
13705
+ {ONLY deviations from standard}
13603
13706
 
13604
13707
  ## ANTI-PATTERNS (THIS PROJECT)
13605
-
13606
- {Things explicitly forbidden HERE}
13607
-
13608
- - **{pattern}**: {why} \u2192 {alternative}
13708
+ {Explicitly forbidden here}
13609
13709
 
13610
13710
  ## UNIQUE STYLES
13611
-
13612
- {Project-specific coding styles}
13613
-
13614
- - **{style}**: {how different}
13711
+ {Project-specific}
13615
13712
 
13616
13713
  ## COMMANDS
13617
-
13618
13714
  \\\`\\\`\\\`bash
13619
- {dev-command}
13620
- {test-command}
13621
- {build-command}
13715
+ {dev/test/build}
13622
13716
  \\\`\\\`\\\`
13623
13717
 
13624
13718
  ## NOTES
13625
-
13626
- {Gotchas, non-obvious info}
13719
+ {Gotchas}
13627
13720
  \`\`\`
13628
13721
 
13629
- ### Quality Gates
13630
-
13631
- - [ ] Size: 50-150 lines
13632
- - [ ] No generic advice ("write clean code")
13633
- - [ ] No obvious info ("tests/ has tests")
13634
- - [ ] Every item is project-specific
13635
-
13636
- **Mark "p3-root" as completed.**
13722
+ **Quality gates**: 50-150 lines, no generic advice, no obvious info.
13637
13723
 
13638
- ---
13639
-
13640
- ## Phase 4: Generate Subdirectory AGENTS.md
13724
+ ### Subdirectory AGENTS.md (Parallel)
13641
13725
 
13642
- **Mark "p4-subdirs" as in_progress.**
13726
+ Launch document-writer agents for each location:
13643
13727
 
13644
- For each location in AGENTS_LOCATIONS (except root), launch **parallel document-writer agents**:
13645
-
13646
- \`\`\`typescript
13647
- for (const loc of AGENTS_LOCATIONS.filter(l => l.path !== ".")) {
13648
- background_task({
13649
- agent: "document-writer",
13650
- prompt: \\\`
13651
- Generate AGENTS.md for: \${loc.path}
13652
-
13653
- CONTEXT:
13654
- - Complexity reason: \${loc.reason}
13655
- - Parent AGENTS.md: ./AGENTS.md (already covers project overview)
13656
-
13657
- CRITICAL RULES:
13658
- 1. Focus ONLY on this directory's specific context
13659
- 2. NEVER repeat parent AGENTS.md content
13660
- 3. Shorter is better - 30-80 lines max
13661
- 4. Telegraphic style - sacrifice grammar
13662
-
13663
- REQUIRED SECTIONS:
13664
- - OVERVIEW (1 line: what this directory does)
13665
- - STRUCTURE (only if >5 subdirs)
13666
- - WHERE TO LOOK (directory-specific tasks)
13667
- - CONVENTIONS (only if DIFFERENT from root)
13668
- - ANTI-PATTERNS (directory-specific only)
13669
-
13670
- OUTPUT: Write to \${loc.path}/AGENTS.md
13671
- \\\`
13672
- })
13673
- }
13728
+ \`\`\`
13729
+ for loc in AGENTS_LOCATIONS (except root):
13730
+ background_task(agent="document-writer", prompt=\\\`
13731
+ Generate AGENTS.md for: \${loc.path}
13732
+ - Reason: \${loc.reason}
13733
+ - 30-80 lines max
13734
+ - NEVER repeat parent content
13735
+ - Sections: OVERVIEW (1 line), STRUCTURE (if >5 subdirs), WHERE TO LOOK, CONVENTIONS (if different), ANTI-PATTERNS
13736
+ \\\`)
13674
13737
  \`\`\`
13675
13738
 
13676
- **Wait for all agents. Mark "p4-subdirs" as completed.**
13739
+ **Wait for all. Mark "generate" as completed.**
13677
13740
 
13678
13741
  ---
13679
13742
 
13680
- ## Phase 5: Review & Deduplicate
13681
-
13682
- **Mark "p5-review" as in_progress.**
13683
-
13684
- ### Validation Checklist
13685
-
13686
- For EACH generated AGENTS.md:
13687
-
13688
- | Check | Action if Fail |
13689
- |-------|----------------|
13690
- | Contains generic advice | REMOVE the line |
13691
- | Repeats parent content | REMOVE the line |
13692
- | Missing required section | ADD it |
13693
- | Over 150 lines (root) / 80 lines (subdir) | TRIM |
13694
- | Verbose explanations | REWRITE telegraphic |
13743
+ ## Phase 4: Review & Deduplicate
13695
13744
 
13696
- ### Cross-Reference Validation
13745
+ **Mark "review" as in_progress.**
13697
13746
 
13698
- \`\`\`
13699
- For each child AGENTS.md:
13700
- For each line in child:
13701
- If similar line exists in parent:
13702
- REMOVE from child (parent already covers)
13703
- \`\`\`
13747
+ For each generated file:
13748
+ - Remove generic advice
13749
+ - Remove parent duplicates
13750
+ - Trim to size limits
13751
+ - Verify telegraphic style
13704
13752
 
13705
- **Mark "p5-review" as completed.**
13753
+ **Mark "review" as completed.**
13706
13754
 
13707
13755
  ---
13708
13756
 
@@ -13711,34 +13759,32 @@ For each child AGENTS.md:
13711
13759
  \`\`\`
13712
13760
  === init-deep Complete ===
13713
13761
 
13714
- Files Generated:
13762
+ Mode: {update | create-new}
13763
+
13764
+ Files:
13715
13765
  \u2713 ./AGENTS.md (root, {N} lines)
13716
13766
  \u2713 ./src/hooks/AGENTS.md ({N} lines)
13717
- \u2713 ./src/tools/AGENTS.md ({N} lines)
13718
13767
 
13719
- Directories Analyzed: {N}
13768
+ Dirs Analyzed: {N}
13720
13769
  AGENTS.md Created: {N}
13721
- Total Lines: {N}
13770
+ AGENTS.md Updated: {N}
13722
13771
 
13723
13772
  Hierarchy:
13724
13773
  ./AGENTS.md
13725
- \u251C\u2500\u2500 src/hooks/AGENTS.md
13726
- \u2514\u2500\u2500 src/tools/AGENTS.md
13774
+ \u2514\u2500\u2500 src/hooks/AGENTS.md
13727
13775
  \`\`\`
13728
13776
 
13729
13777
  ---
13730
13778
 
13731
- ## Anti-Patterns for THIS Command
13779
+ ## Anti-Patterns
13732
13780
 
13733
- - **Over-documenting**: Not every directory needs AGENTS.md
13734
- - **Redundancy**: Child must NOT repeat parent
13781
+ - **Static agent count**: MUST vary agents based on project size/depth
13782
+ - **Sequential execution**: MUST parallel (explore + LSP concurrent)
13783
+ - **Ignoring existing**: ALWAYS read existing first, even with --create-new
13784
+ - **Over-documenting**: Not every dir needs AGENTS.md
13785
+ - **Redundancy**: Child never repeats parent
13735
13786
  - **Generic content**: Remove anything that applies to ALL projects
13736
- - **Sequential execution**: MUST use parallel agents
13737
- - **Deep nesting**: Rarely need AGENTS.md at depth 4+
13738
- - **Verbose style**: "This directory contains..." \u2192 just list it
13739
- - **Ignoring LSP**: If LSP available, USE IT - semantic analysis > text grep
13740
- - **LSP without fallback**: Always have explore agent backup if LSP unavailable
13741
- - **Over-referencing**: Don't trace refs for EVERY symbol - focus on exports only`;
13787
+ - **Verbose style**: Telegraphic or die`;
13742
13788
 
13743
13789
  // src/features/builtin-commands/templates/ralph-loop.ts
13744
13790
  var RALPH_LOOP_TEMPLATE = `You are starting a Ralph Loop - a self-referential development loop that runs until task completion.
@@ -13815,10 +13861,8 @@ function loadBuiltinCommands(disabledCommands) {
13815
13861
  const commands = {};
13816
13862
  for (const [name, definition] of Object.entries(BUILTIN_COMMAND_DEFINITIONS)) {
13817
13863
  if (!disabled.has(name)) {
13818
- commands[name] = {
13819
- name,
13820
- ...definition
13821
- };
13864
+ const { argumentHint: _argumentHint, ...openCodeCompatible } = definition;
13865
+ commands[name] = openCodeCompatible;
13822
13866
  }
13823
13867
  }
13824
13868
  return commands;
@@ -13915,7 +13959,8 @@ function loadSkillsFromDir(skillsDir, scope) {
13915
13959
  function skillsToRecord(skills) {
13916
13960
  const result = {};
13917
13961
  for (const skill of skills) {
13918
- result[skill.name] = skill.definition;
13962
+ const { name: _name, argumentHint: _argumentHint, ...openCodeCompatible } = skill.definition;
13963
+ result[skill.name] = openCodeCompatible;
13919
13964
  }
13920
13965
  return result;
13921
13966
  }
@@ -14558,7 +14603,7 @@ ${body.trim()}
14558
14603
  $ARGUMENTS
14559
14604
  </user-request>`;
14560
14605
  const formattedDescription = `(plugin: ${plugin2.name}) ${data.description || ""}`;
14561
- commands2[namespacedName] = {
14606
+ const definition = {
14562
14607
  name: namespacedName,
14563
14608
  description: formattedDescription,
14564
14609
  template: wrappedTemplate,
@@ -14567,6 +14612,8 @@ $ARGUMENTS
14567
14612
  subtask: data.subtask,
14568
14613
  argumentHint: data["argument-hint"]
14569
14614
  };
14615
+ const { name: _name, argumentHint: _argumentHint, ...openCodeCompatible } = definition;
14616
+ commands2[namespacedName] = openCodeCompatible;
14570
14617
  log(`Loaded plugin command: ${namespacedName}`, { path: commandPath });
14571
14618
  } catch (error) {
14572
14619
  log(`Failed to load plugin command: ${commandPath}`, error);
@@ -14608,12 +14655,14 @@ ${body.trim()}
14608
14655
  <user-request>
14609
14656
  $ARGUMENTS
14610
14657
  </user-request>`;
14611
- skills[namespacedName] = {
14658
+ const definition = {
14612
14659
  name: namespacedName,
14613
14660
  description: formattedDescription,
14614
14661
  template: wrappedTemplate,
14615
14662
  model: sanitizeModelField(data.model)
14616
14663
  };
14664
+ const { name: _name, ...openCodeCompatible } = definition;
14665
+ skills[namespacedName] = openCodeCompatible;
14617
14666
  log(`Loaded plugin skill: ${namespacedName}`, { path: resolvedPath });
14618
14667
  } catch (error) {
14619
14668
  log(`Failed to load plugin skill: ${skillPath}`, error);
@@ -15814,7 +15863,7 @@ ${msg}`);
15814
15863
  // src/tools/lsp/utils.ts
15815
15864
  import { extname as extname2, resolve as resolve7 } from "path";
15816
15865
  import { fileURLToPath as fileURLToPath2 } from "url";
15817
- import { existsSync as existsSync39, readFileSync as readFileSync29, writeFileSync as writeFileSync14 } from "fs";
15866
+ import { existsSync as existsSync39, readFileSync as readFileSync29, writeFileSync as writeFileSync15 } from "fs";
15818
15867
  function findWorkspaceRoot(filePath) {
15819
15868
  let dir = resolve7(filePath);
15820
15869
  if (!existsSync39(dir) || !__require("fs").statSync(dir).isDirectory()) {
@@ -16044,7 +16093,7 @@ function applyTextEditsToFile(filePath, edits) {
16044
16093
  `));
16045
16094
  }
16046
16095
  }
16047
- writeFileSync14(filePath, lines.join(`
16096
+ writeFileSync15(filePath, lines.join(`
16048
16097
  `), "utf-8");
16049
16098
  return { success: true, editCount: edits.length };
16050
16099
  } catch (err) {
@@ -16075,7 +16124,7 @@ function applyWorkspaceEdit(edit) {
16075
16124
  if (change.kind === "create") {
16076
16125
  try {
16077
16126
  const filePath = uriToPath(change.uri);
16078
- writeFileSync14(filePath, "", "utf-8");
16127
+ writeFileSync15(filePath, "", "utf-8");
16079
16128
  result.filesModified.push(filePath);
16080
16129
  } catch (err) {
16081
16130
  result.success = false;
@@ -16086,7 +16135,7 @@ function applyWorkspaceEdit(edit) {
16086
16135
  const oldPath = uriToPath(change.oldUri);
16087
16136
  const newPath = uriToPath(change.newUri);
16088
16137
  const content = readFileSync29(oldPath, "utf-8");
16089
- writeFileSync14(newPath, content, "utf-8");
16138
+ writeFileSync15(newPath, content, "utf-8");
16090
16139
  __require("fs").unlinkSync(oldPath);
16091
16140
  result.filesModified.push(newPath);
16092
16141
  } catch (err) {
@@ -30123,6 +30172,7 @@ import { join as join52 } from "path";
30123
30172
  var OPENCODE_STORAGE9 = getOpenCodeStorageDir();
30124
30173
  var MESSAGE_STORAGE4 = join52(OPENCODE_STORAGE9, "message");
30125
30174
  var PART_STORAGE4 = join52(OPENCODE_STORAGE9, "part");
30175
+ var SESSION_STORAGE = join52(OPENCODE_STORAGE9, "session");
30126
30176
  var TODO_DIR2 = join52(getClaudeConfigDir(), "todos");
30127
30177
  var TRANSCRIPT_DIR2 = join52(getClaudeConfigDir(), "transcripts");
30128
30178
  var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
@@ -30200,6 +30250,38 @@ Has Transcript: Yes (234 entries)`;
30200
30250
  import { existsSync as existsSync46, readdirSync as readdirSync17 } from "fs";
30201
30251
  import { readdir, readFile } from "fs/promises";
30202
30252
  import { join as join53 } from "path";
30253
+ async function getMainSessions(options) {
30254
+ if (!existsSync46(SESSION_STORAGE))
30255
+ return [];
30256
+ const sessions = [];
30257
+ try {
30258
+ const projectDirs = await readdir(SESSION_STORAGE, { withFileTypes: true });
30259
+ for (const projectDir of projectDirs) {
30260
+ if (!projectDir.isDirectory())
30261
+ continue;
30262
+ const projectPath = join53(SESSION_STORAGE, projectDir.name);
30263
+ const sessionFiles = await readdir(projectPath);
30264
+ for (const file2 of sessionFiles) {
30265
+ if (!file2.endsWith(".json"))
30266
+ continue;
30267
+ try {
30268
+ const content = await readFile(join53(projectPath, file2), "utf-8");
30269
+ const meta = JSON.parse(content);
30270
+ if (meta.parentID)
30271
+ continue;
30272
+ if (options.directory && meta.directory !== options.directory)
30273
+ continue;
30274
+ sessions.push(meta);
30275
+ } catch {
30276
+ continue;
30277
+ }
30278
+ }
30279
+ }
30280
+ } catch {
30281
+ return [];
30282
+ }
30283
+ return sessions.sort((a, b) => b.time.updated - a.time.updated);
30284
+ }
30203
30285
  async function getAllSessions() {
30204
30286
  if (!existsSync46(MESSAGE_STORAGE4))
30205
30287
  return [];
@@ -30551,18 +30633,21 @@ var session_list = tool({
30551
30633
  args: {
30552
30634
  limit: tool.schema.number().optional().describe("Maximum number of sessions to return"),
30553
30635
  from_date: tool.schema.string().optional().describe("Filter sessions from this date (ISO 8601 format)"),
30554
- to_date: tool.schema.string().optional().describe("Filter sessions until this date (ISO 8601 format)")
30636
+ to_date: tool.schema.string().optional().describe("Filter sessions until this date (ISO 8601 format)"),
30637
+ project_path: tool.schema.string().optional().describe("Filter sessions by project path (default: current working directory)")
30555
30638
  },
30556
30639
  execute: async (args, _context) => {
30557
30640
  try {
30558
- let sessions = await getAllSessions();
30641
+ const directory = args.project_path ?? process.cwd();
30642
+ let sessions = await getMainSessions({ directory });
30643
+ let sessionIDs = sessions.map((s) => s.id);
30559
30644
  if (args.from_date || args.to_date) {
30560
- sessions = await filterSessionsByDate(sessions, args.from_date, args.to_date);
30645
+ sessionIDs = await filterSessionsByDate(sessionIDs, args.from_date, args.to_date);
30561
30646
  }
30562
30647
  if (args.limit && args.limit > 0) {
30563
- sessions = sessions.slice(0, args.limit);
30648
+ sessionIDs = sessionIDs.slice(0, args.limit);
30564
30649
  }
30565
- return await formatSessionList(sessions);
30650
+ return await formatSessionList(sessionIDs);
30566
30651
  } catch (e) {
30567
30652
  return `Error: ${e instanceof Error ? e.message : String(e)}`;
30568
30653
  }
@@ -31944,7 +32029,10 @@ var HookNameSchema = exports_external.enum([
31944
32029
  "interactive-bash-session",
31945
32030
  "empty-message-sanitizer",
31946
32031
  "thinking-block-validator",
31947
- "ralph-loop"
32032
+ "ralph-loop",
32033
+ "preemptive-compaction",
32034
+ "compaction-context-injector",
32035
+ "claude-code-hooks"
31948
32036
  ]);
31949
32037
  var BuiltinCommandNameSchema = exports_external.enum([
31950
32038
  "init-deep"
@@ -32160,64 +32248,12 @@ var PLAN_PERMISSION = {
32160
32248
  };
32161
32249
 
32162
32250
  // src/index.ts
32163
- import * as fs7 from "fs";
32251
+ import * as fs8 from "fs";
32164
32252
  import * as path7 from "path";
32165
- var AGENT_NAME_MAP = {
32166
- omo: "Sisyphus",
32167
- OmO: "Sisyphus",
32168
- "OmO-Plan": "Planner-Sisyphus",
32169
- "omo-plan": "Planner-Sisyphus",
32170
- sisyphus: "Sisyphus",
32171
- "planner-sisyphus": "Planner-Sisyphus",
32172
- build: "build",
32173
- oracle: "oracle",
32174
- librarian: "librarian",
32175
- explore: "explore",
32176
- "frontend-ui-ux-engineer": "frontend-ui-ux-engineer",
32177
- "document-writer": "document-writer",
32178
- "multimodal-looker": "multimodal-looker"
32179
- };
32180
- function migrateAgentNames(agents) {
32181
- const migrated = {};
32182
- let changed = false;
32183
- for (const [key, value] of Object.entries(agents)) {
32184
- const newKey = AGENT_NAME_MAP[key.toLowerCase()] ?? AGENT_NAME_MAP[key] ?? key;
32185
- if (newKey !== key) {
32186
- changed = true;
32187
- }
32188
- migrated[newKey] = value;
32189
- }
32190
- return { migrated, changed };
32191
- }
32192
- function migrateConfigFile(configPath, rawConfig) {
32193
- let needsWrite = false;
32194
- if (rawConfig.agents && typeof rawConfig.agents === "object") {
32195
- const { migrated, changed } = migrateAgentNames(rawConfig.agents);
32196
- if (changed) {
32197
- rawConfig.agents = migrated;
32198
- needsWrite = true;
32199
- }
32200
- }
32201
- if (rawConfig.omo_agent) {
32202
- rawConfig.sisyphus_agent = rawConfig.omo_agent;
32203
- delete rawConfig.omo_agent;
32204
- needsWrite = true;
32205
- }
32206
- if (needsWrite) {
32207
- try {
32208
- fs7.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + `
32209
- `, "utf-8");
32210
- log(`Migrated config file: ${configPath} (OmO \u2192 Sisyphus)`);
32211
- } catch (err) {
32212
- log(`Failed to write migrated config to ${configPath}:`, err);
32213
- }
32214
- }
32215
- return needsWrite;
32216
- }
32217
32253
  function loadConfigFromPath2(configPath, ctx) {
32218
32254
  try {
32219
- if (fs7.existsSync(configPath)) {
32220
- const content = fs7.readFileSync(configPath, "utf-8");
32255
+ if (fs8.existsSync(configPath)) {
32256
+ const content = fs8.readFileSync(configPath, "utf-8");
32221
32257
  const rawConfig = parseJsonc(content);
32222
32258
  migrateConfigFile(configPath, rawConfig);
32223
32259
  const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
@@ -32322,12 +32358,12 @@ var OhMyOpenCodePlugin = async (ctx) => {
32322
32358
  experimental: pluginConfig.experimental,
32323
32359
  dcpForCompaction: pluginConfig.experimental?.dcp_for_compaction
32324
32360
  }) : null;
32325
- const compactionContextInjector = createCompactionContextInjector();
32326
- const preemptiveCompaction = createPreemptiveCompactionHook(ctx, {
32361
+ const compactionContextInjector = isHookEnabled("compaction-context-injector") ? createCompactionContextInjector() : undefined;
32362
+ const preemptiveCompaction = isHookEnabled("preemptive-compaction") ? createPreemptiveCompactionHook(ctx, {
32327
32363
  experimental: pluginConfig.experimental,
32328
32364
  onBeforeSummarize: compactionContextInjector,
32329
32365
  getModelLimit
32330
- });
32366
+ }) : null;
32331
32367
  const rulesInjector = isHookEnabled("rules-injector") ? createRulesInjectorHook(ctx) : null;
32332
32368
  const autoUpdateChecker = isHookEnabled("auto-update-checker") ? createAutoUpdateCheckerHook(ctx, {
32333
32369
  showStartupToast: isHookEnabled("startup-toast"),