oh-my-opencode 2.4.0 → 2.4.2
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/directory-agents-injector/index.d.ts +4 -0
- package/dist/hooks/directory-readme-injector/index.d.ts +4 -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/hooks/rules-injector/index.d.ts +4 -0
- package/dist/index.js +956 -393
- 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\` |
|
|
1682
|
+
|
|
1683
|
+
#### Step 2: Ask Yourself
|
|
1666
1684
|
|
|
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 |
|
|
1685
|
+
Before touching any frontend file, think:
|
|
1686
|
+
> "Is this change about **how it LOOKS** or **how it WORKS**?"
|
|
1672
1687
|
|
|
1673
|
-
**
|
|
1688
|
+
- **LOOKS** (colors, sizes, positions, animations) \u2192 DELEGATE
|
|
1689
|
+
- **WORKS** (data flow, API integration, state) \u2192 Handle directly
|
|
1674
1690
|
|
|
1675
|
-
|
|
1691
|
+
#### Quick Reference Examples
|
|
1676
1692
|
|
|
1677
|
-
|
|
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,27 @@ 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
|
-
} else {
|
|
3235
|
-
result[name] = finalConfig;
|
|
3274
|
+
config = mergeAgentConfig(config, override);
|
|
3236
3275
|
}
|
|
3276
|
+
result[name] = config;
|
|
3237
3277
|
}
|
|
3238
3278
|
return result;
|
|
3239
3279
|
}
|
|
3240
3280
|
// src/hooks/todo-continuation-enforcer.ts
|
|
3241
|
-
import { existsSync as
|
|
3281
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
|
|
3242
3282
|
import { join as join6 } from "path";
|
|
3243
3283
|
|
|
3244
3284
|
// src/features/claude-code-session-state/state.ts
|
|
@@ -3251,7 +3291,7 @@ function getMainSessionID() {
|
|
|
3251
3291
|
return mainSessionID;
|
|
3252
3292
|
}
|
|
3253
3293
|
// src/features/hook-message-injector/injector.ts
|
|
3254
|
-
import { existsSync as
|
|
3294
|
+
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync2, readdirSync, writeFileSync } from "fs";
|
|
3255
3295
|
import { join as join5 } from "path";
|
|
3256
3296
|
|
|
3257
3297
|
// src/features/hook-message-injector/constants.ts
|
|
@@ -3293,16 +3333,16 @@ function generatePartId() {
|
|
|
3293
3333
|
return `prt_${timestamp}${random}`;
|
|
3294
3334
|
}
|
|
3295
3335
|
function getOrCreateMessageDir(sessionID) {
|
|
3296
|
-
if (!
|
|
3336
|
+
if (!existsSync4(MESSAGE_STORAGE)) {
|
|
3297
3337
|
mkdirSync(MESSAGE_STORAGE, { recursive: true });
|
|
3298
3338
|
}
|
|
3299
3339
|
const directPath = join5(MESSAGE_STORAGE, sessionID);
|
|
3300
|
-
if (
|
|
3340
|
+
if (existsSync4(directPath)) {
|
|
3301
3341
|
return directPath;
|
|
3302
3342
|
}
|
|
3303
3343
|
for (const dir of readdirSync(MESSAGE_STORAGE)) {
|
|
3304
3344
|
const sessionPath = join5(MESSAGE_STORAGE, dir, sessionID);
|
|
3305
|
-
if (
|
|
3345
|
+
if (existsSync4(sessionPath)) {
|
|
3306
3346
|
return sessionPath;
|
|
3307
3347
|
}
|
|
3308
3348
|
}
|
|
@@ -3357,7 +3397,7 @@ function injectHookMessage(sessionID, hookContent, originalMessage) {
|
|
|
3357
3397
|
try {
|
|
3358
3398
|
writeFileSync(join5(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
|
|
3359
3399
|
const partDir = join5(PART_STORAGE, messageID);
|
|
3360
|
-
if (!
|
|
3400
|
+
if (!existsSync4(partDir)) {
|
|
3361
3401
|
mkdirSync(partDir, { recursive: true });
|
|
3362
3402
|
}
|
|
3363
3403
|
writeFileSync(join5(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
|
|
@@ -3376,14 +3416,14 @@ Incomplete tasks remain in your todo list. Continue working on the next pending
|
|
|
3376
3416
|
- Mark each task complete when finished
|
|
3377
3417
|
- Do not stop until all tasks are done`;
|
|
3378
3418
|
function getMessageDir(sessionID) {
|
|
3379
|
-
if (!
|
|
3419
|
+
if (!existsSync5(MESSAGE_STORAGE))
|
|
3380
3420
|
return null;
|
|
3381
3421
|
const directPath = join6(MESSAGE_STORAGE, sessionID);
|
|
3382
|
-
if (
|
|
3422
|
+
if (existsSync5(directPath))
|
|
3383
3423
|
return directPath;
|
|
3384
3424
|
for (const dir of readdirSync2(MESSAGE_STORAGE)) {
|
|
3385
3425
|
const sessionPath = join6(MESSAGE_STORAGE, dir, sessionID);
|
|
3386
|
-
if (
|
|
3426
|
+
if (existsSync5(sessionPath))
|
|
3387
3427
|
return sessionPath;
|
|
3388
3428
|
}
|
|
3389
3429
|
return null;
|
|
@@ -3408,7 +3448,7 @@ function detectInterrupt(error) {
|
|
|
3408
3448
|
}
|
|
3409
3449
|
return false;
|
|
3410
3450
|
}
|
|
3411
|
-
var COUNTDOWN_SECONDS =
|
|
3451
|
+
var COUNTDOWN_SECONDS = 2;
|
|
3412
3452
|
var TOAST_DURATION_MS = 900;
|
|
3413
3453
|
function createTodoContinuationEnforcer(ctx) {
|
|
3414
3454
|
const remindedSessions = new Set;
|
|
@@ -3572,8 +3612,10 @@ function createTodoContinuationEnforcer(ctx) {
|
|
|
3572
3612
|
if (event.type === "message.updated") {
|
|
3573
3613
|
const info = props?.info;
|
|
3574
3614
|
const sessionID = info?.sessionID;
|
|
3575
|
-
|
|
3576
|
-
|
|
3615
|
+
const role = info?.role;
|
|
3616
|
+
const finish = info?.finish;
|
|
3617
|
+
log(`[${HOOK_NAME}] message.updated received`, { sessionID, role, finish });
|
|
3618
|
+
if (sessionID && role === "user") {
|
|
3577
3619
|
const countdown = pendingCountdowns.get(sessionID);
|
|
3578
3620
|
if (countdown) {
|
|
3579
3621
|
clearInterval(countdown.intervalId);
|
|
@@ -3581,9 +3623,9 @@ function createTodoContinuationEnforcer(ctx) {
|
|
|
3581
3623
|
log(`[${HOOK_NAME}] Cancelled countdown on user message`, { sessionID });
|
|
3582
3624
|
}
|
|
3583
3625
|
}
|
|
3584
|
-
if (sessionID &&
|
|
3626
|
+
if (sessionID && role === "assistant" && finish) {
|
|
3585
3627
|
remindedSessions.delete(sessionID);
|
|
3586
|
-
log(`[${HOOK_NAME}] Cleared
|
|
3628
|
+
log(`[${HOOK_NAME}] Cleared reminded state on assistant finish`, { sessionID });
|
|
3587
3629
|
}
|
|
3588
3630
|
}
|
|
3589
3631
|
if (event.type === "session.deleted") {
|
|
@@ -3898,7 +3940,7 @@ function createSessionNotification(ctx, config = {}) {
|
|
|
3898
3940
|
};
|
|
3899
3941
|
}
|
|
3900
3942
|
// src/hooks/session-recovery/storage.ts
|
|
3901
|
-
import { existsSync as
|
|
3943
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
3902
3944
|
import { join as join8 } from "path";
|
|
3903
3945
|
|
|
3904
3946
|
// src/hooks/session-recovery/constants.ts
|
|
@@ -3938,15 +3980,15 @@ function generatePartId2() {
|
|
|
3938
3980
|
return `prt_${timestamp}${random}`;
|
|
3939
3981
|
}
|
|
3940
3982
|
function getMessageDir2(sessionID) {
|
|
3941
|
-
if (!
|
|
3983
|
+
if (!existsSync6(MESSAGE_STORAGE2))
|
|
3942
3984
|
return "";
|
|
3943
3985
|
const directPath = join8(MESSAGE_STORAGE2, sessionID);
|
|
3944
|
-
if (
|
|
3986
|
+
if (existsSync6(directPath)) {
|
|
3945
3987
|
return directPath;
|
|
3946
3988
|
}
|
|
3947
3989
|
for (const dir of readdirSync3(MESSAGE_STORAGE2)) {
|
|
3948
3990
|
const sessionPath = join8(MESSAGE_STORAGE2, dir, sessionID);
|
|
3949
|
-
if (
|
|
3991
|
+
if (existsSync6(sessionPath)) {
|
|
3950
3992
|
return sessionPath;
|
|
3951
3993
|
}
|
|
3952
3994
|
}
|
|
@@ -3954,7 +3996,7 @@ function getMessageDir2(sessionID) {
|
|
|
3954
3996
|
}
|
|
3955
3997
|
function readMessages(sessionID) {
|
|
3956
3998
|
const messageDir = getMessageDir2(sessionID);
|
|
3957
|
-
if (!messageDir || !
|
|
3999
|
+
if (!messageDir || !existsSync6(messageDir))
|
|
3958
4000
|
return [];
|
|
3959
4001
|
const messages = [];
|
|
3960
4002
|
for (const file of readdirSync3(messageDir)) {
|
|
@@ -3977,7 +4019,7 @@ function readMessages(sessionID) {
|
|
|
3977
4019
|
}
|
|
3978
4020
|
function readParts(messageID) {
|
|
3979
4021
|
const partDir = join8(PART_STORAGE2, messageID);
|
|
3980
|
-
if (!
|
|
4022
|
+
if (!existsSync6(partDir))
|
|
3981
4023
|
return [];
|
|
3982
4024
|
const parts = [];
|
|
3983
4025
|
for (const file of readdirSync3(partDir)) {
|
|
@@ -4015,7 +4057,7 @@ function messageHasContent(messageID) {
|
|
|
4015
4057
|
}
|
|
4016
4058
|
function injectTextPart(sessionID, messageID, text) {
|
|
4017
4059
|
const partDir = join8(PART_STORAGE2, messageID);
|
|
4018
|
-
if (!
|
|
4060
|
+
if (!existsSync6(partDir)) {
|
|
4019
4061
|
mkdirSync2(partDir, { recursive: true });
|
|
4020
4062
|
}
|
|
4021
4063
|
const partId = generatePartId2();
|
|
@@ -4044,6 +4086,19 @@ function findEmptyMessages(sessionID) {
|
|
|
4044
4086
|
}
|
|
4045
4087
|
return emptyIds;
|
|
4046
4088
|
}
|
|
4089
|
+
function findEmptyMessageByIndex(sessionID, targetIndex) {
|
|
4090
|
+
const messages = readMessages(sessionID);
|
|
4091
|
+
const indicesToTry = [targetIndex, targetIndex - 1, targetIndex - 2];
|
|
4092
|
+
for (const idx of indicesToTry) {
|
|
4093
|
+
if (idx < 0 || idx >= messages.length)
|
|
4094
|
+
continue;
|
|
4095
|
+
const targetMsg = messages[idx];
|
|
4096
|
+
if (!messageHasContent(targetMsg.id)) {
|
|
4097
|
+
return targetMsg.id;
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
return null;
|
|
4101
|
+
}
|
|
4047
4102
|
function findMessagesWithThinkingBlocks(sessionID) {
|
|
4048
4103
|
const messages = readMessages(sessionID);
|
|
4049
4104
|
const result = [];
|
|
@@ -4079,7 +4134,7 @@ function findMessagesWithOrphanThinking(sessionID) {
|
|
|
4079
4134
|
}
|
|
4080
4135
|
function prependThinkingPart(sessionID, messageID) {
|
|
4081
4136
|
const partDir = join8(PART_STORAGE2, messageID);
|
|
4082
|
-
if (!
|
|
4137
|
+
if (!existsSync6(partDir)) {
|
|
4083
4138
|
mkdirSync2(partDir, { recursive: true });
|
|
4084
4139
|
}
|
|
4085
4140
|
const partId = `prt_0000000000_thinking`;
|
|
@@ -4100,7 +4155,7 @@ function prependThinkingPart(sessionID, messageID) {
|
|
|
4100
4155
|
}
|
|
4101
4156
|
function stripThinkingParts(messageID) {
|
|
4102
4157
|
const partDir = join8(PART_STORAGE2, messageID);
|
|
4103
|
-
if (!
|
|
4158
|
+
if (!existsSync6(partDir))
|
|
4104
4159
|
return false;
|
|
4105
4160
|
let anyRemoved = false;
|
|
4106
4161
|
for (const file of readdirSync3(partDir)) {
|
|
@@ -4120,6 +4175,33 @@ function stripThinkingParts(messageID) {
|
|
|
4120
4175
|
}
|
|
4121
4176
|
return anyRemoved;
|
|
4122
4177
|
}
|
|
4178
|
+
function replaceEmptyTextParts(messageID, replacementText) {
|
|
4179
|
+
const partDir = join8(PART_STORAGE2, messageID);
|
|
4180
|
+
if (!existsSync6(partDir))
|
|
4181
|
+
return false;
|
|
4182
|
+
let anyReplaced = false;
|
|
4183
|
+
for (const file of readdirSync3(partDir)) {
|
|
4184
|
+
if (!file.endsWith(".json"))
|
|
4185
|
+
continue;
|
|
4186
|
+
try {
|
|
4187
|
+
const filePath = join8(partDir, file);
|
|
4188
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
4189
|
+
const part = JSON.parse(content);
|
|
4190
|
+
if (part.type === "text") {
|
|
4191
|
+
const textPart = part;
|
|
4192
|
+
if (!textPart.text?.trim()) {
|
|
4193
|
+
textPart.text = replacementText;
|
|
4194
|
+
textPart.synthetic = true;
|
|
4195
|
+
writeFileSync2(filePath, JSON.stringify(textPart, null, 2));
|
|
4196
|
+
anyReplaced = true;
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
} catch {
|
|
4200
|
+
continue;
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
return anyReplaced;
|
|
4204
|
+
}
|
|
4123
4205
|
function findMessageByIndexNeedingThinking(sessionID, targetIndex) {
|
|
4124
4206
|
const messages = readMessages(sessionID);
|
|
4125
4207
|
if (targetIndex < 0 || targetIndex >= messages.length)
|
|
@@ -4380,13 +4462,13 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
4380
4462
|
var {spawn: spawn3 } = globalThis.Bun;
|
|
4381
4463
|
import { createRequire as createRequire2 } from "module";
|
|
4382
4464
|
import { dirname, join as join10 } from "path";
|
|
4383
|
-
import { existsSync as
|
|
4384
|
-
import * as
|
|
4465
|
+
import { existsSync as existsSync8 } from "fs";
|
|
4466
|
+
import * as fs3 from "fs";
|
|
4385
4467
|
import { tmpdir as tmpdir3 } from "os";
|
|
4386
4468
|
|
|
4387
4469
|
// src/hooks/comment-checker/downloader.ts
|
|
4388
4470
|
var {spawn: spawn2 } = globalThis.Bun;
|
|
4389
|
-
import { existsSync as
|
|
4471
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, chmodSync, unlinkSync as unlinkSync2, appendFileSync as appendFileSync2 } from "fs";
|
|
4390
4472
|
import { join as join9 } from "path";
|
|
4391
4473
|
import { homedir as homedir4, tmpdir as tmpdir2 } from "os";
|
|
4392
4474
|
import { createRequire } from "module";
|
|
@@ -4417,7 +4499,7 @@ function getBinaryName() {
|
|
|
4417
4499
|
}
|
|
4418
4500
|
function getCachedBinaryPath() {
|
|
4419
4501
|
const binaryPath = join9(getCacheDir(), getBinaryName());
|
|
4420
|
-
return
|
|
4502
|
+
return existsSync7(binaryPath) ? binaryPath : null;
|
|
4421
4503
|
}
|
|
4422
4504
|
function getPackageVersion() {
|
|
4423
4505
|
try {
|
|
@@ -4465,7 +4547,7 @@ async function downloadCommentChecker() {
|
|
|
4465
4547
|
const cacheDir = getCacheDir();
|
|
4466
4548
|
const binaryName = getBinaryName();
|
|
4467
4549
|
const binaryPath = join9(cacheDir, binaryName);
|
|
4468
|
-
if (
|
|
4550
|
+
if (existsSync7(binaryPath)) {
|
|
4469
4551
|
debugLog("Binary already cached at:", binaryPath);
|
|
4470
4552
|
return binaryPath;
|
|
4471
4553
|
}
|
|
@@ -4476,7 +4558,7 @@ async function downloadCommentChecker() {
|
|
|
4476
4558
|
debugLog(`Downloading from: ${downloadUrl}`);
|
|
4477
4559
|
console.log(`[oh-my-opencode] Downloading comment-checker binary...`);
|
|
4478
4560
|
try {
|
|
4479
|
-
if (!
|
|
4561
|
+
if (!existsSync7(cacheDir)) {
|
|
4480
4562
|
mkdirSync3(cacheDir, { recursive: true });
|
|
4481
4563
|
}
|
|
4482
4564
|
const response = await fetch(downloadUrl, { redirect: "follow" });
|
|
@@ -4492,10 +4574,10 @@ async function downloadCommentChecker() {
|
|
|
4492
4574
|
} else {
|
|
4493
4575
|
await extractZip(archivePath, cacheDir);
|
|
4494
4576
|
}
|
|
4495
|
-
if (
|
|
4577
|
+
if (existsSync7(archivePath)) {
|
|
4496
4578
|
unlinkSync2(archivePath);
|
|
4497
4579
|
}
|
|
4498
|
-
if (process.platform !== "win32" &&
|
|
4580
|
+
if (process.platform !== "win32" && existsSync7(binaryPath)) {
|
|
4499
4581
|
chmodSync(binaryPath, 493);
|
|
4500
4582
|
}
|
|
4501
4583
|
debugLog(`Successfully downloaded binary to: ${binaryPath}`);
|
|
@@ -4524,7 +4606,7 @@ function debugLog2(...args) {
|
|
|
4524
4606
|
if (DEBUG2) {
|
|
4525
4607
|
const msg = `[${new Date().toISOString()}] [comment-checker:cli] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
4526
4608
|
`;
|
|
4527
|
-
|
|
4609
|
+
fs3.appendFileSync(DEBUG_FILE2, msg);
|
|
4528
4610
|
}
|
|
4529
4611
|
}
|
|
4530
4612
|
function getBinaryName2() {
|
|
@@ -4537,7 +4619,7 @@ function findCommentCheckerPathSync() {
|
|
|
4537
4619
|
const cliPkgPath = require2.resolve("@code-yeongyu/comment-checker/package.json");
|
|
4538
4620
|
const cliDir = dirname(cliPkgPath);
|
|
4539
4621
|
const binaryPath = join10(cliDir, "bin", binaryName);
|
|
4540
|
-
if (
|
|
4622
|
+
if (existsSync8(binaryPath)) {
|
|
4541
4623
|
debugLog2("found binary in main package:", binaryPath);
|
|
4542
4624
|
return binaryPath;
|
|
4543
4625
|
}
|
|
@@ -4563,7 +4645,7 @@ async function getCommentCheckerPath() {
|
|
|
4563
4645
|
}
|
|
4564
4646
|
initPromise = (async () => {
|
|
4565
4647
|
const syncPath = findCommentCheckerPathSync();
|
|
4566
|
-
if (syncPath &&
|
|
4648
|
+
if (syncPath && existsSync8(syncPath)) {
|
|
4567
4649
|
resolvedCliPath = syncPath;
|
|
4568
4650
|
debugLog2("using sync-resolved path:", syncPath);
|
|
4569
4651
|
return syncPath;
|
|
@@ -4597,7 +4679,7 @@ async function runCommentChecker(input, cliPath) {
|
|
|
4597
4679
|
debugLog2("comment-checker binary not found");
|
|
4598
4680
|
return { hasComments: false, message: "" };
|
|
4599
4681
|
}
|
|
4600
|
-
if (!
|
|
4682
|
+
if (!existsSync8(binaryPath)) {
|
|
4601
4683
|
debugLog2("comment-checker binary does not exist:", binaryPath);
|
|
4602
4684
|
return { hasComments: false, message: "" };
|
|
4603
4685
|
}
|
|
@@ -4630,8 +4712,8 @@ async function runCommentChecker(input, cliPath) {
|
|
|
4630
4712
|
}
|
|
4631
4713
|
|
|
4632
4714
|
// src/hooks/comment-checker/index.ts
|
|
4633
|
-
import * as
|
|
4634
|
-
import { existsSync as
|
|
4715
|
+
import * as fs4 from "fs";
|
|
4716
|
+
import { existsSync as existsSync9 } from "fs";
|
|
4635
4717
|
import { tmpdir as tmpdir4 } from "os";
|
|
4636
4718
|
import { join as join11 } from "path";
|
|
4637
4719
|
var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
@@ -4640,7 +4722,7 @@ function debugLog3(...args) {
|
|
|
4640
4722
|
if (DEBUG3) {
|
|
4641
4723
|
const msg = `[${new Date().toISOString()}] [comment-checker:hook] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
4642
4724
|
`;
|
|
4643
|
-
|
|
4725
|
+
fs4.appendFileSync(DEBUG_FILE3, msg);
|
|
4644
4726
|
}
|
|
4645
4727
|
}
|
|
4646
4728
|
var pendingCalls = new Map;
|
|
@@ -4711,7 +4793,7 @@ function createCommentCheckerHooks() {
|
|
|
4711
4793
|
}
|
|
4712
4794
|
try {
|
|
4713
4795
|
const cliPath = await cliPathPromise;
|
|
4714
|
-
if (!cliPath || !
|
|
4796
|
+
if (!cliPath || !existsSync9(cliPath)) {
|
|
4715
4797
|
debugLog3("CLI not available, skipping comment check");
|
|
4716
4798
|
return;
|
|
4717
4799
|
}
|
|
@@ -4782,12 +4864,12 @@ function createToolOutputTruncatorHook(ctx) {
|
|
|
4782
4864
|
};
|
|
4783
4865
|
}
|
|
4784
4866
|
// src/hooks/directory-agents-injector/index.ts
|
|
4785
|
-
import { existsSync as
|
|
4867
|
+
import { existsSync as existsSync11, readFileSync as readFileSync5 } from "fs";
|
|
4786
4868
|
import { dirname as dirname2, join as join14, resolve as resolve2 } from "path";
|
|
4787
4869
|
|
|
4788
4870
|
// src/hooks/directory-agents-injector/storage.ts
|
|
4789
4871
|
import {
|
|
4790
|
-
existsSync as
|
|
4872
|
+
existsSync as existsSync10,
|
|
4791
4873
|
mkdirSync as mkdirSync4,
|
|
4792
4874
|
readFileSync as readFileSync4,
|
|
4793
4875
|
writeFileSync as writeFileSync3,
|
|
@@ -4807,7 +4889,7 @@ function getStoragePath(sessionID) {
|
|
|
4807
4889
|
}
|
|
4808
4890
|
function loadInjectedPaths(sessionID) {
|
|
4809
4891
|
const filePath = getStoragePath(sessionID);
|
|
4810
|
-
if (!
|
|
4892
|
+
if (!existsSync10(filePath))
|
|
4811
4893
|
return new Set;
|
|
4812
4894
|
try {
|
|
4813
4895
|
const content = readFileSync4(filePath, "utf-8");
|
|
@@ -4818,7 +4900,7 @@ function loadInjectedPaths(sessionID) {
|
|
|
4818
4900
|
}
|
|
4819
4901
|
}
|
|
4820
4902
|
function saveInjectedPaths(sessionID, paths) {
|
|
4821
|
-
if (!
|
|
4903
|
+
if (!existsSync10(AGENTS_INJECTOR_STORAGE)) {
|
|
4822
4904
|
mkdirSync4(AGENTS_INJECTOR_STORAGE, { recursive: true });
|
|
4823
4905
|
}
|
|
4824
4906
|
const data = {
|
|
@@ -4830,7 +4912,7 @@ function saveInjectedPaths(sessionID, paths) {
|
|
|
4830
4912
|
}
|
|
4831
4913
|
function clearInjectedPaths(sessionID) {
|
|
4832
4914
|
const filePath = getStoragePath(sessionID);
|
|
4833
|
-
if (
|
|
4915
|
+
if (existsSync10(filePath)) {
|
|
4834
4916
|
unlinkSync3(filePath);
|
|
4835
4917
|
}
|
|
4836
4918
|
}
|
|
@@ -4838,25 +4920,26 @@ function clearInjectedPaths(sessionID) {
|
|
|
4838
4920
|
// src/hooks/directory-agents-injector/index.ts
|
|
4839
4921
|
function createDirectoryAgentsInjectorHook(ctx) {
|
|
4840
4922
|
const sessionCaches = new Map;
|
|
4923
|
+
const pendingBatchReads = new Map;
|
|
4841
4924
|
function getSessionCache(sessionID) {
|
|
4842
4925
|
if (!sessionCaches.has(sessionID)) {
|
|
4843
4926
|
sessionCaches.set(sessionID, loadInjectedPaths(sessionID));
|
|
4844
4927
|
}
|
|
4845
4928
|
return sessionCaches.get(sessionID);
|
|
4846
4929
|
}
|
|
4847
|
-
function resolveFilePath2(
|
|
4848
|
-
if (!
|
|
4930
|
+
function resolveFilePath2(path4) {
|
|
4931
|
+
if (!path4)
|
|
4849
4932
|
return null;
|
|
4850
|
-
if (
|
|
4851
|
-
return
|
|
4852
|
-
return resolve2(ctx.directory,
|
|
4933
|
+
if (path4.startsWith("/"))
|
|
4934
|
+
return path4;
|
|
4935
|
+
return resolve2(ctx.directory, path4);
|
|
4853
4936
|
}
|
|
4854
4937
|
function findAgentsMdUp(startDir) {
|
|
4855
4938
|
const found = [];
|
|
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)
|
|
@@ -4870,35 +4953,59 @@ function createDirectoryAgentsInjectorHook(ctx) {
|
|
|
4870
4953
|
}
|
|
4871
4954
|
return found.reverse();
|
|
4872
4955
|
}
|
|
4873
|
-
|
|
4874
|
-
|
|
4956
|
+
function processFilePathForInjection(filePath, sessionID, output) {
|
|
4957
|
+
const resolved = resolveFilePath2(filePath);
|
|
4958
|
+
if (!resolved)
|
|
4875
4959
|
return;
|
|
4876
|
-
const
|
|
4877
|
-
|
|
4878
|
-
return;
|
|
4879
|
-
const dir = dirname2(filePath);
|
|
4880
|
-
const cache = getSessionCache(input.sessionID);
|
|
4960
|
+
const dir = dirname2(resolved);
|
|
4961
|
+
const cache = getSessionCache(sessionID);
|
|
4881
4962
|
const agentsPaths = findAgentsMdUp(dir);
|
|
4882
|
-
const toInject = [];
|
|
4883
4963
|
for (const agentsPath of agentsPaths) {
|
|
4884
4964
|
const agentsDir = dirname2(agentsPath);
|
|
4885
4965
|
if (cache.has(agentsDir))
|
|
4886
4966
|
continue;
|
|
4887
4967
|
try {
|
|
4888
4968
|
const content = readFileSync5(agentsPath, "utf-8");
|
|
4889
|
-
|
|
4969
|
+
output.output += `
|
|
4970
|
+
|
|
4971
|
+
[Directory Context: ${agentsPath}]
|
|
4972
|
+
${content}`;
|
|
4890
4973
|
cache.add(agentsDir);
|
|
4891
4974
|
} catch {}
|
|
4892
4975
|
}
|
|
4893
|
-
|
|
4976
|
+
saveInjectedPaths(sessionID, cache);
|
|
4977
|
+
}
|
|
4978
|
+
const toolExecuteBefore = async (input, output) => {
|
|
4979
|
+
if (input.tool.toLowerCase() !== "batch")
|
|
4894
4980
|
return;
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4981
|
+
const args = output.args;
|
|
4982
|
+
if (!args?.tool_calls)
|
|
4983
|
+
return;
|
|
4984
|
+
const readFilePaths = [];
|
|
4985
|
+
for (const call of args.tool_calls) {
|
|
4986
|
+
if (call.tool.toLowerCase() === "read" && call.parameters?.filePath) {
|
|
4987
|
+
readFilePaths.push(call.parameters.filePath);
|
|
4988
|
+
}
|
|
4989
|
+
}
|
|
4990
|
+
if (readFilePaths.length > 0) {
|
|
4991
|
+
pendingBatchReads.set(input.callID, readFilePaths);
|
|
4992
|
+
}
|
|
4993
|
+
};
|
|
4994
|
+
const toolExecuteAfter = async (input, output) => {
|
|
4995
|
+
const toolName = input.tool.toLowerCase();
|
|
4996
|
+
if (toolName === "read") {
|
|
4997
|
+
processFilePathForInjection(output.title, input.sessionID, output);
|
|
4998
|
+
return;
|
|
4999
|
+
}
|
|
5000
|
+
if (toolName === "batch") {
|
|
5001
|
+
const filePaths = pendingBatchReads.get(input.callID);
|
|
5002
|
+
if (filePaths) {
|
|
5003
|
+
for (const filePath of filePaths) {
|
|
5004
|
+
processFilePathForInjection(filePath, input.sessionID, output);
|
|
5005
|
+
}
|
|
5006
|
+
pendingBatchReads.delete(input.callID);
|
|
5007
|
+
}
|
|
4900
5008
|
}
|
|
4901
|
-
saveInjectedPaths(input.sessionID, cache);
|
|
4902
5009
|
};
|
|
4903
5010
|
const eventHandler = async ({ event }) => {
|
|
4904
5011
|
const props = event.properties;
|
|
@@ -4918,17 +5025,18 @@ ${content}`;
|
|
|
4918
5025
|
}
|
|
4919
5026
|
};
|
|
4920
5027
|
return {
|
|
5028
|
+
"tool.execute.before": toolExecuteBefore,
|
|
4921
5029
|
"tool.execute.after": toolExecuteAfter,
|
|
4922
5030
|
event: eventHandler
|
|
4923
5031
|
};
|
|
4924
5032
|
}
|
|
4925
5033
|
// src/hooks/directory-readme-injector/index.ts
|
|
4926
|
-
import { existsSync as
|
|
5034
|
+
import { existsSync as existsSync13, readFileSync as readFileSync7 } from "fs";
|
|
4927
5035
|
import { dirname as dirname3, join as join17, resolve as resolve3 } from "path";
|
|
4928
5036
|
|
|
4929
5037
|
// src/hooks/directory-readme-injector/storage.ts
|
|
4930
5038
|
import {
|
|
4931
|
-
existsSync as
|
|
5039
|
+
existsSync as existsSync12,
|
|
4932
5040
|
mkdirSync as mkdirSync5,
|
|
4933
5041
|
readFileSync as readFileSync6,
|
|
4934
5042
|
writeFileSync as writeFileSync4,
|
|
@@ -4948,7 +5056,7 @@ function getStoragePath2(sessionID) {
|
|
|
4948
5056
|
}
|
|
4949
5057
|
function loadInjectedPaths2(sessionID) {
|
|
4950
5058
|
const filePath = getStoragePath2(sessionID);
|
|
4951
|
-
if (!
|
|
5059
|
+
if (!existsSync12(filePath))
|
|
4952
5060
|
return new Set;
|
|
4953
5061
|
try {
|
|
4954
5062
|
const content = readFileSync6(filePath, "utf-8");
|
|
@@ -4959,7 +5067,7 @@ function loadInjectedPaths2(sessionID) {
|
|
|
4959
5067
|
}
|
|
4960
5068
|
}
|
|
4961
5069
|
function saveInjectedPaths2(sessionID, paths) {
|
|
4962
|
-
if (!
|
|
5070
|
+
if (!existsSync12(README_INJECTOR_STORAGE)) {
|
|
4963
5071
|
mkdirSync5(README_INJECTOR_STORAGE, { recursive: true });
|
|
4964
5072
|
}
|
|
4965
5073
|
const data = {
|
|
@@ -4971,7 +5079,7 @@ function saveInjectedPaths2(sessionID, paths) {
|
|
|
4971
5079
|
}
|
|
4972
5080
|
function clearInjectedPaths2(sessionID) {
|
|
4973
5081
|
const filePath = getStoragePath2(sessionID);
|
|
4974
|
-
if (
|
|
5082
|
+
if (existsSync12(filePath)) {
|
|
4975
5083
|
unlinkSync4(filePath);
|
|
4976
5084
|
}
|
|
4977
5085
|
}
|
|
@@ -4979,25 +5087,26 @@ function clearInjectedPaths2(sessionID) {
|
|
|
4979
5087
|
// src/hooks/directory-readme-injector/index.ts
|
|
4980
5088
|
function createDirectoryReadmeInjectorHook(ctx) {
|
|
4981
5089
|
const sessionCaches = new Map;
|
|
5090
|
+
const pendingBatchReads = new Map;
|
|
4982
5091
|
function getSessionCache(sessionID) {
|
|
4983
5092
|
if (!sessionCaches.has(sessionID)) {
|
|
4984
5093
|
sessionCaches.set(sessionID, loadInjectedPaths2(sessionID));
|
|
4985
5094
|
}
|
|
4986
5095
|
return sessionCaches.get(sessionID);
|
|
4987
5096
|
}
|
|
4988
|
-
function resolveFilePath2(
|
|
4989
|
-
if (!
|
|
5097
|
+
function resolveFilePath2(path4) {
|
|
5098
|
+
if (!path4)
|
|
4990
5099
|
return null;
|
|
4991
|
-
if (
|
|
4992
|
-
return
|
|
4993
|
-
return resolve3(ctx.directory,
|
|
5100
|
+
if (path4.startsWith("/"))
|
|
5101
|
+
return path4;
|
|
5102
|
+
return resolve3(ctx.directory, path4);
|
|
4994
5103
|
}
|
|
4995
5104
|
function findReadmeMdUp(startDir) {
|
|
4996
5105
|
const found = [];
|
|
4997
5106
|
let current = startDir;
|
|
4998
5107
|
while (true) {
|
|
4999
5108
|
const readmePath = join17(current, README_FILENAME);
|
|
5000
|
-
if (
|
|
5109
|
+
if (existsSync13(readmePath)) {
|
|
5001
5110
|
found.push(readmePath);
|
|
5002
5111
|
}
|
|
5003
5112
|
if (current === ctx.directory)
|
|
@@ -5011,35 +5120,59 @@ function createDirectoryReadmeInjectorHook(ctx) {
|
|
|
5011
5120
|
}
|
|
5012
5121
|
return found.reverse();
|
|
5013
5122
|
}
|
|
5014
|
-
|
|
5015
|
-
|
|
5123
|
+
function processFilePathForInjection(filePath, sessionID, output) {
|
|
5124
|
+
const resolved = resolveFilePath2(filePath);
|
|
5125
|
+
if (!resolved)
|
|
5016
5126
|
return;
|
|
5017
|
-
const
|
|
5018
|
-
|
|
5019
|
-
return;
|
|
5020
|
-
const dir = dirname3(filePath);
|
|
5021
|
-
const cache = getSessionCache(input.sessionID);
|
|
5127
|
+
const dir = dirname3(resolved);
|
|
5128
|
+
const cache = getSessionCache(sessionID);
|
|
5022
5129
|
const readmePaths = findReadmeMdUp(dir);
|
|
5023
|
-
const toInject = [];
|
|
5024
5130
|
for (const readmePath of readmePaths) {
|
|
5025
5131
|
const readmeDir = dirname3(readmePath);
|
|
5026
5132
|
if (cache.has(readmeDir))
|
|
5027
5133
|
continue;
|
|
5028
5134
|
try {
|
|
5029
5135
|
const content = readFileSync7(readmePath, "utf-8");
|
|
5030
|
-
|
|
5136
|
+
output.output += `
|
|
5137
|
+
|
|
5138
|
+
[Project README: ${readmePath}]
|
|
5139
|
+
${content}`;
|
|
5031
5140
|
cache.add(readmeDir);
|
|
5032
5141
|
} catch {}
|
|
5033
5142
|
}
|
|
5034
|
-
|
|
5143
|
+
saveInjectedPaths2(sessionID, cache);
|
|
5144
|
+
}
|
|
5145
|
+
const toolExecuteBefore = async (input, output) => {
|
|
5146
|
+
if (input.tool.toLowerCase() !== "batch")
|
|
5035
5147
|
return;
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5148
|
+
const args = output.args;
|
|
5149
|
+
if (!args?.tool_calls)
|
|
5150
|
+
return;
|
|
5151
|
+
const readFilePaths = [];
|
|
5152
|
+
for (const call of args.tool_calls) {
|
|
5153
|
+
if (call.tool.toLowerCase() === "read" && call.parameters?.filePath) {
|
|
5154
|
+
readFilePaths.push(call.parameters.filePath);
|
|
5155
|
+
}
|
|
5156
|
+
}
|
|
5157
|
+
if (readFilePaths.length > 0) {
|
|
5158
|
+
pendingBatchReads.set(input.callID, readFilePaths);
|
|
5159
|
+
}
|
|
5160
|
+
};
|
|
5161
|
+
const toolExecuteAfter = async (input, output) => {
|
|
5162
|
+
const toolName = input.tool.toLowerCase();
|
|
5163
|
+
if (toolName === "read") {
|
|
5164
|
+
processFilePathForInjection(output.title, input.sessionID, output);
|
|
5165
|
+
return;
|
|
5166
|
+
}
|
|
5167
|
+
if (toolName === "batch") {
|
|
5168
|
+
const filePaths = pendingBatchReads.get(input.callID);
|
|
5169
|
+
if (filePaths) {
|
|
5170
|
+
for (const filePath of filePaths) {
|
|
5171
|
+
processFilePathForInjection(filePath, input.sessionID, output);
|
|
5172
|
+
}
|
|
5173
|
+
pendingBatchReads.delete(input.callID);
|
|
5174
|
+
}
|
|
5041
5175
|
}
|
|
5042
|
-
saveInjectedPaths2(input.sessionID, cache);
|
|
5043
5176
|
};
|
|
5044
5177
|
const eventHandler = async ({ event }) => {
|
|
5045
5178
|
const props = event.properties;
|
|
@@ -5059,6 +5192,7 @@ ${content}`;
|
|
|
5059
5192
|
}
|
|
5060
5193
|
};
|
|
5061
5194
|
return {
|
|
5195
|
+
"tool.execute.before": toolExecuteBefore,
|
|
5062
5196
|
"tool.execute.after": toolExecuteAfter,
|
|
5063
5197
|
event: eventHandler
|
|
5064
5198
|
};
|
|
@@ -5102,6 +5236,7 @@ var TOKEN_LIMIT_KEYWORDS = [
|
|
|
5102
5236
|
"too many tokens",
|
|
5103
5237
|
"non-empty content"
|
|
5104
5238
|
];
|
|
5239
|
+
var MESSAGE_INDEX_PATTERN = /messages\.(\d+)/;
|
|
5105
5240
|
function extractTokensFromMessage(message) {
|
|
5106
5241
|
for (const pattern of TOKEN_LIMIT_PATTERNS) {
|
|
5107
5242
|
const match = message.match(pattern);
|
|
@@ -5113,6 +5248,13 @@ function extractTokensFromMessage(message) {
|
|
|
5113
5248
|
}
|
|
5114
5249
|
return null;
|
|
5115
5250
|
}
|
|
5251
|
+
function extractMessageIndex2(text) {
|
|
5252
|
+
const match = text.match(MESSAGE_INDEX_PATTERN);
|
|
5253
|
+
if (match) {
|
|
5254
|
+
return parseInt(match[1], 10);
|
|
5255
|
+
}
|
|
5256
|
+
return;
|
|
5257
|
+
}
|
|
5116
5258
|
function isTokenLimitError(text) {
|
|
5117
5259
|
const lower = text.toLowerCase();
|
|
5118
5260
|
return TOKEN_LIMIT_KEYWORDS.some((kw) => lower.includes(kw.toLowerCase()));
|
|
@@ -5123,7 +5265,8 @@ function parseAnthropicTokenLimitError(err) {
|
|
|
5123
5265
|
return {
|
|
5124
5266
|
currentTokens: 0,
|
|
5125
5267
|
maxTokens: 0,
|
|
5126
|
-
errorType: "non-empty content"
|
|
5268
|
+
errorType: "non-empty content",
|
|
5269
|
+
messageIndex: extractMessageIndex2(err)
|
|
5127
5270
|
};
|
|
5128
5271
|
}
|
|
5129
5272
|
if (isTokenLimitError(err)) {
|
|
@@ -5225,7 +5368,8 @@ function parseAnthropicTokenLimitError(err) {
|
|
|
5225
5368
|
return {
|
|
5226
5369
|
currentTokens: 0,
|
|
5227
5370
|
maxTokens: 0,
|
|
5228
|
-
errorType: "non-empty content"
|
|
5371
|
+
errorType: "non-empty content",
|
|
5372
|
+
messageIndex: extractMessageIndex2(combinedText)
|
|
5229
5373
|
};
|
|
5230
5374
|
}
|
|
5231
5375
|
if (isTokenLimitError(combinedText)) {
|
|
@@ -5257,13 +5401,13 @@ var TRUNCATE_CONFIG = {
|
|
|
5257
5401
|
};
|
|
5258
5402
|
|
|
5259
5403
|
// src/hooks/anthropic-auto-compact/storage.ts
|
|
5260
|
-
import { existsSync as
|
|
5404
|
+
import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
5261
5405
|
import { homedir as homedir5 } from "os";
|
|
5262
5406
|
import { join as join18 } from "path";
|
|
5263
5407
|
var OPENCODE_STORAGE5 = join18(xdgData2 ?? "", "opencode", "storage");
|
|
5264
|
-
if (process.platform === "darwin" && !
|
|
5408
|
+
if (process.platform === "darwin" && !existsSync14(OPENCODE_STORAGE5)) {
|
|
5265
5409
|
const localShare = join18(homedir5(), ".local", "share", "opencode", "storage");
|
|
5266
|
-
if (
|
|
5410
|
+
if (existsSync14(localShare)) {
|
|
5267
5411
|
OPENCODE_STORAGE5 = localShare;
|
|
5268
5412
|
}
|
|
5269
5413
|
}
|
|
@@ -5271,15 +5415,15 @@ var MESSAGE_STORAGE3 = join18(OPENCODE_STORAGE5, "message");
|
|
|
5271
5415
|
var PART_STORAGE3 = join18(OPENCODE_STORAGE5, "part");
|
|
5272
5416
|
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
5417
|
function getMessageDir3(sessionID) {
|
|
5274
|
-
if (!
|
|
5418
|
+
if (!existsSync14(MESSAGE_STORAGE3))
|
|
5275
5419
|
return "";
|
|
5276
5420
|
const directPath = join18(MESSAGE_STORAGE3, sessionID);
|
|
5277
|
-
if (
|
|
5421
|
+
if (existsSync14(directPath)) {
|
|
5278
5422
|
return directPath;
|
|
5279
5423
|
}
|
|
5280
5424
|
for (const dir of readdirSync4(MESSAGE_STORAGE3)) {
|
|
5281
5425
|
const sessionPath = join18(MESSAGE_STORAGE3, dir, sessionID);
|
|
5282
|
-
if (
|
|
5426
|
+
if (existsSync14(sessionPath)) {
|
|
5283
5427
|
return sessionPath;
|
|
5284
5428
|
}
|
|
5285
5429
|
}
|
|
@@ -5287,7 +5431,7 @@ function getMessageDir3(sessionID) {
|
|
|
5287
5431
|
}
|
|
5288
5432
|
function getMessageIds(sessionID) {
|
|
5289
5433
|
const messageDir = getMessageDir3(sessionID);
|
|
5290
|
-
if (!messageDir || !
|
|
5434
|
+
if (!messageDir || !existsSync14(messageDir))
|
|
5291
5435
|
return [];
|
|
5292
5436
|
const messageIds = [];
|
|
5293
5437
|
for (const file of readdirSync4(messageDir)) {
|
|
@@ -5303,7 +5447,7 @@ function findToolResultsBySize(sessionID) {
|
|
|
5303
5447
|
const results = [];
|
|
5304
5448
|
for (const messageID of messageIds) {
|
|
5305
5449
|
const partDir = join18(PART_STORAGE3, messageID);
|
|
5306
|
-
if (!
|
|
5450
|
+
if (!existsSync14(partDir))
|
|
5307
5451
|
continue;
|
|
5308
5452
|
for (const file of readdirSync4(partDir)) {
|
|
5309
5453
|
if (!file.endsWith(".json"))
|
|
@@ -5508,32 +5652,59 @@ function clearSessionState(autoCompactState, sessionID) {
|
|
|
5508
5652
|
function getOrCreateEmptyContentAttempt(autoCompactState, sessionID) {
|
|
5509
5653
|
return autoCompactState.emptyContentAttemptBySession.get(sessionID) ?? 0;
|
|
5510
5654
|
}
|
|
5511
|
-
async function fixEmptyMessages(sessionID, autoCompactState, client) {
|
|
5655
|
+
async function fixEmptyMessages(sessionID, autoCompactState, client, messageIndex) {
|
|
5512
5656
|
const attempt = getOrCreateEmptyContentAttempt(autoCompactState, sessionID);
|
|
5513
5657
|
autoCompactState.emptyContentAttemptBySession.set(sessionID, attempt + 1);
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5658
|
+
let fixed = false;
|
|
5659
|
+
const fixedMessageIds = [];
|
|
5660
|
+
if (messageIndex !== undefined) {
|
|
5661
|
+
const targetMessageId = findEmptyMessageByIndex(sessionID, messageIndex);
|
|
5662
|
+
if (targetMessageId) {
|
|
5663
|
+
const replaced = replaceEmptyTextParts(targetMessageId, "[user interrupted]");
|
|
5664
|
+
if (replaced) {
|
|
5665
|
+
fixed = true;
|
|
5666
|
+
fixedMessageIds.push(targetMessageId);
|
|
5667
|
+
} else {
|
|
5668
|
+
const injected = injectTextPart(sessionID, targetMessageId, "[user interrupted]");
|
|
5669
|
+
if (injected) {
|
|
5670
|
+
fixed = true;
|
|
5671
|
+
fixedMessageIds.push(targetMessageId);
|
|
5672
|
+
}
|
|
5522
5673
|
}
|
|
5523
|
-
}
|
|
5524
|
-
return false;
|
|
5674
|
+
}
|
|
5525
5675
|
}
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5676
|
+
if (!fixed) {
|
|
5677
|
+
const emptyMessageIds = findEmptyMessages(sessionID);
|
|
5678
|
+
if (emptyMessageIds.length === 0) {
|
|
5679
|
+
await client.tui.showToast({
|
|
5680
|
+
body: {
|
|
5681
|
+
title: "Empty Content Error",
|
|
5682
|
+
message: "No empty messages found in storage. Cannot auto-recover.",
|
|
5683
|
+
variant: "error",
|
|
5684
|
+
duration: 5000
|
|
5685
|
+
}
|
|
5686
|
+
}).catch(() => {});
|
|
5687
|
+
return false;
|
|
5688
|
+
}
|
|
5689
|
+
for (const messageID of emptyMessageIds) {
|
|
5690
|
+
const replaced = replaceEmptyTextParts(messageID, "[user interrupted]");
|
|
5691
|
+
if (replaced) {
|
|
5692
|
+
fixed = true;
|
|
5693
|
+
fixedMessageIds.push(messageID);
|
|
5694
|
+
} else {
|
|
5695
|
+
const injected = injectTextPart(sessionID, messageID, "[user interrupted]");
|
|
5696
|
+
if (injected) {
|
|
5697
|
+
fixed = true;
|
|
5698
|
+
fixedMessageIds.push(messageID);
|
|
5699
|
+
}
|
|
5700
|
+
}
|
|
5701
|
+
}
|
|
5531
5702
|
}
|
|
5532
5703
|
if (fixed) {
|
|
5533
5704
|
await client.tui.showToast({
|
|
5534
5705
|
body: {
|
|
5535
5706
|
title: "Session Recovery",
|
|
5536
|
-
message: `Fixed ${
|
|
5707
|
+
message: `Fixed ${fixedMessageIds.length} empty message(s). Retrying...`,
|
|
5537
5708
|
variant: "warning",
|
|
5538
5709
|
duration: 3000
|
|
5539
5710
|
}
|
|
@@ -5642,10 +5813,10 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
|
|
|
5642
5813
|
}
|
|
5643
5814
|
}
|
|
5644
5815
|
const retryState = getOrCreateRetryState(autoCompactState, sessionID);
|
|
5645
|
-
if (
|
|
5816
|
+
if (errorData?.errorType?.includes("non-empty content")) {
|
|
5646
5817
|
const attempt = getOrCreateEmptyContentAttempt(autoCompactState, sessionID);
|
|
5647
5818
|
if (attempt < 3) {
|
|
5648
|
-
const fixed = await fixEmptyMessages(sessionID, autoCompactState, client);
|
|
5819
|
+
const fixed = await fixEmptyMessages(sessionID, autoCompactState, client, errorData.messageIndex);
|
|
5649
5820
|
if (fixed) {
|
|
5650
5821
|
autoCompactState.compactionInProgress.delete(sessionID);
|
|
5651
5822
|
setTimeout(() => {
|
|
@@ -5888,6 +6059,200 @@ function createAnthropicAutoCompactHook(ctx, options) {
|
|
|
5888
6059
|
event: eventHandler
|
|
5889
6060
|
};
|
|
5890
6061
|
}
|
|
6062
|
+
// src/hooks/preemptive-compaction/constants.ts
|
|
6063
|
+
var DEFAULT_THRESHOLD = 0.85;
|
|
6064
|
+
var MIN_TOKENS_FOR_COMPACTION = 50000;
|
|
6065
|
+
var COMPACTION_COOLDOWN_MS = 60000;
|
|
6066
|
+
|
|
6067
|
+
// src/hooks/preemptive-compaction/index.ts
|
|
6068
|
+
var CLAUDE_MODEL_PATTERN = /claude-(opus|sonnet|haiku)/i;
|
|
6069
|
+
var CLAUDE_DEFAULT_CONTEXT_LIMIT = 200000;
|
|
6070
|
+
function isSupportedModel(modelID) {
|
|
6071
|
+
return CLAUDE_MODEL_PATTERN.test(modelID);
|
|
6072
|
+
}
|
|
6073
|
+
function createState() {
|
|
6074
|
+
return {
|
|
6075
|
+
lastCompactionTime: new Map,
|
|
6076
|
+
compactionInProgress: new Set
|
|
6077
|
+
};
|
|
6078
|
+
}
|
|
6079
|
+
function createPreemptiveCompactionHook(ctx, options) {
|
|
6080
|
+
const experimental = options?.experimental;
|
|
6081
|
+
const onBeforeSummarize = options?.onBeforeSummarize;
|
|
6082
|
+
const getModelLimit = options?.getModelLimit;
|
|
6083
|
+
const enabled = experimental?.preemptive_compaction !== false;
|
|
6084
|
+
const threshold = experimental?.preemptive_compaction_threshold ?? DEFAULT_THRESHOLD;
|
|
6085
|
+
if (!enabled) {
|
|
6086
|
+
return { event: async () => {} };
|
|
6087
|
+
}
|
|
6088
|
+
const state2 = createState();
|
|
6089
|
+
const checkAndTriggerCompaction = async (sessionID, lastAssistant) => {
|
|
6090
|
+
if (state2.compactionInProgress.has(sessionID))
|
|
6091
|
+
return;
|
|
6092
|
+
const lastCompaction = state2.lastCompactionTime.get(sessionID) ?? 0;
|
|
6093
|
+
if (Date.now() - lastCompaction < COMPACTION_COOLDOWN_MS)
|
|
6094
|
+
return;
|
|
6095
|
+
if (lastAssistant.summary === true)
|
|
6096
|
+
return;
|
|
6097
|
+
const tokens = lastAssistant.tokens;
|
|
6098
|
+
if (!tokens)
|
|
6099
|
+
return;
|
|
6100
|
+
const modelID = lastAssistant.modelID ?? "";
|
|
6101
|
+
const providerID = lastAssistant.providerID ?? "";
|
|
6102
|
+
if (!isSupportedModel(modelID)) {
|
|
6103
|
+
log("[preemptive-compaction] skipping unsupported model", { modelID });
|
|
6104
|
+
return;
|
|
6105
|
+
}
|
|
6106
|
+
const configLimit = getModelLimit?.(providerID, modelID);
|
|
6107
|
+
const contextLimit = configLimit ?? CLAUDE_DEFAULT_CONTEXT_LIMIT;
|
|
6108
|
+
const totalUsed = tokens.input + tokens.cache.read + tokens.output;
|
|
6109
|
+
if (totalUsed < MIN_TOKENS_FOR_COMPACTION)
|
|
6110
|
+
return;
|
|
6111
|
+
const usageRatio = totalUsed / contextLimit;
|
|
6112
|
+
log("[preemptive-compaction] checking", {
|
|
6113
|
+
sessionID,
|
|
6114
|
+
totalUsed,
|
|
6115
|
+
contextLimit,
|
|
6116
|
+
usageRatio: usageRatio.toFixed(2),
|
|
6117
|
+
threshold
|
|
6118
|
+
});
|
|
6119
|
+
if (usageRatio < threshold)
|
|
6120
|
+
return;
|
|
6121
|
+
state2.compactionInProgress.add(sessionID);
|
|
6122
|
+
state2.lastCompactionTime.set(sessionID, Date.now());
|
|
6123
|
+
if (!providerID || !modelID) {
|
|
6124
|
+
state2.compactionInProgress.delete(sessionID);
|
|
6125
|
+
return;
|
|
6126
|
+
}
|
|
6127
|
+
await ctx.client.tui.showToast({
|
|
6128
|
+
body: {
|
|
6129
|
+
title: "Preemptive Compaction",
|
|
6130
|
+
message: `Context at ${(usageRatio * 100).toFixed(0)}% - compacting to prevent overflow...`,
|
|
6131
|
+
variant: "warning",
|
|
6132
|
+
duration: 3000
|
|
6133
|
+
}
|
|
6134
|
+
}).catch(() => {});
|
|
6135
|
+
log("[preemptive-compaction] triggering compaction", { sessionID, usageRatio });
|
|
6136
|
+
try {
|
|
6137
|
+
if (onBeforeSummarize) {
|
|
6138
|
+
await onBeforeSummarize({
|
|
6139
|
+
sessionID,
|
|
6140
|
+
providerID,
|
|
6141
|
+
modelID,
|
|
6142
|
+
usageRatio,
|
|
6143
|
+
directory: ctx.directory
|
|
6144
|
+
});
|
|
6145
|
+
}
|
|
6146
|
+
await ctx.client.session.summarize({
|
|
6147
|
+
path: { id: sessionID },
|
|
6148
|
+
body: { providerID, modelID },
|
|
6149
|
+
query: { directory: ctx.directory }
|
|
6150
|
+
});
|
|
6151
|
+
await ctx.client.tui.showToast({
|
|
6152
|
+
body: {
|
|
6153
|
+
title: "Compaction Complete",
|
|
6154
|
+
message: "Session compacted successfully",
|
|
6155
|
+
variant: "success",
|
|
6156
|
+
duration: 2000
|
|
6157
|
+
}
|
|
6158
|
+
}).catch(() => {});
|
|
6159
|
+
} catch (err) {
|
|
6160
|
+
log("[preemptive-compaction] compaction failed", { sessionID, error: err });
|
|
6161
|
+
} finally {
|
|
6162
|
+
state2.compactionInProgress.delete(sessionID);
|
|
6163
|
+
}
|
|
6164
|
+
};
|
|
6165
|
+
const eventHandler = async ({ event }) => {
|
|
6166
|
+
const props = event.properties;
|
|
6167
|
+
if (event.type === "session.deleted") {
|
|
6168
|
+
const sessionInfo = props?.info;
|
|
6169
|
+
if (sessionInfo?.id) {
|
|
6170
|
+
state2.lastCompactionTime.delete(sessionInfo.id);
|
|
6171
|
+
state2.compactionInProgress.delete(sessionInfo.id);
|
|
6172
|
+
}
|
|
6173
|
+
return;
|
|
6174
|
+
}
|
|
6175
|
+
if (event.type === "message.updated") {
|
|
6176
|
+
const info = props?.info;
|
|
6177
|
+
if (!info)
|
|
6178
|
+
return;
|
|
6179
|
+
if (info.role !== "assistant" || !info.finish)
|
|
6180
|
+
return;
|
|
6181
|
+
const sessionID = info.sessionID;
|
|
6182
|
+
if (!sessionID)
|
|
6183
|
+
return;
|
|
6184
|
+
await checkAndTriggerCompaction(sessionID, info);
|
|
6185
|
+
return;
|
|
6186
|
+
}
|
|
6187
|
+
if (event.type === "session.idle") {
|
|
6188
|
+
const sessionID = props?.sessionID;
|
|
6189
|
+
if (!sessionID)
|
|
6190
|
+
return;
|
|
6191
|
+
try {
|
|
6192
|
+
const resp = await ctx.client.session.messages({
|
|
6193
|
+
path: { id: sessionID },
|
|
6194
|
+
query: { directory: ctx.directory }
|
|
6195
|
+
});
|
|
6196
|
+
const messages = resp.data ?? resp;
|
|
6197
|
+
const assistants = messages.filter((m) => m.info.role === "assistant").map((m) => m.info);
|
|
6198
|
+
if (assistants.length === 0)
|
|
6199
|
+
return;
|
|
6200
|
+
const lastAssistant = assistants[assistants.length - 1];
|
|
6201
|
+
await checkAndTriggerCompaction(sessionID, lastAssistant);
|
|
6202
|
+
} catch {}
|
|
6203
|
+
}
|
|
6204
|
+
};
|
|
6205
|
+
return {
|
|
6206
|
+
event: eventHandler
|
|
6207
|
+
};
|
|
6208
|
+
}
|
|
6209
|
+
// src/hooks/compaction-context-injector/index.ts
|
|
6210
|
+
var SUMMARIZE_CONTEXT_PROMPT = `[COMPACTION CONTEXT INJECTION]
|
|
6211
|
+
|
|
6212
|
+
When summarizing this session, you MUST include the following sections in your summary:
|
|
6213
|
+
|
|
6214
|
+
## 1. User Requests (As-Is)
|
|
6215
|
+
- List all original user requests exactly as they were stated
|
|
6216
|
+
- Preserve the user's exact wording and intent
|
|
6217
|
+
|
|
6218
|
+
## 2. Final Goal
|
|
6219
|
+
- What the user ultimately wanted to achieve
|
|
6220
|
+
- The end result or deliverable expected
|
|
6221
|
+
|
|
6222
|
+
## 3. Work Completed
|
|
6223
|
+
- What has been done so far
|
|
6224
|
+
- Files created/modified
|
|
6225
|
+
- Features implemented
|
|
6226
|
+
- Problems solved
|
|
6227
|
+
|
|
6228
|
+
## 4. Remaining Tasks
|
|
6229
|
+
- What still needs to be done
|
|
6230
|
+
- Pending items from the original request
|
|
6231
|
+
- Follow-up tasks identified during the work
|
|
6232
|
+
|
|
6233
|
+
## 5. MUST NOT Do (Critical Constraints)
|
|
6234
|
+
- Things that were explicitly forbidden
|
|
6235
|
+
- Approaches that failed and should not be retried
|
|
6236
|
+
- User's explicit restrictions or preferences
|
|
6237
|
+
- Anti-patterns identified during the session
|
|
6238
|
+
|
|
6239
|
+
This context is critical for maintaining continuity after compaction.
|
|
6240
|
+
`;
|
|
6241
|
+
function createCompactionContextInjector() {
|
|
6242
|
+
return async (ctx) => {
|
|
6243
|
+
log("[compaction-context-injector] injecting context", { sessionID: ctx.sessionID });
|
|
6244
|
+
const success = injectHookMessage(ctx.sessionID, SUMMARIZE_CONTEXT_PROMPT, {
|
|
6245
|
+
agent: "general",
|
|
6246
|
+
model: { providerID: ctx.providerID, modelID: ctx.modelID },
|
|
6247
|
+
path: { cwd: ctx.directory }
|
|
6248
|
+
});
|
|
6249
|
+
if (success) {
|
|
6250
|
+
log("[compaction-context-injector] context injected", { sessionID: ctx.sessionID });
|
|
6251
|
+
} else {
|
|
6252
|
+
log("[compaction-context-injector] injection failed", { sessionID: ctx.sessionID });
|
|
6253
|
+
}
|
|
6254
|
+
};
|
|
6255
|
+
}
|
|
5891
6256
|
// src/hooks/think-mode/detector.ts
|
|
5892
6257
|
var ENGLISH_PATTERNS = [/\bultrathink\b/i, /\bthink\b/i];
|
|
5893
6258
|
var MULTILINGUAL_KEYWORDS = [
|
|
@@ -6157,7 +6522,7 @@ function createThinkModeHook() {
|
|
|
6157
6522
|
// src/hooks/claude-code-hooks/config.ts
|
|
6158
6523
|
import { homedir as homedir6 } from "os";
|
|
6159
6524
|
import { join as join19 } from "path";
|
|
6160
|
-
import { existsSync as
|
|
6525
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6161
6526
|
function normalizeHookMatcher(raw) {
|
|
6162
6527
|
return {
|
|
6163
6528
|
matcher: raw.matcher ?? raw.pattern ?? "*",
|
|
@@ -6186,7 +6551,7 @@ function getClaudeSettingsPaths(customPath) {
|
|
|
6186
6551
|
join19(process.cwd(), ".claude", "settings.json"),
|
|
6187
6552
|
join19(process.cwd(), ".claude", "settings.local.json")
|
|
6188
6553
|
];
|
|
6189
|
-
if (customPath &&
|
|
6554
|
+
if (customPath && existsSync15(customPath)) {
|
|
6190
6555
|
paths.unshift(customPath);
|
|
6191
6556
|
}
|
|
6192
6557
|
return paths;
|
|
@@ -6210,7 +6575,7 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
6210
6575
|
const paths = getClaudeSettingsPaths(customSettingsPath);
|
|
6211
6576
|
let mergedConfig = {};
|
|
6212
6577
|
for (const settingsPath of paths) {
|
|
6213
|
-
if (
|
|
6578
|
+
if (existsSync15(settingsPath)) {
|
|
6214
6579
|
try {
|
|
6215
6580
|
const content = await Bun.file(settingsPath).text();
|
|
6216
6581
|
const settings = JSON.parse(content);
|
|
@@ -6227,7 +6592,7 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
6227
6592
|
}
|
|
6228
6593
|
|
|
6229
6594
|
// src/hooks/claude-code-hooks/config-loader.ts
|
|
6230
|
-
import { existsSync as
|
|
6595
|
+
import { existsSync as existsSync16 } from "fs";
|
|
6231
6596
|
import { homedir as homedir7 } from "os";
|
|
6232
6597
|
import { join as join20 } from "path";
|
|
6233
6598
|
var USER_CONFIG_PATH = join20(homedir7(), ".config", "opencode", "opencode-cc-plugin.json");
|
|
@@ -6235,7 +6600,7 @@ function getProjectConfigPath() {
|
|
|
6235
6600
|
return join20(process.cwd(), ".opencode", "opencode-cc-plugin.json");
|
|
6236
6601
|
}
|
|
6237
6602
|
async function loadConfigFromPath(path4) {
|
|
6238
|
-
if (!
|
|
6603
|
+
if (!existsSync16(path4)) {
|
|
6239
6604
|
return null;
|
|
6240
6605
|
}
|
|
6241
6606
|
try {
|
|
@@ -6415,7 +6780,7 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
|
|
|
6415
6780
|
|
|
6416
6781
|
// src/hooks/claude-code-hooks/transcript.ts
|
|
6417
6782
|
import { join as join21 } from "path";
|
|
6418
|
-
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as
|
|
6783
|
+
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync17, writeFileSync as writeFileSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
6419
6784
|
import { homedir as homedir8, tmpdir as tmpdir5 } from "os";
|
|
6420
6785
|
import { randomUUID } from "crypto";
|
|
6421
6786
|
var TRANSCRIPT_DIR = join21(homedir8(), ".claude", "transcripts");
|
|
@@ -6423,7 +6788,7 @@ function getTranscriptPath(sessionId) {
|
|
|
6423
6788
|
return join21(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
|
|
6424
6789
|
}
|
|
6425
6790
|
function ensureTranscriptDir() {
|
|
6426
|
-
if (!
|
|
6791
|
+
if (!existsSync17(TRANSCRIPT_DIR)) {
|
|
6427
6792
|
mkdirSync6(TRANSCRIPT_DIR, { recursive: true });
|
|
6428
6793
|
}
|
|
6429
6794
|
}
|
|
@@ -7084,7 +7449,7 @@ import { relative as relative3, resolve as resolve4 } from "path";
|
|
|
7084
7449
|
|
|
7085
7450
|
// src/hooks/rules-injector/finder.ts
|
|
7086
7451
|
import {
|
|
7087
|
-
existsSync as
|
|
7452
|
+
existsSync as existsSync18,
|
|
7088
7453
|
readdirSync as readdirSync5,
|
|
7089
7454
|
realpathSync,
|
|
7090
7455
|
statSync as statSync2
|
|
@@ -7122,7 +7487,7 @@ function findProjectRoot(startPath) {
|
|
|
7122
7487
|
while (true) {
|
|
7123
7488
|
for (const marker of PROJECT_MARKERS) {
|
|
7124
7489
|
const markerPath = join24(current, marker);
|
|
7125
|
-
if (
|
|
7490
|
+
if (existsSync18(markerPath)) {
|
|
7126
7491
|
return current;
|
|
7127
7492
|
}
|
|
7128
7493
|
}
|
|
@@ -7134,7 +7499,7 @@ function findProjectRoot(startPath) {
|
|
|
7134
7499
|
}
|
|
7135
7500
|
}
|
|
7136
7501
|
function findRuleFilesRecursive(dir, results) {
|
|
7137
|
-
if (!
|
|
7502
|
+
if (!existsSync18(dir))
|
|
7138
7503
|
return;
|
|
7139
7504
|
try {
|
|
7140
7505
|
const entries = readdirSync5(dir, { withFileTypes: true });
|
|
@@ -7378,7 +7743,7 @@ function mergeGlobs(existing, newValue) {
|
|
|
7378
7743
|
|
|
7379
7744
|
// src/hooks/rules-injector/storage.ts
|
|
7380
7745
|
import {
|
|
7381
|
-
existsSync as
|
|
7746
|
+
existsSync as existsSync19,
|
|
7382
7747
|
mkdirSync as mkdirSync7,
|
|
7383
7748
|
readFileSync as readFileSync9,
|
|
7384
7749
|
writeFileSync as writeFileSync7,
|
|
@@ -7390,7 +7755,7 @@ function getStoragePath3(sessionID) {
|
|
|
7390
7755
|
}
|
|
7391
7756
|
function loadInjectedRules(sessionID) {
|
|
7392
7757
|
const filePath = getStoragePath3(sessionID);
|
|
7393
|
-
if (!
|
|
7758
|
+
if (!existsSync19(filePath))
|
|
7394
7759
|
return { contentHashes: new Set, realPaths: new Set };
|
|
7395
7760
|
try {
|
|
7396
7761
|
const content = readFileSync9(filePath, "utf-8");
|
|
@@ -7404,7 +7769,7 @@ function loadInjectedRules(sessionID) {
|
|
|
7404
7769
|
}
|
|
7405
7770
|
}
|
|
7406
7771
|
function saveInjectedRules(sessionID, data) {
|
|
7407
|
-
if (!
|
|
7772
|
+
if (!existsSync19(RULES_INJECTOR_STORAGE)) {
|
|
7408
7773
|
mkdirSync7(RULES_INJECTOR_STORAGE, { recursive: true });
|
|
7409
7774
|
}
|
|
7410
7775
|
const storageData = {
|
|
@@ -7417,7 +7782,7 @@ function saveInjectedRules(sessionID, data) {
|
|
|
7417
7782
|
}
|
|
7418
7783
|
function clearInjectedRules(sessionID) {
|
|
7419
7784
|
const filePath = getStoragePath3(sessionID);
|
|
7420
|
-
if (
|
|
7785
|
+
if (existsSync19(filePath)) {
|
|
7421
7786
|
unlinkSync6(filePath);
|
|
7422
7787
|
}
|
|
7423
7788
|
}
|
|
@@ -7426,29 +7791,28 @@ function clearInjectedRules(sessionID) {
|
|
|
7426
7791
|
var TRACKED_TOOLS = ["read", "write", "edit", "multiedit"];
|
|
7427
7792
|
function createRulesInjectorHook(ctx) {
|
|
7428
7793
|
const sessionCaches = new Map;
|
|
7794
|
+
const pendingBatchFiles = new Map;
|
|
7429
7795
|
function getSessionCache(sessionID) {
|
|
7430
7796
|
if (!sessionCaches.has(sessionID)) {
|
|
7431
7797
|
sessionCaches.set(sessionID, loadInjectedRules(sessionID));
|
|
7432
7798
|
}
|
|
7433
7799
|
return sessionCaches.get(sessionID);
|
|
7434
7800
|
}
|
|
7435
|
-
function resolveFilePath2(
|
|
7436
|
-
if (!
|
|
7801
|
+
function resolveFilePath2(path4) {
|
|
7802
|
+
if (!path4)
|
|
7437
7803
|
return null;
|
|
7438
|
-
if (
|
|
7439
|
-
return
|
|
7440
|
-
return resolve4(ctx.directory,
|
|
7804
|
+
if (path4.startsWith("/"))
|
|
7805
|
+
return path4;
|
|
7806
|
+
return resolve4(ctx.directory, path4);
|
|
7441
7807
|
}
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
const filePath = resolveFilePath2(output.title);
|
|
7446
|
-
if (!filePath)
|
|
7808
|
+
function processFilePathForInjection(filePath, sessionID, output) {
|
|
7809
|
+
const resolved = resolveFilePath2(filePath);
|
|
7810
|
+
if (!resolved)
|
|
7447
7811
|
return;
|
|
7448
|
-
const projectRoot = findProjectRoot(
|
|
7449
|
-
const cache2 = getSessionCache(
|
|
7812
|
+
const projectRoot = findProjectRoot(resolved);
|
|
7813
|
+
const cache2 = getSessionCache(sessionID);
|
|
7450
7814
|
const home = homedir10();
|
|
7451
|
-
const ruleFileCandidates = findRuleFiles(projectRoot, home,
|
|
7815
|
+
const ruleFileCandidates = findRuleFiles(projectRoot, home, resolved);
|
|
7452
7816
|
const toInject = [];
|
|
7453
7817
|
for (const candidate of ruleFileCandidates) {
|
|
7454
7818
|
if (isDuplicateByRealPath(candidate.realPath, cache2.realPaths))
|
|
@@ -7456,7 +7820,7 @@ function createRulesInjectorHook(ctx) {
|
|
|
7456
7820
|
try {
|
|
7457
7821
|
const rawContent = readFileSync10(candidate.path, "utf-8");
|
|
7458
7822
|
const { metadata, body } = parseRuleFrontmatter(rawContent);
|
|
7459
|
-
const matchResult = shouldApplyRule(metadata,
|
|
7823
|
+
const matchResult = shouldApplyRule(metadata, resolved, projectRoot);
|
|
7460
7824
|
if (!matchResult.applies)
|
|
7461
7825
|
continue;
|
|
7462
7826
|
const contentHash = createContentHash(body);
|
|
@@ -7483,7 +7847,46 @@ function createRulesInjectorHook(ctx) {
|
|
|
7483
7847
|
[Match: ${rule.matchReason}]
|
|
7484
7848
|
${rule.content}`;
|
|
7485
7849
|
}
|
|
7486
|
-
saveInjectedRules(
|
|
7850
|
+
saveInjectedRules(sessionID, cache2);
|
|
7851
|
+
}
|
|
7852
|
+
function extractFilePathFromToolCall(call) {
|
|
7853
|
+
const params = call.parameters;
|
|
7854
|
+
return params?.filePath ?? params?.file_path ?? params?.path;
|
|
7855
|
+
}
|
|
7856
|
+
const toolExecuteBefore = async (input, output) => {
|
|
7857
|
+
if (input.tool.toLowerCase() !== "batch")
|
|
7858
|
+
return;
|
|
7859
|
+
const args = output.args;
|
|
7860
|
+
if (!args?.tool_calls)
|
|
7861
|
+
return;
|
|
7862
|
+
const filePaths = [];
|
|
7863
|
+
for (const call of args.tool_calls) {
|
|
7864
|
+
if (TRACKED_TOOLS.includes(call.tool.toLowerCase())) {
|
|
7865
|
+
const filePath = extractFilePathFromToolCall(call);
|
|
7866
|
+
if (filePath) {
|
|
7867
|
+
filePaths.push(filePath);
|
|
7868
|
+
}
|
|
7869
|
+
}
|
|
7870
|
+
}
|
|
7871
|
+
if (filePaths.length > 0) {
|
|
7872
|
+
pendingBatchFiles.set(input.callID, filePaths);
|
|
7873
|
+
}
|
|
7874
|
+
};
|
|
7875
|
+
const toolExecuteAfter = async (input, output) => {
|
|
7876
|
+
const toolName = input.tool.toLowerCase();
|
|
7877
|
+
if (TRACKED_TOOLS.includes(toolName)) {
|
|
7878
|
+
processFilePathForInjection(output.title, input.sessionID, output);
|
|
7879
|
+
return;
|
|
7880
|
+
}
|
|
7881
|
+
if (toolName === "batch") {
|
|
7882
|
+
const filePaths = pendingBatchFiles.get(input.callID);
|
|
7883
|
+
if (filePaths) {
|
|
7884
|
+
for (const filePath of filePaths) {
|
|
7885
|
+
processFilePathForInjection(filePath, input.sessionID, output);
|
|
7886
|
+
}
|
|
7887
|
+
pendingBatchFiles.delete(input.callID);
|
|
7888
|
+
}
|
|
7889
|
+
}
|
|
7487
7890
|
};
|
|
7488
7891
|
const eventHandler = async ({ event }) => {
|
|
7489
7892
|
const props = event.properties;
|
|
@@ -7503,6 +7906,7 @@ ${rule.content}`;
|
|
|
7503
7906
|
}
|
|
7504
7907
|
};
|
|
7505
7908
|
return {
|
|
7909
|
+
"tool.execute.before": toolExecuteBefore,
|
|
7506
7910
|
"tool.execute.after": toolExecuteAfter,
|
|
7507
7911
|
event: eventHandler
|
|
7508
7912
|
};
|
|
@@ -7517,7 +7921,7 @@ function createBackgroundNotificationHook(manager) {
|
|
|
7517
7921
|
};
|
|
7518
7922
|
}
|
|
7519
7923
|
// src/hooks/auto-update-checker/checker.ts
|
|
7520
|
-
import * as
|
|
7924
|
+
import * as fs5 from "fs";
|
|
7521
7925
|
import * as path5 from "path";
|
|
7522
7926
|
import { fileURLToPath } from "url";
|
|
7523
7927
|
|
|
@@ -7561,9 +7965,9 @@ function getConfigPaths(directory) {
|
|
|
7561
7965
|
function getLocalDevPath(directory) {
|
|
7562
7966
|
for (const configPath of getConfigPaths(directory)) {
|
|
7563
7967
|
try {
|
|
7564
|
-
if (!
|
|
7968
|
+
if (!fs5.existsSync(configPath))
|
|
7565
7969
|
continue;
|
|
7566
|
-
const content =
|
|
7970
|
+
const content = fs5.readFileSync(configPath, "utf-8");
|
|
7567
7971
|
const config = JSON.parse(stripJsonComments(content));
|
|
7568
7972
|
const plugins = config.plugin ?? [];
|
|
7569
7973
|
for (const entry of plugins) {
|
|
@@ -7583,13 +7987,13 @@ function getLocalDevPath(directory) {
|
|
|
7583
7987
|
}
|
|
7584
7988
|
function findPackageJsonUp(startPath) {
|
|
7585
7989
|
try {
|
|
7586
|
-
const stat =
|
|
7990
|
+
const stat = fs5.statSync(startPath);
|
|
7587
7991
|
let dir = stat.isDirectory() ? startPath : path5.dirname(startPath);
|
|
7588
7992
|
for (let i = 0;i < 10; i++) {
|
|
7589
7993
|
const pkgPath = path5.join(dir, "package.json");
|
|
7590
|
-
if (
|
|
7994
|
+
if (fs5.existsSync(pkgPath)) {
|
|
7591
7995
|
try {
|
|
7592
|
-
const content =
|
|
7996
|
+
const content = fs5.readFileSync(pkgPath, "utf-8");
|
|
7593
7997
|
const pkg = JSON.parse(content);
|
|
7594
7998
|
if (pkg.name === PACKAGE_NAME)
|
|
7595
7999
|
return pkgPath;
|
|
@@ -7611,7 +8015,7 @@ function getLocalDevVersion(directory) {
|
|
|
7611
8015
|
const pkgPath = findPackageJsonUp(localPath);
|
|
7612
8016
|
if (!pkgPath)
|
|
7613
8017
|
return null;
|
|
7614
|
-
const content =
|
|
8018
|
+
const content = fs5.readFileSync(pkgPath, "utf-8");
|
|
7615
8019
|
const pkg = JSON.parse(content);
|
|
7616
8020
|
return pkg.version ?? null;
|
|
7617
8021
|
} catch {
|
|
@@ -7621,9 +8025,9 @@ function getLocalDevVersion(directory) {
|
|
|
7621
8025
|
function findPluginEntry(directory) {
|
|
7622
8026
|
for (const configPath of getConfigPaths(directory)) {
|
|
7623
8027
|
try {
|
|
7624
|
-
if (!
|
|
8028
|
+
if (!fs5.existsSync(configPath))
|
|
7625
8029
|
continue;
|
|
7626
|
-
const content =
|
|
8030
|
+
const content = fs5.readFileSync(configPath, "utf-8");
|
|
7627
8031
|
const config = JSON.parse(stripJsonComments(content));
|
|
7628
8032
|
const plugins = config.plugin ?? [];
|
|
7629
8033
|
for (const entry of plugins) {
|
|
@@ -7644,8 +8048,8 @@ function findPluginEntry(directory) {
|
|
|
7644
8048
|
}
|
|
7645
8049
|
function getCachedVersion() {
|
|
7646
8050
|
try {
|
|
7647
|
-
if (
|
|
7648
|
-
const content =
|
|
8051
|
+
if (fs5.existsSync(INSTALLED_PACKAGE_JSON)) {
|
|
8052
|
+
const content = fs5.readFileSync(INSTALLED_PACKAGE_JSON, "utf-8");
|
|
7649
8053
|
const pkg = JSON.parse(content);
|
|
7650
8054
|
if (pkg.version)
|
|
7651
8055
|
return pkg.version;
|
|
@@ -7655,7 +8059,7 @@ function getCachedVersion() {
|
|
|
7655
8059
|
const currentDir = path5.dirname(fileURLToPath(import.meta.url));
|
|
7656
8060
|
const pkgPath = findPackageJsonUp(currentDir);
|
|
7657
8061
|
if (pkgPath) {
|
|
7658
|
-
const content =
|
|
8062
|
+
const content = fs5.readFileSync(pkgPath, "utf-8");
|
|
7659
8063
|
const pkg = JSON.parse(content);
|
|
7660
8064
|
if (pkg.version)
|
|
7661
8065
|
return pkg.version;
|
|
@@ -7667,7 +8071,7 @@ function getCachedVersion() {
|
|
|
7667
8071
|
}
|
|
7668
8072
|
function updatePinnedVersion(configPath, oldEntry, newVersion) {
|
|
7669
8073
|
try {
|
|
7670
|
-
const content =
|
|
8074
|
+
const content = fs5.readFileSync(configPath, "utf-8");
|
|
7671
8075
|
const newEntry = `${PACKAGE_NAME}@${newVersion}`;
|
|
7672
8076
|
const pluginMatch = content.match(/"plugin"\s*:\s*\[/);
|
|
7673
8077
|
if (!pluginMatch || pluginMatch.index === undefined) {
|
|
@@ -7699,7 +8103,7 @@ function updatePinnedVersion(configPath, oldEntry, newVersion) {
|
|
|
7699
8103
|
log(`[auto-update-checker] No changes made to ${configPath}`);
|
|
7700
8104
|
return false;
|
|
7701
8105
|
}
|
|
7702
|
-
|
|
8106
|
+
fs5.writeFileSync(configPath, updatedContent, "utf-8");
|
|
7703
8107
|
log(`[auto-update-checker] Updated ${configPath}: ${oldEntry} \u2192 ${newEntry}`);
|
|
7704
8108
|
return true;
|
|
7705
8109
|
} catch (err) {
|
|
@@ -7727,17 +8131,17 @@ async function getLatestVersion() {
|
|
|
7727
8131
|
}
|
|
7728
8132
|
|
|
7729
8133
|
// src/hooks/auto-update-checker/cache.ts
|
|
7730
|
-
import * as
|
|
8134
|
+
import * as fs6 from "fs";
|
|
7731
8135
|
import * as path6 from "path";
|
|
7732
8136
|
function stripTrailingCommas(json) {
|
|
7733
8137
|
return json.replace(/,(\s*[}\]])/g, "$1");
|
|
7734
8138
|
}
|
|
7735
8139
|
function removeFromBunLock(packageName) {
|
|
7736
8140
|
const lockPath = path6.join(CACHE_DIR, "bun.lock");
|
|
7737
|
-
if (!
|
|
8141
|
+
if (!fs6.existsSync(lockPath))
|
|
7738
8142
|
return false;
|
|
7739
8143
|
try {
|
|
7740
|
-
const content =
|
|
8144
|
+
const content = fs6.readFileSync(lockPath, "utf-8");
|
|
7741
8145
|
const lock = JSON.parse(stripTrailingCommas(content));
|
|
7742
8146
|
let modified = false;
|
|
7743
8147
|
if (lock.workspaces?.[""]?.dependencies?.[packageName]) {
|
|
@@ -7749,7 +8153,7 @@ function removeFromBunLock(packageName) {
|
|
|
7749
8153
|
modified = true;
|
|
7750
8154
|
}
|
|
7751
8155
|
if (modified) {
|
|
7752
|
-
|
|
8156
|
+
fs6.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
|
|
7753
8157
|
log(`[auto-update-checker] Removed from bun.lock: ${packageName}`);
|
|
7754
8158
|
}
|
|
7755
8159
|
return modified;
|
|
@@ -7764,17 +8168,17 @@ function invalidatePackage(packageName = PACKAGE_NAME) {
|
|
|
7764
8168
|
let packageRemoved = false;
|
|
7765
8169
|
let dependencyRemoved = false;
|
|
7766
8170
|
let lockRemoved = false;
|
|
7767
|
-
if (
|
|
7768
|
-
|
|
8171
|
+
if (fs6.existsSync(pkgDir)) {
|
|
8172
|
+
fs6.rmSync(pkgDir, { recursive: true, force: true });
|
|
7769
8173
|
log(`[auto-update-checker] Package removed: ${pkgDir}`);
|
|
7770
8174
|
packageRemoved = true;
|
|
7771
8175
|
}
|
|
7772
|
-
if (
|
|
7773
|
-
const content =
|
|
8176
|
+
if (fs6.existsSync(pkgJsonPath)) {
|
|
8177
|
+
const content = fs6.readFileSync(pkgJsonPath, "utf-8");
|
|
7774
8178
|
const pkgJson = JSON.parse(content);
|
|
7775
8179
|
if (pkgJson.dependencies?.[packageName]) {
|
|
7776
8180
|
delete pkgJson.dependencies[packageName];
|
|
7777
|
-
|
|
8181
|
+
fs6.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
7778
8182
|
log(`[auto-update-checker] Dependency removed from package.json: ${packageName}`);
|
|
7779
8183
|
dependencyRemoved = true;
|
|
7780
8184
|
}
|
|
@@ -7944,7 +8348,7 @@ async function showLocalDevToast(ctx, version, isSisyphusEnabled) {
|
|
|
7944
8348
|
}
|
|
7945
8349
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
7946
8350
|
import {
|
|
7947
|
-
existsSync as
|
|
8351
|
+
existsSync as existsSync22,
|
|
7948
8352
|
mkdirSync as mkdirSync8,
|
|
7949
8353
|
readFileSync as readFileSync13,
|
|
7950
8354
|
writeFileSync as writeFileSync10,
|
|
@@ -8004,7 +8408,7 @@ function getStoragePath4(sessionID) {
|
|
|
8004
8408
|
}
|
|
8005
8409
|
function loadAgentUsageState(sessionID) {
|
|
8006
8410
|
const filePath = getStoragePath4(sessionID);
|
|
8007
|
-
if (!
|
|
8411
|
+
if (!existsSync22(filePath))
|
|
8008
8412
|
return null;
|
|
8009
8413
|
try {
|
|
8010
8414
|
const content = readFileSync13(filePath, "utf-8");
|
|
@@ -8014,7 +8418,7 @@ function loadAgentUsageState(sessionID) {
|
|
|
8014
8418
|
}
|
|
8015
8419
|
}
|
|
8016
8420
|
function saveAgentUsageState(state2) {
|
|
8017
|
-
if (!
|
|
8421
|
+
if (!existsSync22(AGENT_USAGE_REMINDER_STORAGE)) {
|
|
8018
8422
|
mkdirSync8(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
|
|
8019
8423
|
}
|
|
8020
8424
|
const filePath = getStoragePath4(state2.sessionID);
|
|
@@ -8022,7 +8426,7 @@ function saveAgentUsageState(state2) {
|
|
|
8022
8426
|
}
|
|
8023
8427
|
function clearAgentUsageState(sessionID) {
|
|
8024
8428
|
const filePath = getStoragePath4(sessionID);
|
|
8025
|
-
if (
|
|
8429
|
+
if (existsSync22(filePath)) {
|
|
8026
8430
|
unlinkSync7(filePath);
|
|
8027
8431
|
}
|
|
8028
8432
|
}
|
|
@@ -8249,7 +8653,7 @@ function createNonInteractiveEnvHook(_ctx) {
|
|
|
8249
8653
|
}
|
|
8250
8654
|
// src/hooks/interactive-bash-session/storage.ts
|
|
8251
8655
|
import {
|
|
8252
|
-
existsSync as
|
|
8656
|
+
existsSync as existsSync23,
|
|
8253
8657
|
mkdirSync as mkdirSync9,
|
|
8254
8658
|
readFileSync as readFileSync14,
|
|
8255
8659
|
writeFileSync as writeFileSync11,
|
|
@@ -8276,7 +8680,7 @@ function getStoragePath5(sessionID) {
|
|
|
8276
8680
|
}
|
|
8277
8681
|
function loadInteractiveBashSessionState(sessionID) {
|
|
8278
8682
|
const filePath = getStoragePath5(sessionID);
|
|
8279
|
-
if (!
|
|
8683
|
+
if (!existsSync23(filePath))
|
|
8280
8684
|
return null;
|
|
8281
8685
|
try {
|
|
8282
8686
|
const content = readFileSync14(filePath, "utf-8");
|
|
@@ -8291,7 +8695,7 @@ function loadInteractiveBashSessionState(sessionID) {
|
|
|
8291
8695
|
}
|
|
8292
8696
|
}
|
|
8293
8697
|
function saveInteractiveBashSessionState(state2) {
|
|
8294
|
-
if (!
|
|
8698
|
+
if (!existsSync23(INTERACTIVE_BASH_SESSION_STORAGE)) {
|
|
8295
8699
|
mkdirSync9(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
|
|
8296
8700
|
}
|
|
8297
8701
|
const filePath = getStoragePath5(state2.sessionID);
|
|
@@ -8304,7 +8708,7 @@ function saveInteractiveBashSessionState(state2) {
|
|
|
8304
8708
|
}
|
|
8305
8709
|
function clearInteractiveBashSessionState(sessionID) {
|
|
8306
8710
|
const filePath = getStoragePath5(sessionID);
|
|
8307
|
-
if (
|
|
8711
|
+
if (existsSync23(filePath)) {
|
|
8308
8712
|
unlinkSync8(filePath);
|
|
8309
8713
|
}
|
|
8310
8714
|
}
|
|
@@ -10248,11 +10652,11 @@ async function createGoogleAntigravityAuthPlugin({
|
|
|
10248
10652
|
};
|
|
10249
10653
|
}
|
|
10250
10654
|
// src/features/claude-code-command-loader/loader.ts
|
|
10251
|
-
import { existsSync as
|
|
10655
|
+
import { existsSync as existsSync24, readdirSync as readdirSync6, readFileSync as readFileSync15 } from "fs";
|
|
10252
10656
|
import { homedir as homedir12 } from "os";
|
|
10253
10657
|
import { join as join33, basename } from "path";
|
|
10254
10658
|
function loadCommandsFromDir(commandsDir, scope) {
|
|
10255
|
-
if (!
|
|
10659
|
+
if (!existsSync24(commandsDir)) {
|
|
10256
10660
|
return [];
|
|
10257
10661
|
}
|
|
10258
10662
|
const entries = readdirSync6(commandsDir, { withFileTypes: true });
|
|
@@ -10323,11 +10727,11 @@ function loadOpencodeProjectCommands() {
|
|
|
10323
10727
|
return commandsToRecord(commands);
|
|
10324
10728
|
}
|
|
10325
10729
|
// src/features/claude-code-skill-loader/loader.ts
|
|
10326
|
-
import { existsSync as
|
|
10730
|
+
import { existsSync as existsSync25, readdirSync as readdirSync7, readFileSync as readFileSync16 } from "fs";
|
|
10327
10731
|
import { homedir as homedir13 } from "os";
|
|
10328
10732
|
import { join as join34 } from "path";
|
|
10329
10733
|
function loadSkillsFromDir(skillsDir, scope) {
|
|
10330
|
-
if (!
|
|
10734
|
+
if (!existsSync25(skillsDir)) {
|
|
10331
10735
|
return [];
|
|
10332
10736
|
}
|
|
10333
10737
|
const entries = readdirSync7(skillsDir, { withFileTypes: true });
|
|
@@ -10340,7 +10744,7 @@ function loadSkillsFromDir(skillsDir, scope) {
|
|
|
10340
10744
|
continue;
|
|
10341
10745
|
const resolvedPath = resolveSymlink(skillPath);
|
|
10342
10746
|
const skillMdPath = join34(resolvedPath, "SKILL.md");
|
|
10343
|
-
if (!
|
|
10747
|
+
if (!existsSync25(skillMdPath))
|
|
10344
10748
|
continue;
|
|
10345
10749
|
try {
|
|
10346
10750
|
const content = readFileSync16(skillMdPath, "utf-8");
|
|
@@ -10393,7 +10797,7 @@ function loadProjectSkillsAsCommands() {
|
|
|
10393
10797
|
}, {});
|
|
10394
10798
|
}
|
|
10395
10799
|
// src/features/claude-code-agent-loader/loader.ts
|
|
10396
|
-
import { existsSync as
|
|
10800
|
+
import { existsSync as existsSync26, readdirSync as readdirSync8, readFileSync as readFileSync17 } from "fs";
|
|
10397
10801
|
import { homedir as homedir14 } from "os";
|
|
10398
10802
|
import { join as join35, basename as basename2 } from "path";
|
|
10399
10803
|
function parseToolsConfig(toolsStr) {
|
|
@@ -10409,7 +10813,7 @@ function parseToolsConfig(toolsStr) {
|
|
|
10409
10813
|
return result;
|
|
10410
10814
|
}
|
|
10411
10815
|
function loadAgentsFromDir(agentsDir, scope) {
|
|
10412
|
-
if (!
|
|
10816
|
+
if (!existsSync26(agentsDir)) {
|
|
10413
10817
|
return [];
|
|
10414
10818
|
}
|
|
10415
10819
|
const entries = readdirSync8(agentsDir, { withFileTypes: true });
|
|
@@ -10465,7 +10869,7 @@ function loadProjectAgents() {
|
|
|
10465
10869
|
return result;
|
|
10466
10870
|
}
|
|
10467
10871
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
10468
|
-
import { existsSync as
|
|
10872
|
+
import { existsSync as existsSync27 } from "fs";
|
|
10469
10873
|
import { homedir as homedir15 } from "os";
|
|
10470
10874
|
import { join as join36 } from "path";
|
|
10471
10875
|
|
|
@@ -10542,7 +10946,7 @@ function getMcpConfigPaths() {
|
|
|
10542
10946
|
];
|
|
10543
10947
|
}
|
|
10544
10948
|
async function loadMcpConfigFile(filePath) {
|
|
10545
|
-
if (!
|
|
10949
|
+
if (!existsSync27(filePath)) {
|
|
10546
10950
|
return null;
|
|
10547
10951
|
}
|
|
10548
10952
|
try {
|
|
@@ -10637,6 +11041,32 @@ var BUILTIN_SERVERS = {
|
|
|
10637
11041
|
command: ["vscode-eslint-language-server", "--stdio"],
|
|
10638
11042
|
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts", ".vue"]
|
|
10639
11043
|
},
|
|
11044
|
+
oxlint: {
|
|
11045
|
+
command: ["oxlint", "--lsp"],
|
|
11046
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts", ".vue", ".astro", ".svelte"]
|
|
11047
|
+
},
|
|
11048
|
+
biome: {
|
|
11049
|
+
command: ["biome", "lsp-proxy", "--stdio"],
|
|
11050
|
+
extensions: [
|
|
11051
|
+
".ts",
|
|
11052
|
+
".tsx",
|
|
11053
|
+
".js",
|
|
11054
|
+
".jsx",
|
|
11055
|
+
".mjs",
|
|
11056
|
+
".cjs",
|
|
11057
|
+
".mts",
|
|
11058
|
+
".cts",
|
|
11059
|
+
".json",
|
|
11060
|
+
".jsonc",
|
|
11061
|
+
".vue",
|
|
11062
|
+
".astro",
|
|
11063
|
+
".svelte",
|
|
11064
|
+
".css",
|
|
11065
|
+
".graphql",
|
|
11066
|
+
".gql",
|
|
11067
|
+
".html"
|
|
11068
|
+
]
|
|
11069
|
+
},
|
|
10640
11070
|
gopls: {
|
|
10641
11071
|
command: ["gopls"],
|
|
10642
11072
|
extensions: [".go"]
|
|
@@ -10653,6 +11083,10 @@ var BUILTIN_SERVERS = {
|
|
|
10653
11083
|
command: ["pyright-langserver", "--stdio"],
|
|
10654
11084
|
extensions: [".py", ".pyi"]
|
|
10655
11085
|
},
|
|
11086
|
+
ty: {
|
|
11087
|
+
command: ["ty", "server"],
|
|
11088
|
+
extensions: [".py", ".pyi"]
|
|
11089
|
+
},
|
|
10656
11090
|
ruff: {
|
|
10657
11091
|
command: ["ruff", "server"],
|
|
10658
11092
|
extensions: [".py", ".pyi"]
|
|
@@ -10669,6 +11103,10 @@ var BUILTIN_SERVERS = {
|
|
|
10669
11103
|
command: ["csharp-ls"],
|
|
10670
11104
|
extensions: [".cs"]
|
|
10671
11105
|
},
|
|
11106
|
+
fsharp: {
|
|
11107
|
+
command: ["fsautocomplete"],
|
|
11108
|
+
extensions: [".fs", ".fsi", ".fsx", ".fsscript"]
|
|
11109
|
+
},
|
|
10672
11110
|
"sourcekit-lsp": {
|
|
10673
11111
|
command: ["sourcekit-lsp"],
|
|
10674
11112
|
extensions: [".swift", ".objc", ".objcpp"]
|
|
@@ -10712,26 +11150,125 @@ var BUILTIN_SERVERS = {
|
|
|
10712
11150
|
dart: {
|
|
10713
11151
|
command: ["dart", "language-server", "--lsp"],
|
|
10714
11152
|
extensions: [".dart"]
|
|
11153
|
+
},
|
|
11154
|
+
"terraform-ls": {
|
|
11155
|
+
command: ["terraform-ls", "serve"],
|
|
11156
|
+
extensions: [".tf", ".tfvars"]
|
|
10715
11157
|
}
|
|
10716
11158
|
};
|
|
10717
11159
|
var EXT_TO_LANG = {
|
|
11160
|
+
".abap": "abap",
|
|
11161
|
+
".bat": "bat",
|
|
11162
|
+
".bib": "bibtex",
|
|
11163
|
+
".bibtex": "bibtex",
|
|
11164
|
+
".clj": "clojure",
|
|
11165
|
+
".cljs": "clojure",
|
|
11166
|
+
".cljc": "clojure",
|
|
11167
|
+
".edn": "clojure",
|
|
11168
|
+
".coffee": "coffeescript",
|
|
11169
|
+
".c": "c",
|
|
11170
|
+
".cpp": "cpp",
|
|
11171
|
+
".cxx": "cpp",
|
|
11172
|
+
".cc": "cpp",
|
|
11173
|
+
".c++": "cpp",
|
|
11174
|
+
".cs": "csharp",
|
|
11175
|
+
".css": "css",
|
|
11176
|
+
".d": "d",
|
|
11177
|
+
".pas": "pascal",
|
|
11178
|
+
".pascal": "pascal",
|
|
11179
|
+
".diff": "diff",
|
|
11180
|
+
".patch": "diff",
|
|
11181
|
+
".dart": "dart",
|
|
11182
|
+
".dockerfile": "dockerfile",
|
|
11183
|
+
".ex": "elixir",
|
|
11184
|
+
".exs": "elixir",
|
|
11185
|
+
".erl": "erlang",
|
|
11186
|
+
".hrl": "erlang",
|
|
11187
|
+
".fs": "fsharp",
|
|
11188
|
+
".fsi": "fsharp",
|
|
11189
|
+
".fsx": "fsharp",
|
|
11190
|
+
".fsscript": "fsharp",
|
|
11191
|
+
".gitcommit": "git-commit",
|
|
11192
|
+
".gitrebase": "git-rebase",
|
|
11193
|
+
".go": "go",
|
|
11194
|
+
".groovy": "groovy",
|
|
11195
|
+
".gleam": "gleam",
|
|
11196
|
+
".hbs": "handlebars",
|
|
11197
|
+
".handlebars": "handlebars",
|
|
11198
|
+
".hs": "haskell",
|
|
11199
|
+
".html": "html",
|
|
11200
|
+
".htm": "html",
|
|
11201
|
+
".ini": "ini",
|
|
11202
|
+
".java": "java",
|
|
11203
|
+
".js": "javascript",
|
|
11204
|
+
".jsx": "javascriptreact",
|
|
11205
|
+
".json": "json",
|
|
11206
|
+
".jsonc": "jsonc",
|
|
11207
|
+
".tex": "latex",
|
|
11208
|
+
".latex": "latex",
|
|
11209
|
+
".less": "less",
|
|
11210
|
+
".lua": "lua",
|
|
11211
|
+
".makefile": "makefile",
|
|
11212
|
+
makefile: "makefile",
|
|
11213
|
+
".md": "markdown",
|
|
11214
|
+
".markdown": "markdown",
|
|
11215
|
+
".m": "objective-c",
|
|
11216
|
+
".mm": "objective-cpp",
|
|
11217
|
+
".pl": "perl",
|
|
11218
|
+
".pm": "perl",
|
|
11219
|
+
".pm6": "perl6",
|
|
11220
|
+
".php": "php",
|
|
11221
|
+
".ps1": "powershell",
|
|
11222
|
+
".psm1": "powershell",
|
|
11223
|
+
".pug": "jade",
|
|
11224
|
+
".jade": "jade",
|
|
10718
11225
|
".py": "python",
|
|
10719
11226
|
".pyi": "python",
|
|
11227
|
+
".r": "r",
|
|
11228
|
+
".cshtml": "razor",
|
|
11229
|
+
".razor": "razor",
|
|
11230
|
+
".rb": "ruby",
|
|
11231
|
+
".rake": "ruby",
|
|
11232
|
+
".gemspec": "ruby",
|
|
11233
|
+
".ru": "ruby",
|
|
11234
|
+
".erb": "erb",
|
|
11235
|
+
".html.erb": "erb",
|
|
11236
|
+
".js.erb": "erb",
|
|
11237
|
+
".css.erb": "erb",
|
|
11238
|
+
".json.erb": "erb",
|
|
11239
|
+
".rs": "rust",
|
|
11240
|
+
".scss": "scss",
|
|
11241
|
+
".sass": "sass",
|
|
11242
|
+
".scala": "scala",
|
|
11243
|
+
".shader": "shaderlab",
|
|
11244
|
+
".sh": "shellscript",
|
|
11245
|
+
".bash": "shellscript",
|
|
11246
|
+
".zsh": "shellscript",
|
|
11247
|
+
".ksh": "shellscript",
|
|
11248
|
+
".sql": "sql",
|
|
11249
|
+
".svelte": "svelte",
|
|
11250
|
+
".swift": "swift",
|
|
10720
11251
|
".ts": "typescript",
|
|
10721
11252
|
".tsx": "typescriptreact",
|
|
10722
11253
|
".mts": "typescript",
|
|
10723
11254
|
".cts": "typescript",
|
|
10724
|
-
".
|
|
10725
|
-
".
|
|
11255
|
+
".mtsx": "typescriptreact",
|
|
11256
|
+
".ctsx": "typescriptreact",
|
|
11257
|
+
".xml": "xml",
|
|
11258
|
+
".xsl": "xsl",
|
|
11259
|
+
".yaml": "yaml",
|
|
11260
|
+
".yml": "yaml",
|
|
10726
11261
|
".mjs": "javascript",
|
|
10727
11262
|
".cjs": "javascript",
|
|
10728
|
-
".
|
|
10729
|
-
".
|
|
10730
|
-
".
|
|
10731
|
-
".
|
|
10732
|
-
".
|
|
10733
|
-
".
|
|
10734
|
-
".
|
|
11263
|
+
".vue": "vue",
|
|
11264
|
+
".zig": "zig",
|
|
11265
|
+
".zon": "zig",
|
|
11266
|
+
".astro": "astro",
|
|
11267
|
+
".ml": "ocaml",
|
|
11268
|
+
".mli": "ocaml",
|
|
11269
|
+
".tf": "terraform",
|
|
11270
|
+
".tfvars": "terraform-vars",
|
|
11271
|
+
".hcl": "hcl",
|
|
10735
11272
|
".h": "c",
|
|
10736
11273
|
".hpp": "cpp",
|
|
10737
11274
|
".hh": "cpp",
|
|
@@ -10739,46 +11276,16 @@ var EXT_TO_LANG = {
|
|
|
10739
11276
|
".h++": "cpp",
|
|
10740
11277
|
".objc": "objective-c",
|
|
10741
11278
|
".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
11279
|
".fish": "fish",
|
|
10772
|
-
".
|
|
10773
|
-
".
|
|
10774
|
-
".tfvars": "terraform"
|
|
11280
|
+
".graphql": "graphql",
|
|
11281
|
+
".gql": "graphql"
|
|
10775
11282
|
};
|
|
10776
11283
|
// src/tools/lsp/config.ts
|
|
10777
|
-
import { existsSync as
|
|
11284
|
+
import { existsSync as existsSync28, readFileSync as readFileSync18 } from "fs";
|
|
10778
11285
|
import { join as join37 } from "path";
|
|
10779
11286
|
import { homedir as homedir16 } from "os";
|
|
10780
11287
|
function loadJsonFile(path7) {
|
|
10781
|
-
if (!
|
|
11288
|
+
if (!existsSync28(path7))
|
|
10782
11289
|
return null;
|
|
10783
11290
|
try {
|
|
10784
11291
|
return JSON.parse(readFileSync18(path7, "utf-8"));
|
|
@@ -10881,10 +11388,27 @@ function isServerInstalled(command) {
|
|
|
10881
11388
|
if (command.length === 0)
|
|
10882
11389
|
return false;
|
|
10883
11390
|
const cmd = command[0];
|
|
11391
|
+
const isWindows2 = process.platform === "win32";
|
|
11392
|
+
const ext = isWindows2 ? ".exe" : "";
|
|
10884
11393
|
const pathEnv = process.env.PATH || "";
|
|
10885
|
-
const
|
|
11394
|
+
const pathSeparator = isWindows2 ? ";" : ":";
|
|
11395
|
+
const paths = pathEnv.split(pathSeparator);
|
|
10886
11396
|
for (const p of paths) {
|
|
10887
|
-
if (
|
|
11397
|
+
if (existsSync28(join37(p, cmd)) || existsSync28(join37(p, cmd + ext))) {
|
|
11398
|
+
return true;
|
|
11399
|
+
}
|
|
11400
|
+
}
|
|
11401
|
+
const cwd = process.cwd();
|
|
11402
|
+
const additionalPaths = [
|
|
11403
|
+
join37(cwd, "node_modules", ".bin", cmd),
|
|
11404
|
+
join37(cwd, "node_modules", ".bin", cmd + ext),
|
|
11405
|
+
join37(homedir16(), ".config", "opencode", "bin", cmd),
|
|
11406
|
+
join37(homedir16(), ".config", "opencode", "bin", cmd + ext),
|
|
11407
|
+
join37(homedir16(), ".config", "opencode", "node_modules", ".bin", cmd),
|
|
11408
|
+
join37(homedir16(), ".config", "opencode", "node_modules", ".bin", cmd + ext)
|
|
11409
|
+
];
|
|
11410
|
+
for (const p of additionalPaths) {
|
|
11411
|
+
if (existsSync28(p)) {
|
|
10888
11412
|
return true;
|
|
10889
11413
|
}
|
|
10890
11414
|
}
|
|
@@ -11479,16 +12003,16 @@ ${msg}`);
|
|
|
11479
12003
|
}
|
|
11480
12004
|
// src/tools/lsp/utils.ts
|
|
11481
12005
|
import { extname as extname2, resolve as resolve6 } from "path";
|
|
11482
|
-
import { existsSync as
|
|
12006
|
+
import { existsSync as existsSync29, readFileSync as readFileSync20, writeFileSync as writeFileSync12 } from "fs";
|
|
11483
12007
|
function findWorkspaceRoot(filePath) {
|
|
11484
12008
|
let dir = resolve6(filePath);
|
|
11485
|
-
if (!
|
|
12009
|
+
if (!existsSync29(dir) || !__require("fs").statSync(dir).isDirectory()) {
|
|
11486
12010
|
dir = __require("path").dirname(dir);
|
|
11487
12011
|
}
|
|
11488
12012
|
const markers = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
|
|
11489
12013
|
while (dir !== "/") {
|
|
11490
12014
|
for (const marker of markers) {
|
|
11491
|
-
if (
|
|
12015
|
+
if (existsSync29(__require("path").join(dir, marker))) {
|
|
11492
12016
|
return dir;
|
|
11493
12017
|
}
|
|
11494
12018
|
}
|
|
@@ -24414,11 +24938,11 @@ var lsp_code_action_resolve = tool({
|
|
|
24414
24938
|
// src/tools/ast-grep/constants.ts
|
|
24415
24939
|
import { createRequire as createRequire4 } from "module";
|
|
24416
24940
|
import { dirname as dirname6, join as join39 } from "path";
|
|
24417
|
-
import { existsSync as
|
|
24941
|
+
import { existsSync as existsSync31, statSync as statSync4 } from "fs";
|
|
24418
24942
|
|
|
24419
24943
|
// src/tools/ast-grep/downloader.ts
|
|
24420
24944
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
24421
|
-
import { existsSync as
|
|
24945
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync10, chmodSync as chmodSync2, unlinkSync as unlinkSync9 } from "fs";
|
|
24422
24946
|
import { join as join38 } from "path";
|
|
24423
24947
|
import { homedir as homedir17 } from "os";
|
|
24424
24948
|
import { createRequire as createRequire3 } from "module";
|
|
@@ -24457,7 +24981,7 @@ function getBinaryName3() {
|
|
|
24457
24981
|
}
|
|
24458
24982
|
function getCachedBinaryPath2() {
|
|
24459
24983
|
const binaryPath = join38(getCacheDir3(), getBinaryName3());
|
|
24460
|
-
return
|
|
24984
|
+
return existsSync30(binaryPath) ? binaryPath : null;
|
|
24461
24985
|
}
|
|
24462
24986
|
async function extractZip2(archivePath, destDir) {
|
|
24463
24987
|
const proc = process.platform === "win32" ? spawn5([
|
|
@@ -24484,7 +25008,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
24484
25008
|
const cacheDir = getCacheDir3();
|
|
24485
25009
|
const binaryName = getBinaryName3();
|
|
24486
25010
|
const binaryPath = join38(cacheDir, binaryName);
|
|
24487
|
-
if (
|
|
25011
|
+
if (existsSync30(binaryPath)) {
|
|
24488
25012
|
return binaryPath;
|
|
24489
25013
|
}
|
|
24490
25014
|
const { arch, os: os5 } = platformInfo;
|
|
@@ -24492,7 +25016,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
24492
25016
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
|
|
24493
25017
|
console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
|
|
24494
25018
|
try {
|
|
24495
|
-
if (!
|
|
25019
|
+
if (!existsSync30(cacheDir)) {
|
|
24496
25020
|
mkdirSync10(cacheDir, { recursive: true });
|
|
24497
25021
|
}
|
|
24498
25022
|
const response2 = await fetch(downloadUrl, { redirect: "follow" });
|
|
@@ -24503,10 +25027,10 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
24503
25027
|
const arrayBuffer = await response2.arrayBuffer();
|
|
24504
25028
|
await Bun.write(archivePath, arrayBuffer);
|
|
24505
25029
|
await extractZip2(archivePath, cacheDir);
|
|
24506
|
-
if (
|
|
25030
|
+
if (existsSync30(archivePath)) {
|
|
24507
25031
|
unlinkSync9(archivePath);
|
|
24508
25032
|
}
|
|
24509
|
-
if (process.platform !== "win32" &&
|
|
25033
|
+
if (process.platform !== "win32" && existsSync30(binaryPath)) {
|
|
24510
25034
|
chmodSync2(binaryPath, 493);
|
|
24511
25035
|
}
|
|
24512
25036
|
console.log(`[oh-my-opencode] ast-grep binary ready.`);
|
|
@@ -24558,7 +25082,7 @@ function findSgCliPathSync() {
|
|
|
24558
25082
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
24559
25083
|
const cliDir = dirname6(cliPkgPath);
|
|
24560
25084
|
const sgPath = join39(cliDir, binaryName);
|
|
24561
|
-
if (
|
|
25085
|
+
if (existsSync31(sgPath) && isValidBinary(sgPath)) {
|
|
24562
25086
|
return sgPath;
|
|
24563
25087
|
}
|
|
24564
25088
|
} catch {}
|
|
@@ -24570,7 +25094,7 @@ function findSgCliPathSync() {
|
|
|
24570
25094
|
const pkgDir = dirname6(pkgPath);
|
|
24571
25095
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
24572
25096
|
const binaryPath = join39(pkgDir, astGrepName);
|
|
24573
|
-
if (
|
|
25097
|
+
if (existsSync31(binaryPath) && isValidBinary(binaryPath)) {
|
|
24574
25098
|
return binaryPath;
|
|
24575
25099
|
}
|
|
24576
25100
|
} catch {}
|
|
@@ -24578,7 +25102,7 @@ function findSgCliPathSync() {
|
|
|
24578
25102
|
if (process.platform === "darwin") {
|
|
24579
25103
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
24580
25104
|
for (const path7 of homebrewPaths) {
|
|
24581
|
-
if (
|
|
25105
|
+
if (existsSync31(path7) && isValidBinary(path7)) {
|
|
24582
25106
|
return path7;
|
|
24583
25107
|
}
|
|
24584
25108
|
}
|
|
@@ -24634,11 +25158,11 @@ var DEFAULT_MAX_MATCHES = 500;
|
|
|
24634
25158
|
|
|
24635
25159
|
// src/tools/ast-grep/cli.ts
|
|
24636
25160
|
var {spawn: spawn6 } = globalThis.Bun;
|
|
24637
|
-
import { existsSync as
|
|
25161
|
+
import { existsSync as existsSync32 } from "fs";
|
|
24638
25162
|
var resolvedCliPath3 = null;
|
|
24639
25163
|
var initPromise2 = null;
|
|
24640
25164
|
async function getAstGrepPath() {
|
|
24641
|
-
if (resolvedCliPath3 !== null &&
|
|
25165
|
+
if (resolvedCliPath3 !== null && existsSync32(resolvedCliPath3)) {
|
|
24642
25166
|
return resolvedCliPath3;
|
|
24643
25167
|
}
|
|
24644
25168
|
if (initPromise2) {
|
|
@@ -24646,7 +25170,7 @@ async function getAstGrepPath() {
|
|
|
24646
25170
|
}
|
|
24647
25171
|
initPromise2 = (async () => {
|
|
24648
25172
|
const syncPath = findSgCliPathSync();
|
|
24649
|
-
if (syncPath &&
|
|
25173
|
+
if (syncPath && existsSync32(syncPath)) {
|
|
24650
25174
|
resolvedCliPath3 = syncPath;
|
|
24651
25175
|
setSgCliPath(syncPath);
|
|
24652
25176
|
return syncPath;
|
|
@@ -24680,7 +25204,7 @@ async function runSg(options) {
|
|
|
24680
25204
|
const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
|
|
24681
25205
|
args.push(...paths);
|
|
24682
25206
|
let cliPath = getSgCliPath();
|
|
24683
|
-
if (!
|
|
25207
|
+
if (!existsSync32(cliPath) && cliPath !== "sg") {
|
|
24684
25208
|
const downloadedPath = await getAstGrepPath();
|
|
24685
25209
|
if (downloadedPath) {
|
|
24686
25210
|
cliPath = downloadedPath;
|
|
@@ -24944,12 +25468,12 @@ var ast_grep_replace = tool({
|
|
|
24944
25468
|
var {spawn: spawn7 } = globalThis.Bun;
|
|
24945
25469
|
|
|
24946
25470
|
// src/tools/grep/constants.ts
|
|
24947
|
-
import { existsSync as
|
|
25471
|
+
import { existsSync as existsSync34 } from "fs";
|
|
24948
25472
|
import { join as join41, dirname as dirname7 } from "path";
|
|
24949
25473
|
import { spawnSync } from "child_process";
|
|
24950
25474
|
|
|
24951
25475
|
// src/tools/grep/downloader.ts
|
|
24952
|
-
import { existsSync as
|
|
25476
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync11, chmodSync as chmodSync3, unlinkSync as unlinkSync10, readdirSync as readdirSync9 } from "fs";
|
|
24953
25477
|
import { join as join40 } from "path";
|
|
24954
25478
|
function getInstallDir() {
|
|
24955
25479
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
@@ -24961,7 +25485,7 @@ function getRgPath() {
|
|
|
24961
25485
|
}
|
|
24962
25486
|
function getInstalledRipgrepPath() {
|
|
24963
25487
|
const rgPath = getRgPath();
|
|
24964
|
-
return
|
|
25488
|
+
return existsSync33(rgPath) ? rgPath : null;
|
|
24965
25489
|
}
|
|
24966
25490
|
|
|
24967
25491
|
// src/tools/grep/constants.ts
|
|
@@ -24990,7 +25514,7 @@ function getOpenCodeBundledRg() {
|
|
|
24990
25514
|
join41(execDir, "..", "libexec", rgName)
|
|
24991
25515
|
];
|
|
24992
25516
|
for (const candidate of candidates) {
|
|
24993
|
-
if (
|
|
25517
|
+
if (existsSync34(candidate)) {
|
|
24994
25518
|
return candidate;
|
|
24995
25519
|
}
|
|
24996
25520
|
}
|
|
@@ -25393,11 +25917,11 @@ var glob = tool({
|
|
|
25393
25917
|
}
|
|
25394
25918
|
});
|
|
25395
25919
|
// src/tools/slashcommand/tools.ts
|
|
25396
|
-
import { existsSync as
|
|
25920
|
+
import { existsSync as existsSync35, readdirSync as readdirSync10, readFileSync as readFileSync21 } from "fs";
|
|
25397
25921
|
import { homedir as homedir18 } from "os";
|
|
25398
25922
|
import { join as join42, basename as basename3, dirname as dirname8 } from "path";
|
|
25399
25923
|
function discoverCommandsFromDir(commandsDir, scope) {
|
|
25400
|
-
if (!
|
|
25924
|
+
if (!existsSync35(commandsDir)) {
|
|
25401
25925
|
return [];
|
|
25402
25926
|
}
|
|
25403
25927
|
const entries = readdirSync10(commandsDir, { withFileTypes: true });
|
|
@@ -25568,7 +26092,7 @@ var SkillFrontmatterSchema = exports_external.object({
|
|
|
25568
26092
|
metadata: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
25569
26093
|
});
|
|
25570
26094
|
// src/tools/skill/tools.ts
|
|
25571
|
-
import { existsSync as
|
|
26095
|
+
import { existsSync as existsSync36, readdirSync as readdirSync11, readFileSync as readFileSync22 } from "fs";
|
|
25572
26096
|
import { homedir as homedir19 } from "os";
|
|
25573
26097
|
import { join as join43, basename as basename4 } from "path";
|
|
25574
26098
|
function parseSkillFrontmatter(data) {
|
|
@@ -25581,7 +26105,7 @@ function parseSkillFrontmatter(data) {
|
|
|
25581
26105
|
};
|
|
25582
26106
|
}
|
|
25583
26107
|
function discoverSkillsFromDir(skillsDir, scope) {
|
|
25584
|
-
if (!
|
|
26108
|
+
if (!existsSync36(skillsDir)) {
|
|
25585
26109
|
return [];
|
|
25586
26110
|
}
|
|
25587
26111
|
const entries = readdirSync11(skillsDir, { withFileTypes: true });
|
|
@@ -25593,7 +26117,7 @@ function discoverSkillsFromDir(skillsDir, scope) {
|
|
|
25593
26117
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
25594
26118
|
const resolvedPath = resolveSymlink(skillPath);
|
|
25595
26119
|
const skillMdPath = join43(resolvedPath, "SKILL.md");
|
|
25596
|
-
if (!
|
|
26120
|
+
if (!existsSync36(skillMdPath))
|
|
25597
26121
|
continue;
|
|
25598
26122
|
try {
|
|
25599
26123
|
const content = readFileSync22(skillMdPath, "utf-8");
|
|
@@ -25623,7 +26147,7 @@ var skillListForDescription = availableSkills.map((s) => `- ${s.name}: ${s.descr
|
|
|
25623
26147
|
async function parseSkillMd(skillPath) {
|
|
25624
26148
|
const resolvedPath = resolveSymlink(skillPath);
|
|
25625
26149
|
const skillMdPath = join43(resolvedPath, "SKILL.md");
|
|
25626
|
-
if (!
|
|
26150
|
+
if (!existsSync36(skillMdPath)) {
|
|
25627
26151
|
return null;
|
|
25628
26152
|
}
|
|
25629
26153
|
try {
|
|
@@ -25641,9 +26165,9 @@ async function parseSkillMd(skillPath) {
|
|
|
25641
26165
|
const referencesDir = join43(resolvedPath, "references");
|
|
25642
26166
|
const scriptsDir = join43(resolvedPath, "scripts");
|
|
25643
26167
|
const assetsDir = join43(resolvedPath, "assets");
|
|
25644
|
-
const references =
|
|
25645
|
-
const scripts =
|
|
25646
|
-
const assets =
|
|
26168
|
+
const references = existsSync36(referencesDir) ? readdirSync11(referencesDir).filter((f) => !f.startsWith(".")) : [];
|
|
26169
|
+
const scripts = existsSync36(scriptsDir) ? readdirSync11(scriptsDir).filter((f) => !f.startsWith(".") && !f.startsWith("__")) : [];
|
|
26170
|
+
const assets = existsSync36(assetsDir) ? readdirSync11(assetsDir).filter((f) => !f.startsWith(".")) : [];
|
|
25647
26171
|
return {
|
|
25648
26172
|
name: metadata.name,
|
|
25649
26173
|
path: resolvedPath,
|
|
@@ -25659,7 +26183,7 @@ async function parseSkillMd(skillPath) {
|
|
|
25659
26183
|
}
|
|
25660
26184
|
}
|
|
25661
26185
|
async function discoverSkillsFromDirAsync(skillsDir) {
|
|
25662
|
-
if (!
|
|
26186
|
+
if (!existsSync36(skillsDir)) {
|
|
25663
26187
|
return [];
|
|
25664
26188
|
}
|
|
25665
26189
|
const entries = readdirSync11(skillsDir, { withFileTypes: true });
|
|
@@ -26474,17 +26998,17 @@ var builtinTools = {
|
|
|
26474
26998
|
skill
|
|
26475
26999
|
};
|
|
26476
27000
|
// src/features/background-agent/manager.ts
|
|
26477
|
-
import { existsSync as
|
|
27001
|
+
import { existsSync as existsSync37, readdirSync as readdirSync12 } from "fs";
|
|
26478
27002
|
import { join as join44 } from "path";
|
|
26479
27003
|
function getMessageDir4(sessionID) {
|
|
26480
|
-
if (!
|
|
27004
|
+
if (!existsSync37(MESSAGE_STORAGE))
|
|
26481
27005
|
return null;
|
|
26482
27006
|
const directPath = join44(MESSAGE_STORAGE, sessionID);
|
|
26483
|
-
if (
|
|
27007
|
+
if (existsSync37(directPath))
|
|
26484
27008
|
return directPath;
|
|
26485
27009
|
for (const dir of readdirSync12(MESSAGE_STORAGE)) {
|
|
26486
27010
|
const sessionPath = join44(MESSAGE_STORAGE, dir, sessionID);
|
|
26487
|
-
if (
|
|
27011
|
+
if (existsSync37(sessionPath))
|
|
26488
27012
|
return sessionPath;
|
|
26489
27013
|
}
|
|
26490
27014
|
return null;
|
|
@@ -26955,8 +27479,9 @@ var SisyphusAgentConfigSchema = exports_external.object({
|
|
|
26955
27479
|
});
|
|
26956
27480
|
var ExperimentalConfigSchema = exports_external.object({
|
|
26957
27481
|
aggressive_truncation: exports_external.boolean().optional(),
|
|
26958
|
-
|
|
26959
|
-
|
|
27482
|
+
auto_resume: exports_external.boolean().optional(),
|
|
27483
|
+
preemptive_compaction: exports_external.boolean().optional(),
|
|
27484
|
+
preemptive_compaction_threshold: exports_external.number().min(0.5).max(0.95).optional()
|
|
26960
27485
|
});
|
|
26961
27486
|
var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
26962
27487
|
$schema: exports_external.string().optional(),
|
|
@@ -27042,7 +27567,7 @@ var PLAN_PERMISSION = {
|
|
|
27042
27567
|
};
|
|
27043
27568
|
|
|
27044
27569
|
// src/index.ts
|
|
27045
|
-
import * as
|
|
27570
|
+
import * as fs7 from "fs";
|
|
27046
27571
|
import * as path7 from "path";
|
|
27047
27572
|
var AGENT_NAME_MAP = {
|
|
27048
27573
|
omo: "Sisyphus",
|
|
@@ -27087,7 +27612,7 @@ function migrateConfigFile(configPath, rawConfig) {
|
|
|
27087
27612
|
}
|
|
27088
27613
|
if (needsWrite) {
|
|
27089
27614
|
try {
|
|
27090
|
-
|
|
27615
|
+
fs7.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + `
|
|
27091
27616
|
`, "utf-8");
|
|
27092
27617
|
log(`Migrated config file: ${configPath} (OmO \u2192 Sisyphus)`);
|
|
27093
27618
|
} catch (err) {
|
|
@@ -27098,8 +27623,8 @@ function migrateConfigFile(configPath, rawConfig) {
|
|
|
27098
27623
|
}
|
|
27099
27624
|
function loadConfigFromPath2(configPath) {
|
|
27100
27625
|
try {
|
|
27101
|
-
if (
|
|
27102
|
-
const content =
|
|
27626
|
+
if (fs7.existsSync(configPath)) {
|
|
27627
|
+
const content = fs7.readFileSync(configPath, "utf-8");
|
|
27103
27628
|
const rawConfig = JSON.parse(content);
|
|
27104
27629
|
migrateConfigFile(configPath, rawConfig);
|
|
27105
27630
|
const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
|
|
@@ -27166,6 +27691,18 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
27166
27691
|
const pluginConfig = loadPluginConfig(ctx.directory);
|
|
27167
27692
|
const disabledHooks = new Set(pluginConfig.disabled_hooks ?? []);
|
|
27168
27693
|
const isHookEnabled = (hookName) => !disabledHooks.has(hookName);
|
|
27694
|
+
const modelContextLimitsCache = new Map;
|
|
27695
|
+
let anthropicContext1MEnabled = false;
|
|
27696
|
+
const getModelLimit = (providerID, modelID) => {
|
|
27697
|
+
const key = `${providerID}/${modelID}`;
|
|
27698
|
+
const cached2 = modelContextLimitsCache.get(key);
|
|
27699
|
+
if (cached2)
|
|
27700
|
+
return cached2;
|
|
27701
|
+
if (providerID === "anthropic" && anthropicContext1MEnabled && modelID.includes("sonnet")) {
|
|
27702
|
+
return 1e6;
|
|
27703
|
+
}
|
|
27704
|
+
return;
|
|
27705
|
+
};
|
|
27169
27706
|
const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer") ? createTodoContinuationEnforcer(ctx) : null;
|
|
27170
27707
|
const contextWindowMonitor = isHookEnabled("context-window-monitor") ? createContextWindowMonitorHook(ctx) : null;
|
|
27171
27708
|
const sessionRecovery = isHookEnabled("session-recovery") ? createSessionRecoveryHook(ctx, { experimental: pluginConfig.experimental }) : null;
|
|
@@ -27184,6 +27721,12 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
27184
27721
|
disabledHooks: pluginConfig.claude_code?.hooks ?? true ? undefined : true
|
|
27185
27722
|
});
|
|
27186
27723
|
const anthropicAutoCompact = isHookEnabled("anthropic-auto-compact") ? createAnthropicAutoCompactHook(ctx, { experimental: pluginConfig.experimental }) : null;
|
|
27724
|
+
const compactionContextInjector = createCompactionContextInjector();
|
|
27725
|
+
const preemptiveCompaction = createPreemptiveCompactionHook(ctx, {
|
|
27726
|
+
experimental: pluginConfig.experimental,
|
|
27727
|
+
onBeforeSummarize: compactionContextInjector,
|
|
27728
|
+
getModelLimit
|
|
27729
|
+
});
|
|
27187
27730
|
const rulesInjector = isHookEnabled("rules-injector") ? createRulesInjectorHook(ctx) : null;
|
|
27188
27731
|
const autoUpdateChecker = isHookEnabled("auto-update-checker") ? createAutoUpdateCheckerHook(ctx, {
|
|
27189
27732
|
showStartupToast: isHookEnabled("startup-toast"),
|
|
@@ -27219,6 +27762,22 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
27219
27762
|
await emptyMessageSanitizer?.["experimental.chat.messages.transform"]?.(input, output);
|
|
27220
27763
|
},
|
|
27221
27764
|
config: async (config3) => {
|
|
27765
|
+
const providers = config3.provider;
|
|
27766
|
+
const anthropicBeta = providers?.anthropic?.options?.headers?.["anthropic-beta"];
|
|
27767
|
+
anthropicContext1MEnabled = anthropicBeta?.includes("context-1m") ?? false;
|
|
27768
|
+
if (providers) {
|
|
27769
|
+
for (const [providerID, providerConfig] of Object.entries(providers)) {
|
|
27770
|
+
const models = providerConfig?.models;
|
|
27771
|
+
if (models) {
|
|
27772
|
+
for (const [modelID, modelConfig] of Object.entries(models)) {
|
|
27773
|
+
const contextLimit = modelConfig?.limit?.context;
|
|
27774
|
+
if (contextLimit) {
|
|
27775
|
+
modelContextLimitsCache.set(`${providerID}/${modelID}`, contextLimit);
|
|
27776
|
+
}
|
|
27777
|
+
}
|
|
27778
|
+
}
|
|
27779
|
+
}
|
|
27780
|
+
}
|
|
27222
27781
|
const builtinAgents = createBuiltinAgents(pluginConfig.disabled_agents, pluginConfig.agents, ctx.directory, config3.model);
|
|
27223
27782
|
const userAgents = pluginConfig.claude_code?.agents ?? true ? loadUserAgents() : {};
|
|
27224
27783
|
const projectAgents = pluginConfig.claude_code?.agents ?? true ? loadProjectAgents() : {};
|
|
@@ -27315,6 +27874,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
27315
27874
|
await rulesInjector?.event(input);
|
|
27316
27875
|
await thinkMode?.event(input);
|
|
27317
27876
|
await anthropicAutoCompact?.event(input);
|
|
27877
|
+
await preemptiveCompaction?.event(input);
|
|
27318
27878
|
await agentUsageReminder?.event(input);
|
|
27319
27879
|
await interactiveBashSession?.event(input);
|
|
27320
27880
|
const { event } = input;
|
|
@@ -27356,6 +27916,9 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
27356
27916
|
await claudeCodeHooks["tool.execute.before"](input, output);
|
|
27357
27917
|
await nonInteractiveEnv?.["tool.execute.before"](input, output);
|
|
27358
27918
|
await commentChecker?.["tool.execute.before"](input, output);
|
|
27919
|
+
await directoryAgentsInjector?.["tool.execute.before"]?.(input, output);
|
|
27920
|
+
await directoryReadmeInjector?.["tool.execute.before"]?.(input, output);
|
|
27921
|
+
await rulesInjector?.["tool.execute.before"]?.(input, output);
|
|
27359
27922
|
if (input.tool === "task") {
|
|
27360
27923
|
const args = output.args;
|
|
27361
27924
|
const subagentType = args.subagent_type;
|