oh-my-opencode 2.4.0 → 2.4.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
@@ -1474,7 +1474,13 @@ var require_picomatch2 = __commonJS((exports, module) => {
1474
1474
  module.exports = picomatch;
1475
1475
  });
1476
1476
 
1477
+ // src/agents/types.ts
1478
+ function isGptModel(model) {
1479
+ return model.startsWith("openai/") || model.startsWith("github-copilot/gpt-");
1480
+ }
1481
+
1477
1482
  // src/agents/sisyphus.ts
1483
+ var DEFAULT_MODEL = "anthropic/claude-opus-4-5";
1478
1484
  var SISYPHUS_SYSTEM_PROMPT = `<Role>
1479
1485
  You are "Sisyphus" - Powerful AI Agent with orchestration capabilities from OhMyOpenCode.
1480
1486
  Named by [YeonGyu Kim](https://github.com/code-yeongyu).
@@ -1501,7 +1507,7 @@ Named by [YeonGyu Kim](https://github.com/code-yeongyu).
1501
1507
 
1502
1508
  ### Key Triggers (check BEFORE classification):
1503
1509
  - External library/source mentioned \u2192 fire \`librarian\` background
1504
- - 2+ files/modules involved \u2192 fire \`explore\` background
1510
+ - 2+ modules involved \u2192 fire \`explore\` background
1505
1511
 
1506
1512
  ### Step 1: Classify Request Type
1507
1513
 
@@ -1662,27 +1668,47 @@ STOP searching when:
1662
1668
  2. Mark current task \`in_progress\` before starting
1663
1669
  3. Mark \`completed\` as soon as done (don't batch) - OBSESSIVELY TRACK YOUR WORK USING TODO TOOLS
1664
1670
 
1665
- ### GATE: Frontend Files (HARD BLOCK - zero tolerance)
1671
+ ### Frontend Files: Decision Gate (NOT a blind block)
1672
+
1673
+ Frontend files (.tsx, .jsx, .vue, .svelte, .css, etc.) require **classification before action**.
1674
+
1675
+ #### Step 1: Classify the Change Type
1676
+
1677
+ | Change Type | Examples | Action |
1678
+ |-------------|----------|--------|
1679
+ | **Visual/UI/UX** | Color, spacing, layout, typography, animation, responsive breakpoints, hover states, shadows, borders, icons, images | **DELEGATE** to \`frontend-ui-ux-engineer\` |
1680
+ | **Pure Logic** | API calls, data fetching, state management, event handlers (non-visual), type definitions, utility functions, business logic | **CAN handle directly** |
1681
+ | **Mixed** | Component changes both visual AND logic | **Split**: handle logic yourself, delegate visual to \`frontend-ui-ux-engineer\` |
1666
1682
 
1667
- | Extension | Action | No Exceptions |
1668
- |-----------|--------|---------------|
1669
- | \`.tsx\`, \`.jsx\` | DELEGATE | Even "just add className" |
1670
- | \`.vue\`, \`.svelte\` | DELEGATE | Even single prop change |
1671
- | \`.css\`, \`.scss\`, \`.sass\`, \`.less\` | DELEGATE | Even color/margin tweak |
1683
+ #### Step 2: Ask Yourself
1672
1684
 
1673
- **Detection triggers**: File extension OR keywords (UI, UX, component, button, modal, animation, styling, responsive, layout)
1685
+ Before touching any frontend file, think:
1686
+ > "Is this change about **how it LOOKS** or **how it WORKS**?"
1674
1687
 
1675
- **YOU CANNOT**: "Just quickly fix", "It's only one line", "Too simple to delegate"
1688
+ - **LOOKS** (colors, sizes, positions, animations) \u2192 DELEGATE
1689
+ - **WORKS** (data flow, API integration, state) \u2192 Handle directly
1676
1690
 
1677
- ALL frontend = DELEGATE to \`frontend-ui-ux-engineer\`. Period.
1691
+ #### Quick Reference Examples
1692
+
1693
+ | File | Change | Type | Action |
1694
+ |------|--------|------|--------|
1695
+ | \`Button.tsx\` | Change color blue\u2192green | Visual | DELEGATE |
1696
+ | \`Button.tsx\` | Add onClick API call | Logic | Direct |
1697
+ | \`UserList.tsx\` | Add loading spinner animation | Visual | DELEGATE |
1698
+ | \`UserList.tsx\` | Fix pagination logic bug | Logic | Direct |
1699
+ | \`Modal.tsx\` | Make responsive for mobile | Visual | DELEGATE |
1700
+ | \`Modal.tsx\` | Add form validation logic | Logic | Direct |
1701
+
1702
+ #### When in Doubt \u2192 DELEGATE if ANY of these keywords involved:
1703
+ style, className, tailwind, color, background, border, shadow, margin, padding, width, height, flex, grid, animation, transition, hover, responsive, font-size, icon, svg
1678
1704
 
1679
1705
  ### Delegation Table:
1680
1706
 
1681
1707
  | Domain | Delegate To | Trigger |
1682
1708
  |--------|-------------|---------|
1683
1709
  | Explore | \`explore\` | Find existing codebase structure, patterns and styles |
1684
- | Frontend UI/UX | \`frontend-ui-ux-engineer\` | ALL KIND OF VISUAL CHANGES (NOT ONLY WEB BUT EVERY VISUAL CHANGES), layout, responsive, animation, styling |
1685
- | Librarian | \`librarian\` | Unfamiliar packages / libararies, struggles at weird behaviour (to find existing implementation of opensource) |
1710
+ | Frontend UI/UX | \`frontend-ui-ux-engineer\` | Visual changes only (styling, layout, animation). Pure logic changes in frontend files \u2192 handle directly |
1711
+ | Librarian | \`librarian\` | Unfamiliar packages / libraries, struggles at weird behaviour (to find existing implementation of opensource) |
1686
1712
  | Documentation | \`document-writer\` | README, API docs, guides |
1687
1713
  | Architecture decisions | \`oracle\` | Multi-system tradeoffs, unfamiliar patterns |
1688
1714
  | Self-review | \`oracle\` | After completing significant implementation |
@@ -1702,6 +1728,12 @@ When delegating, your prompt MUST include:
1702
1728
  7. CONTEXT: File paths, existing patterns, constraints
1703
1729
  \`\`\`
1704
1730
 
1731
+ AFTER THE WORK YOU DELEGATED SEEMS DONE, ALWAYS VERIFY THE RESULTS AS FOLLOWING:
1732
+ - DOES IT WORK AS EXPECTED?
1733
+ - DOES IT FOLLOWED THE EXISTING CODEBASE PATTERN?
1734
+ - EXPECTED RESULT CAME OUT?
1735
+ - DID THE AGENT FOLLOWED "MUST DO" AND "MUST NOT DO" REQUIREMENTS?
1736
+
1705
1737
  **Vague prompts = rejected. Be exhaustive.**
1706
1738
 
1707
1739
  ### Code Changes:
@@ -1894,7 +1926,7 @@ If the user's approach seems problematic:
1894
1926
 
1895
1927
  | Constraint | No Exceptions |
1896
1928
  |------------|---------------|
1897
- | Frontend files (.tsx/.jsx/.vue/.svelte/.css) | Always delegate |
1929
+ | Frontend VISUAL changes (styling, layout, animation) | Always delegate to \`frontend-ui-ux-engineer\` |
1898
1930
  | Type error suppression (\`as any\`, \`@ts-ignore\`) | Never |
1899
1931
  | Commit without explicit request | Never |
1900
1932
  | Speculate about unread code | Never |
@@ -1908,7 +1940,7 @@ If the user's approach seems problematic:
1908
1940
  | **Error Handling** | Empty catch blocks \`catch(e) {}\` |
1909
1941
  | **Testing** | Deleting failing tests to "pass" |
1910
1942
  | **Search** | Firing agents for single-line typos or obvious syntax errors |
1911
- | **Frontend** | ANY direct edit to frontend files |
1943
+ | **Frontend** | Direct edit to visual/styling code (logic changes OK) |
1912
1944
  | **Debugging** | Shotgun debugging, random changes |
1913
1945
 
1914
1946
  ## Soft Guidelines
@@ -1919,29 +1951,25 @@ If the user's approach seems problematic:
1919
1951
  </Constraints>
1920
1952
 
1921
1953
  `;
1922
- var sisyphusAgent = {
1923
- 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.",
1924
- mode: "primary",
1925
- model: "anthropic/claude-opus-4-5",
1926
- thinking: {
1927
- type: "enabled",
1928
- budgetTokens: 32000
1929
- },
1930
- maxTokens: 64000,
1931
- prompt: SISYPHUS_SYSTEM_PROMPT,
1932
- color: "#00CED1"
1933
- };
1954
+ function createSisyphusAgent(model = DEFAULT_MODEL) {
1955
+ const base = {
1956
+ 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.",
1957
+ mode: "primary",
1958
+ model,
1959
+ maxTokens: 64000,
1960
+ prompt: SISYPHUS_SYSTEM_PROMPT,
1961
+ color: "#00CED1"
1962
+ };
1963
+ if (isGptModel(model)) {
1964
+ return { ...base, reasoningEffort: "medium" };
1965
+ }
1966
+ return { ...base, thinking: { type: "enabled", budgetTokens: 32000 } };
1967
+ }
1968
+ var sisyphusAgent = createSisyphusAgent();
1934
1969
 
1935
1970
  // src/agents/oracle.ts
