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/README.ja.md +16 -3
- package/README.ko.md +17 -4
- package/README.md +15 -4
- package/README.zh-cn.md +15 -2
- package/dist/agents/oracle.d.ts +1 -0
- package/dist/agents/sisyphus.d.ts +1 -0
- package/dist/agents/types.d.ts +2 -0
- package/dist/agents/utils.test.d.ts +1 -0
- package/dist/config/schema.d.ts +25 -23
- package/dist/hooks/anthropic-auto-compact/types.d.ts +1 -0
- package/dist/hooks/compaction-context-injector/index.d.ts +2 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/preemptive-compaction/constants.d.ts +3 -0
- package/dist/hooks/preemptive-compaction/index.d.ts +24 -0
- package/dist/hooks/preemptive-compaction/types.d.ts +17 -0
- package/dist/index.js +806 -336
- package/dist/shared/config-path.d.ts +4 -1
- package/dist/tools/ast-grep/index.d.ts +22 -22
- package/dist/tools/ast-grep/tools.d.ts +22 -22
- package/dist/tools/index.d.ts +22 -22
- package/package.json +1 -1
- package/dist/agents/build.d.ts +0 -1
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+
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
1685
|
+
Before touching any frontend file, think:
|
|
1686
|
+
> "Is this change about **how it LOOKS** or **how it WORKS**?"
|
|
1674
1687
|
|
|
1675
|
-
**
|
|
1688
|
+
- **LOOKS** (colors, sizes, positions, animations) \u2192 DELEGATE
|
|
1689
|
+
- **WORKS** (data flow, API integration, state) \u2192 Handle directly
|
|
1676
1690
|
|
|
1677
|
-
|
|
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\` |
|
|
1685
|
-
| Librarian | \`librarian\` | Unfamiliar packages /
|
|
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
|
|
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** |
|
|
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
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
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
|
|
1937
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
2402
|
+
---
|
|
2363
2403
|
|
|
2364
|
-
|
|
2365
|
-
**Never compromise on task completion. What you commit to, you deliver.**
|
|
2404
|
+
# Work Principles
|
|
2366
2405
|
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
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
|
-
|
|
2374
|
-
**Approach every codebase with the mindset of a student, always ready to learn.**
|
|
2412
|
+
---
|
|
2375
2413
|
|
|
2376
|
-
|
|
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
|
-
|
|
2381
|
-
**Respect the existing codebase. Your code should blend seamlessly.**
|
|
2416
|
+
Before coding, commit to a **BOLD aesthetic direction**:
|
|
2382
2417
|
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
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
|
-
|
|
2390
|
-
**Keep everyone informed. Hide nothing.**
|
|
2423
|
+
**Key**: Choose a clear direction and execute with precision. Intentionality > intensity.
|
|
2391
2424
|
|
|
2392
|
-
|
|
2393
|
-
-
|
|
2394
|
-
-
|
|
2395
|
-
-
|
|
2396
|
-
|
|
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
|
-
|
|
2431
|
+
---
|
|
2399
2432
|
|
|
2400
|
-
|
|
2433
|
+
# Aesthetic Guidelines
|
|
2401
2434
|
|
|
2402
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
2407
|
-
-
|
|
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
|
-
|
|
2444
|
+
## Spatial Composition
|
|
2445
|
+
Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
|
|
2413
2446
|
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
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
|
-
|
|
2452
|
+
# Anti-Patterns (NEVER)
|
|
2421
2453
|
|
|
2422
|
-
|
|
2423
|
-
-
|
|
2424
|
-
-
|
|
2425
|
-
-
|
|
2426
|
-
-
|
|
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
|
-
|
|
2460
|
+
---
|
|
2430
2461
|
|
|
2431
|
-
|
|
2462
|
+
# Execution
|
|
2432
2463
|
|
|
2433
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3171
|
-
Sisyphus:
|
|
3172
|
-
oracle:
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3234
|
-
|
|
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
|
|
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
|
|
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 (!
|
|
3337
|
+
if (!existsSync4(MESSAGE_STORAGE)) {
|
|
3297
3338
|
mkdirSync(MESSAGE_STORAGE, { recursive: true });
|
|
3298
3339
|
}
|
|
3299
3340
|
const directPath = join5(MESSAGE_STORAGE, sessionID);
|
|
3300
|
-
if (
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
3420
|
+
if (!existsSync5(MESSAGE_STORAGE))
|
|
3380
3421
|
return null;
|
|
3381
3422
|
const directPath = join6(MESSAGE_STORAGE, sessionID);
|
|
3382
|
-
if (
|
|
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 (
|
|
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 =
|
|
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
|
-
|
|
3576
|
-
|
|
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 &&
|
|
3627
|
+
if (sessionID && role === "assistant" && finish) {
|
|
3585
3628
|
remindedSessions.delete(sessionID);
|
|
3586
|
-
log(`[${HOOK_NAME}] Cleared
|
|
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
|
|
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 (!
|
|
3984
|
+
if (!existsSync6(MESSAGE_STORAGE2))
|
|
3942
3985
|
return "";
|
|
3943
3986
|
const directPath = join8(MESSAGE_STORAGE2, sessionID);
|
|
3944
|
-
if (
|
|
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 (
|
|
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 || !
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
4384
|
-
import * as
|
|
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
|
|
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
|
|
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 (
|
|
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 (!
|
|
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 (
|
|
4578
|
+
if (existsSync7(archivePath)) {
|
|
4496
4579
|
unlinkSync2(archivePath);
|
|
4497
4580
|
}
|
|
4498
|
-
if (process.platform !== "win32" &&
|
|
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
|
-
|
|
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 (
|
|
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 &&
|
|
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 (!
|
|
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
|
|
4634
|
-
import { existsSync as
|
|
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
|
-
|
|
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 || !
|
|
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
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
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
|
|
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" && !
|
|
5357
|
+
if (process.platform === "darwin" && !existsSync14(OPENCODE_STORAGE5)) {
|
|
5265
5358
|
const localShare = join18(homedir5(), ".local", "share", "opencode", "storage");
|
|
5266
|
-
if (
|
|
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 (!
|
|
5367
|
+
if (!existsSync14(MESSAGE_STORAGE3))
|
|
5275
5368
|
return "";
|
|
5276
5369
|
const directPath = join18(MESSAGE_STORAGE3, sessionID);
|
|
5277
|
-
if (
|
|
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 (
|
|
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 || !
|
|
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 (!
|
|
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
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
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
|
-
}
|
|
5524
|
-
return false;
|
|
5623
|
+
}
|
|
5525
5624
|
}
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
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 ${
|
|
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 (
|
|
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
|
|
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 &&
|
|
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 (
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 (
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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
|
|
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 (!
|
|
7878
|
+
if (!fs5.existsSync(configPath))
|
|
7565
7879
|
continue;
|
|
7566
|
-
const content =
|
|
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 =
|
|
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 (
|
|
7904
|
+
if (fs5.existsSync(pkgPath)) {
|
|
7591
7905
|
try {
|
|
7592
|
-
const content =
|
|
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 =
|
|
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 (!
|
|
7938
|
+
if (!fs5.existsSync(configPath))
|
|
7625
7939
|
continue;
|
|
7626
|
-
const content =
|
|
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 (
|
|
7648
|
-
const content =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
8051
|
+
if (!fs6.existsSync(lockPath))
|
|
7738
8052
|
return false;
|
|
7739
8053
|
try {
|
|
7740
|
-
const content =
|
|
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
|
-
|
|
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 (
|
|
7768
|
-
|
|
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 (
|
|
7773
|
-
const content =
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
-
".
|
|
10725
|
-
".
|
|
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
|
-
".
|
|
10729
|
-
".
|
|
10730
|
-
".
|
|
10731
|
-
".
|
|
10732
|
-
".
|
|
10733
|
-
".
|
|
10734
|
-
".
|
|
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
|
-
".
|
|
10773
|
-
".
|
|
10774
|
-
".tfvars": "terraform"
|
|
11190
|
+
".graphql": "graphql",
|
|
11191
|
+
".gql": "graphql"
|
|
10775
11192
|
};
|
|
10776
11193
|
// src/tools/lsp/config.ts
|
|
10777
|
-
import { existsSync as
|
|
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 (!
|
|
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
|
|
11304
|
+
const pathSeparator = isWindows2 ? ";" : ":";
|
|
11305
|
+
const paths = pathEnv.split(pathSeparator);
|
|
10886
11306
|
for (const p of paths) {
|
|
10887
|
-
if (
|
|
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
|
|
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 (!
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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 (!
|
|
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 (
|
|
24940
|
+
if (existsSync30(archivePath)) {
|
|
24507
24941
|
unlinkSync9(archivePath);
|
|
24508
24942
|
}
|
|
24509
|
-
if (process.platform !== "win32" &&
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
|
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 &&
|
|
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 &&
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 =
|
|
25645
|
-
const scripts =
|
|
25646
|
-
const assets =
|
|
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 (!
|
|
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
|
|
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 (!
|
|
26914
|
+
if (!existsSync37(MESSAGE_STORAGE))
|
|
26481
26915
|
return null;
|
|
26482
26916
|
const directPath = join44(MESSAGE_STORAGE, sessionID);
|
|
26483
|
-
if (
|
|
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 (
|
|
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
|
-
|
|
26959
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
27102
|
-
const content =
|
|
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;
|