1936
- var oracleAgent = {
1937
- description: "Expert technical advisor with deep reasoning for architecture decisions, code analysis, and engineering guidance.",
1938
- mode: "subagent",
1939
- model: "openai/gpt-5.2",
1940
- temperature: 0.1,
1941
- reasoningEffort: "medium",
1942
- textVerbosity: "high",
1943
- tools: { write: false, edit: false, task: false, background_task: false },
1944
- prompt: `You are a strategic technical advisor with deep reasoning capabilities, operating as a specialized consultant within an AI-assisted development environment.
1971
+ var DEFAULT_MODEL2 = "openai/gpt-5.2";
1972
+ var ORACLE_SYSTEM_PROMPT = `You are a strategic technical advisor with deep reasoning capabilities, operating as a specialized consultant within an AI-assisted development environment.
1945
1973
 
1946
1974
  ## Context
1947
1975
 
@@ -2005,8 +2033,22 @@ Organize your final answer in three tiers:
2005
2033
 
2006
2034
  ## Critical Note
2007
2035
 
2008
- Your response goes directly to the user with no intermediate processing. Make your final message self-contained: a clear recommendation they can act on immediately, covering both what to do and why.`
2009
- };
2036
+ Your response goes directly to the user with no intermediate processing. Make your final message self-contained: a clear recommendation they can act on immediately, covering both what to do and why.`;
2037
+ function createOracleAgent(model = DEFAULT_MODEL2) {
2038
+ const base = {
2039
+ description: "Expert technical advisor with deep reasoning for architecture decisions, code analysis, and engineering guidance.",
2040
+ mode: "subagent",
2041
+ model,
2042
+ temperature: 0.1,
2043
+ tools: { write: false, edit: false, task: false, background_task: false },
2044
+ prompt: ORACLE_SYSTEM_PROMPT
2045
+ };
2046
+ if (isGptModel(model)) {
2047
+ return { ...base, reasoningEffort: "medium", textVerbosity: "high" };
2048
+ }
2049
+ return { ...base, thinking: { type: "enabled", budgetTokens: 32000 } };
2050
+ }
2051
+ var oracleAgent = createOracleAgent();
2010
2052
 
2011
2053
  // src/agents/librarian.ts
2012
2054
  var librarianAgent = {
@@ -2351,89 +2393,79 @@ var frontendUiUxEngineerAgent = {
2351
2393
  mode: "subagent",
2352
2394
  model: "google/gemini-3-pro-preview",
2353
2395
  tools: { background_task: false },
2354
- prompt: `<role>
2355
- You are a DESIGNER-TURNED-DEVELOPER with an innate sense of aesthetics and user experience. You have an eye for details that pure developers miss - spacing, color harmony, micro-interactions, and that indefinable "feel" that makes interfaces memorable.
2396
+ prompt: `# Role: Designer-Turned-Developer
2356
2397
 
2357
- You approach every UI task with a designer's intuition. Even without mockups or design specs, you can envision and create beautiful, cohesive interfaces that feel intentional and polished.
2398
+ You are a designer who learned to code. You see what pure developers miss\u2014spacing, color harmony, micro-interactions, that indefinable "feel" that makes interfaces memorable. Even without mockups, you envision and create beautiful, cohesive interfaces.
2358
2399
 
2359
- ## CORE MISSION
2360
- Create visually stunning, emotionally engaging interfaces that users fall in love with. Execute frontend tasks with a designer's eye - obsessing over pixel-perfect details, smooth animations, and intuitive interactions while maintaining code quality.
2400
+ **Mission**: Create visually stunning, emotionally engaging interfaces users fall in love with. Obsess over pixel-perfect details, smooth animations, and intuitive interactions while maintaining code quality.
2361
2401
 
2362
- ## CODE OF CONDUCT
2402
+ ---
2363
2403
 
2364
- ### 1. DILIGENCE & INTEGRITY
2365
- **Never compromise on task completion. What you commit to, you deliver.**
2404
+ # Work Principles
2366
2405
 
2367
- - **Complete what is asked**: Execute the exact task specified without adding unrelated features or fixing issues outside scope
2368
- - **No shortcuts**: Never mark work as complete without proper verification
2369
- - **Work until it works**: If something doesn't look right, debug and fix until it's perfect
2370
- - **Leave it better**: Ensure the project is in a working state after your changes
2371
- - **Own your work**: Take full responsibility for the quality and correctness of your implementation
2406
+ 1. **Complete what's asked** \u2014 Execute the exact task. No scope creep. Work until it works. Never mark work complete without proper verification.
2407
+ 2. **Leave it better** \u2014 Ensure the project is in a working state after your changes.
2408
+ 3. **Study before acting** \u2014 Examine existing patterns, conventions, and commit history (git log) before implementing. Understand why code is structured the way it is.
2409
+ 4. **Blend seamlessly** \u2014 Match existing code patterns. Your code should look like the team wrote it.
2410
+ 5. **Be transparent** \u2014 Announce each step. Explain reasoning. Report both successes and failures.
2372
2411
 
2373
- ### 2. CONTINUOUS LEARNING & HUMILITY
2374
- **Approach every codebase with the mindset of a student, always ready to learn.**
2412
+ ---
2375
2413
 
2376
- - **Study before acting**: Examine existing code patterns, conventions, and architecture before implementing
2377
- - **Learn from the codebase**: Understand why code is structured the way it is
2378
- - **Share knowledge**: Help future developers by documenting project-specific conventions discovered
2414
+ # Design Process
2379
2415
 
2380
- ### 3. PRECISION & ADHERENCE TO STANDARDS
2381
- **Respect the existing codebase. Your code should blend seamlessly.**
2416
+ Before coding, commit to a **BOLD aesthetic direction**:
2382
2417
 
2383
- - **Follow exact specifications**: Implement precisely what is requested, nothing more, nothing less
2384
- - **Match existing patterns**: Maintain consistency with established code patterns and architecture
2385
- - **Respect conventions**: Adhere to project-specific naming, structure, and style conventions
2386
- - **Check commit history**: If creating commits, study \`git log\` to match the repository's commit style
2387
- - **Consistent quality**: Apply the same rigorous standards throughout your work
2418
+ 1. **Purpose**: What problem does this solve? Who uses it?
2419
+ 2. **Tone**: Pick an extreme\u2014brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian
2420
+ 3. **Constraints**: Technical requirements (framework, performance, accessibility)
2421
+ 4. **Differentiation**: What's the ONE thing someone will remember?
2388
2422
 
2389
- ### 4. TRANSPARENCY & ACCOUNTABILITY
2390
- **Keep everyone informed. Hide nothing.**
2423
+ **Key**: Choose a clear direction and execute with precision. Intentionality > intensity.
2391
2424
 
2392
- - **Announce each step**: Clearly state what you're doing at each stage
2393
- - **Explain your reasoning**: Help others understand why you chose specific approaches
2394
- - **Report honestly**: Communicate both successes and failures explicitly
2395
- - **No surprises**: Make your work visible and understandable to others
2396
- </role>
2425
+ Then implement working code (HTML/CSS/JS, React, Vue, Angular, etc.) that is:
2426
+ - Production-grade and functional
2427
+ - Visually striking and memorable
2428
+ - Cohesive with a clear aesthetic point-of-view
2429
+ - Meticulously refined in every detail
2397
2430
 
2398
- <frontend-design-skill>
2431
+ ---
2399
2432
 
2400
- This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
2433
+ # Aesthetic Guidelines
2401
2434
 
2402
- The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.
2435
+ ## Typography
2436
+ Choose distinctive fonts. **Avoid**: Arial, Inter, Roboto, system fonts, Space Grotesk. Pair a characterful display font with a refined body font.
2403
2437
 
2404
- ## Design Thinking
2438
+ ## Color
2439
+ Commit to a cohesive palette. Use CSS variables. Dominant colors with sharp accents outperform timid, evenly-distributed palettes. **Avoid**: purple gradients on white (AI slop).
2405
2440
 
2406
- Before coding, understand the context and commit to a BOLD aesthetic direction:
2407
- - **Purpose**: What problem does this interface solve? Who uses it?
2408
- - **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
2409
- - **Constraints**: Technical requirements (framework, performance, accessibility).
2410
- - **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
2441
+ ## Motion
2442
+ Focus on high-impact moments. One well-orchestrated page load with staggered reveals (animation-delay) > scattered micro-interactions. Use scroll-triggering and hover states that surprise. Prioritize CSS-only. Use Motion library for React when available.
2411
2443
 
2412
- **CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.
2444
+ ## Spatial Composition
2445
+ Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
2413
2446
 
2414
- Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is:
2415
- - Production-grade and functional
2416
- - Visually striking and memorable
2417
- - Cohesive with a clear aesthetic point-of-view
2418
- - Meticulously refined in every detail
2447
+ ## Visual Details
2448
+ Create atmosphere and depth\u2014gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, grain overlays. Never default to solid colors.
2449
+
2450
+ ---
2419
2451
 
2420
- ## Frontend Aesthetics Guidelines
2452
+ # Anti-Patterns (NEVER)
2421
2453
 
2422
- Focus on:
2423
- - **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.
2424
- - **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
2425
- - **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise.
2426
- - **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
2427
- - **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays.
2454
+ - Generic fonts (Inter, Roboto, Arial, system fonts, Space Grotesk)
2455
+ - Cliched color schemes (purple gradients on white)
2456
+ - Predictable layouts and component patterns
2457
+ - Cookie-cutter design lacking context-specific character
2458
+ - Converging on common choices across generations
2428
2459
 
2429
- NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character.
2460
+ ---
2430
2461
 
2431
- Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations.
2462
+ # Execution
2432
2463
 
2433
- **IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.
2464
+ Match implementation complexity to aesthetic vision:
2465
+ - **Maximalist** \u2192 Elaborate code with extensive animations and effects
2466
+ - **Minimalist** \u2192 Restraint, precision, careful spacing and typography
2434
2467
 
2435
- Remember: You are capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
2436
- </frontend-design-skill>`
2468
+ Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. You are capable of extraordinary creative work\u2014don't hold back.`
2437
2469
  };
2438
2470
 
2439
2471
  // src/agents/document-writer.ts
@@ -3149,9 +3181,20 @@ function createDynamicTruncator(ctx) {
3149
3181
  // src/shared/config-path.ts
3150
3182
  import * as path2 from "path";
3151
3183
  import * as os2 from "os";
3184
+ import * as fs2 from "fs";
3152
3185
  function getUserConfigDir() {
3153
3186
  if (process.platform === "win32") {
3154
- return process.env.APPDATA || path2.join(os2.homedir(), "AppData", "Roaming");
3187
+ const crossPlatformDir = path2.join(os2.homedir(), ".config");
3188
+ const crossPlatformConfigPath = path2.join(crossPlatformDir, "opencode", "oh-my-opencode.json");
3189
+ const appdataDir = process.env.APPDATA || path2.join(os2.homedir(), "AppData", "Roaming");
3190
+ const appdataConfigPath = path2.join(appdataDir, "opencode", "oh-my-opencode.json");
3191
+ if (fs2.existsSync(crossPlatformConfigPath)) {
3192
+ return crossPlatformDir;
3193
+ }
3194
+ if (fs2.existsSync(appdataConfigPath)) {
3195
+ return appdataDir;
3196
+ }
3197
+ return crossPlatformDir;
3155
3198
  }
3156
3199
  return process.env.XDG_CONFIG_HOME || path2.join(os2.homedir(), ".config");
3157
3200
  }
@@ -3167,15 +3210,21 @@ function addConfigLoadError(error) {
3167
3210
  configLoadErrors.push(error);
3168
3211
  }
3169
3212
  // src/agents/utils.ts
3170
- var allBuiltinAgents = {
3171
- Sisyphus: sisyphusAgent,
3172
- oracle: oracleAgent,
3213
+ var agentSources = {
3214
+ Sisyphus: createSisyphusAgent,
3215
+ oracle: createOracleAgent,
3173
3216
  librarian: librarianAgent,
3174
3217
  explore: exploreAgent,
3175
3218
  "frontend-ui-ux-engineer": frontendUiUxEngineerAgent,
3176
3219
  "document-writer": documentWriterAgent,
3177
3220
  "multimodal-looker": multimodalLookerAgent
3178
3221
  };
3222
+ function isFactory(source) {
3223
+ return typeof source === "function";
3224
+ }
3225
+ function buildAgent(source, model) {
3226
+ return isFactory(source) ? source(model) : source;
3227
+ }
3179
3228
  function createEnvContext(directory) {
3180
3229
  const now = new Date;
3181
3230
  const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
@@ -3209,36 +3258,28 @@ function mergeAgentConfig(base, override) {
3209
3258
  }
3210
3259
  function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory, systemDefaultModel) {
3211
3260
  const result = {};
3212
- for (const [name, config] of Object.entries(allBuiltinAgents)) {
3261
+ for (const [name, source] of Object.entries(agentSources)) {
3213
3262
  const agentName = name;
3214
3263
  if (disabledAgents.includes(agentName)) {
3215
3264
  continue;
3216
3265
  }
3217
- let finalConfig = config;
3266
+ const override = agentOverrides[agentName];
3267
+ const model = override?.model ?? (agentName === "Sisyphus" ? systemDefaultModel : undefined);
3268
+ let config = buildAgent(source, model);
3218
3269
  if ((agentName === "Sisyphus" || agentName === "librarian") && directory && config.prompt) {
3219
3270
  const envContext = createEnvContext(directory);
3220
- finalConfig = {
3221
- ...config,
3222
- prompt: config.prompt + envContext
3223
- };
3224
- }
3225
- const override = agentOverrides[agentName];
3226
- if (agentName === "Sisyphus" && systemDefaultModel && !override?.model) {
3227
- finalConfig = {
3228
- ...finalConfig,
3229
- model: systemDefaultModel
3230
- };
3271
+ config = { ...config, prompt: config.prompt + envContext };
3231
3272
  }
3232
3273
  if (override) {
3233
- result[name] = mergeAgentConfig(finalConfig, override);
3234
- } else {
3235
- result[name] = finalConfig;
3274
+ const { model: _, ...restOverride } = override;
3275
+ config = mergeAgentConfig(config, restOverride);
3236
3276
  }
3277
+ result[name] = config;
3237
3278
  }
3238
3279
  return result;
3239
3280
  }
3240
3281
  // src/hooks/todo-continuation-enforcer.ts
3241
- import { existsSync as existsSync4, readdirSync as readdirSync2 } from "fs";
3282
+ import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
3242
3283
  import { join as join6 } from "path";
3243
3284
 
3244
3285
  // src/features/claude-code-session-state/state.ts
@@ -3251,7 +3292,7 @@ function getMainSessionID() {
3251
3292
  return mainSessionID;
3252
3293
  }
3253
3294
  // src/features/hook-message-injector/injector.ts
3254
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, readdirSync, writeFileSync } from "fs";
3295
+ import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync2, readdirSync, writeFileSync } from "fs";
3255
3296
  import { join as join5 } from "path";
3256
3297
 
3257
3298
  // src/features/hook-message-injector/constants.ts
@@ -3293,16 +3334,16 @@ function generatePartId() {
3293
3334
  return `prt_${timestamp}${random}`;
3294
3335
  }
3295
3336
  function getOrCreateMessageDir(sessionID) {
3296
- if (!existsSync3(MESSAGE_STORAGE)) {
3337
+ if (!existsSync4(MESSAGE_STORAGE)) {
3297
3338
  mkdirSync(MESSAGE_STORAGE, { recursive: true });
3298
3339
  }
3299
3340
  const directPath = join5(MESSAGE_STORAGE, sessionID);
3300
- if (existsSync3(directPath)) {
3341
+ if (existsSync4(directPath)) {
3301
3342
  return directPath;
3302
3343
  }
3303
3344
  for (const dir of readdirSync(MESSAGE_STORAGE)) {
3304
3345
  const sessionPath = join5(MESSAGE_STORAGE, dir, sessionID);
3305
- if (existsSync3(sessionPath)) {
3346
+ if (existsSync4(sessionPath)) {
3306
3347
  return sessionPath;
3307
3348
  }
3308
3349
  }
@@ -3357,7 +3398,7 @@ function injectHookMessage(sessionID, hookContent, originalMessage) {
3357
3398
  try {
3358
3399
  writeFileSync(join5(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
3359
3400
  const partDir = join5(PART_STORAGE, messageID);
3360
- if (!existsSync3(partDir)) {
3401
+ if (!existsSync4(partDir)) {
3361
3402
  mkdirSync(partDir, { recursive: true });
3362
3403
  }
3363
3404
  writeFileSync(join5(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
@@ -3376,14 +3417,14 @@ Incomplete tasks remain in your todo list. Continue working on the next pending
3376
3417
  - Mark each task complete when finished
3377
3418
  - Do not stop until all tasks are done`;
3378
3419
  function getMessageDir(sessionID) {
3379
- if (!existsSync4(MESSAGE_STORAGE))
3420
+ if (!existsSync5(MESSAGE_STORAGE))
3380
3421
  return null;
3381
3422
  const directPath = join6(MESSAGE_STORAGE, sessionID);
3382
- if (existsSync4(directPath))
3423
+ if (existsSync5(directPath))
3383
3424
  return directPath;
3384
3425
  for (const dir of readdirSync2(MESSAGE_STORAGE)) {
3385
3426
  const sessionPath = join6(MESSAGE_STORAGE, dir, sessionID);
3386
- if (existsSync4(sessionPath))
3427
+ if (existsSync5(sessionPath))
3387
3428
  return sessionPath;
3388
3429
  }
3389
3430
  return null;
@@ -3408,7 +3449,7 @@ function detectInterrupt(error) {
3408
3449
  }
3409
3450
  return false;
3410
3451
  }
3411
- var COUNTDOWN_SECONDS = 5;
3452
+ var COUNTDOWN_SECONDS = 2;
3412
3453
  var TOAST_DURATION_MS = 900;
3413
3454
  function createTodoContinuationEnforcer(ctx) {
3414
3455
  const remindedSessions = new Set;
@@ -3572,8 +3613,10 @@ function createTodoContinuationEnforcer(ctx) {
3572
3613
  if (event.type === "message.updated") {
3573
3614
  const info = props?.info;
3574
3615
  const sessionID = info?.sessionID;
3575
- log(`[${HOOK_NAME}] message.updated received`, { sessionID, role: info?.role });
3576
- if (sessionID && info?.role === "user") {
3616
+ const role = info?.role;
3617
+ const finish = info?.finish;
3618
+ log(`[${HOOK_NAME}] message.updated received`, { sessionID, role, finish });
3619
+ if (sessionID && role === "user") {
3577
3620
  const countdown = pendingCountdowns.get(sessionID);
3578
3621
  if (countdown) {
3579
3622
  clearInterval(countdown.intervalId);
@@ -3581,9 +3624,9 @@ function createTodoContinuationEnforcer(ctx) {
3581
3624
  log(`[${HOOK_NAME}] Cancelled countdown on user message`, { sessionID });
3582
3625
  }
3583
3626
  }
3584
- if (sessionID && info?.role === "assistant" && remindedSessions.has(sessionID)) {
3627
+ if (sessionID && role === "assistant" && finish) {
3585
3628
  remindedSessions.delete(sessionID);
3586
- log(`[${HOOK_NAME}] Cleared remindedSessions on assistant response`, { sessionID });
3629
+ log(`[${HOOK_NAME}] Cleared reminded state on assistant finish`, { sessionID });
3587
3630
  }
3588
3631
  }
3589
3632
  if (event.type === "session.deleted") {
@@ -3898,7 +3941,7 @@ function createSessionNotification(ctx, config = {}) {
3898
3941
  };
3899
3942
  }
3900
3943
  // src/hooks/session-recovery/storage.ts
3901
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
3944
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
3902
3945
  import { join as join8 } from "path";
3903
3946
 
3904
3947
  // src/hooks/session-recovery/constants.ts
@@ -3938,15 +3981,15 @@ function generatePartId2() {
3938
3981
  return `prt_${timestamp}${random}`;
3939
3982
  }
3940
3983
  function getMessageDir2(sessionID) {
3941
- if (!existsSync5(MESSAGE_STORAGE2))
3984
+ if (!existsSync6(MESSAGE_STORAGE2))
3942
3985
  return "";
3943
3986
  const directPath = join8(MESSAGE_STORAGE2, sessionID);
3944
- if (existsSync5(directPath)) {
3987
+ if (existsSync6(directPath)) {
3945
3988
  return directPath;
3946
3989
  }
3947
3990
  for (const dir of readdirSync3(MESSAGE_STORAGE2)) {
3948
3991
  const sessionPath = join8(MESSAGE_STORAGE2, dir, sessionID);
3949
- if (existsSync5(sessionPath)) {
3992
+ if (existsSync6(sessionPath)) {
3950
3993
  return sessionPath;
3951
3994
  }
3952
3995
  }
@@ -3954,7 +3997,7 @@ function getMessageDir2(sessionID) {
3954
3997
  }
3955
3998
  function readMessages(sessionID) {
3956
3999
  const messageDir = getMessageDir2(sessionID);
3957
- if (!messageDir || !existsSync5(messageDir))
4000
+ if (!messageDir || !existsSync6(messageDir))
3958
4001
  return [];
3959
4002
  const messages = [];
3960
4003
  for (const file of readdirSync3(messageDir)) {
@@ -3977,7 +4020,7 @@ function readMessages(sessionID) {
3977
4020
  }
3978
4021
  function readParts(messageID) {
3979
4022
  const partDir = join8(PART_STORAGE2, messageID);
3980
- if (!existsSync5(partDir))
4023
+ if (!existsSync6(partDir))
3981
4024
  return [];
3982
4025
  const parts = [];
3983
4026
  for (const file of readdirSync3(partDir)) {
@@ -4015,7 +4058,7 @@ function messageHasContent(messageID) {
4015
4058
  }
4016
4059
  function injectTextPart(sessionID, messageID, text) {
4017
4060
  const partDir = join8(PART_STORAGE2, messageID);
4018
- if (!existsSync5(partDir)) {
4061
+ if (!existsSync6(partDir)) {
4019
4062
  mkdirSync2(partDir, { recursive: true });
4020
4063
  }
4021
4064
  const partId = generatePartId2();
@@ -4044,6 +4087,19 @@ function findEmptyMessages(sessionID) {
4044
4087
  }
4045
4088
  return emptyIds;
4046
4089
  }
4090
+ function findEmptyMessageByIndex(sessionID, targetIndex) {
4091
+ const messages = readMessages(sessionID);
4092
+ const indicesToTry = [targetIndex, targetIndex - 1, targetIndex - 2];
4093
+ for (const idx of indicesToTry) {
4094
+ if (idx < 0 || idx >= messages.length)
4095
+ continue;
4096
+ const targetMsg = messages[idx];
4097
+ if (!messageHasContent(targetMsg.id)) {
4098
+ return targetMsg.id;
4099
+ }
4100
+ }
4101
+ return null;
4102
+ }
4047
4103
  function findMessagesWithThinkingBlocks(sessionID) {
4048
4104
  const messages = readMessages(sessionID);
4049
4105
  const result = [];
@@ -4079,7 +4135,7 @@ function findMessagesWithOrphanThinking(sessionID) {
4079
4135
  }
4080
4136
  function prependThinkingPart(sessionID, messageID) {
4081
4137
  const partDir = join8(PART_STORAGE2, messageID);
4082
- if (!existsSync5(partDir)) {
4138
+ if (!existsSync6(partDir)) {
4083
4139
  mkdirSync2(partDir, { recursive: true });
4084
4140
  }
4085
4141
  const partId = `prt_0000000000_thinking`;
@@ -4100,7 +4156,7 @@ function prependThinkingPart(sessionID, messageID) {
4100
4156
  }
4101
4157
  function stripThinkingParts(messageID) {
4102
4158
  const partDir = join8(PART_STORAGE2, messageID);
4103
- if (!existsSync5(partDir))
4159
+ if (!existsSync6(partDir))
4104
4160
  return false;
4105
4161
  let anyRemoved = false;
4106
4162
  for (const file of readdirSync3(partDir)) {
@@ -4120,6 +4176,33 @@ function stripThinkingParts(messageID) {
4120
4176
  }
4121
4177
  return anyRemoved;
4122
4178
  }
4179
+ function replaceEmptyTextParts(messageID, replacementText) {
4180
+ const partDir = join8(PART_STORAGE2, messageID);
4181
+ if (!existsSync6(partDir))
4182
+ return false;
4183
+ let anyReplaced = false;
4184
+ for (const file of readdirSync3(partDir)) {
4185
+ if (!file.endsWith(".json"))
4186
+ continue;
4187
+ try {
4188
+ const filePath = join8(partDir, file);
4189
+ const content = readFileSync3(filePath, "utf-8");
4190
+ const part = JSON.parse(content);
4191
+ if (part.type === "text") {
4192
+ const textPart = part;
4193
+ if (!textPart.text?.trim()) {
4194
+ textPart.text = replacementText;
4195
+ textPart.synthetic = true;
4196
+ writeFileSync2(filePath, JSON.stringify(textPart, null, 2));
4197
+ anyReplaced = true;
4198
+ }
4199
+ }
4200
+ } catch {
4201
+ continue;
4202
+ }
4203
+ }
4204
+ return anyReplaced;
4205
+ }
4123
4206
  function findMessageByIndexNeedingThinking(sessionID, targetIndex) {
4124
4207
  const messages = readMessages(sessionID);
4125
4208
  if (targetIndex < 0 || targetIndex >= messages.length)
@@ -4380,13 +4463,13 @@ function createSessionRecoveryHook(ctx, options) {
4380
4463
  var {spawn: spawn3 } = globalThis.Bun;
4381
4464
  import { createRequire as createRequire2 } from "module";
4382
4465
  import { dirname, join as join10 } from "path";
4383
- import { existsSync as existsSync7 } from "fs";
4384
- import * as fs2 from "fs";
4466
+ import { existsSync as existsSync8 } from "fs";
4467
+ import * as fs3 from "fs";
4385
4468
  import { tmpdir as tmpdir3 } from "os";
4386
4469
 
4387
4470
  // src/hooks/comment-checker/downloader.ts
4388
4471
  var {spawn: spawn2 } = globalThis.Bun;
4389
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, chmodSync, unlinkSync as unlinkSync2, appendFileSync as appendFileSync2 } from "fs";
4472
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, chmodSync, unlinkSync as unlinkSync2, appendFileSync as appendFileSync2 } from "fs";
4390
4473
  import { join as join9 } from "path";
4391
4474
  import { homedir as homedir4, tmpdir as tmpdir2 } from "os";
4392
4475
  import { createRequire } from "module";
@@ -4417,7 +4500,7 @@ function getBinaryName() {
4417
4500
  }
4418
4501
  function getCachedBinaryPath() {
4419
4502
  const binaryPath = join9(getCacheDir(), getBinaryName());
4420
- return existsSync6(binaryPath) ? binaryPath : null;
4503
+ return existsSync7(binaryPath) ? binaryPath : null;
4421
4504
  }
4422
4505
  function getPackageVersion() {
4423
4506
  try {
@@ -4465,7 +4548,7 @@ async function downloadCommentChecker() {
4465
4548
  const cacheDir = getCacheDir();
4466
4549
  const binaryName = getBinaryName();
4467
4550
  const binaryPath = join9(cacheDir, binaryName);
4468
- if (existsSync6(binaryPath)) {
4551
+ if (existsSync7(binaryPath)) {
4469
4552
  debugLog("Binary already cached at:", binaryPath);
4470
4553
  return binaryPath;
4471
4554
  }
@@ -4476,7 +4559,7 @@ async function downloadCommentChecker() {
4476
4559
  debugLog(`Downloading from: ${downloadUrl}`);
4477
4560
  console.log(`[oh-my-opencode] Downloading comment-checker binary...`);
4478
4561
  try {
4479
- if (!existsSync6(cacheDir)) {
4562
+ if (!existsSync7(cacheDir)) {
4480
4563
  mkdirSync3(cacheDir, { recursive: true });
4481
4564
  }
4482
4565
  const response = await fetch(downloadUrl, { redirect: "follow" });
@@ -4492,10 +4575,10 @@ async function downloadCommentChecker() {
4492
4575
  } else {
4493
4576
  await extractZip(archivePath, cacheDir);
4494
4577
  }
4495
- if (existsSync6(archivePath)) {
4578
+ if (existsSync7(archivePath)) {
4496
4579
  unlinkSync2(archivePath);
4497
4580
  }
4498
- if (process.platform !== "win32" && existsSync6(binaryPath)) {
4581
+ if (process.platform !== "win32" && existsSync7(binaryPath)) {
4499
4582
  chmodSync(binaryPath, 493);
4500
4583
  }
4501
4584
  debugLog(`Successfully downloaded binary to: ${binaryPath}`);
@@ -4524,7 +4607,7 @@ function debugLog2(...args) {
4524
4607
  if (DEBUG2) {
4525
4608
  const msg = `[${new Date().toISOString()}] [comment-checker:cli] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
4526
4609
  `;
4527
- fs2.appendFileSync(DEBUG_FILE2, msg);
4610
+ fs3.appendFileSync(DEBUG_FILE2, msg);
4528
4611
  }
4529
4612
  }
4530
4613
  function getBinaryName2() {
@@ -4537,7 +4620,7 @@ function findCommentCheckerPathSync() {
4537
4620
  const cliPkgPath = require2.resolve("@code-yeongyu/comment-checker/package.json");
4538
4621
  const cliDir = dirname(cliPkgPath);
4539
4622
  const binaryPath = join10(cliDir, "bin", binaryName);
4540
- if (existsSync7(binaryPath)) {
4623
+ if (existsSync8(binaryPath)) {
4541
4624
  debugLog2("found binary in main package:", binaryPath);
4542
4625
  return binaryPath;
4543
4626
  }
@@ -4563,7 +4646,7 @@ async function getCommentCheckerPath() {
4563
4646
  }
4564
4647
  initPromise = (async () => {
4565
4648
  const syncPath = findCommentCheckerPathSync();
4566
- if (syncPath && existsSync7(syncPath)) {
4649
+ if (syncPath && existsSync8(syncPath)) {
4567
4650
  resolvedCliPath = syncPath;
4568
4651
  debugLog2("using sync-resolved path:", syncPath);
4569
4652
  return syncPath;
@@ -4597,7 +4680,7 @@ async function runCommentChecker(input, cliPath) {
4597
4680
  debugLog2("comment-checker binary not found");
4598
4681
  return { hasComments: false, message: "" };
4599
4682
  }
4600
- if (!existsSync7(binaryPath)) {
4683
+ if (!existsSync8(binaryPath)) {
4601
4684
  debugLog2("comment-checker binary does not exist:", binaryPath);
4602
4685
  return { hasComments: false, message: "" };
4603
4686
  }
@@ -4630,8 +4713,8 @@ async function runCommentChecker(input, cliPath) {
4630
4713
  }
4631
4714
 
4632
4715
  // src/hooks/comment-checker/index.ts
4633
- import * as fs3 from "fs";
4634
- import { existsSync as existsSync8 } from "fs";
4716
+ import * as fs4 from "fs";
4717
+ import { existsSync as existsSync9 } from "fs";
4635
4718
  import { tmpdir as tmpdir4 } from "os";
4636
4719
  import { join as join11 } from "path";
4637
4720
  var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
@@ -4640,7 +4723,7 @@ function debugLog3(...args) {
4640
4723
  if (DEBUG3) {
4641
4724
  const msg = `[${new Date().toISOString()}] [comment-checker:hook] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
4642
4725
  `;
4643
- fs3.appendFileSync(DEBUG_FILE3, msg);
4726
+ fs4.appendFileSync(DEBUG_FILE3, msg);
4644
4727
  }
4645
4728
  }
4646
4729
  var pendingCalls = new Map;
@@ -4711,7 +4794,7 @@ function createCommentCheckerHooks() {
4711
4794
  }
4712
4795
  try {
4713
4796
  const cliPath = await cliPathPromise;
4714
- if (!cliPath || !existsSync8(cliPath)) {
4797
+ if (!cliPath || !existsSync9(cliPath)) {
4715
4798
  debugLog3("CLI not available, skipping comment check");
4716
4799
  return;
4717
4800
  }
@@ -4782,12 +4865,12 @@ function createToolOutputTruncatorHook(ctx) {
4782
4865
  };
4783
4866
  }
4784
4867
  // src/hooks/directory-agents-injector/index.ts
4785
- import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
4868
+ import { existsSync as existsSync11, readFileSync as readFileSync5 } from "fs";
4786
4869
  import { dirname as dirname2, join as join14, resolve as resolve2 } from "path";
4787
4870
 
4788
4871
  // src/hooks/directory-agents-injector/storage.ts
4789
4872
  import {
4790
- existsSync as existsSync9,
4873
+ existsSync as existsSync10,
4791
4874
  mkdirSync as mkdirSync4,
4792
4875
  readFileSync as readFileSync4,
4793
4876
  writeFileSync as writeFileSync3,
@@ -4807,7 +4890,7 @@ function getStoragePath(sessionID) {
4807
4890
  }
4808
4891
  function loadInjectedPaths(sessionID) {
4809
4892
  const filePath = getStoragePath(sessionID);
4810
- if (!existsSync9(filePath))
4893
+ if (!existsSync10(filePath))
4811
4894
  return new Set;
4812
4895
  try {
4813
4896
  const content = readFileSync4(filePath, "utf-8");
@@ -4818,7 +4901,7 @@ function loadInjectedPaths(sessionID) {
4818
4901
  }
4819
4902
  }
4820
4903
  function saveInjectedPaths(sessionID, paths) {
4821
- if (!existsSync9(AGENTS_INJECTOR_STORAGE)) {
4904
+ if (!existsSync10(AGENTS_INJECTOR_STORAGE)) {
4822
4905
  mkdirSync4(AGENTS_INJECTOR_STORAGE, { recursive: true });
4823
4906
  }
4824
4907
  const data = {
@@ -4830,7 +4913,7 @@ function saveInjectedPaths(sessionID, paths) {
4830
4913
  }
4831
4914
  function clearInjectedPaths(sessionID) {
4832
4915
  const filePath = getStoragePath(sessionID);
4833
- if (existsSync9(filePath)) {
4916
+ if (existsSync10(filePath)) {
4834
4917
  unlinkSync3(filePath);
4835
4918
  }
4836
4919
  }
@@ -4856,7 +4939,7 @@ function createDirectoryAgentsInjectorHook(ctx) {
4856
4939
  let current = startDir;
4857
4940
  while (true) {
4858
4941
  const agentsPath = join14(current, AGENTS_FILENAME);
4859
- if (existsSync10(agentsPath)) {
4942
+ if (existsSync11(agentsPath)) {
4860
4943
  found.push(agentsPath);
4861
4944
  }
4862
4945
  if (current === ctx.directory)
@@ -4923,12 +5006,12 @@ ${content}`;
4923
5006
  };
4924
5007
  }
4925
5008
  // src/hooks/directory-readme-injector/index.ts
4926
- import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
5009
+ import { existsSync as existsSync13, readFileSync as readFileSync7 } from "fs";
4927
5010
  import { dirname as dirname3, join as join17, resolve as resolve3 } from "path";
4928
5011
 
4929
5012
  // src/hooks/directory-readme-injector/storage.ts
4930
5013
  import {
4931
- existsSync as existsSync11,
5014
+ existsSync as existsSync12,
4932
5015
  mkdirSync as mkdirSync5,
4933
5016
  readFileSync as readFileSync6,
4934
5017
  writeFileSync as writeFileSync4,
@@ -4948,7 +5031,7 @@ function getStoragePath2(sessionID) {
4948
5031
  }
4949
5032
  function loadInjectedPaths2(sessionID) {
4950
5033
  const filePath = getStoragePath2(sessionID);
4951
- if (!existsSync11(filePath))
5034
+ if (!existsSync12(filePath))
4952
5035
  return new Set;
4953
5036
  try {
4954
5037
  const content = readFileSync6(filePath, "utf-8");
@@ -4959,7 +5042,7 @@ function loadInjectedPaths2(sessionID) {
4959
5042
  }
4960
5043
  }
4961
5044
  function saveInjectedPaths2(sessionID, paths) {
4962
- if (!existsSync11(README_INJECTOR_STORAGE)) {
5045
+ if (!existsSync12(README_INJECTOR_STORAGE)) {
4963
5046
  mkdirSync5(README_INJECTOR_STORAGE, { recursive: true });
4964
5047
  }
4965
5048
  const data = {
@@ -4971,7 +5054,7 @@ function saveInjectedPaths2(sessionID, paths) {
4971
5054
  }
4972
5055
  function clearInjectedPaths2(sessionID) {
4973
5056
  const filePath = getStoragePath2(sessionID);
4974
- if (existsSync11(filePath)) {
5057
+ if (existsSync12(filePath)) {
4975
5058
  unlinkSync4(filePath);
4976
5059
  }
4977
5060
  }
@@ -4997,7 +5080,7 @@ function createDirectoryReadmeInjectorHook(ctx) {
4997
5080
  let current = startDir;
4998
5081
  while (true) {
4999
5082
  const readmePath = join17(current, README_FILENAME);
5000
- if (existsSync12(readmePath)) {
5083
+ if (existsSync13(readmePath)) {
5001
5084
  found.push(readmePath);
5002
5085
  }
5003
5086
  if (current === ctx.directory)
@@ -5102,6 +5185,7 @@ var TOKEN_LIMIT_KEYWORDS = [
5102
5185
  "too many tokens",
5103
5186
  "non-empty content"
5104
5187
  ];
5188
+ var MESSAGE_INDEX_PATTERN = /messages\.(\d+)/;
5105
5189
  function extractTokensFromMessage(message) {
5106
5190
  for (const pattern of TOKEN_LIMIT_PATTERNS) {
5107
5191
  const match = message.match(pattern);
@@ -5113,6 +5197,13 @@ function extractTokensFromMessage(message) {
5113
5197
  }
5114
5198
  return null;
5115
5199
  }
5200
+ function extractMessageIndex2(text) {
5201
+ const match = text.match(MESSAGE_INDEX_PATTERN);
5202
+ if (match) {
5203
+ return parseInt(match[1], 10);
5204
+ }
5205
+ return;
5206
+ }
5116
5207
  function isTokenLimitError(text) {
5117
5208
  const lower = text.toLowerCase();
5118
5209
  return TOKEN_LIMIT_KEYWORDS.some((kw) => lower.includes(kw.toLowerCase()));
@@ -5123,7 +5214,8 @@ function parseAnthropicTokenLimitError(err) {
5123
5214
  return {
5124
5215
  currentTokens: 0,
5125
5216
  maxTokens: 0,
5126
- errorType: "non-empty content"
5217
+ errorType: "non-empty content",
5218
+ messageIndex: extractMessageIndex2(err)
5127
5219
  };
5128
5220
  }
5129
5221
  if (isTokenLimitError(err)) {
@@ -5225,7 +5317,8 @@ function parseAnthropicTokenLimitError(err) {
5225
5317
  return {
5226
5318
  currentTokens: 0,
5227
5319
  maxTokens: 0,
5228
- errorType: "non-empty content"
5320
+ errorType: "non-empty content",
5321
+ messageIndex: extractMessageIndex2(combinedText)
5229
5322
  };
5230
5323
  }
5231
5324
  if (isTokenLimitError(combinedText)) {
@@ -5257,13 +5350,13 @@ var TRUNCATE_CONFIG = {
5257
5350
  };
5258
5351
 
5259
5352
  // src/hooks/anthropic-auto-compact/storage.ts
5260
- import { existsSync as existsSync13, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
5353
+ import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
5261
5354
  import { homedir as homedir5 } from "os";
5262
5355
  import { join as join18 } from "path";
5263
5356
  var OPENCODE_STORAGE5 = join18(xdgData2 ?? "", "opencode", "storage");
5264
- if (process.platform === "darwin" && !existsSync13(OPENCODE_STORAGE5)) {
5357
+ if (process.platform === "darwin" && !existsSync14(OPENCODE_STORAGE5)) {
5265
5358
  const localShare = join18(homedir5(), ".local", "share", "opencode", "storage");
5266
- if (existsSync13(localShare)) {
5359
+ if (existsSync14(localShare)) {
5267
5360
  OPENCODE_STORAGE5 = localShare;
5268
5361
  }
5269
5362
  }
@@ -5271,15 +5364,15 @@ var MESSAGE_STORAGE3 = join18(OPENCODE_STORAGE5, "message");
5271
5364
  var PART_STORAGE3 = join18(OPENCODE_STORAGE5, "part");
5272
5365
  var TRUNCATION_MESSAGE = "[TOOL RESULT TRUNCATED - Context limit exceeded. Original output was too large and has been truncated to recover the session. Please re-run this tool if you need the full output.]";
5273
5366
  function getMessageDir3(sessionID) {
5274
- if (!existsSync13(MESSAGE_STORAGE3))
5367
+ if (!existsSync14(MESSAGE_STORAGE3))
5275
5368
  return "";
5276
5369
  const directPath = join18(MESSAGE_STORAGE3, sessionID);
5277
- if (existsSync13(directPath)) {
5370
+ if (existsSync14(directPath)) {
5278
5371
  return directPath;
5279
5372
  }
5280
5373
  for (const dir of readdirSync4(MESSAGE_STORAGE3)) {
5281
5374
  const sessionPath = join18(MESSAGE_STORAGE3, dir, sessionID);
5282
- if (existsSync13(sessionPath)) {
5375
+ if (existsSync14(sessionPath)) {
5283
5376
  return sessionPath;
5284
5377
  }
5285
5378
  }
@@ -5287,7 +5380,7 @@ function getMessageDir3(sessionID) {
5287
5380
  }
5288
5381
  function getMessageIds(sessionID) {
5289
5382
  const messageDir = getMessageDir3(sessionID);
5290
- if (!messageDir || !existsSync13(messageDir))
5383
+ if (!messageDir || !existsSync14(messageDir))
5291
5384
  return [];
5292
5385
  const messageIds = [];
5293
5386
  for (const file of readdirSync4(messageDir)) {
@@ -5303,7 +5396,7 @@ function findToolResultsBySize(sessionID) {
5303
5396
  const results = [];
5304
5397
  for (const messageID of messageIds) {
5305
5398
  const partDir = join18(PART_STORAGE3, messageID);
5306
- if (!existsSync13(partDir))
5399
+ if (!existsSync14(partDir))
5307
5400
  continue;
5308
5401
  for (const file of readdirSync4(partDir)) {
5309
5402
  if (!file.endsWith(".json"))
@@ -5508,32 +5601,59 @@ function clearSessionState(autoCompactState, sessionID) {
5508
5601
  function getOrCreateEmptyContentAttempt(autoCompactState, sessionID) {
5509
5602
  return autoCompactState.emptyContentAttemptBySession.get(sessionID) ?? 0;
5510
5603
  }
5511
- async function fixEmptyMessages(sessionID, autoCompactState, client) {
5604
+ async function fixEmptyMessages(sessionID, autoCompactState, client, messageIndex) {
5512
5605
  const attempt = getOrCreateEmptyContentAttempt(autoCompactState, sessionID);
5513
5606
  autoCompactState.emptyContentAttemptBySession.set(sessionID, attempt + 1);
5514
- const emptyMessageIds = findEmptyMessages(sessionID);
5515
- if (emptyMessageIds.length === 0) {
5516
- await client.tui.showToast({
5517
- body: {
5518
- title: "Empty Content Error",
5519
- message: "No empty messages found in storage. Cannot auto-recover.",
5520
- variant: "error",
5521
- duration: 5000
5607
+ let fixed = false;
5608
+ const fixedMessageIds = [];
5609
+ if (messageIndex !== undefined) {
5610
+ const targetMessageId = findEmptyMessageByIndex(sessionID, messageIndex);
5611
+ if (targetMessageId) {
5612
+ const replaced = replaceEmptyTextParts(targetMessageId, "[user interrupted]");
5613
+ if (replaced) {
5614
+ fixed = true;
5615
+ fixedMessageIds.push(targetMessageId);
5616
+ } else {
5617
+ const injected = injectTextPart(sessionID, targetMessageId, "[user interrupted]");
5618
+ if (injected) {
5619
+ fixed = true;
5620
+ fixedMessageIds.push(targetMessageId);
5621
+ }
5522
5622
  }
5523
- }).catch(() => {});
5524
- return false;
5623
+ }
5525
5624
  }
5526
- let fixed = false;
5527
- for (const messageID of emptyMessageIds) {
5528
- const success = injectTextPart(sessionID, messageID, "[user interrupted]");
5529
- if (success)
5530
- fixed = true;
5625
+ if (!fixed) {
5626
+ const emptyMessageIds = findEmptyMessages(sessionID);
5627
+ if (emptyMessageIds.length === 0) {
5628
+ await client.tui.showToast({
5629
+ body: {
5630
+ title: "Empty Content Error",
5631
+ message: "No empty messages found in storage. Cannot auto-recover.",
5632
+ variant: "error",
5633
+ duration: 5000
5634
+ }
5635
+ }).catch(() => {});
5636
+ return false;
5637
+ }
5638
+ for (const messageID of emptyMessageIds) {
5639
+ const replaced = replaceEmptyTextParts(messageID, "[user interrupted]");
5640
+ if (replaced) {
5641
+ fixed = true;
5642
+ fixedMessageIds.push(messageID);
5643
+ } else {
5644
+ const injected = injectTextPart(sessionID, messageID, "[user interrupted]");
5645
+ if (injected) {
5646
+ fixed = true;
5647
+ fixedMessageIds.push(messageID);
5648
+ }
5649
+ }
5650
+ }
5531
5651
  }
5532
5652
  if (fixed) {
5533
5653
  await client.tui.showToast({
5534
5654
  body: {
5535
5655
  title: "Session Recovery",
5536
- message: `Fixed ${emptyMessageIds.length} empty messages. Retrying...`,
5656
+ message: `Fixed ${fixedMessageIds.length} empty message(s). Retrying...`,
5537
5657
  variant: "warning",
5538
5658
  duration: 3000
5539
5659
  }
@@ -5642,10 +5762,10 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
5642
5762
  }
5643
5763
  }
5644
5764
  const retryState = getOrCreateRetryState(autoCompactState, sessionID);
5645
- if (experimental?.empty_message_recovery && errorData?.errorType?.includes("non-empty content")) {
5765
+ if (errorData?.errorType?.includes("non-empty content")) {
5646
5766
  const attempt = getOrCreateEmptyContentAttempt(autoCompactState, sessionID);
5647
5767
  if (attempt < 3) {
5648
- const fixed = await fixEmptyMessages(sessionID, autoCompactState, client);
5768
+ const fixed = await fixEmptyMessages(sessionID, autoCompactState, client, errorData.messageIndex);
5649
5769
  if (fixed) {
5650
5770
  autoCompactState.compactionInProgress.delete(sessionID);
5651
5771
  setTimeout(() => {
@@ -5888,6 +6008,200 @@ function createAnthropicAutoCompactHook(ctx, options) {
5888
6008
  event: eventHandler
5889
6009
  };
5890
6010
  }
6011
+ // src/hooks/preemptive-compaction/constants.ts
6012
+ var DEFAULT_THRESHOLD = 0.85;
6013
+ var MIN_TOKENS_FOR_COMPACTION = 50000;
6014
+ var COMPACTION_COOLDOWN_MS = 60000;
6015
+
6016
+ // src/hooks/preemptive-compaction/index.ts
6017
+ var CLAUDE_MODEL_PATTERN = /claude-(opus|sonnet|haiku)/i;
6018
+ var CLAUDE_DEFAULT_CONTEXT_LIMIT = 200000;
6019
+ function isSupportedModel(modelID) {
6020
+ return CLAUDE_MODEL_PATTERN.test(modelID);
6021
+ }
6022
+ function createState() {
6023
+ return {
6024
+ lastCompactionTime: new Map,
6025
+ compactionInProgress: new Set
6026
+ };
6027
+ }
6028
+ function createPreemptiveCompactionHook(ctx, options) {
6029
+ const experimental = options?.experimental;
6030
+ const onBeforeSummarize = options?.onBeforeSummarize;
6031
+ const getModelLimit = options?.getModelLimit;
6032
+ const enabled = experimental?.preemptive_compaction !== false;
6033
+ const threshold = experimental?.preemptive_compaction_threshold ?? DEFAULT_THRESHOLD;
6034
+ if (!enabled) {
6035
+ return { event: async () => {} };
6036
+ }
6037
+ const state2 = createState();
6038
+ const checkAndTriggerCompaction = async (sessionID, lastAssistant) => {
6039
+ if (state2.compactionInProgress.has(sessionID))
6040
+ return;
6041
+ const lastCompaction = state2.lastCompactionTime.get(sessionID) ?? 0;
6042
+ if (Date.now() - lastCompaction < COMPACTION_COOLDOWN_MS)
6043
+ return;
6044
+ if (lastAssistant.summary === true)
6045
+ return;
6046
+ const tokens = lastAssistant.tokens;
6047
+ if (!tokens)
6048
+ return;
6049
+ const modelID = lastAssistant.modelID ?? "";
6050
+ const providerID = lastAssistant.providerID ?? "";
6051
+ if (!isSupportedModel(modelID)) {
6052
+ log("[preemptive-compaction] skipping unsupported model", { modelID });
6053
+ return;
6054
+ }
6055
+ const configLimit = getModelLimit?.(providerID, modelID);
6056
+ const contextLimit = configLimit ?? CLAUDE_DEFAULT_CONTEXT_LIMIT;
6057
+ const totalUsed = tokens.input + tokens.cache.read + tokens.output;
6058
+ if (totalUsed < MIN_TOKENS_FOR_COMPACTION)
6059
+ return;
6060
+ const usageRatio = totalUsed / contextLimit;
6061
+ log("[preemptive-compaction] checking", {
6062
+ sessionID,
6063
+ totalUsed,
6064
+ contextLimit,
6065
+ usageRatio: usageRatio.toFixed(2),
6066
+ threshold
6067
+ });
6068
+ if (usageRatio < threshold)
6069
+ return;
6070
+ state2.compactionInProgress.add(sessionID);
6071
+ state2.lastCompactionTime.set(sessionID, Date.now());
6072
+ if (!providerID || !modelID) {
6073
+ state2.compactionInProgress.delete(sessionID);
6074
+ return;
6075
+ }
6076
+ await ctx.client.tui.showToast({
6077
+ body: {
6078
+ title: "Preemptive Compaction",
6079
+ message: `Context at ${(usageRatio * 100).toFixed(0)}% - compacting to prevent overflow...`,
6080
+ variant: "warning",
6081
+ duration: 3000
6082
+ }
6083
+ }).catch(() => {});
6084
+ log("[preemptive-compaction] triggering compaction", { sessionID, usageRatio });
6085
+ try {
6086
+ if (onBeforeSummarize) {
6087
+ await onBeforeSummarize({
6088
+ sessionID,
6089
+ providerID,
6090
+ modelID,
6091
+ usageRatio,
6092
+ directory: ctx.directory
6093
+ });
6094
+ }
6095
+ await ctx.client.session.summarize({
6096
+ path: { id: sessionID },
6097
+ body: { providerID, modelID },
6098
+ query: { directory: ctx.directory }
6099
+ });
6100
+ await ctx.client.tui.showToast({
6101
+ body: {
6102
+ title: "Compaction Complete",
6103
+ message: "Session compacted successfully",
6104
+ variant: "success",
6105
+ duration: 2000
6106
+ }
6107
+ }).catch(() => {});
6108
+ } catch (err) {
6109
+ log("[preemptive-compaction] compaction failed", { sessionID, error: err });
6110
+ } finally {
6111
+ state2.compactionInProgress.delete(sessionID);
6112
+ }
6113
+ };
6114
+ const eventHandler = async ({ event }) => {
6115
+ const props = event.properties;
6116
+ if (event.type === "session.deleted") {
6117
+ const sessionInfo = props?.info;
6118
+ if (sessionInfo?.id) {
6119
+ state2.lastCompactionTime.delete(sessionInfo.id);
6120
+ state2.compactionInProgress.delete(sessionInfo.id);
6121
+ }
6122
+ return;
6123
+ }
6124
+ if (event.type === "message.updated") {
6125
+ const info = props?.info;
6126
+ if (!info)
6127
+ return;
6128
+ if (info.role !== "assistant" || !info.finish)
6129
+ return;
6130
+ const sessionID = info.sessionID;
6131
+ if (!sessionID)
6132
+ return;
6133
+ await checkAndTriggerCompaction(sessionID, info);
6134
+ return;
6135
+ }
6136
+ if (event.type === "session.idle") {
6137
+ const sessionID = props?.sessionID;
6138
+ if (!sessionID)
6139
+ return;
6140
+ try {
6141
+ const resp = await ctx.client.session.messages({
6142
+ path: { id: sessionID },
6143
+ query: { directory: ctx.directory }
6144
+ });
6145
+ const messages = resp.data ?? resp;
6146
+ const assistants = messages.filter((m) => m.info.role === "assistant").map((m) => m.info);
6147
+ if (assistants.length === 0)
6148
+ return;
6149
+ const lastAssistant = assistants[assistants.length - 1];
6150
+ await checkAndTriggerCompaction(sessionID, lastAssistant);
6151
+ } catch {}
6152
+ }
6153
+ };
6154
+ return {
6155
+ event: eventHandler
6156
+ };
6157
+ }
6158
+ // src/hooks/compaction-context-injector/index.ts
6159
+ var SUMMARIZE_CONTEXT_PROMPT = `[COMPACTION CONTEXT INJECTION]
6160
+
6161
+ When summarizing this session, you MUST include the following sections in your summary:
6162
+
6163
+ ## 1. User Requests (As-Is)
6164
+ - List all original user requests exactly as they were stated
6165
+ - Preserve the user's exact wording and intent
6166
+
6167
+ ## 2. Final Goal
6168
+ - What the user ultimately wanted to achieve
6169
+ - The end result or deliverable expected
6170
+
6171
+ ## 3. Work Completed
6172
+ - What has been done so far
6173
+ - Files created/modified
6174
+ - Features implemented
6175
+ - Problems solved
6176
+
6177
+ ## 4. Remaining Tasks
6178
+ - What still needs to be done
6179
+ - Pending items from the original request
6180
+ - Follow-up tasks identified during the work
6181
+
6182
+ ## 5. MUST NOT Do (Critical Constraints)
6183
+ - Things that were explicitly forbidden
6184
+ - Approaches that failed and should not be retried
6185
+ - User's explicit restrictions or preferences
6186
+ - Anti-patterns identified during the session
6187
+
6188
+ This context is critical for maintaining continuity after compaction.
6189
+ `;
6190
+ function createCompactionContextInjector() {
6191
+ return async (ctx) => {
6192
+ log("[compaction-context-injector] injecting context", { sessionID: ctx.sessionID });
6193
+ const success = injectHookMessage(ctx.sessionID, SUMMARIZE_CONTEXT_PROMPT, {
6194
+ agent: "general",
6195
+ model: { providerID: ctx.providerID, modelID: ctx.modelID },
6196
+ path: { cwd: ctx.directory }
6197
+ });
6198
+ if (success) {
6199
+ log("[compaction-context-injector] context injected", { sessionID: ctx.sessionID });
6200
+ } else {
6201
+ log("[compaction-context-injector] injection failed", { sessionID: ctx.sessionID });
6202
+ }
6203
+ };
6204
+ }
5891
6205
  // src/hooks/think-mode/detector.ts
5892
6206
  var ENGLISH_PATTERNS = [/\bultrathink\b/i, /\bthink\b/i];
5893
6207
  var MULTILINGUAL_KEYWORDS = [
@@ -6157,7 +6471,7 @@ function createThinkModeHook() {
6157
6471
  // src/hooks/claude-code-hooks/config.ts
6158
6472
  import { homedir as homedir6 } from "os";
6159
6473
  import { join as join19 } from "path";
6160
- import { existsSync as existsSync14 } from "fs";
6474
+ import { existsSync as existsSync15 } from "fs";
6161
6475
  function normalizeHookMatcher(raw) {
6162
6476
  return {
6163
6477
  matcher: raw.matcher ?? raw.pattern ?? "*",
@@ -6186,7 +6500,7 @@ function getClaudeSettingsPaths(customPath) {
6186
6500
  join19(process.cwd(), ".claude", "settings.json"),
6187
6501
  join19(process.cwd(), ".claude", "settings.local.json")
6188
6502
  ];
6189
- if (customPath && existsSync14(customPath)) {
6503
+ if (customPath && existsSync15(customPath)) {
6190
6504
  paths.unshift(customPath);
6191
6505
  }
6192
6506
  return paths;
@@ -6210,7 +6524,7 @@ async function loadClaudeHooksConfig(customSettingsPath) {
6210
6524
  const paths = getClaudeSettingsPaths(customSettingsPath);
6211
6525
  let mergedConfig = {};
6212
6526
  for (const settingsPath of paths) {
6213
- if (existsSync14(settingsPath)) {
6527
+ if (existsSync15(settingsPath)) {
6214
6528
  try {
6215
6529
  const content = await Bun.file(settingsPath).text();
6216
6530
  const settings = JSON.parse(content);
@@ -6227,7 +6541,7 @@ async function loadClaudeHooksConfig(customSettingsPath) {
6227
6541
  }
6228
6542
 
6229
6543
  // src/hooks/claude-code-hooks/config-loader.ts
6230
- import { existsSync as existsSync15 } from "fs";
6544
+ import { existsSync as existsSync16 } from "fs";
6231
6545
  import { homedir as homedir7 } from "os";
6232
6546
  import { join as join20 } from "path";
6233
6547
  var USER_CONFIG_PATH = join20(homedir7(), ".config", "opencode", "opencode-cc-plugin.json");
@@ -6235,7 +6549,7 @@ function getProjectConfigPath() {
6235
6549
  return join20(process.cwd(), ".opencode", "opencode-cc-plugin.json");
6236
6550
  }
6237
6551
  async function loadConfigFromPath(path4) {
6238
- if (!existsSync15(path4)) {
6552
+ if (!existsSync16(path4)) {
6239
6553
  return null;
6240
6554
  }
6241
6555
  try {
@@ -6415,7 +6729,7 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
6415
6729
 
6416
6730
  // src/hooks/claude-code-hooks/transcript.ts
6417
6731
  import { join as join21 } from "path";
6418
- import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync16, writeFileSync as writeFileSync6, unlinkSync as unlinkSync5 } from "fs";
6732
+ import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync17, writeFileSync as writeFileSync6, unlinkSync as unlinkSync5 } from "fs";
6419
6733
  import { homedir as homedir8, tmpdir as tmpdir5 } from "os";
6420
6734
  import { randomUUID } from "crypto";
6421
6735
  var TRANSCRIPT_DIR = join21(homedir8(), ".claude", "transcripts");
@@ -6423,7 +6737,7 @@ function getTranscriptPath(sessionId) {
6423
6737
  return join21(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
6424
6738
  }
6425
6739
  function ensureTranscriptDir() {
6426
- if (!existsSync16(TRANSCRIPT_DIR)) {
6740
+ if (!existsSync17(TRANSCRIPT_DIR)) {
6427
6741
  mkdirSync6(TRANSCRIPT_DIR, { recursive: true });
6428
6742
  }
6429
6743
  }
@@ -7084,7 +7398,7 @@ import { relative as relative3, resolve as resolve4 } from "path";
7084
7398
 
7085
7399
  // src/hooks/rules-injector/finder.ts
7086
7400
  import {
7087
- existsSync as existsSync17,
7401
+ existsSync as existsSync18,
7088
7402
  readdirSync as readdirSync5,
7089
7403
  realpathSync,
7090
7404
  statSync as statSync2
@@ -7122,7 +7436,7 @@ function findProjectRoot(startPath) {
7122
7436
  while (true) {
7123
7437
  for (const marker of PROJECT_MARKERS) {
7124
7438
  const markerPath = join24(current, marker);
7125
- if (existsSync17(markerPath)) {
7439
+ if (existsSync18(markerPath)) {
7126
7440
  return current;
7127
7441
  }
7128
7442
  }
@@ -7134,7 +7448,7 @@ function findProjectRoot(startPath) {
7134
7448
  }
7135
7449
  }
7136
7450
  function findRuleFilesRecursive(dir, results) {
7137
- if (!existsSync17(dir))
7451
+ if (!existsSync18(dir))
7138
7452
  return;
7139
7453
  try {
7140
7454
  const entries = readdirSync5(dir, { withFileTypes: true });
@@ -7378,7 +7692,7 @@ function mergeGlobs(existing, newValue) {
7378
7692
 
7379
7693
  // src/hooks/rules-injector/storage.ts
7380
7694
  import {
7381
- existsSync as existsSync18,
7695
+ existsSync as existsSync19,
7382
7696
  mkdirSync as mkdirSync7,
7383
7697
  readFileSync as readFileSync9,
7384
7698
  writeFileSync as writeFileSync7,
@@ -7390,7 +7704,7 @@ function getStoragePath3(sessionID) {
7390
7704
  }
7391
7705
  function loadInjectedRules(sessionID) {
7392
7706
  const filePath = getStoragePath3(sessionID);
7393
- if (!existsSync18(filePath))
7707
+ if (!existsSync19(filePath))
7394
7708
  return { contentHashes: new Set, realPaths: new Set };
7395
7709
  try {
7396
7710
  const content = readFileSync9(filePath, "utf-8");
@@ -7404,7 +7718,7 @@ function loadInjectedRules(sessionID) {
7404
7718
  }
7405
7719
  }
7406
7720
  function saveInjectedRules(sessionID, data) {
7407
- if (!existsSync18(RULES_INJECTOR_STORAGE)) {
7721
+ if (!existsSync19(RULES_INJECTOR_STORAGE)) {
7408
7722
  mkdirSync7(RULES_INJECTOR_STORAGE, { recursive: true });
7409
7723
  }
7410
7724
  const storageData = {
@@ -7417,7 +7731,7 @@ function saveInjectedRules(sessionID, data) {
7417
7731
  }
7418
7732
  function clearInjectedRules(sessionID) {
7419
7733
  const filePath = getStoragePath3(sessionID);
7420
- if (existsSync18(filePath)) {
7734
+ if (existsSync19(filePath)) {
7421
7735
  unlinkSync6(filePath);
7422
7736
  }
7423
7737
  }
@@ -7517,7 +7831,7 @@ function createBackgroundNotificationHook(manager) {
7517
7831
  };
7518
7832
  }
7519
7833
  // src/hooks/auto-update-checker/checker.ts
7520
- import * as fs4 from "fs";
7834
+ import * as fs5 from "fs";
7521
7835
  import * as path5 from "path";
7522
7836
  import { fileURLToPath } from "url";
7523
7837
 
@@ -7561,9 +7875,9 @@ function getConfigPaths(directory) {
7561
7875
  function getLocalDevPath(directory) {
7562
7876
  for (const configPath of getConfigPaths(directory)) {
7563
7877
  try {
7564
- if (!fs4.existsSync(configPath))
7878
+ if (!fs5.existsSync(configPath))
7565
7879
  continue;
7566
- const content = fs4.readFileSync(configPath, "utf-8");
7880
+ const content = fs5.readFileSync(configPath, "utf-8");
7567
7881
  const config = JSON.parse(stripJsonComments(content));
7568
7882
  const plugins = config.plugin ?? [];
7569
7883
  for (const entry of plugins) {
@@ -7583,13 +7897,13 @@ function getLocalDevPath(directory) {
7583
7897
  }
7584
7898
  function findPackageJsonUp(startPath) {
7585
7899
  try {
7586
- const stat = fs4.statSync(startPath);
7900
+ const stat = fs5.statSync(startPath);
7587
7901
  let dir = stat.isDirectory() ? startPath : path5.dirname(startPath);
7588
7902
  for (let i = 0;i < 10; i++) {
7589
7903
  const pkgPath = path5.join(dir, "package.json");
7590
- if (fs4.existsSync(pkgPath)) {
7904
+ if (fs5.existsSync(pkgPath)) {
7591
7905
  try {
7592
- const content = fs4.readFileSync(pkgPath, "utf-8");
7906
+ const content = fs5.readFileSync(pkgPath, "utf-8");
7593
7907
  const pkg = JSON.parse(content);
7594
7908
  if (pkg.name === PACKAGE_NAME)
7595
7909
  return pkgPath;
@@ -7611,7 +7925,7 @@ function getLocalDevVersion(directory) {
7611
7925
  const pkgPath = findPackageJsonUp(localPath);
7612
7926
  if (!pkgPath)
7613
7927
  return null;
7614
- const content = fs4.readFileSync(pkgPath, "utf-8");
7928
+ const content = fs5.readFileSync(pkgPath, "utf-8");
7615
7929
  const pkg = JSON.parse(content);
7616
7930
  return pkg.version ?? null;
7617
7931
  } catch {
@@ -7621,9 +7935,9 @@ function getLocalDevVersion(directory) {
7621
7935
  function findPluginEntry(directory) {
7622
7936
  for (const configPath of getConfigPaths(directory)) {
7623
7937
  try {
7624
- if (!fs4.existsSync(configPath))
7938
+ if (!fs5.existsSync(configPath))
7625
7939
  continue;
7626
- const content = fs4.readFileSync(configPath, "utf-8");
7940
+ const content = fs5.readFileSync(configPath, "utf-8");
7627
7941
  const config = JSON.parse(stripJsonComments(content));
7628
7942
  const plugins = config.plugin ?? [];
7629
7943
  for (const entry of plugins) {
@@ -7644,8 +7958,8 @@ function findPluginEntry(directory) {
7644
7958
  }
7645
7959
  function getCachedVersion() {
7646
7960
  try {
7647
- if (fs4.existsSync(INSTALLED_PACKAGE_JSON)) {
7648
- const content = fs4.readFileSync(INSTALLED_PACKAGE_JSON, "utf-8");
7961
+ if (fs5.existsSync(INSTALLED_PACKAGE_JSON)) {
7962
+ const content = fs5.readFileSync(INSTALLED_PACKAGE_JSON, "utf-8");
7649
7963
  const pkg = JSON.parse(content);
7650
7964
  if (pkg.version)
7651
7965
  return pkg.version;
@@ -7655,7 +7969,7 @@ function getCachedVersion() {
7655
7969
  const currentDir = path5.dirname(fileURLToPath(import.meta.url));
7656
7970
  const pkgPath = findPackageJsonUp(currentDir);
7657
7971
  if (pkgPath) {
7658
- const content = fs4.readFileSync(pkgPath, "utf-8");
7972
+ const content = fs5.readFileSync(pkgPath, "utf-8");
7659
7973
  const pkg = JSON.parse(content);
7660
7974
  if (pkg.version)
7661
7975
  return pkg.version;
@@ -7667,7 +7981,7 @@ function getCachedVersion() {
7667
7981
  }
7668
7982
  function updatePinnedVersion(configPath, oldEntry, newVersion) {
7669
7983
  try {
7670
- const content = fs4.readFileSync(configPath, "utf-8");
7984
+ const content = fs5.readFileSync(configPath, "utf-8");
7671
7985
  const newEntry = `${PACKAGE_NAME}@${newVersion}`;
7672
7986
  const pluginMatch = content.match(/"plugin"\s*:\s*\[/);
7673
7987
  if (!pluginMatch || pluginMatch.index === undefined) {
@@ -7699,7 +8013,7 @@ function updatePinnedVersion(configPath, oldEntry, newVersion) {
7699
8013
  log(`[auto-update-checker] No changes made to ${configPath}`);
7700
8014
  return false;
7701
8015
  }
7702
- fs4.writeFileSync(configPath, updatedContent, "utf-8");
8016
+ fs5.writeFileSync(configPath, updatedContent, "utf-8");
7703
8017
  log(`[auto-update-checker] Updated ${configPath}: ${oldEntry} \u2192 ${newEntry}`);
7704
8018
  return true;
7705
8019
  } catch (err) {
@@ -7727,17 +8041,17 @@ async function getLatestVersion() {
7727
8041
  }
7728
8042
 
7729
8043
  // src/hooks/auto-update-checker/cache.ts
7730
- import * as fs5 from "fs";
8044
+ import * as fs6 from "fs";
7731
8045
  import * as path6 from "path";
7732
8046
  function stripTrailingCommas(json) {
7733
8047
  return json.replace(/,(\s*[}\]])/g, "$1");
7734
8048
  }
7735
8049
  function removeFromBunLock(packageName) {
7736
8050
  const lockPath = path6.join(CACHE_DIR, "bun.lock");
7737
- if (!fs5.existsSync(lockPath))
8051
+ if (!fs6.existsSync(lockPath))
7738
8052
  return false;
7739
8053
  try {
7740
- const content = fs5.readFileSync(lockPath, "utf-8");
8054
+ const content = fs6.readFileSync(lockPath, "utf-8");
7741
8055
  const lock = JSON.parse(stripTrailingCommas(content));
7742
8056
  let modified = false;
7743
8057
  if (lock.workspaces?.[""]?.dependencies?.[packageName]) {
@@ -7749,7 +8063,7 @@ function removeFromBunLock(packageName) {
7749
8063
  modified = true;
7750
8064
  }
7751
8065
  if (modified) {
7752
- fs5.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
8066
+ fs6.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
7753
8067
  log(`[auto-update-checker] Removed from bun.lock: ${packageName}`);
7754
8068
  }
7755
8069
  return modified;
@@ -7764,17 +8078,17 @@ function invalidatePackage(packageName = PACKAGE_NAME) {
7764
8078
  let packageRemoved = false;
7765
8079
  let dependencyRemoved = false;
7766
8080
  let lockRemoved = false;
7767
- if (fs5.existsSync(pkgDir)) {
7768
- fs5.rmSync(pkgDir, { recursive: true, force: true });
8081
+ if (fs6.existsSync(pkgDir)) {
8082
+ fs6.rmSync(pkgDir, { recursive: true, force: true });
7769
8083
  log(`[auto-update-checker] Package removed: ${pkgDir}`);
7770
8084
  packageRemoved = true;
7771
8085
  }
7772
- if (fs5.existsSync(pkgJsonPath)) {
7773
- const content = fs5.readFileSync(pkgJsonPath, "utf-8");
8086
+ if (fs6.existsSync(pkgJsonPath)) {
8087
+ const content = fs6.readFileSync(pkgJsonPath, "utf-8");
7774
8088
  const pkgJson = JSON.parse(content);
7775
8089
  if (pkgJson.dependencies?.[packageName]) {
7776
8090
  delete pkgJson.dependencies[packageName];
7777
- fs5.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
8091
+ fs6.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
7778
8092
  log(`[auto-update-checker] Dependency removed from package.json: ${packageName}`);
7779
8093
  dependencyRemoved = true;
7780
8094
  }
@@ -7944,7 +8258,7 @@ async function showLocalDevToast(ctx, version, isSisyphusEnabled) {
7944
8258
  }
7945
8259
  // src/hooks/agent-usage-reminder/storage.ts
7946
8260
  import {
7947
- existsSync as existsSync21,
8261
+ existsSync as existsSync22,
7948
8262
  mkdirSync as mkdirSync8,
7949
8263
  readFileSync as readFileSync13,
7950
8264
  writeFileSync as writeFileSync10,
@@ -8004,7 +8318,7 @@ function getStoragePath4(sessionID) {
8004
8318
  }
8005
8319
  function loadAgentUsageState(sessionID) {
8006
8320
  const filePath = getStoragePath4(sessionID);
8007
- if (!existsSync21(filePath))
8321
+ if (!existsSync22(filePath))
8008
8322
  return null;
8009
8323
  try {
8010
8324
  const content = readFileSync13(filePath, "utf-8");
@@ -8014,7 +8328,7 @@ function loadAgentUsageState(sessionID) {
8014
8328
  }
8015
8329
  }
8016
8330
  function saveAgentUsageState(state2) {
8017
- if (!existsSync21(AGENT_USAGE_REMINDER_STORAGE)) {
8331
+ if (!existsSync22(AGENT_USAGE_REMINDER_STORAGE)) {
8018
8332
  mkdirSync8(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
8019
8333
  }
8020
8334
  const filePath = getStoragePath4(state2.sessionID);
@@ -8022,7 +8336,7 @@ function saveAgentUsageState(state2) {
8022
8336
  }
8023
8337
  function clearAgentUsageState(sessionID) {
8024
8338
  const filePath = getStoragePath4(sessionID);
8025
- if (existsSync21(filePath)) {
8339
+ if (existsSync22(filePath)) {
8026
8340
  unlinkSync7(filePath);
8027
8341
  }
8028
8342
  }
@@ -8249,7 +8563,7 @@ function createNonInteractiveEnvHook(_ctx) {
8249
8563
  }
8250
8564
  // src/hooks/interactive-bash-session/storage.ts
8251
8565
  import {
8252
- existsSync as existsSync22,
8566
+ existsSync as existsSync23,
8253
8567
  mkdirSync as mkdirSync9,
8254
8568
  readFileSync as readFileSync14,
8255
8569
  writeFileSync as writeFileSync11,
@@ -8276,7 +8590,7 @@ function getStoragePath5(sessionID) {
8276
8590
  }
8277
8591
  function loadInteractiveBashSessionState(sessionID) {
8278
8592
  const filePath = getStoragePath5(sessionID);
8279
- if (!existsSync22(filePath))
8593
+ if (!existsSync23(filePath))
8280
8594
  return null;
8281
8595
  try {
8282
8596
  const content = readFileSync14(filePath, "utf-8");
@@ -8291,7 +8605,7 @@ function loadInteractiveBashSessionState(sessionID) {
8291
8605
  }
8292
8606
  }
8293
8607
  function saveInteractiveBashSessionState(state2) {
8294
- if (!existsSync22(INTERACTIVE_BASH_SESSION_STORAGE)) {
8608
+ if (!existsSync23(INTERACTIVE_BASH_SESSION_STORAGE)) {
8295
8609
  mkdirSync9(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
8296
8610
  }
8297
8611
  const filePath = getStoragePath5(state2.sessionID);
@@ -8304,7 +8618,7 @@ function saveInteractiveBashSessionState(state2) {
8304
8618
  }
8305
8619
  function clearInteractiveBashSessionState(sessionID) {
8306
8620
  const filePath = getStoragePath5(sessionID);
8307
- if (existsSync22(filePath)) {
8621
+ if (existsSync23(filePath)) {
8308
8622
  unlinkSync8(filePath);
8309
8623
  }
8310
8624
  }
@@ -10248,11 +10562,11 @@ async function createGoogleAntigravityAuthPlugin({
10248
10562
  };
10249
10563
  }
10250
10564
  // src/features/claude-code-command-loader/loader.ts
10251
- import { existsSync as existsSync23, readdirSync as readdirSync6, readFileSync as readFileSync15 } from "fs";
10565
+ import { existsSync as existsSync24, readdirSync as readdirSync6, readFileSync as readFileSync15 } from "fs";
10252
10566
  import { homedir as homedir12 } from "os";
10253
10567
  import { join as join33, basename } from "path";
10254
10568
  function loadCommandsFromDir(commandsDir, scope) {
10255
- if (!existsSync23(commandsDir)) {
10569
+ if (!existsSync24(commandsDir)) {
10256
10570
  return [];
10257
10571
  }
10258
10572
  const entries = readdirSync6(commandsDir, { withFileTypes: true });
@@ -10323,11 +10637,11 @@ function loadOpencodeProjectCommands() {
10323
10637
  return commandsToRecord(commands);
10324
10638
  }
10325
10639
  // src/features/claude-code-skill-loader/loader.ts
10326
- import { existsSync as existsSync24, readdirSync as readdirSync7, readFileSync as readFileSync16 } from "fs";
10640
+ import { existsSync as existsSync25, readdirSync as readdirSync7, readFileSync as readFileSync16 } from "fs";
10327
10641
  import { homedir as homedir13 } from "os";
10328
10642
  import { join as join34 } from "path";
10329
10643
  function loadSkillsFromDir(skillsDir, scope) {
10330
- if (!existsSync24(skillsDir)) {
10644
+ if (!existsSync25(skillsDir)) {
10331
10645
  return [];
10332
10646
  }
10333
10647
  const entries = readdirSync7(skillsDir, { withFileTypes: true });
@@ -10340,7 +10654,7 @@ function loadSkillsFromDir(skillsDir, scope) {
10340
10654
  continue;
10341
10655
  const resolvedPath = resolveSymlink(skillPath);
10342
10656
  const skillMdPath = join34(resolvedPath, "SKILL.md");
10343
- if (!existsSync24(skillMdPath))
10657
+ if (!existsSync25(skillMdPath))
10344
10658
  continue;
10345
10659
  try {
10346
10660
  const content = readFileSync16(skillMdPath, "utf-8");
@@ -10393,7 +10707,7 @@ function loadProjectSkillsAsCommands() {
10393
10707
  }, {});
10394
10708
  }
10395
10709
  // src/features/claude-code-agent-loader/loader.ts
10396
- import { existsSync as existsSync25, readdirSync as readdirSync8, readFileSync as readFileSync17 } from "fs";
10710
+ import { existsSync as existsSync26, readdirSync as readdirSync8, readFileSync as readFileSync17 } from "fs";
10397
10711
  import { homedir as homedir14 } from "os";
10398
10712
  import { join as join35, basename as basename2 } from "path";
10399
10713
  function parseToolsConfig(toolsStr) {
@@ -10409,7 +10723,7 @@ function parseToolsConfig(toolsStr) {
10409
10723
  return result;
10410
10724
  }
10411
10725
  function loadAgentsFromDir(agentsDir, scope) {
10412
- if (!existsSync25(agentsDir)) {
10726
+ if (!existsSync26(agentsDir)) {
10413
10727
  return [];
10414
10728
  }
10415
10729
  const entries = readdirSync8(agentsDir, { withFileTypes: true });
@@ -10465,7 +10779,7 @@ function loadProjectAgents() {
10465
10779
  return result;
10466
10780
  }
10467
10781
  // src/features/claude-code-mcp-loader/loader.ts
10468
- import { existsSync as existsSync26 } from "fs";
10782
+ import { existsSync as existsSync27 } from "fs";
10469
10783
  import { homedir as homedir15 } from "os";
10470
10784
  import { join as join36 } from "path";
10471
10785
 
@@ -10542,7 +10856,7 @@ function getMcpConfigPaths() {
10542
10856
  ];
10543
10857
  }
10544
10858
  async function loadMcpConfigFile(filePath) {
10545
- if (!existsSync26(filePath)) {
10859
+ if (!existsSync27(filePath)) {
10546
10860
  return null;
10547
10861
  }
10548
10862
  try {
@@ -10637,6 +10951,32 @@ var BUILTIN_SERVERS = {
10637
10951
  command: ["vscode-eslint-language-server", "--stdio"],
10638
10952
  extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts", ".vue"]
10639
10953
  },
10954
+ oxlint: {
10955
+ command: ["oxlint", "--lsp"],
10956
+ extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts", ".vue", ".astro", ".svelte"]
10957
+ },
10958
+ biome: {
10959
+ command: ["biome", "lsp-proxy", "--stdio"],
10960
+ extensions: [
10961
+ ".ts",
10962
+ ".tsx",
10963
+ ".js",
10964
+ ".jsx",
10965
+ ".mjs",
10966
+ ".cjs",
10967
+ ".mts",
10968
+ ".cts",
10969
+ ".json",
10970
+ ".jsonc",
10971
+ ".vue",
10972
+ ".astro",
10973
+ ".svelte",
10974
+ ".css",
10975
+ ".graphql",
10976
+ ".gql",
10977
+ ".html"
10978
+ ]
10979
+ },
10640
10980
  gopls: {
10641
10981
  command: ["gopls"],
10642
10982
  extensions: [".go"]
@@ -10653,6 +10993,10 @@ var BUILTIN_SERVERS = {
10653
10993
  command: ["pyright-langserver", "--stdio"],
10654
10994
  extensions: [".py", ".pyi"]
10655
10995
  },
10996
+ ty: {
10997
+ command: ["ty", "server"],
10998
+ extensions: [".py", ".pyi"]
10999
+ },
10656
11000
  ruff: {
10657
11001
  command: ["ruff", "server"],
10658
11002
  extensions: [".py", ".pyi"]
@@ -10669,6 +11013,10 @@ var BUILTIN_SERVERS = {
10669
11013
  command: ["csharp-ls"],
10670
11014
  extensions: [".cs"]
10671
11015
  },
11016
+ fsharp: {
11017
+ command: ["fsautocomplete"],
11018
+ extensions: [".fs", ".fsi", ".fsx", ".fsscript"]
11019
+ },
10672
11020
  "sourcekit-lsp": {
10673
11021
  command: ["sourcekit-lsp"],
10674
11022
  extensions: [".swift", ".objc", ".objcpp"]
@@ -10712,26 +11060,125 @@ var BUILTIN_SERVERS = {
10712
11060
  dart: {
10713
11061
  command: ["dart", "language-server", "--lsp"],
10714
11062
  extensions: [".dart"]
11063
+ },
11064
+ "terraform-ls": {
11065
+ command: ["terraform-ls", "serve"],
11066
+ extensions: [".tf", ".tfvars"]
10715
11067
  }
10716
11068
  };
10717
11069
  var EXT_TO_LANG = {
11070
+ ".abap": "abap",
11071
+ ".bat": "bat",
11072
+ ".bib": "bibtex",
11073
+ ".bibtex": "bibtex",
11074
+ ".clj": "clojure",
11075
+ ".cljs": "clojure",
11076
+ ".cljc": "clojure",
11077
+ ".edn": "clojure",
11078
+ ".coffee": "coffeescript",
11079
+ ".c": "c",
11080
+ ".cpp": "cpp",
11081
+ ".cxx": "cpp",
11082
+ ".cc": "cpp",
11083
+ ".c++": "cpp",
11084
+ ".cs": "csharp",
11085
+ ".css": "css",
11086
+ ".d": "d",
11087
+ ".pas": "pascal",
11088
+ ".pascal": "pascal",
11089
+ ".diff": "diff",
11090
+ ".patch": "diff",
11091
+ ".dart": "dart",
11092
+ ".dockerfile": "dockerfile",
11093
+ ".ex": "elixir",
11094
+ ".exs": "elixir",
11095
+ ".erl": "erlang",
11096
+ ".hrl": "erlang",
11097
+ ".fs": "fsharp",
11098
+ ".fsi": "fsharp",
11099
+ ".fsx": "fsharp",
11100
+ ".fsscript": "fsharp",
11101
+ ".gitcommit": "git-commit",
11102
+ ".gitrebase": "git-rebase",
11103
+ ".go": "go",
11104
+ ".groovy": "groovy",
11105
+ ".gleam": "gleam",
11106
+ ".hbs": "handlebars",
11107
+ ".handlebars": "handlebars",
11108
+ ".hs": "haskell",
11109
+ ".html": "html",
11110
+ ".htm": "html",
11111
+ ".ini": "ini",
11112
+ ".java": "java",
11113
+ ".js": "javascript",
11114
+ ".jsx": "javascriptreact",
11115
+ ".json": "json",
11116
+ ".jsonc": "jsonc",
11117
+ ".tex": "latex",
11118
+ ".latex": "latex",
11119
+ ".less": "less",
11120
+ ".lua": "lua",
11121
+ ".makefile": "makefile",
11122
+ makefile: "makefile",
11123
+ ".md": "markdown",
11124
+ ".markdown": "markdown",
11125
+ ".m": "objective-c",
11126
+ ".mm": "objective-cpp",
11127
+ ".pl": "perl",
11128
+ ".pm": "perl",
11129
+ ".pm6": "perl6",
11130
+ ".php": "php",
11131
+ ".ps1": "powershell",
11132
+ ".psm1": "powershell",
11133
+ ".pug": "jade",
11134
+ ".jade": "jade",
10718
11135
  ".py": "python",
10719
11136
  ".pyi": "python",
11137
+ ".r": "r",
11138
+ ".cshtml": "razor",
11139
+ ".razor": "razor",
11140
+ ".rb": "ruby",
11141
+ ".rake": "ruby",
11142
+ ".gemspec": "ruby",
11143
+ ".ru": "ruby",
11144
+ ".erb": "erb",
11145
+ ".html.erb": "erb",
11146
+ ".js.erb": "erb",
11147
+ ".css.erb": "erb",
11148
+ ".json.erb": "erb",
11149
+ ".rs": "rust",
11150
+ ".scss": "scss",
11151
+ ".sass": "sass",
11152
+ ".scala": "scala",
11153
+ ".shader": "shaderlab",
11154
+ ".sh": "shellscript",
11155
+ ".bash": "shellscript",
11156
+ ".zsh": "shellscript",
11157
+ ".ksh": "shellscript",
11158
+ ".sql": "sql",
11159
+ ".svelte": "svelte",
11160
+ ".swift": "swift",
10720
11161
  ".ts": "typescript",
10721
11162
  ".tsx": "typescriptreact",
10722
11163
  ".mts": "typescript",
10723
11164
  ".cts": "typescript",
10724
- ".js": "javascript",
10725
- ".jsx": "javascriptreact",
11165
+ ".mtsx": "typescriptreact",
11166
+ ".ctsx": "typescriptreact",
11167
+ ".xml": "xml",
11168
+ ".xsl": "xsl",
11169
+ ".yaml": "yaml",
11170
+ ".yml": "yaml",
10726
11171
  ".mjs": "javascript",
10727
11172
  ".cjs": "javascript",
10728
- ".go": "go",
10729
- ".rs": "rust",
10730
- ".c": "c",
10731
- ".cpp": "cpp",
10732
- ".cc": "cpp",
10733
- ".cxx": "cpp",
10734
- ".c++": "cpp",
11173
+ ".vue": "vue",
11174
+ ".zig": "zig",
11175
+ ".zon": "zig",
11176
+ ".astro": "astro",
11177
+ ".ml": "ocaml",
11178
+ ".mli": "ocaml",
11179
+ ".tf": "terraform",
11180
+ ".tfvars": "terraform-vars",
11181
+ ".hcl": "hcl",
10735
11182
  ".h": "c",
10736
11183
  ".hpp": "cpp",
10737
11184
  ".hh": "cpp",
@@ -10739,46 +11186,16 @@ var EXT_TO_LANG = {
10739
11186
  ".h++": "cpp",
10740
11187
  ".objc": "objective-c",
10741
11188
  ".objcpp": "objective-cpp",
10742
- ".java": "java",
10743
- ".rb": "ruby",
10744
- ".rake": "ruby",
10745
- ".gemspec": "ruby",
10746
- ".ru": "ruby",
10747
- ".lua": "lua",
10748
- ".swift": "swift",
10749
- ".cs": "csharp",
10750
- ".php": "php",
10751
- ".dart": "dart",
10752
- ".ex": "elixir",
10753
- ".exs": "elixir",
10754
- ".zig": "zig",
10755
- ".zon": "zig",
10756
- ".vue": "vue",
10757
- ".svelte": "svelte",
10758
- ".astro": "astro",
10759
- ".yaml": "yaml",
10760
- ".yml": "yaml",
10761
- ".json": "json",
10762
- ".jsonc": "jsonc",
10763
- ".html": "html",
10764
- ".htm": "html",
10765
- ".css": "css",
10766
- ".scss": "scss",
10767
- ".less": "less",
10768
- ".sh": "shellscript",
10769
- ".bash": "shellscript",
10770
- ".zsh": "shellscript",
10771
11189
  ".fish": "fish",
10772
- ".md": "markdown",
10773
- ".tf": "terraform",
10774
- ".tfvars": "terraform"
11190
+ ".graphql": "graphql",
11191
+ ".gql": "graphql"
10775
11192
  };
10776
11193
  // src/tools/lsp/config.ts
10777
- import { existsSync as existsSync27, readFileSync as readFileSync18 } from "fs";
11194
+ import { existsSync as existsSync28, readFileSync as readFileSync18 } from "fs";
10778
11195
  import { join as join37 } from "path";
10779
11196
  import { homedir as homedir16 } from "os";
10780
11197
  function loadJsonFile(path7) {
10781
- if (!existsSync27(path7))
11198
+ if (!existsSync28(path7))
10782
11199
  return null;
10783
11200
  try {
10784
11201
  return JSON.parse(readFileSync18(path7, "utf-8"));
@@ -10881,10 +11298,27 @@ function isServerInstalled(command) {
10881
11298
  if (command.length === 0)
10882
11299
  return false;
10883
11300
  const cmd = command[0];
11301
+ const isWindows2 = process.platform === "win32";
11302
+ const ext = isWindows2 ? ".exe" : "";
10884
11303
  const pathEnv = process.env.PATH || "";
10885
- const paths = pathEnv.split(":");
11304
+ const pathSeparator = isWindows2 ? ";" : ":";
11305
+ const paths = pathEnv.split(pathSeparator);
10886
11306
  for (const p of paths) {
10887
- if (existsSync27(join37(p, cmd))) {
11307
+ if (existsSync28(join37(p, cmd)) || existsSync28(join37(p, cmd + ext))) {
11308
+ return true;
11309
+ }
11310
+ }
11311
+ const cwd = process.cwd();
11312
+ const additionalPaths = [
11313
+ join37(cwd, "node_modules", ".bin", cmd),
11314
+ join37(cwd, "node_modules", ".bin", cmd + ext),
11315
+ join37(homedir16(), ".config", "opencode", "bin", cmd),
11316
+ join37(homedir16(), ".config", "opencode", "bin", cmd + ext),
11317
+ join37(homedir16(), ".config", "opencode", "node_modules", ".bin", cmd),
11318
+ join37(homedir16(), ".config", "opencode", "node_modules", ".bin", cmd + ext)
11319
+ ];
11320
+ for (const p of additionalPaths) {
11321
+ if (existsSync28(p)) {
10888
11322
  return true;
10889
11323
  }
10890
11324
  }
@@ -11479,16 +11913,16 @@ ${msg}`);
11479
11913
  }
11480
11914
  // src/tools/lsp/utils.ts
11481
11915
  import { extname as extname2, resolve as resolve6 } from "path";
11482
- import { existsSync as existsSync28, readFileSync as readFileSync20, writeFileSync as writeFileSync12 } from "fs";
11916
+ import { existsSync as existsSync29, readFileSync as readFileSync20, writeFileSync as writeFileSync12 } from "fs";
11483
11917
  function findWorkspaceRoot(filePath) {
11484
11918
  let dir = resolve6(filePath);
11485
- if (!existsSync28(dir) || !__require("fs").statSync(dir).isDirectory()) {
11919
+ if (!existsSync29(dir) || !__require("fs").statSync(dir).isDirectory()) {
11486
11920
  dir = __require("path").dirname(dir);
11487
11921
  }
11488
11922
  const markers = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
11489
11923
  while (dir !== "/") {
11490
11924
  for (const marker of markers) {
11491
- if (existsSync28(__require("path").join(dir, marker))) {
11925
+ if (existsSync29(__require("path").join(dir, marker))) {
11492
11926
  return dir;
11493
11927
  }
11494
11928
  }
@@ -24414,11 +24848,11 @@ var lsp_code_action_resolve = tool({
24414
24848
  // src/tools/ast-grep/constants.ts
24415
24849
  import { createRequire as createRequire4 } from "module";
24416
24850
  import { dirname as dirname6, join as join39 } from "path";
24417
- import { existsSync as existsSync30, statSync as statSync4 } from "fs";
24851
+ import { existsSync as existsSync31, statSync as statSync4 } from "fs";
24418
24852
 
24419
24853
  // src/tools/ast-grep/downloader.ts
24420
24854
  var {spawn: spawn5 } = globalThis.Bun;
24421
- import { existsSync as existsSync29, mkdirSync as mkdirSync10, chmodSync as chmodSync2, unlinkSync as unlinkSync9 } from "fs";
24855
+ import { existsSync as existsSync30, mkdirSync as mkdirSync10, chmodSync as chmodSync2, unlinkSync as unlinkSync9 } from "fs";
24422
24856
  import { join as join38 } from "path";
24423
24857
  import { homedir as homedir17 } from "os";
24424
24858
  import { createRequire as createRequire3 } from "module";
@@ -24457,7 +24891,7 @@ function getBinaryName3() {
24457
24891
  }
24458
24892
  function getCachedBinaryPath2() {
24459
24893
  const binaryPath = join38(getCacheDir3(), getBinaryName3());
24460
- return existsSync29(binaryPath) ? binaryPath : null;
24894
+ return existsSync30(binaryPath) ? binaryPath : null;
24461
24895
  }
24462
24896
  async function extractZip2(archivePath, destDir) {
24463
24897
  const proc = process.platform === "win32" ? spawn5([
@@ -24484,7 +24918,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
24484
24918
  const cacheDir = getCacheDir3();
24485
24919
  const binaryName = getBinaryName3();
24486
24920
  const binaryPath = join38(cacheDir, binaryName);
24487
- if (existsSync29(binaryPath)) {
24921
+ if (existsSync30(binaryPath)) {
24488
24922
  return binaryPath;
24489
24923
  }
24490
24924
  const { arch, os: os5 } = platformInfo;
@@ -24492,7 +24926,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
24492
24926
  const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
24493
24927
  console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
24494
24928
  try {
24495
- if (!existsSync29(cacheDir)) {
24929
+ if (!existsSync30(cacheDir)) {
24496
24930
  mkdirSync10(cacheDir, { recursive: true });
24497
24931
  }
24498
24932
  const response2 = await fetch(downloadUrl, { redirect: "follow" });
@@ -24503,10 +24937,10 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
24503
24937
  const arrayBuffer = await response2.arrayBuffer();
24504
24938
  await Bun.write(archivePath, arrayBuffer);
24505
24939
  await extractZip2(archivePath, cacheDir);
24506
- if (existsSync29(archivePath)) {
24940
+ if (existsSync30(archivePath)) {
24507
24941
  unlinkSync9(archivePath);
24508
24942
  }
24509
- if (process.platform !== "win32" && existsSync29(binaryPath)) {
24943
+ if (process.platform !== "win32" && existsSync30(binaryPath)) {
24510
24944
  chmodSync2(binaryPath, 493);
24511
24945
  }
24512
24946
  console.log(`[oh-my-opencode] ast-grep binary ready.`);
@@ -24558,7 +24992,7 @@ function findSgCliPathSync() {
24558
24992
  const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
24559
24993
  const cliDir = dirname6(cliPkgPath);
24560
24994
  const sgPath = join39(cliDir, binaryName);
24561
- if (existsSync30(sgPath) && isValidBinary(sgPath)) {
24995
+ if (existsSync31(sgPath) && isValidBinary(sgPath)) {
24562
24996
  return sgPath;
24563
24997
  }
24564
24998
  } catch {}
@@ -24570,7 +25004,7 @@ function findSgCliPathSync() {
24570
25004
  const pkgDir = dirname6(pkgPath);
24571
25005
  const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
24572
25006
  const binaryPath = join39(pkgDir, astGrepName);
24573
- if (existsSync30(binaryPath) && isValidBinary(binaryPath)) {
25007
+ if (existsSync31(binaryPath) && isValidBinary(binaryPath)) {
24574
25008
  return binaryPath;
24575
25009
  }
24576
25010
  } catch {}
@@ -24578,7 +25012,7 @@ function findSgCliPathSync() {
24578
25012
  if (process.platform === "darwin") {
24579
25013
  const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
24580
25014
  for (const path7 of homebrewPaths) {
24581
- if (existsSync30(path7) && isValidBinary(path7)) {
25015
+ if (existsSync31(path7) && isValidBinary(path7)) {
24582
25016
  return path7;
24583
25017
  }
24584
25018
  }
@@ -24634,11 +25068,11 @@ var DEFAULT_MAX_MATCHES = 500;
24634
25068
 
24635
25069
  // src/tools/ast-grep/cli.ts
24636
25070
  var {spawn: spawn6 } = globalThis.Bun;
24637
- import { existsSync as existsSync31 } from "fs";
25071
+ import { existsSync as existsSync32 } from "fs";
24638
25072
  var resolvedCliPath3 = null;
24639
25073
  var initPromise2 = null;
24640
25074
  async function getAstGrepPath() {
24641
- if (resolvedCliPath3 !== null && existsSync31(resolvedCliPath3)) {
25075
+ if (resolvedCliPath3 !== null && existsSync32(resolvedCliPath3)) {
24642
25076
  return resolvedCliPath3;
24643
25077
  }
24644
25078
  if (initPromise2) {
@@ -24646,7 +25080,7 @@ async function getAstGrepPath() {
24646
25080
  }
24647
25081
  initPromise2 = (async () => {
24648
25082
  const syncPath = findSgCliPathSync();
24649
- if (syncPath && existsSync31(syncPath)) {
25083
+ if (syncPath && existsSync32(syncPath)) {
24650
25084
  resolvedCliPath3 = syncPath;
24651
25085
  setSgCliPath(syncPath);
24652
25086
  return syncPath;
@@ -24680,7 +25114,7 @@ async function runSg(options) {
24680
25114
  const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
24681
25115
  args.push(...paths);
24682
25116
  let cliPath = getSgCliPath();
24683
- if (!existsSync31(cliPath) && cliPath !== "sg") {
25117
+ if (!existsSync32(cliPath) && cliPath !== "sg") {
24684
25118
  const downloadedPath = await getAstGrepPath();
24685
25119
  if (downloadedPath) {
24686
25120
  cliPath = downloadedPath;
@@ -24944,12 +25378,12 @@ var ast_grep_replace = tool({
24944
25378
  var {spawn: spawn7 } = globalThis.Bun;
24945
25379
 
24946
25380
  // src/tools/grep/constants.ts
24947
- import { existsSync as existsSync33 } from "fs";
25381
+ import { existsSync as existsSync34 } from "fs";
24948
25382
  import { join as join41, dirname as dirname7 } from "path";
24949
25383
  import { spawnSync } from "child_process";
24950
25384
 
24951
25385
  // src/tools/grep/downloader.ts
24952
- import { existsSync as existsSync32, mkdirSync as mkdirSync11, chmodSync as chmodSync3, unlinkSync as unlinkSync10, readdirSync as readdirSync9 } from "fs";
25386
+ import { existsSync as existsSync33, mkdirSync as mkdirSync11, chmodSync as chmodSync3, unlinkSync as unlinkSync10, readdirSync as readdirSync9 } from "fs";
24953
25387
  import { join as join40 } from "path";
24954
25388
  function getInstallDir() {
24955
25389
  const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
@@ -24961,7 +25395,7 @@ function getRgPath() {
24961
25395
  }
24962
25396
  function getInstalledRipgrepPath() {
24963
25397
  const rgPath = getRgPath();
24964
- return existsSync32(rgPath) ? rgPath : null;
25398
+ return existsSync33(rgPath) ? rgPath : null;
24965
25399
  }
24966
25400
 
24967
25401
  // src/tools/grep/constants.ts
@@ -24990,7 +25424,7 @@ function getOpenCodeBundledRg() {
24990
25424
  join41(execDir, "..", "libexec", rgName)
24991
25425
  ];
24992
25426
  for (const candidate of candidates) {
24993
- if (existsSync33(candidate)) {
25427
+ if (existsSync34(candidate)) {
24994
25428
  return candidate;
24995
25429
  }
24996
25430
  }
@@ -25393,11 +25827,11 @@ var glob = tool({
25393
25827
  }
25394
25828
  });
25395
25829
  // src/tools/slashcommand/tools.ts
25396
- import { existsSync as existsSync34, readdirSync as readdirSync10, readFileSync as readFileSync21 } from "fs";
25830
+ import { existsSync as existsSync35, readdirSync as readdirSync10, readFileSync as readFileSync21 } from "fs";
25397
25831
  import { homedir as homedir18 } from "os";
25398
25832
  import { join as join42, basename as basename3, dirname as dirname8 } from "path";
25399
25833
  function discoverCommandsFromDir(commandsDir, scope) {
25400
- if (!existsSync34(commandsDir)) {
25834
+ if (!existsSync35(commandsDir)) {
25401
25835
  return [];
25402
25836
  }
25403
25837
  const entries = readdirSync10(commandsDir, { withFileTypes: true });
@@ -25568,7 +26002,7 @@ var SkillFrontmatterSchema = exports_external.object({
25568
26002
  metadata: exports_external.record(exports_external.string(), exports_external.string()).optional()
25569
26003
  });
25570
26004
  // src/tools/skill/tools.ts
25571
- import { existsSync as existsSync35, readdirSync as readdirSync11, readFileSync as readFileSync22 } from "fs";
26005
+ import { existsSync as existsSync36, readdirSync as readdirSync11, readFileSync as readFileSync22 } from "fs";
25572
26006
  import { homedir as homedir19 } from "os";
25573
26007
  import { join as join43, basename as basename4 } from "path";
25574
26008
  function parseSkillFrontmatter(data) {
@@ -25581,7 +26015,7 @@ function parseSkillFrontmatter(data) {
25581
26015
  };
25582
26016
  }
25583
26017
  function discoverSkillsFromDir(skillsDir, scope) {
25584
- if (!existsSync35(skillsDir)) {
26018
+ if (!existsSync36(skillsDir)) {
25585
26019
  return [];
25586
26020
  }
25587
26021
  const entries = readdirSync11(skillsDir, { withFileTypes: true });
@@ -25593,7 +26027,7 @@ function discoverSkillsFromDir(skillsDir, scope) {
25593
26027
  if (entry.isDirectory() || entry.isSymbolicLink()) {
25594
26028
  const resolvedPath = resolveSymlink(skillPath);
25595
26029
  const skillMdPath = join43(resolvedPath, "SKILL.md");
25596
- if (!existsSync35(skillMdPath))
26030
+ if (!existsSync36(skillMdPath))
25597
26031
  continue;
25598
26032
  try {
25599
26033
  const content = readFileSync22(skillMdPath, "utf-8");
@@ -25623,7 +26057,7 @@ var skillListForDescription = availableSkills.map((s) => `- ${s.name}: ${s.descr
25623
26057
  async function parseSkillMd(skillPath) {
25624
26058
  const resolvedPath = resolveSymlink(skillPath);
25625
26059
  const skillMdPath = join43(resolvedPath, "SKILL.md");
25626
- if (!existsSync35(skillMdPath)) {
26060
+ if (!existsSync36(skillMdPath)) {
25627
26061
  return null;
25628
26062
  }
25629
26063
  try {
@@ -25641,9 +26075,9 @@ async function parseSkillMd(skillPath) {
25641
26075
  const referencesDir = join43(resolvedPath, "references");
25642
26076
  const scriptsDir = join43(resolvedPath, "scripts");
25643
26077
  const assetsDir = join43(resolvedPath, "assets");
25644
- const references = existsSync35(referencesDir) ? readdirSync11(referencesDir).filter((f) => !f.startsWith(".")) : [];
25645
- const scripts = existsSync35(scriptsDir) ? readdirSync11(scriptsDir).filter((f) => !f.startsWith(".") && !f.startsWith("__")) : [];
25646
- const assets = existsSync35(assetsDir) ? readdirSync11(assetsDir).filter((f) => !f.startsWith(".")) : [];
26078
+ const references = existsSync36(referencesDir) ? readdirSync11(referencesDir).filter((f) => !f.startsWith(".")) : [];
26079
+ const scripts = existsSync36(scriptsDir) ? readdirSync11(scriptsDir).filter((f) => !f.startsWith(".") && !f.startsWith("__")) : [];
26080
+ const assets = existsSync36(assetsDir) ? readdirSync11(assetsDir).filter((f) => !f.startsWith(".")) : [];
25647
26081
  return {
25648
26082
  name: metadata.name,
25649
26083
  path: resolvedPath,
@@ -25659,7 +26093,7 @@ async function parseSkillMd(skillPath) {
25659
26093
  }
25660
26094
  }
25661
26095
  async function discoverSkillsFromDirAsync(skillsDir) {
25662
- if (!existsSync35(skillsDir)) {
26096
+ if (!existsSync36(skillsDir)) {
25663
26097
  return [];
25664
26098
  }
25665
26099
  const entries = readdirSync11(skillsDir, { withFileTypes: true });
@@ -26474,17 +26908,17 @@ var builtinTools = {
26474
26908
  skill
26475
26909
  };
26476
26910
  // src/features/background-agent/manager.ts
26477
- import { existsSync as existsSync36, readdirSync as readdirSync12 } from "fs";
26911
+ import { existsSync as existsSync37, readdirSync as readdirSync12 } from "fs";
26478
26912
  import { join as join44 } from "path";
26479
26913
  function getMessageDir4(sessionID) {
26480
- if (!existsSync36(MESSAGE_STORAGE))
26914
+ if (!existsSync37(MESSAGE_STORAGE))
26481
26915
  return null;
26482
26916
  const directPath = join44(MESSAGE_STORAGE, sessionID);
26483
- if (existsSync36(directPath))
26917
+ if (existsSync37(directPath))
26484
26918
  return directPath;
26485
26919
  for (const dir of readdirSync12(MESSAGE_STORAGE)) {
26486
26920
  const sessionPath = join44(MESSAGE_STORAGE, dir, sessionID);
26487
- if (existsSync36(sessionPath))
26921
+ if (existsSync37(sessionPath))
26488
26922
  return sessionPath;
26489
26923
  }
26490
26924
  return null;
@@ -26955,8 +27389,9 @@ var SisyphusAgentConfigSchema = exports_external.object({
26955
27389
  });
26956
27390
  var ExperimentalConfigSchema = exports_external.object({
26957
27391
  aggressive_truncation: exports_external.boolean().optional(),
26958
- empty_message_recovery: exports_external.boolean().optional(),
26959
- auto_resume: exports_external.boolean().optional()
27392
+ auto_resume: exports_external.boolean().optional(),
27393
+ preemptive_compaction: exports_external.boolean().optional(),
27394
+ preemptive_compaction_threshold: exports_external.number().min(0.5).max(0.95).optional()
26960
27395
  });
26961
27396
  var OhMyOpenCodeConfigSchema = exports_external.object({
26962
27397
  $schema: exports_external.string().optional(),
@@ -27042,7 +27477,7 @@ var PLAN_PERMISSION = {
27042
27477
  };
27043
27478
 
27044
27479
  // src/index.ts
27045
- import * as fs6 from "fs";
27480
+ import * as fs7 from "fs";
27046
27481
  import * as path7 from "path";
27047
27482
  var AGENT_NAME_MAP = {
27048
27483
  omo: "Sisyphus",
@@ -27087,7 +27522,7 @@ function migrateConfigFile(configPath, rawConfig) {
27087
27522
  }
27088
27523
  if (needsWrite) {
27089
27524
  try {
27090
- fs6.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + `
27525
+ fs7.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + `
27091
27526
  `, "utf-8");
27092
27527
  log(`Migrated config file: ${configPath} (OmO \u2192 Sisyphus)`);
27093
27528
  } catch (err) {
@@ -27098,8 +27533,8 @@ function migrateConfigFile(configPath, rawConfig) {
27098
27533
  }
27099
27534
  function loadConfigFromPath2(configPath) {
27100
27535
  try {
27101
- if (fs6.existsSync(configPath)) {
27102
- const content = fs6.readFileSync(configPath, "utf-8");
27536
+ if (fs7.existsSync(configPath)) {
27537
+ const content = fs7.readFileSync(configPath, "utf-8");
27103
27538
  const rawConfig = JSON.parse(content);
27104
27539
  migrateConfigFile(configPath, rawConfig);
27105
27540
  const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
@@ -27166,6 +27601,18 @@ var OhMyOpenCodePlugin = async (ctx) => {
27166
27601
  const pluginConfig = loadPluginConfig(ctx.directory);
27167
27602
  const disabledHooks = new Set(pluginConfig.disabled_hooks ?? []);
27168
27603
  const isHookEnabled = (hookName) => !disabledHooks.has(hookName);
27604
+ const modelContextLimitsCache = new Map;
27605
+ let anthropicContext1MEnabled = false;
27606
+ const getModelLimit = (providerID, modelID) => {
27607
+ const key = `${providerID}/${modelID}`;
27608
+ const cached2 = modelContextLimitsCache.get(key);
27609
+ if (cached2)
27610
+ return cached2;
27611
+ if (providerID === "anthropic" && anthropicContext1MEnabled && modelID.includes("sonnet")) {
27612
+ return 1e6;
27613
+ }
27614
+ return;
27615
+ };
27169
27616
  const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer") ? createTodoContinuationEnforcer(ctx) : null;
27170
27617
  const contextWindowMonitor = isHookEnabled("context-window-monitor") ? createContextWindowMonitorHook(ctx) : null;
27171
27618
  const sessionRecovery = isHookEnabled("session-recovery") ? createSessionRecoveryHook(ctx, { experimental: pluginConfig.experimental }) : null;
@@ -27184,6 +27631,12 @@ var OhMyOpenCodePlugin = async (ctx) => {
27184
27631
  disabledHooks: pluginConfig.claude_code?.hooks ?? true ? undefined : true
27185
27632
  });
27186
27633
  const anthropicAutoCompact = isHookEnabled("anthropic-auto-compact") ? createAnthropicAutoCompactHook(ctx, { experimental: pluginConfig.experimental }) : null;
27634
+ const compactionContextInjector = createCompactionContextInjector();
27635
+ const preemptiveCompaction = createPreemptiveCompactionHook(ctx, {
27636
+ experimental: pluginConfig.experimental,
27637
+ onBeforeSummarize: compactionContextInjector,
27638
+ getModelLimit
27639
+ });
27187
27640
  const rulesInjector = isHookEnabled("rules-injector") ? createRulesInjectorHook(ctx) : null;
27188
27641
  const autoUpdateChecker = isHookEnabled("auto-update-checker") ? createAutoUpdateCheckerHook(ctx, {
27189
27642
  showStartupToast: isHookEnabled("startup-toast"),
@@ -27219,6 +27672,22 @@ var OhMyOpenCodePlugin = async (ctx) => {
27219
27672
  await emptyMessageSanitizer?.["experimental.chat.messages.transform"]?.(input, output);
27220
27673
  },
27221
27674
  config: async (config3) => {
27675
+ const providers = config3.provider;
27676
+ const anthropicBeta = providers?.anthropic?.options?.headers?.["anthropic-beta"];
27677
+ anthropicContext1MEnabled = anthropicBeta?.includes("context-1m") ?? false;
27678
+ if (providers) {
27679
+ for (const [providerID, providerConfig] of Object.entries(providers)) {
27680
+ const models = providerConfig?.models;
27681
+ if (models) {
27682
+ for (const [modelID, modelConfig] of Object.entries(models)) {
27683
+ const contextLimit = modelConfig?.limit?.context;
27684
+ if (contextLimit) {
27685
+ modelContextLimitsCache.set(`${providerID}/${modelID}`, contextLimit);
27686
+ }
27687
+ }
27688
+ }
27689
+ }
27690
+ }
27222
27691
  const builtinAgents = createBuiltinAgents(pluginConfig.disabled_agents, pluginConfig.agents, ctx.directory, config3.model);
27223
27692
  const userAgents = pluginConfig.claude_code?.agents ?? true ? loadUserAgents() : {};
27224
27693
  const projectAgents = pluginConfig.claude_code?.agents ?? true ? loadProjectAgents() : {};
@@ -27315,6 +27784,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
27315
27784
  await rulesInjector?.event(input);
27316
27785
  await thinkMode?.event(input);
27317
27786
  await anthropicAutoCompact?.event(input);
27787
+ await preemptiveCompaction?.event(input);
27318
27788
  await agentUsageReminder?.event(input);
27319
27789
  await interactiveBashSession?.event(input);
27320
27790
  const { event } = input;