oh-my-opencode 2.5.1 → 2.5.3
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/LICENSE.md +82 -0
- package/README.ja.md +79 -40
- package/README.ko.md +78 -39
- package/README.md +79 -40
- package/README.zh-cn.md +79 -40
- package/dist/agents/build-prompt.d.ts +31 -0
- package/dist/agents/document-writer.d.ts +1 -0
- package/dist/agents/explore.d.ts +1 -0
- package/dist/agents/frontend-ui-ux-engineer.d.ts +1 -0
- package/dist/agents/librarian.d.ts +1 -0
- package/dist/agents/multimodal-looker.d.ts +1 -0
- package/dist/agents/types.d.ts +3 -1
- package/dist/cli/config-manager.d.ts +0 -1
- package/dist/cli/index.js +1664 -3
- package/dist/cli/run/completion.d.ts +2 -0
- package/dist/cli/run/completion.test.d.ts +1 -0
- package/dist/cli/run/events.d.ts +7 -0
- package/dist/cli/run/events.test.d.ts +1 -0
- package/dist/cli/run/index.d.ts +2 -0
- package/dist/cli/run/runner.d.ts +2 -0
- package/dist/cli/run/types.d.ts +45 -0
- package/dist/config/schema.d.ts +126 -2
- package/dist/hooks/claude-code-hooks/config-loader.d.ts +1 -0
- package/dist/hooks/claude-code-hooks/index.d.ts +5 -0
- package/dist/hooks/claude-code-hooks/pre-compact.d.ts +16 -0
- package/dist/hooks/claude-code-hooks/types.d.ts +17 -1
- package/dist/hooks/non-interactive-env/detector.d.ts +1 -0
- package/dist/hooks/non-interactive-env/index.d.ts +1 -0
- package/dist/hooks/session-notification.test.d.ts +1 -0
- package/dist/hooks/todo-continuation-enforcer.d.ts +5 -1
- package/dist/index.js +1143 -339
- package/dist/shared/data-path.d.ts +15 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/tools/index.d.ts +52 -0
- package/dist/tools/session-manager/constants.d.ts +11 -0
- package/dist/tools/session-manager/index.d.ts +3 -0
- package/dist/tools/session-manager/storage.d.ts +8 -0
- package/dist/tools/session-manager/storage.test.d.ts +1 -0
- package/dist/tools/session-manager/tools.d.ts +52 -0
- package/dist/tools/session-manager/tools.test.d.ts +1 -0
- package/dist/tools/session-manager/types.d.ts +71 -0
- package/dist/tools/session-manager/utils.d.ts +11 -0
- package/dist/tools/session-manager/utils.test.d.ts +1 -0
- package/package.json +3 -2
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -222,8 +222,8 @@ var require_utils = __commonJS((exports) => {
|
|
|
222
222
|
}
|
|
223
223
|
return output;
|
|
224
224
|
};
|
|
225
|
-
exports.basename = (
|
|
226
|
-
const segs =
|
|
225
|
+
exports.basename = (path5, { windows } = {}) => {
|
|
226
|
+
const segs = path5.split(windows ? /[\\/]/ : "/");
|
|
227
227
|
const last = segs[segs.length - 1];
|
|
228
228
|
if (last === "") {
|
|
229
229
|
return segs[segs.length - 2];
|
|
@@ -1508,6 +1508,8 @@ Named by [YeonGyu Kim](https://github.com/code-yeongyu).
|
|
|
1508
1508
|
### Key Triggers (check BEFORE classification):
|
|
1509
1509
|
- External library/source mentioned \u2192 fire \`librarian\` background
|
|
1510
1510
|
- 2+ modules involved \u2192 fire \`explore\` background
|
|
1511
|
+
- **GitHub mention (@mention in issue/PR)** \u2192 This is a WORK REQUEST. Plan full cycle: investigate \u2192 implement \u2192 create PR
|
|
1512
|
+
- **"Look into" + "create PR"** \u2192 Not just research. Full implementation cycle expected.
|
|
1511
1513
|
|
|
1512
1514
|
### Step 1: Classify Request Type
|
|
1513
1515
|
|
|
@@ -1517,6 +1519,7 @@ Named by [YeonGyu Kim](https://github.com/code-yeongyu).
|
|
|
1517
1519
|
| **Explicit** | Specific file/line, clear command | Execute directly |
|
|
1518
1520
|
| **Exploratory** | "How does X work?", "Find Y" | Fire explore (1-3) + tools in parallel |
|
|
1519
1521
|
| **Open-ended** | "Improve", "Refactor", "Add feature" | Assess codebase first |
|
|
1522
|
+
| **GitHub Work** | Mentioned in issue, "look into X and create PR" | **Full cycle**: investigate \u2192 implement \u2192 verify \u2192 create PR (see GitHub Workflow section) |
|
|
1520
1523
|
| **Ambiguous** | Unclear scope, multiple interpretations | Ask ONE clarifying question |
|
|
1521
1524
|
|
|
1522
1525
|
### Step 2: Check for Ambiguity
|
|
@@ -1664,7 +1667,7 @@ STOP searching when:
|
|
|
1664
1667
|
## Phase 2B - Implementation
|
|
1665
1668
|
|
|
1666
1669
|
### Pre-Implementation:
|
|
1667
|
-
1. If task has 2+ steps \u2192 Create todo list IMMEDIATELY, IN SUPER DETAIL.
|
|
1670
|
+
1. If task has 2+ steps \u2192 Create todo list IMMEDIATELY, IN SUPER DETAIL. No announcements\u2014just create it.
|
|
1668
1671
|
2. Mark current task \`in_progress\` before starting
|
|
1669
1672
|
3. Mark \`completed\` as soon as done (don't batch) - OBSESSIVELY TRACK YOUR WORK USING TODO TOOLS
|
|
1670
1673
|
|
|
@@ -1736,6 +1739,41 @@ AFTER THE WORK YOU DELEGATED SEEMS DONE, ALWAYS VERIFY THE RESULTS AS FOLLOWING:
|
|
|
1736
1739
|
|
|
1737
1740
|
**Vague prompts = rejected. Be exhaustive.**
|
|
1738
1741
|
|
|
1742
|
+
### GitHub Workflow (CRITICAL - When mentioned in issues/PRs):
|
|
1743
|
+
|
|
1744
|
+
When you're mentioned in GitHub issues or asked to "look into" something and "create PR":
|
|
1745
|
+
|
|
1746
|
+
**This is NOT just investigation. This is a COMPLETE WORK CYCLE.**
|
|
1747
|
+
|
|
1748
|
+
#### Pattern Recognition:
|
|
1749
|
+
- "@sisyphus look into X"
|
|
1750
|
+
- "look into X and create PR"
|
|
1751
|
+
- "investigate Y and make PR"
|
|
1752
|
+
- Mentioned in issue comments
|
|
1753
|
+
|
|
1754
|
+
#### Required Workflow (NON-NEGOTIABLE):
|
|
1755
|
+
1. **Investigate**: Understand the problem thoroughly
|
|
1756
|
+
- Read issue/PR context completely
|
|
1757
|
+
- Search codebase for relevant code
|
|
1758
|
+
- Identify root cause and scope
|
|
1759
|
+
2. **Implement**: Make the necessary changes
|
|
1760
|
+
- Follow existing codebase patterns
|
|
1761
|
+
- Add tests if applicable
|
|
1762
|
+
- Verify with lsp_diagnostics
|
|
1763
|
+
3. **Verify**: Ensure everything works
|
|
1764
|
+
- Run build if exists
|
|
1765
|
+
- Run tests if exists
|
|
1766
|
+
- Check for regressions
|
|
1767
|
+
4. **Create PR**: Complete the cycle
|
|
1768
|
+
- Use \`gh pr create\` with meaningful title and description
|
|
1769
|
+
- Reference the original issue number
|
|
1770
|
+
- Summarize what was changed and why
|
|
1771
|
+
|
|
1772
|
+
**EMPHASIS**: "Look into" does NOT mean "just investigate and report back."
|
|
1773
|
+
It means "investigate, understand, implement a solution, and create a PR."
|
|
1774
|
+
|
|
1775
|
+
**If the user says "look into X and create PR", they expect a PR, not just analysis.**
|
|
1776
|
+
|
|
1739
1777
|
### Code Changes:
|
|
1740
1778
|
- Match existing patterns (if codebase is disciplined)
|
|
1741
1779
|
- Propose approach first (if codebase is chaotic)
|
|
@@ -1831,6 +1869,8 @@ Oracle is an expensive, high-quality reasoning model. Use it wisely.
|
|
|
1831
1869
|
|
|
1832
1870
|
### Usage Pattern:
|
|
1833
1871
|
Briefly announce "Consulting Oracle for [reason]" before invocation.
|
|
1872
|
+
|
|
1873
|
+
**Exception**: This is the ONLY case where you announce before acting. For all other work, start immediately without status updates.
|
|
1834
1874
|
</Oracle_Usage>
|
|
1835
1875
|
|
|
1836
1876
|
<Task_Management>
|
|
@@ -1894,6 +1934,7 @@ Should I proceed with [recommendation], or would you prefer differently?
|
|
|
1894
1934
|
## Communication Style
|
|
1895
1935
|
|
|
1896
1936
|
### Be Concise
|
|
1937
|
+
- Start work immediately. No acknowledgments ("I'm on it", "Let me...", "I'll start...")
|
|
1897
1938
|
- Answer directly without preamble
|
|
1898
1939
|
- Don't summarize what you did unless asked
|
|
1899
1940
|
- Don't explain your code unless asked
|
|
@@ -1908,6 +1949,16 @@ Never start responses with:
|
|
|
1908
1949
|
|
|
1909
1950
|
Just respond directly to the substance.
|
|
1910
1951
|
|
|
1952
|
+
### No Status Updates
|
|
1953
|
+
Never start responses with casual acknowledgments:
|
|
1954
|
+
- "Hey I'm on it..."
|
|
1955
|
+
- "I'm working on this..."
|
|
1956
|
+
- "Let me start by..."
|
|
1957
|
+
- "I'll get to work on..."
|
|
1958
|
+
- "I'm going to..."
|
|
1959
|
+
|
|
1960
|
+
Just start working. Use todos for progress tracking\u2014that's what they're for.
|
|
1961
|
+
|
|
1911
1962
|
### When User is Wrong
|
|
1912
1963
|
If the user's approach seems problematic:
|
|
1913
1964
|
- Don't blindly implement it
|
|
@@ -2051,13 +2102,15 @@ function createOracleAgent(model = DEFAULT_MODEL2) {
|
|
|
2051
2102
|
var oracleAgent = createOracleAgent();
|
|
2052
2103
|
|
|
2053
2104
|
// src/agents/librarian.ts
|
|
2054
|
-
var
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2105
|
+
var DEFAULT_MODEL3 = "anthropic/claude-sonnet-4-5";
|
|
2106
|
+
function createLibrarianAgent(model = DEFAULT_MODEL3) {
|
|
2107
|
+
return {
|
|
2108
|
+
description: "Specialized codebase understanding agent for multi-repository analysis, searching remote codebases, retrieving official documentation, and finding implementation examples using GitHub CLI, Context7, and Web Search. MUST BE USED when users ask to look up code in remote repositories, explain library internals, or find usage examples in open source.",
|
|
2109
|
+
mode: "subagent",
|
|
2110
|
+
model,
|
|
2111
|
+
temperature: 0.1,
|
|
2112
|
+
tools: { write: false, edit: false, background_task: false },
|
|
2113
|
+
prompt: `# THE LIBRARIAN
|
|
2061
2114
|
|
|
2062
2115
|
You are **THE LIBRARIAN**, a specialized open-source codebase understanding agent.
|
|
2063
2116
|
|
|
@@ -2287,16 +2340,20 @@ grep_app_searchGitHub(query: "useQuery")
|
|
|
2287
2340
|
5. **BE CONCISE**: Facts > opinions, evidence > speculation
|
|
2288
2341
|
|
|
2289
2342
|
`
|
|
2290
|
-
};
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
var librarianAgent = createLibrarianAgent();
|
|
2291
2346
|
|
|
2292
2347
|
// src/agents/explore.ts
|
|
2293
|
-
var
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2348
|
+
var DEFAULT_MODEL4 = "opencode/grok-code";
|
|
2349
|
+
function createExploreAgent(model = DEFAULT_MODEL4) {
|
|
2350
|
+
return {
|
|
2351
|
+
description: 'Contextual grep for codebases. Answers "Where is X?", "Which file has Y?", "Find the code that does Z". Fire multiple in parallel for broad searches. Specify thoroughness: "quick" for basic, "medium" for moderate, "very thorough" for comprehensive analysis.',
|
|
2352
|
+
mode: "subagent",
|
|
2353
|
+
model,
|
|
2354
|
+
temperature: 0.1,
|
|
2355
|
+
tools: { write: false, edit: false, background_task: false },
|
|
2356
|
+
prompt: `You are a codebase search specialist. Your job: find files and code, return actionable results.
|
|
2300
2357
|
|
|
2301
2358
|
## Your Mission
|
|
2302
2359
|
|
|
@@ -2385,15 +2442,19 @@ grep_app searches millions of public GitHub repos instantly \u2014 use it for ex
|
|
|
2385
2442
|
3. **Cross-validate with local tools** (grep, ast_grep_search, LSP) before trusting results
|
|
2386
2443
|
|
|
2387
2444
|
Flood with parallel calls. Trust only cross-validated results.`
|
|
2388
|
-
};
|
|
2445
|
+
};
|
|
2446
|
+
}
|
|
2447
|
+
var exploreAgent = createExploreAgent();
|
|
2389
2448
|
|
|
2390
2449
|
// src/agents/frontend-ui-ux-engineer.ts
|
|
2391
|
-
var
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2450
|
+
var DEFAULT_MODEL5 = "google/gemini-3-pro-preview";
|
|
2451
|
+
function createFrontendUiUxEngineerAgent(model = DEFAULT_MODEL5) {
|
|
2452
|
+
return {
|
|
2453
|
+
description: "A designer-turned-developer who crafts stunning UI/UX even without design mockups. Code may be a bit messy, but the visual output is always fire.",
|
|
2454
|
+
mode: "subagent",
|
|
2455
|
+
model,
|
|
2456
|
+
tools: { background_task: false },
|
|
2457
|
+
prompt: `# Role: Designer-Turned-Developer
|
|
2397
2458
|
|
|
2398
2459
|
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.
|
|
2399
2460
|
|
|
@@ -2466,15 +2527,19 @@ Match implementation complexity to aesthetic vision:
|
|
|
2466
2527
|
- **Minimalist** \u2192 Restraint, precision, careful spacing and typography
|
|
2467
2528
|
|
|
2468
2529
|
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.`
|
|
2469
|
-
};
|
|
2530
|
+
};
|
|
2531
|
+
}
|
|
2532
|
+
var frontendUiUxEngineerAgent = createFrontendUiUxEngineerAgent();
|
|
2470
2533
|
|
|
2471
2534
|
// src/agents/document-writer.ts
|
|
2472
|
-
var
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2535
|
+
var DEFAULT_MODEL6 = "google/gemini-3-flash-preview";
|
|
2536
|
+
function createDocumentWriterAgent(model = DEFAULT_MODEL6) {
|
|
2537
|
+
return {
|
|
2538
|
+
description: "A technical writer who crafts clear, comprehensive documentation. Specializes in README files, API docs, architecture docs, and user guides. MUST BE USED when executing documentation tasks from ai-todo list plans.",
|
|
2539
|
+
mode: "subagent",
|
|
2540
|
+
model,
|
|
2541
|
+
tools: { background_task: false },
|
|
2542
|
+
prompt: `<role>
|
|
2478
2543
|
You are a TECHNICAL WRITER with deep engineering background who transforms complex codebases into crystal-clear documentation. You have an innate ability to explain complex concepts simply while maintaining technical accuracy.
|
|
2479
2544
|
|
|
2480
2545
|
You approach every documentation task with both a developer's understanding and a reader's empathy. Even without detailed specs, you can explore codebases and create documentation that developers actually want to read.
|
|
@@ -2668,16 +2733,20 @@ STOP HERE - DO NOT CONTINUE TO NEXT TASK
|
|
|
2668
2733
|
|
|
2669
2734
|
You are a technical writer who creates documentation that developers actually want to read.
|
|
2670
2735
|
</guide>`
|
|
2671
|
-
};
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2738
|
+
var documentWriterAgent = createDocumentWriterAgent();
|
|
2672
2739
|
|
|
2673
2740
|
// src/agents/multimodal-looker.ts
|
|
2674
|
-
var
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2741
|
+
var DEFAULT_MODEL7 = "google/gemini-3-flash";
|
|
2742
|
+
function createMultimodalLookerAgent(model = DEFAULT_MODEL7) {
|
|
2743
|
+
return {
|
|
2744
|
+
description: "Analyze media files (PDFs, images, diagrams) that require interpretation beyond raw text. Extracts specific information or summaries from documents, describes visual content. Use when you need analyzed/extracted data rather than literal file contents.",
|
|
2745
|
+
mode: "subagent",
|
|
2746
|
+
model,
|
|
2747
|
+
temperature: 0.1,
|
|
2748
|
+
tools: { write: false, edit: false, bash: false, background_task: false },
|
|
2749
|
+
prompt: `You interpret media files that cannot be read as plain text.
|
|
2681
2750
|
|
|
2682
2751
|
Your job: examine the attached file and extract ONLY what was requested.
|
|
2683
2752
|
|
|
@@ -2709,7 +2778,9 @@ Response rules:
|
|
|
2709
2778
|
- Be thorough on the goal, concise on everything else
|
|
2710
2779
|
|
|
2711
2780
|
Your output goes straight to the main agent for continued work.`
|
|
2712
|
-
};
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2783
|
+
var multimodalLookerAgent = createMultimodalLookerAgent();
|
|
2713
2784
|
// src/shared/frontmatter.ts
|
|
2714
2785
|
function parseFrontmatter(content) {
|
|
2715
2786
|
const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
|
|
@@ -3198,6 +3269,18 @@ function getUserConfigDir() {
|
|
|
3198
3269
|
}
|
|
3199
3270
|
return process.env.XDG_CONFIG_HOME || path2.join(os2.homedir(), ".config");
|
|
3200
3271
|
}
|
|
3272
|
+
// src/shared/data-path.ts
|
|
3273
|
+
import * as path3 from "path";
|
|
3274
|
+
import * as os3 from "os";
|
|
3275
|
+
function getDataDir() {
|
|
3276
|
+
if (process.platform === "win32") {
|
|
3277
|
+
return process.env.LOCALAPPDATA ?? path3.join(os3.homedir(), "AppData", "Local");
|
|
3278
|
+
}
|
|
3279
|
+
return process.env.XDG_DATA_HOME ?? path3.join(os3.homedir(), ".local", "share");
|
|
3280
|
+
}
|
|
3281
|
+
function getOpenCodeStorageDir() {
|
|
3282
|
+
return path3.join(getDataDir(), "opencode", "storage");
|
|
3283
|
+
}
|
|
3201
3284
|
// src/shared/config-errors.ts
|
|
3202
3285
|
var configLoadErrors = [];
|
|
3203
3286
|
function getConfigLoadErrors() {
|
|
@@ -3213,11 +3296,11 @@ function addConfigLoadError(error) {
|
|
|
3213
3296
|
var agentSources = {
|
|
3214
3297
|
Sisyphus: createSisyphusAgent,
|
|
3215
3298
|
oracle: createOracleAgent,
|
|
3216
|
-
librarian:
|
|
3217
|
-
explore:
|
|
3218
|
-
"frontend-ui-ux-engineer":
|
|
3219
|
-
"document-writer":
|
|
3220
|
-
"multimodal-looker":
|
|
3299
|
+
librarian: createLibrarianAgent,
|
|
3300
|
+
explore: createExploreAgent,
|
|
3301
|
+
"frontend-ui-ux-engineer": createFrontendUiUxEngineerAgent,
|
|
3302
|
+
"document-writer": createDocumentWriterAgent,
|
|
3303
|
+
"multimodal-looker": createMultimodalLookerAgent
|
|
3221
3304
|
};
|
|
3222
3305
|
function isFactory(source) {
|
|
3223
3306
|
return typeof source === "function";
|
|
@@ -3254,7 +3337,13 @@ Here is some useful information about the environment you are running in:
|
|
|
3254
3337
|
</env>`;
|
|
3255
3338
|
}
|
|
3256
3339
|
function mergeAgentConfig(base, override) {
|
|
3257
|
-
|
|
3340
|
+
const { prompt_append, ...rest } = override;
|
|
3341
|
+
const merged = deepMerge(base, rest);
|
|
3342
|
+
if (prompt_append && merged.prompt) {
|
|
3343
|
+
merged.prompt = merged.prompt + `
|
|
3344
|
+
` + prompt_append;
|
|
3345
|
+
}
|
|
3346
|
+
return merged;
|
|
3258
3347
|
}
|
|
3259
3348
|
function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory, systemDefaultModel) {
|
|
3260
3349
|
const result = {};
|
|
@@ -3279,7 +3368,7 @@ function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory
|
|
|
3279
3368
|
}
|
|
3280
3369
|
// src/hooks/todo-continuation-enforcer.ts
|
|
3281
3370
|
import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
|
|
3282
|
-
import { join as
|
|
3371
|
+
import { join as join7 } from "path";
|
|
3283
3372
|
|
|
3284
3373
|
// src/features/claude-code-session-state/state.ts
|
|
3285
3374
|
var subagentSessions = new Set;
|
|
@@ -3292,15 +3381,13 @@ function getMainSessionID() {
|
|
|
3292
3381
|
}
|
|
3293
3382
|
// src/features/hook-message-injector/injector.ts
|
|
3294
3383
|
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync2, readdirSync, writeFileSync } from "fs";
|
|
3295
|
-
import { join as
|
|
3384
|
+
import { join as join6 } from "path";
|
|
3296
3385
|
|
|
3297
3386
|
// src/features/hook-message-injector/constants.ts
|
|
3298
|
-
import { join as
|
|
3299
|
-
|
|
3300
|
-
var
|
|
3301
|
-
var
|
|
3302
|
-
var MESSAGE_STORAGE = join4(OPENCODE_STORAGE, "message");
|
|
3303
|
-
var PART_STORAGE = join4(OPENCODE_STORAGE, "part");
|
|
3387
|
+
import { join as join5 } from "path";
|
|
3388
|
+
var OPENCODE_STORAGE = getOpenCodeStorageDir();
|
|
3389
|
+
var MESSAGE_STORAGE = join5(OPENCODE_STORAGE, "message");
|
|
3390
|
+
var PART_STORAGE = join5(OPENCODE_STORAGE, "part");
|
|
3304
3391
|
|
|
3305
3392
|
// src/features/hook-message-injector/injector.ts
|
|
3306
3393
|
function findNearestMessageWithFields(messageDir) {
|
|
@@ -3308,7 +3395,7 @@ function findNearestMessageWithFields(messageDir) {
|
|
|
3308
3395
|
const files = readdirSync(messageDir).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
3309
3396
|
for (const file of files) {
|
|
3310
3397
|
try {
|
|
3311
|
-
const content = readFileSync2(
|
|
3398
|
+
const content = readFileSync2(join6(messageDir, file), "utf-8");
|
|
3312
3399
|
const msg = JSON.parse(content);
|
|
3313
3400
|
if (msg.agent && msg.model?.providerID && msg.model?.modelID) {
|
|
3314
3401
|
return msg;
|
|
@@ -3336,12 +3423,12 @@ function getOrCreateMessageDir(sessionID) {
|
|
|
3336
3423
|
if (!existsSync4(MESSAGE_STORAGE)) {
|
|
3337
3424
|
mkdirSync(MESSAGE_STORAGE, { recursive: true });
|
|
3338
3425
|
}
|
|
3339
|
-
const directPath =
|
|
3426
|
+
const directPath = join6(MESSAGE_STORAGE, sessionID);
|
|
3340
3427
|
if (existsSync4(directPath)) {
|
|
3341
3428
|
return directPath;
|
|
3342
3429
|
}
|
|
3343
3430
|
for (const dir of readdirSync(MESSAGE_STORAGE)) {
|
|
3344
|
-
const sessionPath =
|
|
3431
|
+
const sessionPath = join6(MESSAGE_STORAGE, dir, sessionID);
|
|
3345
3432
|
if (existsSync4(sessionPath)) {
|
|
3346
3433
|
return sessionPath;
|
|
3347
3434
|
}
|
|
@@ -3395,17 +3482,34 @@ function injectHookMessage(sessionID, hookContent, originalMessage) {
|
|
|
3395
3482
|
sessionID
|
|
3396
3483
|
};
|
|
3397
3484
|
try {
|
|
3398
|
-
writeFileSync(
|
|
3399
|
-
const partDir =
|
|
3485
|
+
writeFileSync(join6(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
|
|
3486
|
+
const partDir = join6(PART_STORAGE, messageID);
|
|
3400
3487
|
if (!existsSync4(partDir)) {
|
|
3401
3488
|
mkdirSync(partDir, { recursive: true });
|
|
3402
3489
|
}
|
|
3403
|
-
writeFileSync(
|
|
3490
|
+
writeFileSync(join6(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
|
|
3404
3491
|
return true;
|
|
3405
3492
|
} catch {
|
|
3406
3493
|
return false;
|
|
3407
3494
|
}
|
|
3408
3495
|
}
|
|
3496
|
+
// src/hooks/non-interactive-env/detector.ts
|
|
3497
|
+
function isNonInteractive() {
|
|
3498
|
+
if (process.env.CI === "true" || process.env.CI === "1") {
|
|
3499
|
+
return true;
|
|
3500
|
+
}
|
|
3501
|
+
if (process.env.OPENCODE_RUN === "true" || process.env.OPENCODE_NON_INTERACTIVE === "true") {
|
|
3502
|
+
return true;
|
|
3503
|
+
}
|
|
3504
|
+
if (process.env.GITHUB_ACTIONS === "true") {
|
|
3505
|
+
return true;
|
|
3506
|
+
}
|
|
3507
|
+
if (process.stdout.isTTY !== true) {
|
|
3508
|
+
return true;
|
|
3509
|
+
}
|
|
3510
|
+
return false;
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3409
3513
|
// src/hooks/todo-continuation-enforcer.ts
|
|
3410
3514
|
var HOOK_NAME = "todo-continuation-enforcer";
|
|
3411
3515
|
var CONTINUATION_PROMPT = `[SYSTEM REMINDER - TODO CONTINUATION]
|
|
@@ -3418,11 +3522,11 @@ Incomplete tasks remain in your todo list. Continue working on the next pending
|
|
|
3418
3522
|
function getMessageDir(sessionID) {
|
|
3419
3523
|
if (!existsSync5(MESSAGE_STORAGE))
|
|
3420
3524
|
return null;
|
|
3421
|
-
const directPath =
|
|
3525
|
+
const directPath = join7(MESSAGE_STORAGE, sessionID);
|
|
3422
3526
|
if (existsSync5(directPath))
|
|
3423
3527
|
return directPath;
|
|
3424
3528
|
for (const dir of readdirSync2(MESSAGE_STORAGE)) {
|
|
3425
|
-
const sessionPath =
|
|
3529
|
+
const sessionPath = join7(MESSAGE_STORAGE, dir, sessionID);
|
|
3426
3530
|
if (existsSync5(sessionPath))
|
|
3427
3531
|
return sessionPath;
|
|
3428
3532
|
}
|
|
@@ -3450,12 +3554,14 @@ function detectInterrupt(error) {
|
|
|
3450
3554
|
}
|
|
3451
3555
|
var COUNTDOWN_SECONDS = 2;
|
|
3452
3556
|
var TOAST_DURATION_MS = 900;
|
|
3453
|
-
function createTodoContinuationEnforcer(ctx) {
|
|
3557
|
+
function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
3558
|
+
const { backgroundManager } = options;
|
|
3454
3559
|
const remindedSessions = new Set;
|
|
3455
3560
|
const interruptedSessions = new Set;
|
|
3456
3561
|
const errorSessions = new Set;
|
|
3457
3562
|
const recoveringSessions = new Set;
|
|
3458
3563
|
const pendingCountdowns = new Map;
|
|
3564
|
+
const preemptivelyInjectedSessions = new Set;
|
|
3459
3565
|
const markRecovering = (sessionID) => {
|
|
3460
3566
|
recoveringSessions.add(sessionID);
|
|
3461
3567
|
};
|
|
@@ -3623,10 +3729,59 @@ function createTodoContinuationEnforcer(ctx) {
|
|
|
3623
3729
|
log(`[${HOOK_NAME}] Cancelled countdown on user message`, { sessionID });
|
|
3624
3730
|
}
|
|
3625
3731
|
remindedSessions.delete(sessionID);
|
|
3732
|
+
preemptivelyInjectedSessions.delete(sessionID);
|
|
3626
3733
|
}
|
|
3627
3734
|
if (sessionID && role === "assistant" && finish) {
|
|
3628
3735
|
remindedSessions.delete(sessionID);
|
|
3629
|
-
|
|
3736
|
+
preemptivelyInjectedSessions.delete(sessionID);
|
|
3737
|
+
log(`[${HOOK_NAME}] Cleared reminded/preemptive state on assistant finish`, { sessionID });
|
|
3738
|
+
const isTerminalFinish = finish && !["tool-calls", "unknown"].includes(finish);
|
|
3739
|
+
if (isTerminalFinish && isNonInteractive()) {
|
|
3740
|
+
log(`[${HOOK_NAME}] Terminal finish in non-interactive mode`, { sessionID, finish });
|
|
3741
|
+
const mainSessionID2 = getMainSessionID();
|
|
3742
|
+
if (mainSessionID2 && sessionID !== mainSessionID2) {
|
|
3743
|
+
log(`[${HOOK_NAME}] Skipped preemptive: not main session`, { sessionID, mainSessionID: mainSessionID2 });
|
|
3744
|
+
return;
|
|
3745
|
+
}
|
|
3746
|
+
if (preemptivelyInjectedSessions.has(sessionID)) {
|
|
3747
|
+
log(`[${HOOK_NAME}] Skipped preemptive: already injected`, { sessionID });
|
|
3748
|
+
return;
|
|
3749
|
+
}
|
|
3750
|
+
if (recoveringSessions.has(sessionID) || errorSessions.has(sessionID) || interruptedSessions.has(sessionID)) {
|
|
3751
|
+
log(`[${HOOK_NAME}] Skipped preemptive: session in error/recovery state`, { sessionID });
|
|
3752
|
+
return;
|
|
3753
|
+
}
|
|
3754
|
+
const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((t) => t.status === "running") : false;
|
|
3755
|
+
let hasIncompleteTodos = false;
|
|
3756
|
+
try {
|
|
3757
|
+
const response = await ctx.client.session.todo({ path: { id: sessionID } });
|
|
3758
|
+
const todos = response.data ?? response;
|
|
3759
|
+
hasIncompleteTodos = todos?.some((t) => t.status !== "completed" && t.status !== "cancelled") ?? false;
|
|
3760
|
+
} catch {
|
|
3761
|
+
log(`[${HOOK_NAME}] Failed to fetch todos for preemptive check`, { sessionID });
|
|
3762
|
+
}
|
|
3763
|
+
if (hasRunningBgTasks || hasIncompleteTodos) {
|
|
3764
|
+
log(`[${HOOK_NAME}] Preemptive injection needed`, { sessionID, hasRunningBgTasks, hasIncompleteTodos });
|
|
3765
|
+
preemptivelyInjectedSessions.add(sessionID);
|
|
3766
|
+
try {
|
|
3767
|
+
const messageDir = getMessageDir(sessionID);
|
|
3768
|
+
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
3769
|
+
const prompt = hasRunningBgTasks ? "[SYSTEM] Background tasks are still running. Wait for their completion before proceeding." : CONTINUATION_PROMPT;
|
|
3770
|
+
await ctx.client.session.prompt({
|
|
3771
|
+
path: { id: sessionID },
|
|
3772
|
+
body: {
|
|
3773
|
+
agent: prevMessage?.agent,
|
|
3774
|
+
parts: [{ type: "text", text: prompt }]
|
|
3775
|
+
},
|
|
3776
|
+
query: { directory: ctx.directory }
|
|
3777
|
+
});
|
|
3778
|
+
log(`[${HOOK_NAME}] Preemptive injection successful`, { sessionID });
|
|
3779
|
+
} catch (err) {
|
|
3780
|
+
log(`[${HOOK_NAME}] Preemptive injection failed`, { sessionID, error: String(err) });
|
|
3781
|
+
preemptivelyInjectedSessions.delete(sessionID);
|
|
3782
|
+
}
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3630
3785
|
}
|
|
3631
3786
|
}
|
|
3632
3787
|
if (event.type === "session.deleted") {
|
|
@@ -3636,6 +3791,7 @@ function createTodoContinuationEnforcer(ctx) {
|
|
|
3636
3791
|
interruptedSessions.delete(sessionInfo.id);
|
|
3637
3792
|
errorSessions.delete(sessionInfo.id);
|
|
3638
3793
|
recoveringSessions.delete(sessionInfo.id);
|
|
3794
|
+
preemptivelyInjectedSessions.delete(sessionInfo.id);
|
|
3639
3795
|
const countdown = pendingCountdowns.get(sessionInfo.id);
|
|
3640
3796
|
if (countdown) {
|
|
3641
3797
|
clearInterval(countdown.intervalId);
|
|
@@ -3897,6 +4053,9 @@ function createSessionNotification(ctx, config = {}) {
|
|
|
3897
4053
|
return;
|
|
3898
4054
|
if (subagentSessions.has(sessionID))
|
|
3899
4055
|
return;
|
|
4056
|
+
const mainSessionID2 = getMainSessionID();
|
|
4057
|
+
if (mainSessionID2 && sessionID !== mainSessionID2)
|
|
4058
|
+
return;
|
|
3900
4059
|
if (notifiedSessions.has(sessionID))
|
|
3901
4060
|
return;
|
|
3902
4061
|
if (pendingTimers.has(sessionID))
|
|
@@ -3942,24 +4101,24 @@ function createSessionNotification(ctx, config = {}) {
|
|
|
3942
4101
|
}
|
|
3943
4102
|
// src/hooks/session-recovery/storage.ts
|
|
3944
4103
|
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
3945
|
-
import { join as
|
|
4104
|
+
import { join as join9 } from "path";
|
|
3946
4105
|
|
|
3947
4106
|
// src/hooks/session-recovery/constants.ts
|
|
3948
|
-
import { join as
|
|
4107
|
+
import { join as join8 } from "path";
|
|
3949
4108
|
|
|
3950
4109
|
// node_modules/xdg-basedir/index.js
|
|
3951
|
-
import
|
|
3952
|
-
import
|
|
3953
|
-
var homeDirectory =
|
|
4110
|
+
import os4 from "os";
|
|
4111
|
+
import path4 from "path";
|
|
4112
|
+
var homeDirectory = os4.homedir();
|
|
3954
4113
|
var { env } = process;
|
|
3955
|
-
var
|
|
3956
|
-
var xdgConfig = env.XDG_CONFIG_HOME || (homeDirectory ?
|
|
3957
|
-
var xdgState = env.XDG_STATE_HOME || (homeDirectory ?
|
|
3958
|
-
var xdgCache = env.XDG_CACHE_HOME || (homeDirectory ?
|
|
4114
|
+
var xdgData = env.XDG_DATA_HOME || (homeDirectory ? path4.join(homeDirectory, ".local", "share") : undefined);
|
|
4115
|
+
var xdgConfig = env.XDG_CONFIG_HOME || (homeDirectory ? path4.join(homeDirectory, ".config") : undefined);
|
|
4116
|
+
var xdgState = env.XDG_STATE_HOME || (homeDirectory ? path4.join(homeDirectory, ".local", "state") : undefined);
|
|
4117
|
+
var xdgCache = env.XDG_CACHE_HOME || (homeDirectory ? path4.join(homeDirectory, ".cache") : undefined);
|
|
3959
4118
|
var xdgRuntime = env.XDG_RUNTIME_DIR || undefined;
|
|
3960
4119
|
var xdgDataDirectories = (env.XDG_DATA_DIRS || "/usr/local/share/:/usr/share/").split(":");
|
|
3961
|
-
if (
|
|
3962
|
-
xdgDataDirectories.unshift(
|
|
4120
|
+
if (xdgData) {
|
|
4121
|
+
xdgDataDirectories.unshift(xdgData);
|
|
3963
4122
|
}
|
|
3964
4123
|
var xdgConfigDirectories = (env.XDG_CONFIG_DIRS || "/etc/xdg").split(":");
|
|
3965
4124
|
if (xdgConfig) {
|
|
@@ -3967,9 +4126,9 @@ if (xdgConfig) {
|
|
|
3967
4126
|
}
|
|
3968
4127
|
|
|
3969
4128
|
// src/hooks/session-recovery/constants.ts
|
|
3970
|
-
var OPENCODE_STORAGE2 =
|
|
3971
|
-
var MESSAGE_STORAGE2 =
|
|
3972
|
-
var PART_STORAGE2 =
|
|
4129
|
+
var OPENCODE_STORAGE2 = join8(xdgData ?? "", "opencode", "storage");
|
|
4130
|
+
var MESSAGE_STORAGE2 = join8(OPENCODE_STORAGE2, "message");
|
|
4131
|
+
var PART_STORAGE2 = join8(OPENCODE_STORAGE2, "part");
|
|
3973
4132
|
var THINKING_TYPES = new Set(["thinking", "redacted_thinking", "reasoning"]);
|
|
3974
4133
|
var META_TYPES = new Set(["step-start", "step-finish"]);
|
|
3975
4134
|
var CONTENT_TYPES = new Set(["text", "tool", "tool_use", "tool_result"]);
|
|
@@ -3983,12 +4142,12 @@ function generatePartId2() {
|
|
|
3983
4142
|
function getMessageDir2(sessionID) {
|
|
3984
4143
|
if (!existsSync6(MESSAGE_STORAGE2))
|
|
3985
4144
|
return "";
|
|
3986
|
-
const directPath =
|
|
4145
|
+
const directPath = join9(MESSAGE_STORAGE2, sessionID);
|
|
3987
4146
|
if (existsSync6(directPath)) {
|
|
3988
4147
|
return directPath;
|
|
3989
4148
|
}
|
|
3990
4149
|
for (const dir of readdirSync3(MESSAGE_STORAGE2)) {
|
|
3991
|
-
const sessionPath =
|
|
4150
|
+
const sessionPath = join9(MESSAGE_STORAGE2, dir, sessionID);
|
|
3992
4151
|
if (existsSync6(sessionPath)) {
|
|
3993
4152
|
return sessionPath;
|
|
3994
4153
|
}
|
|
@@ -4004,7 +4163,7 @@ function readMessages(sessionID) {
|
|
|
4004
4163
|
if (!file.endsWith(".json"))
|
|
4005
4164
|
continue;
|
|
4006
4165
|
try {
|
|
4007
|
-
const content = readFileSync3(
|
|
4166
|
+
const content = readFileSync3(join9(messageDir, file), "utf-8");
|
|
4008
4167
|
messages.push(JSON.parse(content));
|
|
4009
4168
|
} catch {
|
|
4010
4169
|
continue;
|
|
@@ -4019,7 +4178,7 @@ function readMessages(sessionID) {
|
|
|
4019
4178
|
});
|
|
4020
4179
|
}
|
|
4021
4180
|
function readParts(messageID) {
|
|
4022
|
-
const partDir =
|
|
4181
|
+
const partDir = join9(PART_STORAGE2, messageID);
|
|
4023
4182
|
if (!existsSync6(partDir))
|
|
4024
4183
|
return [];
|
|
4025
4184
|
const parts = [];
|
|
@@ -4027,7 +4186,7 @@ function readParts(messageID) {
|
|
|
4027
4186
|
if (!file.endsWith(".json"))
|
|
4028
4187
|
continue;
|
|
4029
4188
|
try {
|
|
4030
|
-
const content = readFileSync3(
|
|
4189
|
+
const content = readFileSync3(join9(partDir, file), "utf-8");
|
|
4031
4190
|
parts.push(JSON.parse(content));
|
|
4032
4191
|
} catch {
|
|
4033
4192
|
continue;
|
|
@@ -4057,7 +4216,7 @@ function messageHasContent(messageID) {
|
|
|
4057
4216
|
return parts.some(hasContent);
|
|
4058
4217
|
}
|
|
4059
4218
|
function injectTextPart(sessionID, messageID, text) {
|
|
4060
|
-
const partDir =
|
|
4219
|
+
const partDir = join9(PART_STORAGE2, messageID);
|
|
4061
4220
|
if (!existsSync6(partDir)) {
|
|
4062
4221
|
mkdirSync2(partDir, { recursive: true });
|
|
4063
4222
|
}
|
|
@@ -4071,7 +4230,7 @@ function injectTextPart(sessionID, messageID, text) {
|
|
|
4071
4230
|
synthetic: true
|
|
4072
4231
|
};
|
|
4073
4232
|
try {
|
|
4074
|
-
writeFileSync2(
|
|
4233
|
+
writeFileSync2(join9(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
|
|
4075
4234
|
return true;
|
|
4076
4235
|
} catch {
|
|
4077
4236
|
return false;
|
|
@@ -4134,7 +4293,7 @@ function findMessagesWithOrphanThinking(sessionID) {
|
|
|
4134
4293
|
return result;
|
|
4135
4294
|
}
|
|
4136
4295
|
function prependThinkingPart(sessionID, messageID) {
|
|
4137
|
-
const partDir =
|
|
4296
|
+
const partDir = join9(PART_STORAGE2, messageID);
|
|
4138
4297
|
if (!existsSync6(partDir)) {
|
|
4139
4298
|
mkdirSync2(partDir, { recursive: true });
|
|
4140
4299
|
}
|
|
@@ -4148,14 +4307,14 @@ function prependThinkingPart(sessionID, messageID) {
|
|
|
4148
4307
|
synthetic: true
|
|
4149
4308
|
};
|
|
4150
4309
|
try {
|
|
4151
|
-
writeFileSync2(
|
|
4310
|
+
writeFileSync2(join9(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
|
|
4152
4311
|
return true;
|
|
4153
4312
|
} catch {
|
|
4154
4313
|
return false;
|
|
4155
4314
|
}
|
|
4156
4315
|
}
|
|
4157
4316
|
function stripThinkingParts(messageID) {
|
|
4158
|
-
const partDir =
|
|
4317
|
+
const partDir = join9(PART_STORAGE2, messageID);
|
|
4159
4318
|
if (!existsSync6(partDir))
|
|
4160
4319
|
return false;
|
|
4161
4320
|
let anyRemoved = false;
|
|
@@ -4163,7 +4322,7 @@ function stripThinkingParts(messageID) {
|
|
|
4163
4322
|
if (!file.endsWith(".json"))
|
|
4164
4323
|
continue;
|
|
4165
4324
|
try {
|
|
4166
|
-
const filePath =
|
|
4325
|
+
const filePath = join9(partDir, file);
|
|
4167
4326
|
const content = readFileSync3(filePath, "utf-8");
|
|
4168
4327
|
const part = JSON.parse(content);
|
|
4169
4328
|
if (THINKING_TYPES.has(part.type)) {
|
|
@@ -4177,7 +4336,7 @@ function stripThinkingParts(messageID) {
|
|
|
4177
4336
|
return anyRemoved;
|
|
4178
4337
|
}
|
|
4179
4338
|
function replaceEmptyTextParts(messageID, replacementText) {
|
|
4180
|
-
const partDir =
|
|
4339
|
+
const partDir = join9(PART_STORAGE2, messageID);
|
|
4181
4340
|
if (!existsSync6(partDir))
|
|
4182
4341
|
return false;
|
|
4183
4342
|
let anyReplaced = false;
|
|
@@ -4185,7 +4344,7 @@ function replaceEmptyTextParts(messageID, replacementText) {
|
|
|
4185
4344
|
if (!file.endsWith(".json"))
|
|
4186
4345
|
continue;
|
|
4187
4346
|
try {
|
|
4188
|
-
const filePath =
|
|
4347
|
+
const filePath = join9(partDir, file);
|
|
4189
4348
|
const content = readFileSync3(filePath, "utf-8");
|
|
4190
4349
|
const part = JSON.parse(content);
|
|
4191
4350
|
if (part.type === "text") {
|
|
@@ -4462,7 +4621,7 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
4462
4621
|
// src/hooks/comment-checker/cli.ts
|
|
4463
4622
|
var {spawn: spawn3 } = globalThis.Bun;
|
|
4464
4623
|
import { createRequire as createRequire2 } from "module";
|
|
4465
|
-
import { dirname, join as
|
|
4624
|
+
import { dirname, join as join11 } from "path";
|
|
4466
4625
|
import { existsSync as existsSync8 } from "fs";
|
|
4467
4626
|
import * as fs3 from "fs";
|
|
4468
4627
|
import { tmpdir as tmpdir3 } from "os";
|
|
@@ -4470,11 +4629,11 @@ import { tmpdir as tmpdir3 } from "os";
|
|
|
4470
4629
|
// src/hooks/comment-checker/downloader.ts
|
|
4471
4630
|
var {spawn: spawn2 } = globalThis.Bun;
|
|
4472
4631
|
import { existsSync as existsSync7, mkdirSync as mkdirSync3, chmodSync, unlinkSync as unlinkSync2, appendFileSync as appendFileSync2 } from "fs";
|
|
4473
|
-
import { join as
|
|
4632
|
+
import { join as join10 } from "path";
|
|
4474
4633
|
import { homedir as homedir4, tmpdir as tmpdir2 } from "os";
|
|
4475
4634
|
import { createRequire } from "module";
|
|
4476
4635
|
var DEBUG = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4477
|
-
var DEBUG_FILE =
|
|
4636
|
+
var DEBUG_FILE = join10(tmpdir2(), "comment-checker-debug.log");
|
|
4478
4637
|
function debugLog(...args) {
|
|
4479
4638
|
if (DEBUG) {
|
|
4480
4639
|
const msg = `[${new Date().toISOString()}] [comment-checker:downloader] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
@@ -4492,14 +4651,14 @@ var PLATFORM_MAP = {
|
|
|
4492
4651
|
};
|
|
4493
4652
|
function getCacheDir() {
|
|
4494
4653
|
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
4495
|
-
const base = xdgCache2 ||
|
|
4496
|
-
return
|
|
4654
|
+
const base = xdgCache2 || join10(homedir4(), ".cache");
|
|
4655
|
+
return join10(base, "oh-my-opencode", "bin");
|
|
4497
4656
|
}
|
|
4498
4657
|
function getBinaryName() {
|
|
4499
4658
|
return process.platform === "win32" ? "comment-checker.exe" : "comment-checker";
|
|
4500
4659
|
}
|
|
4501
4660
|
function getCachedBinaryPath() {
|
|
4502
|
-
const binaryPath =
|
|
4661
|
+
const binaryPath = join10(getCacheDir(), getBinaryName());
|
|
4503
4662
|
return existsSync7(binaryPath) ? binaryPath : null;
|
|
4504
4663
|
}
|
|
4505
4664
|
function getPackageVersion() {
|
|
@@ -4547,14 +4706,14 @@ async function downloadCommentChecker() {
|
|
|
4547
4706
|
}
|
|
4548
4707
|
const cacheDir = getCacheDir();
|
|
4549
4708
|
const binaryName = getBinaryName();
|
|
4550
|
-
const binaryPath =
|
|
4709
|
+
const binaryPath = join10(cacheDir, binaryName);
|
|
4551
4710
|
if (existsSync7(binaryPath)) {
|
|
4552
4711
|
debugLog("Binary already cached at:", binaryPath);
|
|
4553
4712
|
return binaryPath;
|
|
4554
4713
|
}
|
|
4555
4714
|
const version = getPackageVersion();
|
|
4556
|
-
const { os:
|
|
4557
|
-
const assetName = `comment-checker_v${version}_${
|
|
4715
|
+
const { os: os5, arch, ext } = platformInfo;
|
|
4716
|
+
const assetName = `comment-checker_v${version}_${os5}_${arch}.${ext}`;
|
|
4558
4717
|
const downloadUrl = `https://github.com/${REPO}/releases/download/v${version}/${assetName}`;
|
|
4559
4718
|
debugLog(`Downloading from: ${downloadUrl}`);
|
|
4560
4719
|
console.log(`[oh-my-opencode] Downloading comment-checker binary...`);
|
|
@@ -4566,7 +4725,7 @@ async function downloadCommentChecker() {
|
|
|
4566
4725
|
if (!response.ok) {
|
|
4567
4726
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
4568
4727
|
}
|
|
4569
|
-
const archivePath =
|
|
4728
|
+
const archivePath = join10(cacheDir, assetName);
|
|
4570
4729
|
const arrayBuffer = await response.arrayBuffer();
|
|
4571
4730
|
await Bun.write(archivePath, arrayBuffer);
|
|
4572
4731
|
debugLog(`Downloaded archive to: ${archivePath}`);
|
|
@@ -4602,7 +4761,7 @@ async function ensureCommentCheckerBinary() {
|
|
|
4602
4761
|
|
|
4603
4762
|
// src/hooks/comment-checker/cli.ts
|
|
4604
4763
|
var DEBUG2 = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4605
|
-
var DEBUG_FILE2 =
|
|
4764
|
+
var DEBUG_FILE2 = join11(tmpdir3(), "comment-checker-debug.log");
|
|
4606
4765
|
function debugLog2(...args) {
|
|
4607
4766
|
if (DEBUG2) {
|
|
4608
4767
|
const msg = `[${new Date().toISOString()}] [comment-checker:cli] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
@@ -4619,7 +4778,7 @@ function findCommentCheckerPathSync() {
|
|
|
4619
4778
|
const require2 = createRequire2(import.meta.url);
|
|
4620
4779
|
const cliPkgPath = require2.resolve("@code-yeongyu/comment-checker/package.json");
|
|
4621
4780
|
const cliDir = dirname(cliPkgPath);
|
|
4622
|
-
const binaryPath =
|
|
4781
|
+
const binaryPath = join11(cliDir, "bin", binaryName);
|
|
4623
4782
|
if (existsSync8(binaryPath)) {
|
|
4624
4783
|
debugLog2("found binary in main package:", binaryPath);
|
|
4625
4784
|
return binaryPath;
|
|
@@ -4666,8 +4825,8 @@ async function getCommentCheckerPath() {
|
|
|
4666
4825
|
function startBackgroundInit() {
|
|
4667
4826
|
if (!initPromise) {
|
|
4668
4827
|
initPromise = getCommentCheckerPath();
|
|
4669
|
-
initPromise.then((
|
|
4670
|
-
debugLog2("background init complete:",
|
|
4828
|
+
initPromise.then((path5) => {
|
|
4829
|
+
debugLog2("background init complete:", path5 || "no binary");
|
|
4671
4830
|
}).catch((err) => {
|
|
4672
4831
|
debugLog2("background init error:", err);
|
|
4673
4832
|
});
|
|
@@ -4716,9 +4875,9 @@ async function runCommentChecker(input, cliPath) {
|
|
|
4716
4875
|
import * as fs4 from "fs";
|
|
4717
4876
|
import { existsSync as existsSync9 } from "fs";
|
|
4718
4877
|
import { tmpdir as tmpdir4 } from "os";
|
|
4719
|
-
import { join as
|
|
4878
|
+
import { join as join12 } from "path";
|
|
4720
4879
|
var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4721
|
-
var DEBUG_FILE3 =
|
|
4880
|
+
var DEBUG_FILE3 = join12(tmpdir4(), "comment-checker-debug.log");
|
|
4722
4881
|
function debugLog3(...args) {
|
|
4723
4882
|
if (DEBUG3) {
|
|
4724
4883
|
const msg = `[${new Date().toISOString()}] [comment-checker:hook] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
@@ -4742,8 +4901,8 @@ function createCommentCheckerHooks() {
|
|
|
4742
4901
|
debugLog3("createCommentCheckerHooks called");
|
|
4743
4902
|
startBackgroundInit();
|
|
4744
4903
|
cliPathPromise = getCommentCheckerPath();
|
|
4745
|
-
cliPathPromise.then((
|
|
4746
|
-
debugLog3("CLI path resolved:",
|
|
4904
|
+
cliPathPromise.then((path5) => {
|
|
4905
|
+
debugLog3("CLI path resolved:", path5 || "disabled (no binary)");
|
|
4747
4906
|
}).catch((err) => {
|
|
4748
4907
|
debugLog3("CLI path resolution error:", err);
|
|
4749
4908
|
});
|
|
@@ -4850,7 +5009,7 @@ var TRUNCATABLE_TOOLS = [
|
|
|
4850
5009
|
];
|
|
4851
5010
|
function createToolOutputTruncatorHook(ctx, options) {
|
|
4852
5011
|
const truncator = createDynamicTruncator(ctx);
|
|
4853
|
-
const truncateAll = options?.experimental?.truncate_all_tool_outputs ??
|
|
5012
|
+
const truncateAll = options?.experimental?.truncate_all_tool_outputs ?? true;
|
|
4854
5013
|
const toolExecuteAfter = async (input, output) => {
|
|
4855
5014
|
if (!truncateAll && !TRUNCATABLE_TOOLS.includes(input.tool))
|
|
4856
5015
|
return;
|
|
@@ -4867,7 +5026,7 @@ function createToolOutputTruncatorHook(ctx, options) {
|
|
|
4867
5026
|
}
|
|
4868
5027
|
// src/hooks/directory-agents-injector/index.ts
|
|
4869
5028
|
import { existsSync as existsSync11, readFileSync as readFileSync5 } from "fs";
|
|
4870
|
-
import { dirname as dirname2, join as
|
|
5029
|
+
import { dirname as dirname2, join as join15, resolve as resolve2 } from "path";
|
|
4871
5030
|
|
|
4872
5031
|
// src/hooks/directory-agents-injector/storage.ts
|
|
4873
5032
|
import {
|
|
@@ -4877,17 +5036,17 @@ import {
|
|
|
4877
5036
|
writeFileSync as writeFileSync3,
|
|
4878
5037
|
unlinkSync as unlinkSync3
|
|
4879
5038
|
} from "fs";
|
|
4880
|
-
import { join as
|
|
5039
|
+
import { join as join14 } from "path";
|
|
4881
5040
|
|
|
4882
5041
|
// src/hooks/directory-agents-injector/constants.ts
|
|
4883
|
-
import { join as
|
|
4884
|
-
var OPENCODE_STORAGE3 =
|
|
4885
|
-
var AGENTS_INJECTOR_STORAGE =
|
|
5042
|
+
import { join as join13 } from "path";
|
|
5043
|
+
var OPENCODE_STORAGE3 = join13(xdgData ?? "", "opencode", "storage");
|
|
5044
|
+
var AGENTS_INJECTOR_STORAGE = join13(OPENCODE_STORAGE3, "directory-agents");
|
|
4886
5045
|
var AGENTS_FILENAME = "AGENTS.md";
|
|
4887
5046
|
|
|
4888
5047
|
// src/hooks/directory-agents-injector/storage.ts
|
|
4889
5048
|
function getStoragePath(sessionID) {
|
|
4890
|
-
return
|
|
5049
|
+
return join14(AGENTS_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
4891
5050
|
}
|
|
4892
5051
|
function loadInjectedPaths(sessionID) {
|
|
4893
5052
|
const filePath = getStoragePath(sessionID);
|
|
@@ -4929,18 +5088,18 @@ function createDirectoryAgentsInjectorHook(ctx) {
|
|
|
4929
5088
|
}
|
|
4930
5089
|
return sessionCaches.get(sessionID);
|
|
4931
5090
|
}
|
|
4932
|
-
function resolveFilePath2(
|
|
4933
|
-
if (!
|
|
5091
|
+
function resolveFilePath2(path5) {
|
|
5092
|
+
if (!path5)
|
|
4934
5093
|
return null;
|
|
4935
|
-
if (
|
|
4936
|
-
return
|
|
4937
|
-
return resolve2(ctx.directory,
|
|
5094
|
+
if (path5.startsWith("/"))
|
|
5095
|
+
return path5;
|
|
5096
|
+
return resolve2(ctx.directory, path5);
|
|
4938
5097
|
}
|
|
4939
5098
|
function findAgentsMdUp(startDir) {
|
|
4940
5099
|
const found = [];
|
|
4941
5100
|
let current = startDir;
|
|
4942
5101
|
while (true) {
|
|
4943
|
-
const agentsPath =
|
|
5102
|
+
const agentsPath = join15(current, AGENTS_FILENAME);
|
|
4944
5103
|
if (existsSync11(agentsPath)) {
|
|
4945
5104
|
found.push(agentsPath);
|
|
4946
5105
|
}
|
|
@@ -5034,7 +5193,7 @@ ${content}`;
|
|
|
5034
5193
|
}
|
|
5035
5194
|
// src/hooks/directory-readme-injector/index.ts
|
|
5036
5195
|
import { existsSync as existsSync13, readFileSync as readFileSync7 } from "fs";
|
|
5037
|
-
import { dirname as dirname3, join as
|
|
5196
|
+
import { dirname as dirname3, join as join18, resolve as resolve3 } from "path";
|
|
5038
5197
|
|
|
5039
5198
|
// src/hooks/directory-readme-injector/storage.ts
|
|
5040
5199
|
import {
|
|
@@ -5044,17 +5203,17 @@ import {
|
|
|
5044
5203
|
writeFileSync as writeFileSync4,
|
|
5045
5204
|
unlinkSync as unlinkSync4
|
|
5046
5205
|
} from "fs";
|
|
5047
|
-
import { join as
|
|
5206
|
+
import { join as join17 } from "path";
|
|
5048
5207
|
|
|
5049
5208
|
// src/hooks/directory-readme-injector/constants.ts
|
|
5050
|
-
import { join as
|
|
5051
|
-
var OPENCODE_STORAGE4 =
|
|
5052
|
-
var README_INJECTOR_STORAGE =
|
|
5209
|
+
import { join as join16 } from "path";
|
|
5210
|
+
var OPENCODE_STORAGE4 = join16(xdgData ?? "", "opencode", "storage");
|
|
5211
|
+
var README_INJECTOR_STORAGE = join16(OPENCODE_STORAGE4, "directory-readme");
|
|
5053
5212
|
var README_FILENAME = "README.md";
|
|
5054
5213
|
|
|
5055
5214
|
// src/hooks/directory-readme-injector/storage.ts
|
|
5056
5215
|
function getStoragePath2(sessionID) {
|
|
5057
|
-
return
|
|
5216
|
+
return join17(README_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
5058
5217
|
}
|
|
5059
5218
|
function loadInjectedPaths2(sessionID) {
|
|
5060
5219
|
const filePath = getStoragePath2(sessionID);
|
|
@@ -5096,18 +5255,18 @@ function createDirectoryReadmeInjectorHook(ctx) {
|
|
|
5096
5255
|
}
|
|
5097
5256
|
return sessionCaches.get(sessionID);
|
|
5098
5257
|
}
|
|
5099
|
-
function resolveFilePath2(
|
|
5100
|
-
if (!
|
|
5258
|
+
function resolveFilePath2(path5) {
|
|
5259
|
+
if (!path5)
|
|
5101
5260
|
return null;
|
|
5102
|
-
if (
|
|
5103
|
-
return
|
|
5104
|
-
return resolve3(ctx.directory,
|
|
5261
|
+
if (path5.startsWith("/"))
|
|
5262
|
+
return path5;
|
|
5263
|
+
return resolve3(ctx.directory, path5);
|
|
5105
5264
|
}
|
|
5106
5265
|
function findReadmeMdUp(startDir) {
|
|
5107
5266
|
const found = [];
|
|
5108
5267
|
let current = startDir;
|
|
5109
5268
|
while (true) {
|
|
5110
|
-
const readmePath =
|
|
5269
|
+
const readmePath = join18(current, README_FILENAME);
|
|
5111
5270
|
if (existsSync13(readmePath)) {
|
|
5112
5271
|
found.push(readmePath);
|
|
5113
5272
|
}
|
|
@@ -5405,26 +5564,26 @@ var TRUNCATE_CONFIG = {
|
|
|
5405
5564
|
// src/hooks/anthropic-auto-compact/storage.ts
|
|
5406
5565
|
import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
5407
5566
|
import { homedir as homedir5 } from "os";
|
|
5408
|
-
import { join as
|
|
5409
|
-
var OPENCODE_STORAGE5 =
|
|
5567
|
+
import { join as join19 } from "path";
|
|
5568
|
+
var OPENCODE_STORAGE5 = join19(xdgData ?? "", "opencode", "storage");
|
|
5410
5569
|
if (process.platform === "darwin" && !existsSync14(OPENCODE_STORAGE5)) {
|
|
5411
|
-
const localShare =
|
|
5570
|
+
const localShare = join19(homedir5(), ".local", "share", "opencode", "storage");
|
|
5412
5571
|
if (existsSync14(localShare)) {
|
|
5413
5572
|
OPENCODE_STORAGE5 = localShare;
|
|
5414
5573
|
}
|
|
5415
5574
|
}
|
|
5416
|
-
var MESSAGE_STORAGE3 =
|
|
5417
|
-
var PART_STORAGE3 =
|
|
5575
|
+
var MESSAGE_STORAGE3 = join19(OPENCODE_STORAGE5, "message");
|
|
5576
|
+
var PART_STORAGE3 = join19(OPENCODE_STORAGE5, "part");
|
|
5418
5577
|
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.]";
|
|
5419
5578
|
function getMessageDir3(sessionID) {
|
|
5420
5579
|
if (!existsSync14(MESSAGE_STORAGE3))
|
|
5421
5580
|
return "";
|
|
5422
|
-
const directPath =
|
|
5581
|
+
const directPath = join19(MESSAGE_STORAGE3, sessionID);
|
|
5423
5582
|
if (existsSync14(directPath)) {
|
|
5424
5583
|
return directPath;
|
|
5425
5584
|
}
|
|
5426
5585
|
for (const dir of readdirSync4(MESSAGE_STORAGE3)) {
|
|
5427
|
-
const sessionPath =
|
|
5586
|
+
const sessionPath = join19(MESSAGE_STORAGE3, dir, sessionID);
|
|
5428
5587
|
if (existsSync14(sessionPath)) {
|
|
5429
5588
|
return sessionPath;
|
|
5430
5589
|
}
|
|
@@ -5448,14 +5607,14 @@ function findToolResultsBySize(sessionID) {
|
|
|
5448
5607
|
const messageIds = getMessageIds(sessionID);
|
|
5449
5608
|
const results = [];
|
|
5450
5609
|
for (const messageID of messageIds) {
|
|
5451
|
-
const partDir =
|
|
5610
|
+
const partDir = join19(PART_STORAGE3, messageID);
|
|
5452
5611
|
if (!existsSync14(partDir))
|
|
5453
5612
|
continue;
|
|
5454
5613
|
for (const file of readdirSync4(partDir)) {
|
|
5455
5614
|
if (!file.endsWith(".json"))
|
|
5456
5615
|
continue;
|
|
5457
5616
|
try {
|
|
5458
|
-
const partPath =
|
|
5617
|
+
const partPath = join19(partDir, file);
|
|
5459
5618
|
const content = readFileSync8(partPath, "utf-8");
|
|
5460
5619
|
const part = JSON.parse(content);
|
|
5461
5620
|
if (part.type === "tool" && part.state?.output && !part.truncated) {
|
|
@@ -6063,7 +6222,7 @@ function createAnthropicAutoCompactHook(ctx, options) {
|
|
|
6063
6222
|
}
|
|
6064
6223
|
// src/hooks/preemptive-compaction/index.ts
|
|
6065
6224
|
import { existsSync as existsSync15, readdirSync as readdirSync5 } from "fs";
|
|
6066
|
-
import { join as
|
|
6225
|
+
import { join as join20 } from "path";
|
|
6067
6226
|
|
|
6068
6227
|
// src/hooks/preemptive-compaction/constants.ts
|
|
6069
6228
|
var DEFAULT_THRESHOLD = 0.85;
|
|
@@ -6079,11 +6238,11 @@ function isSupportedModel(modelID) {
|
|
|
6079
6238
|
function getMessageDir4(sessionID) {
|
|
6080
6239
|
if (!existsSync15(MESSAGE_STORAGE))
|
|
6081
6240
|
return null;
|
|
6082
|
-
const directPath =
|
|
6241
|
+
const directPath = join20(MESSAGE_STORAGE, sessionID);
|
|
6083
6242
|
if (existsSync15(directPath))
|
|
6084
6243
|
return directPath;
|
|
6085
6244
|
for (const dir of readdirSync5(MESSAGE_STORAGE)) {
|
|
6086
|
-
const sessionPath =
|
|
6245
|
+
const sessionPath = join20(MESSAGE_STORAGE, dir, sessionID);
|
|
6087
6246
|
if (existsSync15(sessionPath))
|
|
6088
6247
|
return sessionPath;
|
|
6089
6248
|
}
|
|
@@ -6569,7 +6728,7 @@ function createThinkModeHook() {
|
|
|
6569
6728
|
}
|
|
6570
6729
|
// src/hooks/claude-code-hooks/config.ts
|
|
6571
6730
|
import { homedir as homedir6 } from "os";
|
|
6572
|
-
import { join as
|
|
6731
|
+
import { join as join21 } from "path";
|
|
6573
6732
|
import { existsSync as existsSync16 } from "fs";
|
|
6574
6733
|
function normalizeHookMatcher(raw) {
|
|
6575
6734
|
return {
|
|
@@ -6583,7 +6742,8 @@ function normalizeHooksConfig(raw) {
|
|
|
6583
6742
|
"PreToolUse",
|
|
6584
6743
|
"PostToolUse",
|
|
6585
6744
|
"UserPromptSubmit",
|
|
6586
|
-
"Stop"
|
|
6745
|
+
"Stop",
|
|
6746
|
+
"PreCompact"
|
|
6587
6747
|
];
|
|
6588
6748
|
for (const eventType of eventTypes) {
|
|
6589
6749
|
if (raw[eventType]) {
|
|
@@ -6595,9 +6755,9 @@ function normalizeHooksConfig(raw) {
|
|
|
6595
6755
|
function getClaudeSettingsPaths(customPath) {
|
|
6596
6756
|
const home = homedir6();
|
|
6597
6757
|
const paths = [
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6758
|
+
join21(home, ".claude", "settings.json"),
|
|
6759
|
+
join21(process.cwd(), ".claude", "settings.json"),
|
|
6760
|
+
join21(process.cwd(), ".claude", "settings.local.json")
|
|
6601
6761
|
];
|
|
6602
6762
|
if (customPath && existsSync16(customPath)) {
|
|
6603
6763
|
paths.unshift(customPath);
|
|
@@ -6610,7 +6770,8 @@ function mergeHooksConfig(base, override) {
|
|
|
6610
6770
|
"PreToolUse",
|
|
6611
6771
|
"PostToolUse",
|
|
6612
6772
|
"UserPromptSubmit",
|
|
6613
|
-
"Stop"
|
|
6773
|
+
"Stop",
|
|
6774
|
+
"PreCompact"
|
|
6614
6775
|
];
|
|
6615
6776
|
for (const eventType of eventTypes) {
|
|
6616
6777
|
if (override[eventType]) {
|
|
@@ -6642,20 +6803,20 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
6642
6803
|
// src/hooks/claude-code-hooks/config-loader.ts
|
|
6643
6804
|
import { existsSync as existsSync17 } from "fs";
|
|
6644
6805
|
import { homedir as homedir7 } from "os";
|
|
6645
|
-
import { join as
|
|
6646
|
-
var USER_CONFIG_PATH =
|
|
6806
|
+
import { join as join22 } from "path";
|
|
6807
|
+
var USER_CONFIG_PATH = join22(homedir7(), ".config", "opencode", "opencode-cc-plugin.json");
|
|
6647
6808
|
function getProjectConfigPath() {
|
|
6648
|
-
return
|
|
6809
|
+
return join22(process.cwd(), ".opencode", "opencode-cc-plugin.json");
|
|
6649
6810
|
}
|
|
6650
|
-
async function loadConfigFromPath(
|
|
6651
|
-
if (!existsSync17(
|
|
6811
|
+
async function loadConfigFromPath(path5) {
|
|
6812
|
+
if (!existsSync17(path5)) {
|
|
6652
6813
|
return null;
|
|
6653
6814
|
}
|
|
6654
6815
|
try {
|
|
6655
|
-
const content = await Bun.file(
|
|
6816
|
+
const content = await Bun.file(path5).text();
|
|
6656
6817
|
return JSON.parse(content);
|
|
6657
6818
|
} catch (error) {
|
|
6658
|
-
log("Failed to load config", { path:
|
|
6819
|
+
log("Failed to load config", { path: path5, error });
|
|
6659
6820
|
return null;
|
|
6660
6821
|
}
|
|
6661
6822
|
}
|
|
@@ -6668,7 +6829,8 @@ function mergeDisabledHooks(base, override) {
|
|
|
6668
6829
|
Stop: override.Stop ?? base.Stop,
|
|
6669
6830
|
PreToolUse: override.PreToolUse ?? base.PreToolUse,
|
|
6670
6831
|
PostToolUse: override.PostToolUse ?? base.PostToolUse,
|
|
6671
|
-
UserPromptSubmit: override.UserPromptSubmit ?? base.UserPromptSubmit
|
|
6832
|
+
UserPromptSubmit: override.UserPromptSubmit ?? base.UserPromptSubmit,
|
|
6833
|
+
PreCompact: override.PreCompact ?? base.PreCompact
|
|
6672
6834
|
};
|
|
6673
6835
|
}
|
|
6674
6836
|
async function loadPluginExtendedConfig() {
|
|
@@ -6827,13 +6989,13 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
|
|
|
6827
6989
|
}
|
|
6828
6990
|
|
|
6829
6991
|
// src/hooks/claude-code-hooks/transcript.ts
|
|
6830
|
-
import { join as
|
|
6992
|
+
import { join as join23 } from "path";
|
|
6831
6993
|
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync18, writeFileSync as writeFileSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
6832
6994
|
import { homedir as homedir8, tmpdir as tmpdir5 } from "os";
|
|
6833
6995
|
import { randomUUID } from "crypto";
|
|
6834
|
-
var TRANSCRIPT_DIR =
|
|
6996
|
+
var TRANSCRIPT_DIR = join23(homedir8(), ".claude", "transcripts");
|
|
6835
6997
|
function getTranscriptPath(sessionId) {
|
|
6836
|
-
return
|
|
6998
|
+
return join23(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
|
|
6837
6999
|
}
|
|
6838
7000
|
function ensureTranscriptDir() {
|
|
6839
7001
|
if (!existsSync18(TRANSCRIPT_DIR)) {
|
|
@@ -6842,10 +7004,10 @@ function ensureTranscriptDir() {
|
|
|
6842
7004
|
}
|
|
6843
7005
|
function appendTranscriptEntry(sessionId, entry) {
|
|
6844
7006
|
ensureTranscriptDir();
|
|
6845
|
-
const
|
|
7007
|
+
const path5 = getTranscriptPath(sessionId);
|
|
6846
7008
|
const line = JSON.stringify(entry) + `
|
|
6847
7009
|
`;
|
|
6848
|
-
appendFileSync5(
|
|
7010
|
+
appendFileSync5(path5, line);
|
|
6849
7011
|
}
|
|
6850
7012
|
function recordToolUse(sessionId, toolName, toolInput) {
|
|
6851
7013
|
appendTranscriptEntry(sessionId, {
|
|
@@ -6923,7 +7085,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
6923
7085
|
}
|
|
6924
7086
|
};
|
|
6925
7087
|
entries.push(JSON.stringify(currentEntry));
|
|
6926
|
-
const tempPath =
|
|
7088
|
+
const tempPath = join23(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
6927
7089
|
writeFileSync6(tempPath, entries.join(`
|
|
6928
7090
|
`) + `
|
|
6929
7091
|
`);
|
|
@@ -6943,7 +7105,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
6943
7105
|
]
|
|
6944
7106
|
}
|
|
6945
7107
|
};
|
|
6946
|
-
const tempPath =
|
|
7108
|
+
const tempPath = join23(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
6947
7109
|
writeFileSync6(tempPath, JSON.stringify(currentEntry) + `
|
|
6948
7110
|
`);
|
|
6949
7111
|
return tempPath;
|
|
@@ -6952,11 +7114,11 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
6952
7114
|
}
|
|
6953
7115
|
}
|
|
6954
7116
|
}
|
|
6955
|
-
function deleteTempTranscript(
|
|
6956
|
-
if (!
|
|
7117
|
+
function deleteTempTranscript(path5) {
|
|
7118
|
+
if (!path5)
|
|
6957
7119
|
return;
|
|
6958
7120
|
try {
|
|
6959
|
-
unlinkSync5(
|
|
7121
|
+
unlinkSync5(path5);
|
|
6960
7122
|
} catch {}
|
|
6961
7123
|
}
|
|
6962
7124
|
|
|
@@ -7155,11 +7317,11 @@ ${USER_PROMPT_SUBMIT_TAG_CLOSE}`);
|
|
|
7155
7317
|
}
|
|
7156
7318
|
|
|
7157
7319
|
// src/hooks/claude-code-hooks/todo.ts
|
|
7158
|
-
import { join as
|
|
7320
|
+
import { join as join24 } from "path";
|
|
7159
7321
|
import { homedir as homedir9 } from "os";
|
|
7160
|
-
var TODO_DIR =
|
|
7322
|
+
var TODO_DIR = join24(homedir9(), ".claude", "todos");
|
|
7161
7323
|
function getTodoPath(sessionId) {
|
|
7162
|
-
return
|
|
7324
|
+
return join24(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
|
|
7163
7325
|
}
|
|
7164
7326
|
|
|
7165
7327
|
// src/hooks/claude-code-hooks/stop.ts
|
|
@@ -7224,6 +7386,74 @@ async function executeStopHooks(ctx, config, extendedConfig) {
|
|
|
7224
7386
|
return { block: false };
|
|
7225
7387
|
}
|
|
7226
7388
|
|
|
7389
|
+
// src/hooks/claude-code-hooks/pre-compact.ts
|
|
7390
|
+
async function executePreCompactHooks(ctx, config, extendedConfig) {
|
|
7391
|
+
if (!config) {
|
|
7392
|
+
return { context: [] };
|
|
7393
|
+
}
|
|
7394
|
+
const matchers = findMatchingHooks(config, "PreCompact", "*");
|
|
7395
|
+
if (matchers.length === 0) {
|
|
7396
|
+
return { context: [] };
|
|
7397
|
+
}
|
|
7398
|
+
const stdinData = {
|
|
7399
|
+
session_id: ctx.sessionId,
|
|
7400
|
+
cwd: ctx.cwd,
|
|
7401
|
+
hook_event_name: "PreCompact",
|
|
7402
|
+
hook_source: "opencode-plugin"
|
|
7403
|
+
};
|
|
7404
|
+
const startTime = Date.now();
|
|
7405
|
+
let firstHookName;
|
|
7406
|
+
const collectedContext = [];
|
|
7407
|
+
for (const matcher of matchers) {
|
|
7408
|
+
for (const hook of matcher.hooks) {
|
|
7409
|
+
if (hook.type !== "command")
|
|
7410
|
+
continue;
|
|
7411
|
+
if (isHookCommandDisabled("PreCompact", hook.command, extendedConfig ?? null)) {
|
|
7412
|
+
log("PreCompact hook command skipped (disabled by config)", { command: hook.command });
|
|
7413
|
+
continue;
|
|
7414
|
+
}
|
|
7415
|
+
const hookName = hook.command.split("/").pop() || hook.command;
|
|
7416
|
+
if (!firstHookName)
|
|
7417
|
+
firstHookName = hookName;
|
|
7418
|
+
const result = await executeHookCommand(hook.command, JSON.stringify(stdinData), ctx.cwd, { forceZsh: DEFAULT_CONFIG.forceZsh, zshPath: DEFAULT_CONFIG.zshPath });
|
|
7419
|
+
if (result.exitCode === 2) {
|
|
7420
|
+
log("PreCompact hook blocked", { hookName, stderr: result.stderr });
|
|
7421
|
+
continue;
|
|
7422
|
+
}
|
|
7423
|
+
if (result.stdout) {
|
|
7424
|
+
try {
|
|
7425
|
+
const output = JSON.parse(result.stdout);
|
|
7426
|
+
if (output.hookSpecificOutput?.additionalContext) {
|
|
7427
|
+
collectedContext.push(...output.hookSpecificOutput.additionalContext);
|
|
7428
|
+
} else if (output.context) {
|
|
7429
|
+
collectedContext.push(...output.context);
|
|
7430
|
+
}
|
|
7431
|
+
if (output.continue === false) {
|
|
7432
|
+
return {
|
|
7433
|
+
context: collectedContext,
|
|
7434
|
+
elapsedMs: Date.now() - startTime,
|
|
7435
|
+
hookName: firstHookName,
|
|
7436
|
+
continue: output.continue,
|
|
7437
|
+
stopReason: output.stopReason,
|
|
7438
|
+
suppressOutput: output.suppressOutput,
|
|
7439
|
+
systemMessage: output.systemMessage
|
|
7440
|
+
};
|
|
7441
|
+
}
|
|
7442
|
+
} catch {
|
|
7443
|
+
if (result.stdout.trim()) {
|
|
7444
|
+
collectedContext.push(result.stdout.trim());
|
|
7445
|
+
}
|
|
7446
|
+
}
|
|
7447
|
+
}
|
|
7448
|
+
}
|
|
7449
|
+
}
|
|
7450
|
+
return {
|
|
7451
|
+
context: collectedContext,
|
|
7452
|
+
elapsedMs: Date.now() - startTime,
|
|
7453
|
+
hookName: firstHookName
|
|
7454
|
+
};
|
|
7455
|
+
}
|
|
7456
|
+
|
|
7227
7457
|
// src/hooks/claude-code-hooks/tool-input-cache.ts
|
|
7228
7458
|
var cache = new Map;
|
|
7229
7459
|
var CACHE_TTL = 60000;
|
|
@@ -7256,6 +7486,27 @@ var sessionErrorState = new Map;
|
|
|
7256
7486
|
var sessionInterruptState = new Map;
|
|
7257
7487
|
function createClaudeCodeHooksHook(ctx, config = {}) {
|
|
7258
7488
|
return {
|
|
7489
|
+
"experimental.session.compacting": async (input, output) => {
|
|
7490
|
+
if (isHookDisabled(config, "PreCompact")) {
|
|
7491
|
+
return;
|
|
7492
|
+
}
|
|
7493
|
+
const claudeConfig = await loadClaudeHooksConfig();
|
|
7494
|
+
const extendedConfig = await loadPluginExtendedConfig();
|
|
7495
|
+
const preCompactCtx = {
|
|
7496
|
+
sessionId: input.sessionID,
|
|
7497
|
+
cwd: ctx.directory
|
|
7498
|
+
};
|
|
7499
|
+
const result = await executePreCompactHooks(preCompactCtx, claudeConfig, extendedConfig);
|
|
7500
|
+
if (result.context.length > 0) {
|
|
7501
|
+
log("PreCompact hooks injecting context", {
|
|
7502
|
+
sessionID: input.sessionID,
|
|
7503
|
+
contextCount: result.context.length,
|
|
7504
|
+
hookName: result.hookName,
|
|
7505
|
+
elapsedMs: result.elapsedMs
|
|
7506
|
+
});
|
|
7507
|
+
output.context.push(...result.context);
|
|
7508
|
+
}
|
|
7509
|
+
},
|
|
7259
7510
|
"chat.message": async (input, output) => {
|
|
7260
7511
|
const interruptState = sessionInterruptState.get(input.sessionID);
|
|
7261
7512
|
if (interruptState?.interrupted) {
|
|
@@ -7505,12 +7756,12 @@ import {
|
|
|
7505
7756
|
realpathSync,
|
|
7506
7757
|
statSync as statSync2
|
|
7507
7758
|
} from "fs";
|
|
7508
|
-
import { dirname as dirname4, join as
|
|
7759
|
+
import { dirname as dirname4, join as join26, relative } from "path";
|
|
7509
7760
|
|
|
7510
7761
|
// src/hooks/rules-injector/constants.ts
|
|
7511
|
-
import { join as
|
|
7512
|
-
var OPENCODE_STORAGE6 =
|
|
7513
|
-
var RULES_INJECTOR_STORAGE =
|
|
7762
|
+
import { join as join25 } from "path";
|
|
7763
|
+
var OPENCODE_STORAGE6 = join25(xdgData ?? "", "opencode", "storage");
|
|
7764
|
+
var RULES_INJECTOR_STORAGE = join25(OPENCODE_STORAGE6, "rules-injector");
|
|
7514
7765
|
var PROJECT_MARKERS = [
|
|
7515
7766
|
".git",
|
|
7516
7767
|
"pyproject.toml",
|
|
@@ -7537,7 +7788,7 @@ function findProjectRoot(startPath) {
|
|
|
7537
7788
|
}
|
|
7538
7789
|
while (true) {
|
|
7539
7790
|
for (const marker of PROJECT_MARKERS) {
|
|
7540
|
-
const markerPath =
|
|
7791
|
+
const markerPath = join26(current, marker);
|
|
7541
7792
|
if (existsSync19(markerPath)) {
|
|
7542
7793
|
return current;
|
|
7543
7794
|
}
|
|
@@ -7555,7 +7806,7 @@ function findRuleFilesRecursive(dir, results) {
|
|
|
7555
7806
|
try {
|
|
7556
7807
|
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
7557
7808
|
for (const entry of entries) {
|
|
7558
|
-
const fullPath =
|
|
7809
|
+
const fullPath = join26(dir, entry.name);
|
|
7559
7810
|
if (entry.isDirectory()) {
|
|
7560
7811
|
findRuleFilesRecursive(fullPath, results);
|
|
7561
7812
|
} else if (entry.isFile()) {
|
|
@@ -7581,7 +7832,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
7581
7832
|
let distance = 0;
|
|
7582
7833
|
while (true) {
|
|
7583
7834
|
for (const [parent, subdir] of PROJECT_RULE_SUBDIRS) {
|
|
7584
|
-
const ruleDir =
|
|
7835
|
+
const ruleDir = join26(currentDir, parent, subdir);
|
|
7585
7836
|
const files = [];
|
|
7586
7837
|
findRuleFilesRecursive(ruleDir, files);
|
|
7587
7838
|
for (const filePath of files) {
|
|
@@ -7605,7 +7856,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
7605
7856
|
currentDir = parentDir;
|
|
7606
7857
|
distance++;
|
|
7607
7858
|
}
|
|
7608
|
-
const userRuleDir =
|
|
7859
|
+
const userRuleDir = join26(homeDir, USER_RULE_DIR);
|
|
7609
7860
|
const userFiles = [];
|
|
7610
7861
|
findRuleFilesRecursive(userRuleDir, userFiles);
|
|
7611
7862
|
for (const filePath of userFiles) {
|
|
@@ -7800,9 +8051,9 @@ import {
|
|
|
7800
8051
|
writeFileSync as writeFileSync7,
|
|
7801
8052
|
unlinkSync as unlinkSync6
|
|
7802
8053
|
} from "fs";
|
|
7803
|
-
import { join as
|
|
8054
|
+
import { join as join27 } from "path";
|
|
7804
8055
|
function getStoragePath3(sessionID) {
|
|
7805
|
-
return
|
|
8056
|
+
return join27(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
7806
8057
|
}
|
|
7807
8058
|
function loadInjectedRules(sessionID) {
|
|
7808
8059
|
const filePath = getStoragePath3(sessionID);
|
|
@@ -7849,12 +8100,12 @@ function createRulesInjectorHook(ctx) {
|
|
|
7849
8100
|
}
|
|
7850
8101
|
return sessionCaches.get(sessionID);
|
|
7851
8102
|
}
|
|
7852
|
-
function resolveFilePath2(
|
|
7853
|
-
if (!
|
|
8103
|
+
function resolveFilePath2(path5) {
|
|
8104
|
+
if (!path5)
|
|
7854
8105
|
return null;
|
|
7855
|
-
if (
|
|
7856
|
-
return
|
|
7857
|
-
return resolve4(ctx.directory,
|
|
8106
|
+
if (path5.startsWith("/"))
|
|
8107
|
+
return path5;
|
|
8108
|
+
return resolve4(ctx.directory, path5);
|
|
7858
8109
|
}
|
|
7859
8110
|
function processFilePathForInjection(filePath, sessionID, output) {
|
|
7860
8111
|
const resolved = resolveFilePath2(filePath);
|
|
@@ -7973,33 +8224,33 @@ function createBackgroundNotificationHook(manager) {
|
|
|
7973
8224
|
}
|
|
7974
8225
|
// src/hooks/auto-update-checker/checker.ts
|
|
7975
8226
|
import * as fs5 from "fs";
|
|
7976
|
-
import * as
|
|
8227
|
+
import * as path6 from "path";
|
|
7977
8228
|
import { fileURLToPath } from "url";
|
|
7978
8229
|
|
|
7979
8230
|
// src/hooks/auto-update-checker/constants.ts
|
|
7980
|
-
import * as
|
|
7981
|
-
import * as
|
|
8231
|
+
import * as path5 from "path";
|
|
8232
|
+
import * as os5 from "os";
|
|
7982
8233
|
var PACKAGE_NAME = "oh-my-opencode";
|
|
7983
8234
|
var NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
|
|
7984
8235
|
var NPM_FETCH_TIMEOUT = 5000;
|
|
7985
8236
|
function getCacheDir2() {
|
|
7986
8237
|
if (process.platform === "win32") {
|
|
7987
|
-
return
|
|
8238
|
+
return path5.join(process.env.LOCALAPPDATA ?? os5.homedir(), "opencode");
|
|
7988
8239
|
}
|
|
7989
|
-
return
|
|
8240
|
+
return path5.join(os5.homedir(), ".cache", "opencode");
|
|
7990
8241
|
}
|
|
7991
8242
|
var CACHE_DIR = getCacheDir2();
|
|
7992
|
-
var VERSION_FILE =
|
|
7993
|
-
var INSTALLED_PACKAGE_JSON =
|
|
8243
|
+
var VERSION_FILE = path5.join(CACHE_DIR, "version");
|
|
8244
|
+
var INSTALLED_PACKAGE_JSON = path5.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json");
|
|
7994
8245
|
function getUserConfigDir2() {
|
|
7995
8246
|
if (process.platform === "win32") {
|
|
7996
|
-
return process.env.APPDATA ??
|
|
8247
|
+
return process.env.APPDATA ?? path5.join(os5.homedir(), "AppData", "Roaming");
|
|
7997
8248
|
}
|
|
7998
|
-
return process.env.XDG_CONFIG_HOME ??
|
|
8249
|
+
return process.env.XDG_CONFIG_HOME ?? path5.join(os5.homedir(), ".config");
|
|
7999
8250
|
}
|
|
8000
8251
|
var USER_CONFIG_DIR = getUserConfigDir2();
|
|
8001
|
-
var USER_OPENCODE_CONFIG =
|
|
8002
|
-
var USER_OPENCODE_CONFIG_JSONC =
|
|
8252
|
+
var USER_OPENCODE_CONFIG = path5.join(USER_CONFIG_DIR, "opencode", "opencode.json");
|
|
8253
|
+
var USER_OPENCODE_CONFIG_JSONC = path5.join(USER_CONFIG_DIR, "opencode", "opencode.jsonc");
|
|
8003
8254
|
|
|
8004
8255
|
// src/hooks/auto-update-checker/checker.ts
|
|
8005
8256
|
function stripJsonComments(json) {
|
|
@@ -8007,8 +8258,8 @@ function stripJsonComments(json) {
|
|
|
8007
8258
|
}
|
|
8008
8259
|
function getConfigPaths(directory) {
|
|
8009
8260
|
return [
|
|
8010
|
-
|
|
8011
|
-
|
|
8261
|
+
path6.join(directory, ".opencode", "opencode.json"),
|
|
8262
|
+
path6.join(directory, ".opencode", "opencode.jsonc"),
|
|
8012
8263
|
USER_OPENCODE_CONFIG,
|
|
8013
8264
|
USER_OPENCODE_CONFIG_JSONC
|
|
8014
8265
|
];
|
|
@@ -8039,9 +8290,9 @@ function getLocalDevPath(directory) {
|
|
|
8039
8290
|
function findPackageJsonUp(startPath) {
|
|
8040
8291
|
try {
|
|
8041
8292
|
const stat = fs5.statSync(startPath);
|
|
8042
|
-
let dir = stat.isDirectory() ? startPath :
|
|
8293
|
+
let dir = stat.isDirectory() ? startPath : path6.dirname(startPath);
|
|
8043
8294
|
for (let i = 0;i < 10; i++) {
|
|
8044
|
-
const pkgPath =
|
|
8295
|
+
const pkgPath = path6.join(dir, "package.json");
|
|
8045
8296
|
if (fs5.existsSync(pkgPath)) {
|
|
8046
8297
|
try {
|
|
8047
8298
|
const content = fs5.readFileSync(pkgPath, "utf-8");
|
|
@@ -8050,7 +8301,7 @@ function findPackageJsonUp(startPath) {
|
|
|
8050
8301
|
return pkgPath;
|
|
8051
8302
|
} catch {}
|
|
8052
8303
|
}
|
|
8053
|
-
const parent =
|
|
8304
|
+
const parent = path6.dirname(dir);
|
|
8054
8305
|
if (parent === dir)
|
|
8055
8306
|
break;
|
|
8056
8307
|
dir = parent;
|
|
@@ -8107,7 +8358,7 @@ function getCachedVersion() {
|
|
|
8107
8358
|
}
|
|
8108
8359
|
} catch {}
|
|
8109
8360
|
try {
|
|
8110
|
-
const currentDir =
|
|
8361
|
+
const currentDir = path6.dirname(fileURLToPath(import.meta.url));
|
|
8111
8362
|
const pkgPath = findPackageJsonUp(currentDir);
|
|
8112
8363
|
if (pkgPath) {
|
|
8113
8364
|
const content = fs5.readFileSync(pkgPath, "utf-8");
|
|
@@ -8183,12 +8434,12 @@ async function getLatestVersion() {
|
|
|
8183
8434
|
|
|
8184
8435
|
// src/hooks/auto-update-checker/cache.ts
|
|
8185
8436
|
import * as fs6 from "fs";
|
|
8186
|
-
import * as
|
|
8437
|
+
import * as path7 from "path";
|
|
8187
8438
|
function stripTrailingCommas(json) {
|
|
8188
8439
|
return json.replace(/,(\s*[}\]])/g, "$1");
|
|
8189
8440
|
}
|
|
8190
8441
|
function removeFromBunLock(packageName) {
|
|
8191
|
-
const lockPath =
|
|
8442
|
+
const lockPath = path7.join(CACHE_DIR, "bun.lock");
|
|
8192
8443
|
if (!fs6.existsSync(lockPath))
|
|
8193
8444
|
return false;
|
|
8194
8445
|
try {
|
|
@@ -8214,8 +8465,8 @@ function removeFromBunLock(packageName) {
|
|
|
8214
8465
|
}
|
|
8215
8466
|
function invalidatePackage(packageName = PACKAGE_NAME) {
|
|
8216
8467
|
try {
|
|
8217
|
-
const pkgDir =
|
|
8218
|
-
const pkgJsonPath =
|
|
8468
|
+
const pkgDir = path7.join(CACHE_DIR, "node_modules", packageName);
|
|
8469
|
+
const pkgJsonPath = path7.join(CACHE_DIR, "package.json");
|
|
8219
8470
|
let packageRemoved = false;
|
|
8220
8471
|
let dependencyRemoved = false;
|
|
8221
8472
|
let lockRemoved = false;
|
|
@@ -8409,12 +8660,12 @@ import {
|
|
|
8409
8660
|
writeFileSync as writeFileSync10,
|
|
8410
8661
|
unlinkSync as unlinkSync7
|
|
8411
8662
|
} from "fs";
|
|
8412
|
-
import { join as
|
|
8663
|
+
import { join as join32 } from "path";
|
|
8413
8664
|
|
|
8414
8665
|
// src/hooks/agent-usage-reminder/constants.ts
|
|
8415
|
-
import { join as
|
|
8416
|
-
var OPENCODE_STORAGE7 =
|
|
8417
|
-
var AGENT_USAGE_REMINDER_STORAGE =
|
|
8666
|
+
import { join as join31 } from "path";
|
|
8667
|
+
var OPENCODE_STORAGE7 = join31(xdgData ?? "", "opencode", "storage");
|
|
8668
|
+
var AGENT_USAGE_REMINDER_STORAGE = join31(OPENCODE_STORAGE7, "agent-usage-reminder");
|
|
8418
8669
|
var TARGET_TOOLS = new Set([
|
|
8419
8670
|
"grep",
|
|
8420
8671
|
"safe_grep",
|
|
@@ -8459,7 +8710,7 @@ ALWAYS prefer: Multiple parallel background_task calls > Direct tool calls
|
|
|
8459
8710
|
|
|
8460
8711
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
8461
8712
|
function getStoragePath4(sessionID) {
|
|
8462
|
-
return
|
|
8713
|
+
return join32(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
|
|
8463
8714
|
}
|
|
8464
8715
|
function loadAgentUsageState(sessionID) {
|
|
8465
8716
|
const filePath = getStoragePath4(sessionID);
|
|
@@ -8781,12 +9032,12 @@ import {
|
|
|
8781
9032
|
writeFileSync as writeFileSync11,
|
|
8782
9033
|
unlinkSync as unlinkSync8
|
|
8783
9034
|
} from "fs";
|
|
8784
|
-
import { join as
|
|
9035
|
+
import { join as join34 } from "path";
|
|
8785
9036
|
|
|
8786
9037
|
// src/hooks/interactive-bash-session/constants.ts
|
|
8787
|
-
import { join as
|
|
8788
|
-
var OPENCODE_STORAGE8 =
|
|
8789
|
-
var INTERACTIVE_BASH_SESSION_STORAGE =
|
|
9038
|
+
import { join as join33 } from "path";
|
|
9039
|
+
var OPENCODE_STORAGE8 = join33(xdgData ?? "", "opencode", "storage");
|
|
9040
|
+
var INTERACTIVE_BASH_SESSION_STORAGE = join33(OPENCODE_STORAGE8, "interactive-bash-session");
|
|
8790
9041
|
var OMO_SESSION_PREFIX = "omo-";
|
|
8791
9042
|
function buildSessionReminderMessage(sessions) {
|
|
8792
9043
|
if (sessions.length === 0)
|
|
@@ -8798,7 +9049,7 @@ function buildSessionReminderMessage(sessions) {
|
|
|
8798
9049
|
|
|
8799
9050
|
// src/hooks/interactive-bash-session/storage.ts
|
|
8800
9051
|
function getStoragePath5(sessionID) {
|
|
8801
|
-
return
|
|
9052
|
+
return join34(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
|
|
8802
9053
|
}
|
|
8803
9054
|
function loadInteractiveBashSessionState(sessionID) {
|
|
8804
9055
|
const filePath = getStoragePath5(sessionID);
|
|
@@ -10776,7 +11027,7 @@ async function createGoogleAntigravityAuthPlugin({
|
|
|
10776
11027
|
// src/features/claude-code-command-loader/loader.ts
|
|
10777
11028
|
import { existsSync as existsSync25, readdirSync as readdirSync7, readFileSync as readFileSync15 } from "fs";
|
|
10778
11029
|
import { homedir as homedir12 } from "os";
|
|
10779
|
-
import { join as
|
|
11030
|
+
import { join as join35, basename } from "path";
|
|
10780
11031
|
function loadCommandsFromDir(commandsDir, scope) {
|
|
10781
11032
|
if (!existsSync25(commandsDir)) {
|
|
10782
11033
|
return [];
|
|
@@ -10786,7 +11037,7 @@ function loadCommandsFromDir(commandsDir, scope) {
|
|
|
10786
11037
|
for (const entry of entries) {
|
|
10787
11038
|
if (!isMarkdownFile(entry))
|
|
10788
11039
|
continue;
|
|
10789
|
-
const commandPath =
|
|
11040
|
+
const commandPath = join35(commandsDir, entry.name);
|
|
10790
11041
|
const commandName = basename(entry.name, ".md");
|
|
10791
11042
|
try {
|
|
10792
11043
|
const content = readFileSync15(commandPath, "utf-8");
|
|
@@ -10829,29 +11080,29 @@ function commandsToRecord(commands) {
|
|
|
10829
11080
|
return result;
|
|
10830
11081
|
}
|
|
10831
11082
|
function loadUserCommands() {
|
|
10832
|
-
const userCommandsDir =
|
|
11083
|
+
const userCommandsDir = join35(homedir12(), ".claude", "commands");
|
|
10833
11084
|
const commands = loadCommandsFromDir(userCommandsDir, "user");
|
|
10834
11085
|
return commandsToRecord(commands);
|
|
10835
11086
|
}
|
|
10836
11087
|
function loadProjectCommands() {
|
|
10837
|
-
const projectCommandsDir =
|
|
11088
|
+
const projectCommandsDir = join35(process.cwd(), ".claude", "commands");
|
|
10838
11089
|
const commands = loadCommandsFromDir(projectCommandsDir, "project");
|
|
10839
11090
|
return commandsToRecord(commands);
|
|
10840
11091
|
}
|
|
10841
11092
|
function loadOpencodeGlobalCommands() {
|
|
10842
|
-
const opencodeCommandsDir =
|
|
11093
|
+
const opencodeCommandsDir = join35(homedir12(), ".config", "opencode", "command");
|
|
10843
11094
|
const commands = loadCommandsFromDir(opencodeCommandsDir, "opencode");
|
|
10844
11095
|
return commandsToRecord(commands);
|
|
10845
11096
|
}
|
|
10846
11097
|
function loadOpencodeProjectCommands() {
|
|
10847
|
-
const opencodeProjectDir =
|
|
11098
|
+
const opencodeProjectDir = join35(process.cwd(), ".opencode", "command");
|
|
10848
11099
|
const commands = loadCommandsFromDir(opencodeProjectDir, "opencode-project");
|
|
10849
11100
|
return commandsToRecord(commands);
|
|
10850
11101
|
}
|
|
10851
11102
|
// src/features/claude-code-skill-loader/loader.ts
|
|
10852
11103
|
import { existsSync as existsSync26, readdirSync as readdirSync8, readFileSync as readFileSync16 } from "fs";
|
|
10853
11104
|
import { homedir as homedir13 } from "os";
|
|
10854
|
-
import { join as
|
|
11105
|
+
import { join as join36 } from "path";
|
|
10855
11106
|
function loadSkillsFromDir(skillsDir, scope) {
|
|
10856
11107
|
if (!existsSync26(skillsDir)) {
|
|
10857
11108
|
return [];
|
|
@@ -10861,11 +11112,11 @@ function loadSkillsFromDir(skillsDir, scope) {
|
|
|
10861
11112
|
for (const entry of entries) {
|
|
10862
11113
|
if (entry.name.startsWith("."))
|
|
10863
11114
|
continue;
|
|
10864
|
-
const skillPath =
|
|
11115
|
+
const skillPath = join36(skillsDir, entry.name);
|
|
10865
11116
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
10866
11117
|
continue;
|
|
10867
11118
|
const resolvedPath = resolveSymlink(skillPath);
|
|
10868
|
-
const skillMdPath =
|
|
11119
|
+
const skillMdPath = join36(resolvedPath, "SKILL.md");
|
|
10869
11120
|
if (!existsSync26(skillMdPath))
|
|
10870
11121
|
continue;
|
|
10871
11122
|
try {
|
|
@@ -10903,7 +11154,7 @@ $ARGUMENTS
|
|
|
10903
11154
|
return skills;
|
|
10904
11155
|
}
|
|
10905
11156
|
function loadUserSkillsAsCommands() {
|
|
10906
|
-
const userSkillsDir =
|
|
11157
|
+
const userSkillsDir = join36(homedir13(), ".claude", "skills");
|
|
10907
11158
|
const skills = loadSkillsFromDir(userSkillsDir, "user");
|
|
10908
11159
|
return skills.reduce((acc, skill) => {
|
|
10909
11160
|
acc[skill.name] = skill.definition;
|
|
@@ -10911,7 +11162,7 @@ function loadUserSkillsAsCommands() {
|
|
|
10911
11162
|
}, {});
|
|
10912
11163
|
}
|
|
10913
11164
|
function loadProjectSkillsAsCommands() {
|
|
10914
|
-
const projectSkillsDir =
|
|
11165
|
+
const projectSkillsDir = join36(process.cwd(), ".claude", "skills");
|
|
10915
11166
|
const skills = loadSkillsFromDir(projectSkillsDir, "project");
|
|
10916
11167
|
return skills.reduce((acc, skill) => {
|
|
10917
11168
|
acc[skill.name] = skill.definition;
|
|
@@ -10921,7 +11172,7 @@ function loadProjectSkillsAsCommands() {
|
|
|
10921
11172
|
// src/features/claude-code-agent-loader/loader.ts
|
|
10922
11173
|
import { existsSync as existsSync27, readdirSync as readdirSync9, readFileSync as readFileSync17 } from "fs";
|
|
10923
11174
|
import { homedir as homedir14 } from "os";
|
|
10924
|
-
import { join as
|
|
11175
|
+
import { join as join37, basename as basename2 } from "path";
|
|
10925
11176
|
function parseToolsConfig(toolsStr) {
|
|
10926
11177
|
if (!toolsStr)
|
|
10927
11178
|
return;
|
|
@@ -10943,7 +11194,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
10943
11194
|
for (const entry of entries) {
|
|
10944
11195
|
if (!isMarkdownFile(entry))
|
|
10945
11196
|
continue;
|
|
10946
|
-
const agentPath =
|
|
11197
|
+
const agentPath = join37(agentsDir, entry.name);
|
|
10947
11198
|
const agentName = basename2(entry.name, ".md");
|
|
10948
11199
|
try {
|
|
10949
11200
|
const content = readFileSync17(agentPath, "utf-8");
|
|
@@ -10973,7 +11224,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
10973
11224
|
return agents;
|
|
10974
11225
|
}
|
|
10975
11226
|
function loadUserAgents() {
|
|
10976
|
-
const userAgentsDir =
|
|
11227
|
+
const userAgentsDir = join37(homedir14(), ".claude", "agents");
|
|
10977
11228
|
const agents = loadAgentsFromDir(userAgentsDir, "user");
|
|
10978
11229
|
const result = {};
|
|
10979
11230
|
for (const agent of agents) {
|
|
@@ -10982,7 +11233,7 @@ function loadUserAgents() {
|
|
|
10982
11233
|
return result;
|
|
10983
11234
|
}
|
|
10984
11235
|
function loadProjectAgents() {
|
|
10985
|
-
const projectAgentsDir =
|
|
11236
|
+
const projectAgentsDir = join37(process.cwd(), ".claude", "agents");
|
|
10986
11237
|
const agents = loadAgentsFromDir(projectAgentsDir, "project");
|
|
10987
11238
|
const result = {};
|
|
10988
11239
|
for (const agent of agents) {
|
|
@@ -10993,7 +11244,7 @@ function loadProjectAgents() {
|
|
|
10993
11244
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
10994
11245
|
import { existsSync as existsSync28 } from "fs";
|
|
10995
11246
|
import { homedir as homedir15 } from "os";
|
|
10996
|
-
import { join as
|
|
11247
|
+
import { join as join38 } from "path";
|
|
10997
11248
|
|
|
10998
11249
|
// src/features/claude-code-mcp-loader/env-expander.ts
|
|
10999
11250
|
function expandEnvVars(value) {
|
|
@@ -11062,9 +11313,9 @@ function getMcpConfigPaths() {
|
|
|
11062
11313
|
const home = homedir15();
|
|
11063
11314
|
const cwd = process.cwd();
|
|
11064
11315
|
return [
|
|
11065
|
-
{ path:
|
|
11066
|
-
{ path:
|
|
11067
|
-
{ path:
|
|
11316
|
+
{ path: join38(home, ".claude", ".mcp.json"), scope: "user" },
|
|
11317
|
+
{ path: join38(cwd, ".mcp.json"), scope: "project" },
|
|
11318
|
+
{ path: join38(cwd, ".claude", ".mcp.json"), scope: "local" }
|
|
11068
11319
|
];
|
|
11069
11320
|
}
|
|
11070
11321
|
async function loadMcpConfigFile(filePath) {
|
|
@@ -11083,13 +11334,13 @@ async function loadMcpConfigs() {
|
|
|
11083
11334
|
const servers = {};
|
|
11084
11335
|
const loadedServers = [];
|
|
11085
11336
|
const paths = getMcpConfigPaths();
|
|
11086
|
-
for (const { path:
|
|
11087
|
-
const config = await loadMcpConfigFile(
|
|
11337
|
+
for (const { path: path8, scope } of paths) {
|
|
11338
|
+
const config = await loadMcpConfigFile(path8);
|
|
11088
11339
|
if (!config?.mcpServers)
|
|
11089
11340
|
continue;
|
|
11090
11341
|
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
|
11091
11342
|
if (serverConfig.disabled) {
|
|
11092
|
-
log(`Skipping disabled MCP server "${name}"`, { path:
|
|
11343
|
+
log(`Skipping disabled MCP server "${name}"`, { path: path8 });
|
|
11093
11344
|
continue;
|
|
11094
11345
|
}
|
|
11095
11346
|
try {
|
|
@@ -11100,7 +11351,7 @@ async function loadMcpConfigs() {
|
|
|
11100
11351
|
loadedServers.splice(existingIndex, 1);
|
|
11101
11352
|
}
|
|
11102
11353
|
loadedServers.push({ name, scope, config: transformed });
|
|
11103
|
-
log(`Loaded MCP server "${name}" from ${scope}`, { path:
|
|
11354
|
+
log(`Loaded MCP server "${name}" from ${scope}`, { path: path8 });
|
|
11104
11355
|
} catch (error) {
|
|
11105
11356
|
log(`Failed to transform MCP server "${name}"`, error);
|
|
11106
11357
|
}
|
|
@@ -11404,13 +11655,13 @@ var EXT_TO_LANG = {
|
|
|
11404
11655
|
};
|
|
11405
11656
|
// src/tools/lsp/config.ts
|
|
11406
11657
|
import { existsSync as existsSync29, readFileSync as readFileSync18 } from "fs";
|
|
11407
|
-
import { join as
|
|
11658
|
+
import { join as join39 } from "path";
|
|
11408
11659
|
import { homedir as homedir16 } from "os";
|
|
11409
|
-
function loadJsonFile(
|
|
11410
|
-
if (!existsSync29(
|
|
11660
|
+
function loadJsonFile(path8) {
|
|
11661
|
+
if (!existsSync29(path8))
|
|
11411
11662
|
return null;
|
|
11412
11663
|
try {
|
|
11413
|
-
return JSON.parse(readFileSync18(
|
|
11664
|
+
return JSON.parse(readFileSync18(path8, "utf-8"));
|
|
11414
11665
|
} catch {
|
|
11415
11666
|
return null;
|
|
11416
11667
|
}
|
|
@@ -11418,9 +11669,9 @@ function loadJsonFile(path7) {
|
|
|
11418
11669
|
function getConfigPaths2() {
|
|
11419
11670
|
const cwd = process.cwd();
|
|
11420
11671
|
return {
|
|
11421
|
-
project:
|
|
11422
|
-
user:
|
|
11423
|
-
opencode:
|
|
11672
|
+
project: join39(cwd, ".opencode", "oh-my-opencode.json"),
|
|
11673
|
+
user: join39(homedir16(), ".config", "opencode", "oh-my-opencode.json"),
|
|
11674
|
+
opencode: join39(homedir16(), ".config", "opencode", "opencode.json")
|
|
11424
11675
|
};
|
|
11425
11676
|
}
|
|
11426
11677
|
function loadAllConfigs() {
|
|
@@ -11516,18 +11767,18 @@ function isServerInstalled(command) {
|
|
|
11516
11767
|
const pathSeparator = isWindows2 ? ";" : ":";
|
|
11517
11768
|
const paths = pathEnv.split(pathSeparator);
|
|
11518
11769
|
for (const p of paths) {
|
|
11519
|
-
if (existsSync29(
|
|
11770
|
+
if (existsSync29(join39(p, cmd)) || existsSync29(join39(p, cmd + ext))) {
|
|
11520
11771
|
return true;
|
|
11521
11772
|
}
|
|
11522
11773
|
}
|
|
11523
11774
|
const cwd = process.cwd();
|
|
11524
11775
|
const additionalPaths = [
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11776
|
+
join39(cwd, "node_modules", ".bin", cmd),
|
|
11777
|
+
join39(cwd, "node_modules", ".bin", cmd + ext),
|
|
11778
|
+
join39(homedir16(), ".config", "opencode", "bin", cmd),
|
|
11779
|
+
join39(homedir16(), ".config", "opencode", "bin", cmd + ext),
|
|
11780
|
+
join39(homedir16(), ".config", "opencode", "node_modules", ".bin", cmd),
|
|
11781
|
+
join39(homedir16(), ".config", "opencode", "node_modules", ".bin", cmd + ext)
|
|
11531
11782
|
];
|
|
11532
11783
|
for (const p of additionalPaths) {
|
|
11533
11784
|
if (existsSync29(p)) {
|
|
@@ -13139,10 +13390,10 @@ function mergeDefs(...defs) {
|
|
|
13139
13390
|
function cloneDef(schema) {
|
|
13140
13391
|
return mergeDefs(schema._zod.def);
|
|
13141
13392
|
}
|
|
13142
|
-
function getElementAtPath(obj,
|
|
13143
|
-
if (!
|
|
13393
|
+
function getElementAtPath(obj, path8) {
|
|
13394
|
+
if (!path8)
|
|
13144
13395
|
return obj;
|
|
13145
|
-
return
|
|
13396
|
+
return path8.reduce((acc, key) => acc?.[key], obj);
|
|
13146
13397
|
}
|
|
13147
13398
|
function promiseAllObject(promisesObj) {
|
|
13148
13399
|
const keys = Object.keys(promisesObj);
|
|
@@ -13501,11 +13752,11 @@ function aborted(x, startIndex = 0) {
|
|
|
13501
13752
|
}
|
|
13502
13753
|
return false;
|
|
13503
13754
|
}
|
|
13504
|
-
function prefixIssues(
|
|
13755
|
+
function prefixIssues(path8, issues) {
|
|
13505
13756
|
return issues.map((iss) => {
|
|
13506
13757
|
var _a;
|
|
13507
13758
|
(_a = iss).path ?? (_a.path = []);
|
|
13508
|
-
iss.path.unshift(
|
|
13759
|
+
iss.path.unshift(path8);
|
|
13509
13760
|
return iss;
|
|
13510
13761
|
});
|
|
13511
13762
|
}
|
|
@@ -13673,7 +13924,7 @@ function treeifyError(error, _mapper) {
|
|
|
13673
13924
|
return issue2.message;
|
|
13674
13925
|
};
|
|
13675
13926
|
const result = { errors: [] };
|
|
13676
|
-
const processError = (error2,
|
|
13927
|
+
const processError = (error2, path8 = []) => {
|
|
13677
13928
|
var _a, _b;
|
|
13678
13929
|
for (const issue2 of error2.issues) {
|
|
13679
13930
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -13683,7 +13934,7 @@ function treeifyError(error, _mapper) {
|
|
|
13683
13934
|
} else if (issue2.code === "invalid_element") {
|
|
13684
13935
|
processError({ issues: issue2.issues }, issue2.path);
|
|
13685
13936
|
} else {
|
|
13686
|
-
const fullpath = [...
|
|
13937
|
+
const fullpath = [...path8, ...issue2.path];
|
|
13687
13938
|
if (fullpath.length === 0) {
|
|
13688
13939
|
result.errors.push(mapper(issue2));
|
|
13689
13940
|
continue;
|
|
@@ -13715,8 +13966,8 @@ function treeifyError(error, _mapper) {
|
|
|
13715
13966
|
}
|
|
13716
13967
|
function toDotPath(_path) {
|
|
13717
13968
|
const segs = [];
|
|
13718
|
-
const
|
|
13719
|
-
for (const seg of
|
|
13969
|
+
const path8 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
13970
|
+
for (const seg of path8) {
|
|
13720
13971
|
if (typeof seg === "number")
|
|
13721
13972
|
segs.push(`[${seg}]`);
|
|
13722
13973
|
else if (typeof seg === "symbol")
|
|
@@ -25059,13 +25310,13 @@ var lsp_code_action_resolve = tool({
|
|
|
25059
25310
|
});
|
|
25060
25311
|
// src/tools/ast-grep/constants.ts
|
|
25061
25312
|
import { createRequire as createRequire4 } from "module";
|
|
25062
|
-
import { dirname as dirname6, join as
|
|
25313
|
+
import { dirname as dirname6, join as join41 } from "path";
|
|
25063
25314
|
import { existsSync as existsSync32, statSync as statSync4 } from "fs";
|
|
25064
25315
|
|
|
25065
25316
|
// src/tools/ast-grep/downloader.ts
|
|
25066
25317
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
25067
25318
|
import { existsSync as existsSync31, mkdirSync as mkdirSync10, chmodSync as chmodSync2, unlinkSync as unlinkSync9 } from "fs";
|
|
25068
|
-
import { join as
|
|
25319
|
+
import { join as join40 } from "path";
|
|
25069
25320
|
import { homedir as homedir17 } from "os";
|
|
25070
25321
|
import { createRequire as createRequire3 } from "module";
|
|
25071
25322
|
var REPO2 = "ast-grep/ast-grep";
|
|
@@ -25091,18 +25342,18 @@ var PLATFORM_MAP2 = {
|
|
|
25091
25342
|
function getCacheDir3() {
|
|
25092
25343
|
if (process.platform === "win32") {
|
|
25093
25344
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
25094
|
-
const base2 = localAppData ||
|
|
25095
|
-
return
|
|
25345
|
+
const base2 = localAppData || join40(homedir17(), "AppData", "Local");
|
|
25346
|
+
return join40(base2, "oh-my-opencode", "bin");
|
|
25096
25347
|
}
|
|
25097
25348
|
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
25098
|
-
const base = xdgCache2 ||
|
|
25099
|
-
return
|
|
25349
|
+
const base = xdgCache2 || join40(homedir17(), ".cache");
|
|
25350
|
+
return join40(base, "oh-my-opencode", "bin");
|
|
25100
25351
|
}
|
|
25101
25352
|
function getBinaryName3() {
|
|
25102
25353
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
25103
25354
|
}
|
|
25104
25355
|
function getCachedBinaryPath2() {
|
|
25105
|
-
const binaryPath =
|
|
25356
|
+
const binaryPath = join40(getCacheDir3(), getBinaryName3());
|
|
25106
25357
|
return existsSync31(binaryPath) ? binaryPath : null;
|
|
25107
25358
|
}
|
|
25108
25359
|
async function extractZip2(archivePath, destDir) {
|
|
@@ -25129,12 +25380,12 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
25129
25380
|
}
|
|
25130
25381
|
const cacheDir = getCacheDir3();
|
|
25131
25382
|
const binaryName = getBinaryName3();
|
|
25132
|
-
const binaryPath =
|
|
25383
|
+
const binaryPath = join40(cacheDir, binaryName);
|
|
25133
25384
|
if (existsSync31(binaryPath)) {
|
|
25134
25385
|
return binaryPath;
|
|
25135
25386
|
}
|
|
25136
|
-
const { arch, os:
|
|
25137
|
-
const assetName = `app-${arch}-${
|
|
25387
|
+
const { arch, os: os6 } = platformInfo;
|
|
25388
|
+
const assetName = `app-${arch}-${os6}.zip`;
|
|
25138
25389
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
|
|
25139
25390
|
console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
|
|
25140
25391
|
try {
|
|
@@ -25145,7 +25396,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
25145
25396
|
if (!response2.ok) {
|
|
25146
25397
|
throw new Error(`HTTP ${response2.status}: ${response2.statusText}`);
|
|
25147
25398
|
}
|
|
25148
|
-
const archivePath =
|
|
25399
|
+
const archivePath = join40(cacheDir, assetName);
|
|
25149
25400
|
const arrayBuffer = await response2.arrayBuffer();
|
|
25150
25401
|
await Bun.write(archivePath, arrayBuffer);
|
|
25151
25402
|
await extractZip2(archivePath, cacheDir);
|
|
@@ -25203,7 +25454,7 @@ function findSgCliPathSync() {
|
|
|
25203
25454
|
const require2 = createRequire4(import.meta.url);
|
|
25204
25455
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
25205
25456
|
const cliDir = dirname6(cliPkgPath);
|
|
25206
|
-
const sgPath =
|
|
25457
|
+
const sgPath = join41(cliDir, binaryName);
|
|
25207
25458
|
if (existsSync32(sgPath) && isValidBinary(sgPath)) {
|
|
25208
25459
|
return sgPath;
|
|
25209
25460
|
}
|
|
@@ -25215,7 +25466,7 @@ function findSgCliPathSync() {
|
|
|
25215
25466
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
25216
25467
|
const pkgDir = dirname6(pkgPath);
|
|
25217
25468
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
25218
|
-
const binaryPath =
|
|
25469
|
+
const binaryPath = join41(pkgDir, astGrepName);
|
|
25219
25470
|
if (existsSync32(binaryPath) && isValidBinary(binaryPath)) {
|
|
25220
25471
|
return binaryPath;
|
|
25221
25472
|
}
|
|
@@ -25223,9 +25474,9 @@ function findSgCliPathSync() {
|
|
|
25223
25474
|
}
|
|
25224
25475
|
if (process.platform === "darwin") {
|
|
25225
25476
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
25226
|
-
for (const
|
|
25227
|
-
if (existsSync32(
|
|
25228
|
-
return
|
|
25477
|
+
for (const path8 of homebrewPaths) {
|
|
25478
|
+
if (existsSync32(path8) && isValidBinary(path8)) {
|
|
25479
|
+
return path8;
|
|
25229
25480
|
}
|
|
25230
25481
|
}
|
|
25231
25482
|
}
|
|
@@ -25243,8 +25494,8 @@ function getSgCliPath() {
|
|
|
25243
25494
|
}
|
|
25244
25495
|
return "sg";
|
|
25245
25496
|
}
|
|
25246
|
-
function setSgCliPath(
|
|
25247
|
-
resolvedCliPath2 =
|
|
25497
|
+
function setSgCliPath(path8) {
|
|
25498
|
+
resolvedCliPath2 = path8;
|
|
25248
25499
|
}
|
|
25249
25500
|
var SG_CLI_PATH = getSgCliPath();
|
|
25250
25501
|
var CLI_LANGUAGES = [
|
|
@@ -25591,19 +25842,19 @@ var {spawn: spawn7 } = globalThis.Bun;
|
|
|
25591
25842
|
|
|
25592
25843
|
// src/tools/grep/constants.ts
|
|
25593
25844
|
import { existsSync as existsSync35 } from "fs";
|
|
25594
|
-
import { join as
|
|
25845
|
+
import { join as join43, dirname as dirname7 } from "path";
|
|
25595
25846
|
import { spawnSync } from "child_process";
|
|
25596
25847
|
|
|
25597
25848
|
// src/tools/grep/downloader.ts
|
|
25598
25849
|
import { existsSync as existsSync34, mkdirSync as mkdirSync11, chmodSync as chmodSync3, unlinkSync as unlinkSync10, readdirSync as readdirSync10 } from "fs";
|
|
25599
|
-
import { join as
|
|
25850
|
+
import { join as join42 } from "path";
|
|
25600
25851
|
function getInstallDir() {
|
|
25601
25852
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
25602
|
-
return
|
|
25853
|
+
return join42(homeDir, ".cache", "oh-my-opencode", "bin");
|
|
25603
25854
|
}
|
|
25604
25855
|
function getRgPath() {
|
|
25605
25856
|
const isWindows2 = process.platform === "win32";
|
|
25606
|
-
return
|
|
25857
|
+
return join42(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
|
|
25607
25858
|
}
|
|
25608
25859
|
function getInstalledRipgrepPath() {
|
|
25609
25860
|
const rgPath = getRgPath();
|
|
@@ -25630,10 +25881,10 @@ function getOpenCodeBundledRg() {
|
|
|
25630
25881
|
const isWindows2 = process.platform === "win32";
|
|
25631
25882
|
const rgName = isWindows2 ? "rg.exe" : "rg";
|
|
25632
25883
|
const candidates = [
|
|
25633
|
-
|
|
25634
|
-
|
|
25635
|
-
|
|
25636
|
-
|
|
25884
|
+
join43(execDir, rgName),
|
|
25885
|
+
join43(execDir, "bin", rgName),
|
|
25886
|
+
join43(execDir, "..", "bin", rgName),
|
|
25887
|
+
join43(execDir, "..", "libexec", rgName)
|
|
25637
25888
|
];
|
|
25638
25889
|
for (const candidate of candidates) {
|
|
25639
25890
|
if (existsSync35(candidate)) {
|
|
@@ -26041,7 +26292,7 @@ var glob = tool({
|
|
|
26041
26292
|
// src/tools/slashcommand/tools.ts
|
|
26042
26293
|
import { existsSync as existsSync36, readdirSync as readdirSync11, readFileSync as readFileSync21 } from "fs";
|
|
26043
26294
|
import { homedir as homedir18 } from "os";
|
|
26044
|
-
import { join as
|
|
26295
|
+
import { join as join44, basename as basename3, dirname as dirname8 } from "path";
|
|
26045
26296
|
function discoverCommandsFromDir(commandsDir, scope) {
|
|
26046
26297
|
if (!existsSync36(commandsDir)) {
|
|
26047
26298
|
return [];
|
|
@@ -26051,7 +26302,7 @@ function discoverCommandsFromDir(commandsDir, scope) {
|
|
|
26051
26302
|
for (const entry of entries) {
|
|
26052
26303
|
if (!isMarkdownFile(entry))
|
|
26053
26304
|
continue;
|
|
26054
|
-
const commandPath =
|
|
26305
|
+
const commandPath = join44(commandsDir, entry.name);
|
|
26055
26306
|
const commandName = basename3(entry.name, ".md");
|
|
26056
26307
|
try {
|
|
26057
26308
|
const content = readFileSync21(commandPath, "utf-8");
|
|
@@ -26079,10 +26330,10 @@ function discoverCommandsFromDir(commandsDir, scope) {
|
|
|
26079
26330
|
return commands;
|
|
26080
26331
|
}
|
|
26081
26332
|
function discoverCommandsSync() {
|
|
26082
|
-
const userCommandsDir =
|
|
26083
|
-
const projectCommandsDir =
|
|
26084
|
-
const opencodeGlobalDir =
|
|
26085
|
-
const opencodeProjectDir =
|
|
26333
|
+
const userCommandsDir = join44(homedir18(), ".claude", "commands");
|
|
26334
|
+
const projectCommandsDir = join44(process.cwd(), ".claude", "commands");
|
|
26335
|
+
const opencodeGlobalDir = join44(homedir18(), ".config", "opencode", "command");
|
|
26336
|
+
const opencodeProjectDir = join44(process.cwd(), ".opencode", "command");
|
|
26086
26337
|
const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
|
|
26087
26338
|
const opencodeGlobalCommands = discoverCommandsFromDir(opencodeGlobalDir, "opencode");
|
|
26088
26339
|
const projectCommands = discoverCommandsFromDir(projectCommandsDir, "project");
|
|
@@ -26205,6 +26456,484 @@ Provide a command name to execute.`;
|
|
|
26205
26456
|
Try a different command name.`;
|
|
26206
26457
|
}
|
|
26207
26458
|
});
|
|
26459
|
+
// src/tools/session-manager/constants.ts
|
|
26460
|
+
import { join as join45 } from "path";
|
|
26461
|
+
import { homedir as homedir19 } from "os";
|
|
26462
|
+
var OPENCODE_STORAGE9 = getOpenCodeStorageDir();
|
|
26463
|
+
var MESSAGE_STORAGE4 = join45(OPENCODE_STORAGE9, "message");
|
|
26464
|
+
var PART_STORAGE4 = join45(OPENCODE_STORAGE9, "part");
|
|
26465
|
+
var TODO_DIR2 = join45(homedir19(), ".claude", "todos");
|
|
26466
|
+
var TRANSCRIPT_DIR2 = join45(homedir19(), ".claude", "transcripts");
|
|
26467
|
+
var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
|
|
26468
|
+
|
|
26469
|
+
Returns a list of available session IDs with metadata including message count, date range, and agents used.
|
|
26470
|
+
|
|
26471
|
+
Arguments:
|
|
26472
|
+
- limit (optional): Maximum number of sessions to return
|
|
26473
|
+
- from_date (optional): Filter sessions from this date (ISO 8601 format)
|
|
26474
|
+
- to_date (optional): Filter sessions until this date (ISO 8601 format)
|
|
26475
|
+
|
|
26476
|
+
Example output:
|
|
26477
|
+
| Session ID | Messages | First | Last | Agents |
|
|
26478
|
+
|------------|----------|-------|------|--------|
|
|
26479
|
+
| ses_abc123 | 45 | 2025-12-20 | 2025-12-24 | build, oracle |
|
|
26480
|
+
| ses_def456 | 12 | 2025-12-19 | 2025-12-19 | build |`;
|
|
26481
|
+
var SESSION_READ_DESCRIPTION = `Read messages and history from an OpenCode session.
|
|
26482
|
+
|
|
26483
|
+
Returns a formatted view of session messages with role, timestamp, and content. Optionally includes todos and transcript data.
|
|
26484
|
+
|
|
26485
|
+
Arguments:
|
|
26486
|
+
- session_id (required): Session ID to read
|
|
26487
|
+
- include_todos (optional): Include todo list if available (default: false)
|
|
26488
|
+
- include_transcript (optional): Include transcript log if available (default: false)
|
|
26489
|
+
- limit (optional): Maximum number of messages to return (default: all)
|
|
26490
|
+
|
|
26491
|
+
Example output:
|
|
26492
|
+
Session: ses_abc123
|
|
26493
|
+
Messages: 45
|
|
26494
|
+
Date Range: 2025-12-20 to 2025-12-24
|
|
26495
|
+
|
|
26496
|
+
[Message 1] user (2025-12-20 10:30:00)
|
|
26497
|
+
Hello, can you help me with...
|
|
26498
|
+
|
|
26499
|
+
[Message 2] assistant (2025-12-20 10:30:15)
|
|
26500
|
+
Of course! Let me help you with...`;
|
|
26501
|
+
var SESSION_SEARCH_DESCRIPTION = `Search for content within OpenCode session messages.
|
|
26502
|
+
|
|
26503
|
+
Performs full-text search across session messages and returns matching excerpts with context.
|
|
26504
|
+
|
|
26505
|
+
Arguments:
|
|
26506
|
+
- query (required): Search query string
|
|
26507
|
+
- session_id (optional): Search within specific session only (default: all sessions)
|
|
26508
|
+
- case_sensitive (optional): Case-sensitive search (default: false)
|
|
26509
|
+
- limit (optional): Maximum number of results to return (default: 20)
|
|
26510
|
+
|
|
26511
|
+
Example output:
|
|
26512
|
+
Found 3 matches across 2 sessions:
|
|
26513
|
+
|
|
26514
|
+
[ses_abc123] Message msg_001 (user)
|
|
26515
|
+
...implement the **session manager** tool...
|
|
26516
|
+
|
|
26517
|
+
[ses_abc123] Message msg_005 (assistant)
|
|
26518
|
+
...I'll create a **session manager** with full search...
|
|
26519
|
+
|
|
26520
|
+
[ses_def456] Message msg_012 (user)
|
|
26521
|
+
...use the **session manager** to find...`;
|
|
26522
|
+
var SESSION_INFO_DESCRIPTION = `Get metadata and statistics about an OpenCode session.
|
|
26523
|
+
|
|
26524
|
+
Returns detailed information about a session including message count, date range, agents used, and available data sources.
|
|
26525
|
+
|
|
26526
|
+
Arguments:
|
|
26527
|
+
- session_id (required): Session ID to inspect
|
|
26528
|
+
|
|
26529
|
+
Example output:
|
|
26530
|
+
Session ID: ses_abc123
|
|
26531
|
+
Messages: 45
|
|
26532
|
+
Date Range: 2025-12-20 10:30:00 to 2025-12-24 15:45:30
|
|
26533
|
+
Duration: 4 days, 5 hours
|
|
26534
|
+
Agents Used: build, oracle, librarian
|
|
26535
|
+
Has Todos: Yes (12 items, 8 completed)
|
|
26536
|
+
Has Transcript: Yes (234 entries)`;
|
|
26537
|
+
|
|
26538
|
+
// src/tools/session-manager/storage.ts
|
|
26539
|
+
import { existsSync as existsSync37, readdirSync as readdirSync12, readFileSync as readFileSync22 } from "fs";
|
|
26540
|
+
import { join as join46 } from "path";
|
|
26541
|
+
function getAllSessions() {
|
|
26542
|
+
if (!existsSync37(MESSAGE_STORAGE4))
|
|
26543
|
+
return [];
|
|
26544
|
+
const sessions = [];
|
|
26545
|
+
function scanDirectory(dir) {
|
|
26546
|
+
try {
|
|
26547
|
+
for (const entry of readdirSync12(dir, { withFileTypes: true })) {
|
|
26548
|
+
if (entry.isDirectory()) {
|
|
26549
|
+
const sessionPath = join46(dir, entry.name);
|
|
26550
|
+
const files = readdirSync12(sessionPath);
|
|
26551
|
+
if (files.some((f) => f.endsWith(".json"))) {
|
|
26552
|
+
sessions.push(entry.name);
|
|
26553
|
+
} else {
|
|
26554
|
+
scanDirectory(sessionPath);
|
|
26555
|
+
}
|
|
26556
|
+
}
|
|
26557
|
+
}
|
|
26558
|
+
} catch {
|
|
26559
|
+
return;
|
|
26560
|
+
}
|
|
26561
|
+
}
|
|
26562
|
+
scanDirectory(MESSAGE_STORAGE4);
|
|
26563
|
+
return [...new Set(sessions)];
|
|
26564
|
+
}
|
|
26565
|
+
function getMessageDir5(sessionID) {
|
|
26566
|
+
if (!existsSync37(MESSAGE_STORAGE4))
|
|
26567
|
+
return "";
|
|
26568
|
+
const directPath = join46(MESSAGE_STORAGE4, sessionID);
|
|
26569
|
+
if (existsSync37(directPath)) {
|
|
26570
|
+
return directPath;
|
|
26571
|
+
}
|
|
26572
|
+
for (const dir of readdirSync12(MESSAGE_STORAGE4)) {
|
|
26573
|
+
const sessionPath = join46(MESSAGE_STORAGE4, dir, sessionID);
|
|
26574
|
+
if (existsSync37(sessionPath)) {
|
|
26575
|
+
return sessionPath;
|
|
26576
|
+
}
|
|
26577
|
+
}
|
|
26578
|
+
return "";
|
|
26579
|
+
}
|
|
26580
|
+
function sessionExists(sessionID) {
|
|
26581
|
+
return getMessageDir5(sessionID) !== "";
|
|
26582
|
+
}
|
|
26583
|
+
function readSessionMessages(sessionID) {
|
|
26584
|
+
const messageDir = getMessageDir5(sessionID);
|
|
26585
|
+
if (!messageDir || !existsSync37(messageDir))
|
|
26586
|
+
return [];
|
|
26587
|
+
const messages = [];
|
|
26588
|
+
for (const file2 of readdirSync12(messageDir)) {
|
|
26589
|
+
if (!file2.endsWith(".json"))
|
|
26590
|
+
continue;
|
|
26591
|
+
try {
|
|
26592
|
+
const content = readFileSync22(join46(messageDir, file2), "utf-8");
|
|
26593
|
+
const meta = JSON.parse(content);
|
|
26594
|
+
const parts = readParts2(meta.id);
|
|
26595
|
+
messages.push({
|
|
26596
|
+
id: meta.id,
|
|
26597
|
+
role: meta.role,
|
|
26598
|
+
agent: meta.agent,
|
|
26599
|
+
time: meta.time,
|
|
26600
|
+
parts
|
|
26601
|
+
});
|
|
26602
|
+
} catch {
|
|
26603
|
+
continue;
|
|
26604
|
+
}
|
|
26605
|
+
}
|
|
26606
|
+
return messages.sort((a, b) => {
|
|
26607
|
+
const aTime = a.time?.created ?? 0;
|
|
26608
|
+
const bTime = b.time?.created ?? 0;
|
|
26609
|
+
if (aTime !== bTime)
|
|
26610
|
+
return aTime - bTime;
|
|
26611
|
+
return a.id.localeCompare(b.id);
|
|
26612
|
+
});
|
|
26613
|
+
}
|
|
26614
|
+
function readParts2(messageID) {
|
|
26615
|
+
const partDir = join46(PART_STORAGE4, messageID);
|
|
26616
|
+
if (!existsSync37(partDir))
|
|
26617
|
+
return [];
|
|
26618
|
+
const parts = [];
|
|
26619
|
+
for (const file2 of readdirSync12(partDir)) {
|
|
26620
|
+
if (!file2.endsWith(".json"))
|
|
26621
|
+
continue;
|
|
26622
|
+
try {
|
|
26623
|
+
const content = readFileSync22(join46(partDir, file2), "utf-8");
|
|
26624
|
+
parts.push(JSON.parse(content));
|
|
26625
|
+
} catch {
|
|
26626
|
+
continue;
|
|
26627
|
+
}
|
|
26628
|
+
}
|
|
26629
|
+
return parts.sort((a, b) => a.id.localeCompare(b.id));
|
|
26630
|
+
}
|
|
26631
|
+
function readSessionTodos(sessionID) {
|
|
26632
|
+
if (!existsSync37(TODO_DIR2))
|
|
26633
|
+
return [];
|
|
26634
|
+
const todoFiles = readdirSync12(TODO_DIR2).filter((f) => f.includes(sessionID) && f.endsWith(".json"));
|
|
26635
|
+
for (const file2 of todoFiles) {
|
|
26636
|
+
try {
|
|
26637
|
+
const content = readFileSync22(join46(TODO_DIR2, file2), "utf-8");
|
|
26638
|
+
const data = JSON.parse(content);
|
|
26639
|
+
if (Array.isArray(data)) {
|
|
26640
|
+
return data.map((item) => ({
|
|
26641
|
+
id: item.id || "",
|
|
26642
|
+
content: item.content || "",
|
|
26643
|
+
status: item.status || "pending",
|
|
26644
|
+
priority: item.priority
|
|
26645
|
+
}));
|
|
26646
|
+
}
|
|
26647
|
+
} catch {
|
|
26648
|
+
continue;
|
|
26649
|
+
}
|
|
26650
|
+
}
|
|
26651
|
+
return [];
|
|
26652
|
+
}
|
|
26653
|
+
function readSessionTranscript(sessionID) {
|
|
26654
|
+
if (!existsSync37(TRANSCRIPT_DIR2))
|
|
26655
|
+
return 0;
|
|
26656
|
+
const transcriptFile = join46(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
|
|
26657
|
+
if (!existsSync37(transcriptFile))
|
|
26658
|
+
return 0;
|
|
26659
|
+
try {
|
|
26660
|
+
const content = readFileSync22(transcriptFile, "utf-8");
|
|
26661
|
+
return content.trim().split(`
|
|
26662
|
+
`).filter(Boolean).length;
|
|
26663
|
+
} catch {
|
|
26664
|
+
return 0;
|
|
26665
|
+
}
|
|
26666
|
+
}
|
|
26667
|
+
function getSessionInfo(sessionID) {
|
|
26668
|
+
const messages = readSessionMessages(sessionID);
|
|
26669
|
+
if (messages.length === 0)
|
|
26670
|
+
return null;
|
|
26671
|
+
const agentsUsed = new Set;
|
|
26672
|
+
let firstMessage;
|
|
26673
|
+
let lastMessage;
|
|
26674
|
+
for (const msg of messages) {
|
|
26675
|
+
if (msg.agent)
|
|
26676
|
+
agentsUsed.add(msg.agent);
|
|
26677
|
+
if (msg.time?.created) {
|
|
26678
|
+
const date5 = new Date(msg.time.created);
|
|
26679
|
+
if (!firstMessage || date5 < firstMessage)
|
|
26680
|
+
firstMessage = date5;
|
|
26681
|
+
if (!lastMessage || date5 > lastMessage)
|
|
26682
|
+
lastMessage = date5;
|
|
26683
|
+
}
|
|
26684
|
+
}
|
|
26685
|
+
const todos = readSessionTodos(sessionID);
|
|
26686
|
+
const transcriptEntries = readSessionTranscript(sessionID);
|
|
26687
|
+
return {
|
|
26688
|
+
id: sessionID,
|
|
26689
|
+
message_count: messages.length,
|
|
26690
|
+
first_message: firstMessage,
|
|
26691
|
+
last_message: lastMessage,
|
|
26692
|
+
agents_used: Array.from(agentsUsed),
|
|
26693
|
+
has_todos: todos.length > 0,
|
|
26694
|
+
has_transcript: transcriptEntries > 0,
|
|
26695
|
+
todos,
|
|
26696
|
+
transcript_entries: transcriptEntries
|
|
26697
|
+
};
|
|
26698
|
+
}
|
|
26699
|
+
|
|
26700
|
+
// src/tools/session-manager/utils.ts
|
|
26701
|
+
function formatSessionList(sessionIDs) {
|
|
26702
|
+
if (sessionIDs.length === 0) {
|
|
26703
|
+
return "No sessions found.";
|
|
26704
|
+
}
|
|
26705
|
+
const infos = sessionIDs.map((id) => getSessionInfo(id)).filter((info) => info !== null);
|
|
26706
|
+
if (infos.length === 0) {
|
|
26707
|
+
return "No valid sessions found.";
|
|
26708
|
+
}
|
|
26709
|
+
const headers = ["Session ID", "Messages", "First", "Last", "Agents"];
|
|
26710
|
+
const rows = infos.map((info) => [
|
|
26711
|
+
info.id,
|
|
26712
|
+
info.message_count.toString(),
|
|
26713
|
+
info.first_message?.toISOString().split("T")[0] ?? "N/A",
|
|
26714
|
+
info.last_message?.toISOString().split("T")[0] ?? "N/A",
|
|
26715
|
+
info.agents_used.join(", ") || "none"
|
|
26716
|
+
]);
|
|
26717
|
+
const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
26718
|
+
const formatRow = (cells) => {
|
|
26719
|
+
return "| " + cells.map((cell, i) => cell.padEnd(colWidths[i])).join(" | ").trim() + " |";
|
|
26720
|
+
};
|
|
26721
|
+
const separator = "|" + colWidths.map((w) => "-".repeat(w + 2)).join("|") + "|";
|
|
26722
|
+
return [formatRow(headers), separator, ...rows.map(formatRow)].join(`
|
|
26723
|
+
`);
|
|
26724
|
+
}
|
|
26725
|
+
function formatSessionMessages(messages, includeTodos, todos) {
|
|
26726
|
+
if (messages.length === 0) {
|
|
26727
|
+
return "No messages found in this session.";
|
|
26728
|
+
}
|
|
26729
|
+
const lines = [];
|
|
26730
|
+
for (const msg of messages) {
|
|
26731
|
+
const timestamp = msg.time?.created ? new Date(msg.time.created).toISOString() : "Unknown time";
|
|
26732
|
+
const agent = msg.agent ? ` (${msg.agent})` : "";
|
|
26733
|
+
lines.push(`
|
|
26734
|
+
[${msg.role}${agent}] ${timestamp}`);
|
|
26735
|
+
for (const part of msg.parts) {
|
|
26736
|
+
if (part.type === "text" && part.text) {
|
|
26737
|
+
lines.push(part.text.trim());
|
|
26738
|
+
} else if (part.type === "thinking" && part.thinking) {
|
|
26739
|
+
lines.push(`[thinking] ${part.thinking.substring(0, 200)}...`);
|
|
26740
|
+
} else if ((part.type === "tool_use" || part.type === "tool") && part.tool) {
|
|
26741
|
+
const input = part.input ? JSON.stringify(part.input).substring(0, 100) : "";
|
|
26742
|
+
lines.push(`[tool: ${part.tool}] ${input}`);
|
|
26743
|
+
} else if (part.type === "tool_result") {
|
|
26744
|
+
const output = part.output ? part.output.substring(0, 200) : "";
|
|
26745
|
+
lines.push(`[tool result] ${output}...`);
|
|
26746
|
+
}
|
|
26747
|
+
}
|
|
26748
|
+
}
|
|
26749
|
+
if (includeTodos && todos && todos.length > 0) {
|
|
26750
|
+
lines.push(`
|
|
26751
|
+
|
|
26752
|
+
=== Todos ===`);
|
|
26753
|
+
for (const todo of todos) {
|
|
26754
|
+
const status = todo.status === "completed" ? "\u2713" : todo.status === "in_progress" ? "\u2192" : "\u25CB";
|
|
26755
|
+
lines.push(`${status} [${todo.status}] ${todo.content}`);
|
|
26756
|
+
}
|
|
26757
|
+
}
|
|
26758
|
+
return lines.join(`
|
|
26759
|
+
`);
|
|
26760
|
+
}
|
|
26761
|
+
function formatSessionInfo(info) {
|
|
26762
|
+
const lines = [
|
|
26763
|
+
`Session ID: ${info.id}`,
|
|
26764
|
+
`Messages: ${info.message_count}`,
|
|
26765
|
+
`Date Range: ${info.first_message?.toISOString() ?? "N/A"} to ${info.last_message?.toISOString() ?? "N/A"}`,
|
|
26766
|
+
`Agents Used: ${info.agents_used.join(", ") || "none"}`,
|
|
26767
|
+
`Has Todos: ${info.has_todos ? `Yes (${info.todos?.length ?? 0} items)` : "No"}`,
|
|
26768
|
+
`Has Transcript: ${info.has_transcript ? `Yes (${info.transcript_entries} entries)` : "No"}`
|
|
26769
|
+
];
|
|
26770
|
+
if (info.first_message && info.last_message) {
|
|
26771
|
+
const duration3 = info.last_message.getTime() - info.first_message.getTime();
|
|
26772
|
+
const days = Math.floor(duration3 / (1000 * 60 * 60 * 24));
|
|
26773
|
+
const hours = Math.floor(duration3 % (1000 * 60 * 60 * 24) / (1000 * 60 * 60));
|
|
26774
|
+
if (days > 0 || hours > 0) {
|
|
26775
|
+
lines.push(`Duration: ${days} days, ${hours} hours`);
|
|
26776
|
+
}
|
|
26777
|
+
}
|
|
26778
|
+
return lines.join(`
|
|
26779
|
+
`);
|
|
26780
|
+
}
|
|
26781
|
+
function formatSearchResults(results) {
|
|
26782
|
+
if (results.length === 0) {
|
|
26783
|
+
return "No matches found.";
|
|
26784
|
+
}
|
|
26785
|
+
const lines = [`Found ${results.length} matches:
|
|
26786
|
+
`];
|
|
26787
|
+
for (const result of results) {
|
|
26788
|
+
const timestamp = result.timestamp ? new Date(result.timestamp).toISOString() : "";
|
|
26789
|
+
lines.push(`[${result.session_id}] ${result.message_id} (${result.role}) ${timestamp}`);
|
|
26790
|
+
lines.push(` ${result.excerpt}`);
|
|
26791
|
+
lines.push(` Matches: ${result.match_count}
|
|
26792
|
+
`);
|
|
26793
|
+
}
|
|
26794
|
+
return lines.join(`
|
|
26795
|
+
`);
|
|
26796
|
+
}
|
|
26797
|
+
function filterSessionsByDate(sessionIDs, fromDate, toDate) {
|
|
26798
|
+
if (!fromDate && !toDate)
|
|
26799
|
+
return sessionIDs;
|
|
26800
|
+
const from = fromDate ? new Date(fromDate) : null;
|
|
26801
|
+
const to = toDate ? new Date(toDate) : null;
|
|
26802
|
+
return sessionIDs.filter((id) => {
|
|
26803
|
+
const info = getSessionInfo(id);
|
|
26804
|
+
if (!info || !info.last_message)
|
|
26805
|
+
return false;
|
|
26806
|
+
if (from && info.last_message < from)
|
|
26807
|
+
return false;
|
|
26808
|
+
if (to && info.last_message > to)
|
|
26809
|
+
return false;
|
|
26810
|
+
return true;
|
|
26811
|
+
});
|
|
26812
|
+
}
|
|
26813
|
+
function searchInSession(sessionID, query, caseSensitive = false) {
|
|
26814
|
+
const messages = readSessionMessages(sessionID);
|
|
26815
|
+
const results = [];
|
|
26816
|
+
const searchQuery = caseSensitive ? query : query.toLowerCase();
|
|
26817
|
+
for (const msg of messages) {
|
|
26818
|
+
let matchCount = 0;
|
|
26819
|
+
let excerpts = [];
|
|
26820
|
+
for (const part of msg.parts) {
|
|
26821
|
+
if (part.type === "text" && part.text) {
|
|
26822
|
+
const text = caseSensitive ? part.text : part.text.toLowerCase();
|
|
26823
|
+
const matches = text.split(searchQuery).length - 1;
|
|
26824
|
+
if (matches > 0) {
|
|
26825
|
+
matchCount += matches;
|
|
26826
|
+
const index = text.indexOf(searchQuery);
|
|
26827
|
+
if (index !== -1) {
|
|
26828
|
+
const start = Math.max(0, index - 50);
|
|
26829
|
+
const end = Math.min(text.length, index + searchQuery.length + 50);
|
|
26830
|
+
let excerpt = part.text.substring(start, end);
|
|
26831
|
+
if (start > 0)
|
|
26832
|
+
excerpt = "..." + excerpt;
|
|
26833
|
+
if (end < text.length)
|
|
26834
|
+
excerpt = excerpt + "...";
|
|
26835
|
+
excerpts.push(excerpt);
|
|
26836
|
+
}
|
|
26837
|
+
}
|
|
26838
|
+
}
|
|
26839
|
+
}
|
|
26840
|
+
if (matchCount > 0) {
|
|
26841
|
+
results.push({
|
|
26842
|
+
session_id: sessionID,
|
|
26843
|
+
message_id: msg.id,
|
|
26844
|
+
role: msg.role,
|
|
26845
|
+
excerpt: excerpts[0] || "",
|
|
26846
|
+
match_count: matchCount,
|
|
26847
|
+
timestamp: msg.time?.created
|
|
26848
|
+
});
|
|
26849
|
+
}
|
|
26850
|
+
}
|
|
26851
|
+
return results;
|
|
26852
|
+
}
|
|
26853
|
+
|
|
26854
|
+
// src/tools/session-manager/tools.ts
|
|
26855
|
+
var session_list = tool({
|
|
26856
|
+
description: SESSION_LIST_DESCRIPTION,
|
|
26857
|
+
args: {
|
|
26858
|
+
limit: tool.schema.number().optional().describe("Maximum number of sessions to return"),
|
|
26859
|
+
from_date: tool.schema.string().optional().describe("Filter sessions from this date (ISO 8601 format)"),
|
|
26860
|
+
to_date: tool.schema.string().optional().describe("Filter sessions until this date (ISO 8601 format)")
|
|
26861
|
+
},
|
|
26862
|
+
execute: async (args, _context) => {
|
|
26863
|
+
try {
|
|
26864
|
+
let sessions = getAllSessions();
|
|
26865
|
+
if (args.from_date || args.to_date) {
|
|
26866
|
+
sessions = filterSessionsByDate(sessions, args.from_date, args.to_date);
|
|
26867
|
+
}
|
|
26868
|
+
if (args.limit && args.limit > 0) {
|
|
26869
|
+
sessions = sessions.slice(0, args.limit);
|
|
26870
|
+
}
|
|
26871
|
+
return formatSessionList(sessions);
|
|
26872
|
+
} catch (e) {
|
|
26873
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
26874
|
+
}
|
|
26875
|
+
}
|
|
26876
|
+
});
|
|
26877
|
+
var session_read = tool({
|
|
26878
|
+
description: SESSION_READ_DESCRIPTION,
|
|
26879
|
+
args: {
|
|
26880
|
+
session_id: tool.schema.string().describe("Session ID to read"),
|
|
26881
|
+
include_todos: tool.schema.boolean().optional().describe("Include todo list if available (default: false)"),
|
|
26882
|
+
include_transcript: tool.schema.boolean().optional().describe("Include transcript log if available (default: false)"),
|
|
26883
|
+
limit: tool.schema.number().optional().describe("Maximum number of messages to return (default: all)")
|
|
26884
|
+
},
|
|
26885
|
+
execute: async (args, _context) => {
|
|
26886
|
+
try {
|
|
26887
|
+
if (!sessionExists(args.session_id)) {
|
|
26888
|
+
return `Session not found: ${args.session_id}`;
|
|
26889
|
+
}
|
|
26890
|
+
let messages = readSessionMessages(args.session_id);
|
|
26891
|
+
if (args.limit && args.limit > 0) {
|
|
26892
|
+
messages = messages.slice(0, args.limit);
|
|
26893
|
+
}
|
|
26894
|
+
const todos = args.include_todos ? readSessionTodos(args.session_id) : undefined;
|
|
26895
|
+
return formatSessionMessages(messages, args.include_todos, todos);
|
|
26896
|
+
} catch (e) {
|
|
26897
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
26898
|
+
}
|
|
26899
|
+
}
|
|
26900
|
+
});
|
|
26901
|
+
var session_search = tool({
|
|
26902
|
+
description: SESSION_SEARCH_DESCRIPTION,
|
|
26903
|
+
args: {
|
|
26904
|
+
query: tool.schema.string().describe("Search query string"),
|
|
26905
|
+
session_id: tool.schema.string().optional().describe("Search within specific session only (default: all sessions)"),
|
|
26906
|
+
case_sensitive: tool.schema.boolean().optional().describe("Case-sensitive search (default: false)"),
|
|
26907
|
+
limit: tool.schema.number().optional().describe("Maximum number of results to return (default: 20)")
|
|
26908
|
+
},
|
|
26909
|
+
execute: async (args, _context) => {
|
|
26910
|
+
try {
|
|
26911
|
+
const sessions = args.session_id ? [args.session_id] : getAllSessions();
|
|
26912
|
+
const allResults = sessions.flatMap((sid) => searchInSession(sid, args.query, args.case_sensitive));
|
|
26913
|
+
const limited = args.limit && args.limit > 0 ? allResults.slice(0, args.limit) : allResults.slice(0, 20);
|
|
26914
|
+
return formatSearchResults(limited);
|
|
26915
|
+
} catch (e) {
|
|
26916
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
26917
|
+
}
|
|
26918
|
+
}
|
|
26919
|
+
});
|
|
26920
|
+
var session_info = tool({
|
|
26921
|
+
description: SESSION_INFO_DESCRIPTION,
|
|
26922
|
+
args: {
|
|
26923
|
+
session_id: tool.schema.string().describe("Session ID to inspect")
|
|
26924
|
+
},
|
|
26925
|
+
execute: async (args, _context) => {
|
|
26926
|
+
try {
|
|
26927
|
+
const info = getSessionInfo(args.session_id);
|
|
26928
|
+
if (!info) {
|
|
26929
|
+
return `Session not found: ${args.session_id}`;
|
|
26930
|
+
}
|
|
26931
|
+
return formatSessionInfo(info);
|
|
26932
|
+
} catch (e) {
|
|
26933
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
26934
|
+
}
|
|
26935
|
+
}
|
|
26936
|
+
});
|
|
26208
26937
|
// src/tools/interactive-bash/constants.ts
|
|
26209
26938
|
var DEFAULT_TIMEOUT_MS4 = 60000;
|
|
26210
26939
|
var BLOCKED_TMUX_SUBCOMMANDS = [
|
|
@@ -26238,12 +26967,12 @@ async function findTmuxPath() {
|
|
|
26238
26967
|
return null;
|
|
26239
26968
|
}
|
|
26240
26969
|
const stdout = await new Response(proc.stdout).text();
|
|
26241
|
-
const
|
|
26970
|
+
const path8 = stdout.trim().split(`
|
|
26242
26971
|
`)[0];
|
|
26243
|
-
if (!
|
|
26972
|
+
if (!path8) {
|
|
26244
26973
|
return null;
|
|
26245
26974
|
}
|
|
26246
|
-
const verifyProc = spawn9([
|
|
26975
|
+
const verifyProc = spawn9([path8, "-V"], {
|
|
26247
26976
|
stdout: "pipe",
|
|
26248
26977
|
stderr: "pipe"
|
|
26249
26978
|
});
|
|
@@ -26251,7 +26980,7 @@ async function findTmuxPath() {
|
|
|
26251
26980
|
if (verifyExitCode !== 0) {
|
|
26252
26981
|
return null;
|
|
26253
26982
|
}
|
|
26254
|
-
return
|
|
26983
|
+
return path8;
|
|
26255
26984
|
} catch {
|
|
26256
26985
|
return null;
|
|
26257
26986
|
}
|
|
@@ -26264,9 +26993,9 @@ async function getTmuxPath() {
|
|
|
26264
26993
|
return initPromise3;
|
|
26265
26994
|
}
|
|
26266
26995
|
initPromise3 = (async () => {
|
|
26267
|
-
const
|
|
26268
|
-
tmuxPath =
|
|
26269
|
-
return
|
|
26996
|
+
const path8 = await findTmuxPath();
|
|
26997
|
+
tmuxPath = path8;
|
|
26998
|
+
return path8;
|
|
26270
26999
|
})();
|
|
26271
27000
|
return initPromise3;
|
|
26272
27001
|
}
|
|
@@ -26905,20 +27634,24 @@ var builtinTools = {
|
|
|
26905
27634
|
ast_grep_replace,
|
|
26906
27635
|
grep,
|
|
26907
27636
|
glob,
|
|
26908
|
-
slashcommand
|
|
27637
|
+
slashcommand,
|
|
27638
|
+
session_list,
|
|
27639
|
+
session_read,
|
|
27640
|
+
session_search,
|
|
27641
|
+
session_info
|
|
26909
27642
|
};
|
|
26910
27643
|
// src/features/background-agent/manager.ts
|
|
26911
|
-
import { existsSync as
|
|
26912
|
-
import { join as
|
|
26913
|
-
function
|
|
26914
|
-
if (!
|
|
27644
|
+
import { existsSync as existsSync38, readdirSync as readdirSync13 } from "fs";
|
|
27645
|
+
import { join as join47 } from "path";
|
|
27646
|
+
function getMessageDir6(sessionID) {
|
|
27647
|
+
if (!existsSync38(MESSAGE_STORAGE))
|
|
26915
27648
|
return null;
|
|
26916
|
-
const directPath =
|
|
26917
|
-
if (
|
|
27649
|
+
const directPath = join47(MESSAGE_STORAGE, sessionID);
|
|
27650
|
+
if (existsSync38(directPath))
|
|
26918
27651
|
return directPath;
|
|
26919
|
-
for (const dir of
|
|
26920
|
-
const sessionPath =
|
|
26921
|
-
if (
|
|
27652
|
+
for (const dir of readdirSync13(MESSAGE_STORAGE)) {
|
|
27653
|
+
const sessionPath = join47(MESSAGE_STORAGE, dir, sessionID);
|
|
27654
|
+
if (existsSync38(sessionPath))
|
|
26922
27655
|
return sessionPath;
|
|
26923
27656
|
}
|
|
26924
27657
|
return null;
|
|
@@ -27154,7 +27887,7 @@ class BackgroundManager {
|
|
|
27154
27887
|
log("[background-agent] Sending notification to parent session:", { parentSessionID: task.parentSessionID });
|
|
27155
27888
|
setTimeout(async () => {
|
|
27156
27889
|
try {
|
|
27157
|
-
const messageDir =
|
|
27890
|
+
const messageDir = getMessageDir6(task.parentSessionID);
|
|
27158
27891
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
27159
27892
|
await this.client.session.prompt({
|
|
27160
27893
|
path: { id: task.parentSessionID },
|
|
@@ -27322,6 +28055,7 @@ var OverridableAgentNameSchema = exports_external.enum([
|
|
|
27322
28055
|
"build",
|
|
27323
28056
|
"plan",
|
|
27324
28057
|
"Sisyphus",
|
|
28058
|
+
"Builder-Sisyphus",
|
|
27325
28059
|
"Planner-Sisyphus",
|
|
27326
28060
|
"oracle",
|
|
27327
28061
|
"librarian",
|
|
@@ -27358,6 +28092,7 @@ var AgentOverrideConfigSchema = exports_external.object({
|
|
|
27358
28092
|
temperature: exports_external.number().min(0).max(2).optional(),
|
|
27359
28093
|
top_p: exports_external.number().min(0).max(1).optional(),
|
|
27360
28094
|
prompt: exports_external.string().optional(),
|
|
28095
|
+
prompt_append: exports_external.string().optional(),
|
|
27361
28096
|
tools: exports_external.record(exports_external.string(), exports_external.boolean()).optional(),
|
|
27362
28097
|
disable: exports_external.boolean().optional(),
|
|
27363
28098
|
description: exports_external.string().optional(),
|
|
@@ -27369,6 +28104,7 @@ var AgentOverridesSchema = exports_external.object({
|
|
|
27369
28104
|
build: AgentOverrideConfigSchema.optional(),
|
|
27370
28105
|
plan: AgentOverrideConfigSchema.optional(),
|
|
27371
28106
|
Sisyphus: AgentOverrideConfigSchema.optional(),
|
|
28107
|
+
"Builder-Sisyphus": AgentOverrideConfigSchema.optional(),
|
|
27372
28108
|
"Planner-Sisyphus": AgentOverrideConfigSchema.optional(),
|
|
27373
28109
|
oracle: AgentOverrideConfigSchema.optional(),
|
|
27374
28110
|
librarian: AgentOverrideConfigSchema.optional(),
|
|
@@ -27385,14 +28121,18 @@ var ClaudeCodeConfigSchema = exports_external.object({
|
|
|
27385
28121
|
hooks: exports_external.boolean().optional()
|
|
27386
28122
|
});
|
|
27387
28123
|
var SisyphusAgentConfigSchema = exports_external.object({
|
|
27388
|
-
disabled: exports_external.boolean().optional()
|
|
28124
|
+
disabled: exports_external.boolean().optional(),
|
|
28125
|
+
builder_enabled: exports_external.boolean().optional(),
|
|
28126
|
+
planner_enabled: exports_external.boolean().optional(),
|
|
28127
|
+
replace_build: exports_external.boolean().optional(),
|
|
28128
|
+
replace_plan: exports_external.boolean().optional()
|
|
27389
28129
|
});
|
|
27390
28130
|
var ExperimentalConfigSchema = exports_external.object({
|
|
27391
28131
|
aggressive_truncation: exports_external.boolean().optional(),
|
|
27392
28132
|
auto_resume: exports_external.boolean().optional(),
|
|
27393
28133
|
preemptive_compaction: exports_external.boolean().optional(),
|
|
27394
28134
|
preemptive_compaction_threshold: exports_external.number().min(0.5).max(0.95).optional(),
|
|
27395
|
-
truncate_all_tool_outputs: exports_external.boolean().
|
|
28135
|
+
truncate_all_tool_outputs: exports_external.boolean().default(true)
|
|
27396
28136
|
});
|
|
27397
28137
|
var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
27398
28138
|
$schema: exports_external.string().optional(),
|
|
@@ -27477,9 +28217,53 @@ var PLAN_PERMISSION = {
|
|
|
27477
28217
|
webfetch: "allow"
|
|
27478
28218
|
};
|
|
27479
28219
|
|
|
28220
|
+
// src/agents/build-prompt.ts
|
|
28221
|
+
var BUILD_SYSTEM_PROMPT = `<system-reminder>
|
|
28222
|
+
# Build Mode - System Reminder
|
|
28223
|
+
|
|
28224
|
+
BUILD MODE ACTIVE - you are in EXECUTION phase. Your responsibility is to:
|
|
28225
|
+
- Implement features and make code changes
|
|
28226
|
+
- Execute commands and run tests
|
|
28227
|
+
- Fix bugs and refactor code
|
|
28228
|
+
- Deploy and build systems
|
|
28229
|
+
- Make all necessary file modifications
|
|
28230
|
+
|
|
28231
|
+
You have FULL permissions to edit files, run commands, and make system changes.
|
|
28232
|
+
This is the implementation phase - execute decisively and thoroughly.
|
|
28233
|
+
|
|
28234
|
+
---
|
|
28235
|
+
|
|
28236
|
+
## Responsibility
|
|
28237
|
+
|
|
28238
|
+
Your current responsibility is to implement, build, and execute. You should:
|
|
28239
|
+
- Write and modify code to accomplish the user's goals
|
|
28240
|
+
- Run tests and builds to verify your changes
|
|
28241
|
+
- Fix errors and issues that arise
|
|
28242
|
+
- Use all available tools to complete the task efficiently
|
|
28243
|
+
- Delegate to specialized agents when appropriate for better results
|
|
28244
|
+
|
|
28245
|
+
**NOTE:** You should ask the user for clarification when requirements are ambiguous,
|
|
28246
|
+
but once the path is clear, execute confidently. The goal is to deliver working,
|
|
28247
|
+
tested, production-ready solutions.
|
|
28248
|
+
|
|
28249
|
+
---
|
|
28250
|
+
|
|
28251
|
+
## Important
|
|
28252
|
+
|
|
28253
|
+
The user wants you to execute and implement. You SHOULD make edits, run necessary
|
|
28254
|
+
tools, and make changes to accomplish the task. Use your full capabilities to
|
|
28255
|
+
deliver excellent results.
|
|
28256
|
+
</system-reminder>
|
|
28257
|
+
`;
|
|
28258
|
+
var BUILD_PERMISSION = {
|
|
28259
|
+
edit: "ask",
|
|
28260
|
+
bash: "ask",
|
|
28261
|
+
webfetch: "allow"
|
|
28262
|
+
};
|
|
28263
|
+
|
|
27480
28264
|
// src/index.ts
|
|
27481
28265
|
import * as fs7 from "fs";
|
|
27482
|
-
import * as
|
|
28266
|
+
import * as path8 from "path";
|
|
27483
28267
|
var AGENT_NAME_MAP = {
|
|
27484
28268
|
omo: "Sisyphus",
|
|
27485
28269
|
OmO: "Sisyphus",
|
|
@@ -27582,8 +28366,8 @@ function mergeConfigs(base, override) {
|
|
|
27582
28366
|
};
|
|
27583
28367
|
}
|
|
27584
28368
|
function loadPluginConfig(directory) {
|
|
27585
|
-
const userConfigPath =
|
|
27586
|
-
const projectConfigPath =
|
|
28369
|
+
const userConfigPath = path8.join(getUserConfigDir(), "opencode", "oh-my-opencode.json");
|
|
28370
|
+
const projectConfigPath = path8.join(directory, ".opencode", "oh-my-opencode.json");
|
|
27587
28371
|
let config3 = loadConfigFromPath2(userConfigPath) ?? {};
|
|
27588
28372
|
const projectConfig = loadConfigFromPath2(projectConfigPath);
|
|
27589
28373
|
if (projectConfig) {
|
|
@@ -27614,14 +28398,9 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
27614
28398
|
}
|
|
27615
28399
|
return;
|
|
27616
28400
|
};
|
|
27617
|
-
const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer") ? createTodoContinuationEnforcer(ctx) : null;
|
|
27618
28401
|
const contextWindowMonitor = isHookEnabled("context-window-monitor") ? createContextWindowMonitorHook(ctx) : null;
|
|
27619
28402
|
const sessionRecovery = isHookEnabled("session-recovery") ? createSessionRecoveryHook(ctx, { experimental: pluginConfig.experimental }) : null;
|
|
27620
28403
|
const sessionNotification = isHookEnabled("session-notification") ? createSessionNotification(ctx) : null;
|
|
27621
|
-
if (sessionRecovery && todoContinuationEnforcer) {
|
|
27622
|
-
sessionRecovery.setOnAbortCallback(todoContinuationEnforcer.markRecovering);
|
|
27623
|
-
sessionRecovery.setOnRecoveryCompleteCallback(todoContinuationEnforcer.markRecoveryComplete);
|
|
27624
|
-
}
|
|
27625
28404
|
const commentChecker = isHookEnabled("comment-checker") ? createCommentCheckerHooks() : null;
|
|
27626
28405
|
const toolOutputTruncator = isHookEnabled("tool-output-truncator") ? createToolOutputTruncatorHook(ctx, { experimental: pluginConfig.experimental }) : null;
|
|
27627
28406
|
const directoryAgentsInjector = isHookEnabled("directory-agents-injector") ? createDirectoryAgentsInjectorHook(ctx) : null;
|
|
@@ -27650,6 +28429,11 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
27650
28429
|
const interactiveBashSession = isHookEnabled("interactive-bash-session") ? createInteractiveBashSessionHook(ctx) : null;
|
|
27651
28430
|
const emptyMessageSanitizer = isHookEnabled("empty-message-sanitizer") ? createEmptyMessageSanitizerHook() : null;
|
|
27652
28431
|
const backgroundManager = new BackgroundManager(ctx);
|
|
28432
|
+
const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer") ? createTodoContinuationEnforcer(ctx, { backgroundManager }) : null;
|
|
28433
|
+
if (sessionRecovery && todoContinuationEnforcer) {
|
|
28434
|
+
sessionRecovery.setOnAbortCallback(todoContinuationEnforcer.markRecovering);
|
|
28435
|
+
sessionRecovery.setOnRecoveryCompleteCallback(todoContinuationEnforcer.markRecoveryComplete);
|
|
28436
|
+
}
|
|
27653
28437
|
const backgroundNotificationHook = isHookEnabled("background-notification") ? createBackgroundNotificationHook(backgroundManager) : null;
|
|
27654
28438
|
const backgroundTools = createBackgroundTools(backgroundManager, ctx.client);
|
|
27655
28439
|
const callOmoAgent = createCallOmoAgent(ctx, backgroundManager);
|
|
@@ -27693,26 +28477,46 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
27693
28477
|
const userAgents = pluginConfig.claude_code?.agents ?? true ? loadUserAgents() : {};
|
|
27694
28478
|
const projectAgents = pluginConfig.claude_code?.agents ?? true ? loadProjectAgents() : {};
|
|
27695
28479
|
const isSisyphusEnabled = pluginConfig.sisyphus_agent?.disabled !== true;
|
|
28480
|
+
const builderEnabled = pluginConfig.sisyphus_agent?.builder_enabled ?? false;
|
|
28481
|
+
const plannerEnabled = pluginConfig.sisyphus_agent?.planner_enabled ?? true;
|
|
28482
|
+
const replaceBuild = pluginConfig.sisyphus_agent?.replace_build ?? true;
|
|
28483
|
+
const replacePlan = pluginConfig.sisyphus_agent?.replace_plan ?? true;
|
|
27696
28484
|
if (isSisyphusEnabled && builtinAgents.Sisyphus) {
|
|
27697
|
-
const
|
|
27698
|
-
|
|
27699
|
-
const plannerSisyphusBase = {
|
|
27700
|
-
...planConfigWithoutName,
|
|
27701
|
-
prompt: PLAN_SYSTEM_PROMPT,
|
|
27702
|
-
permission: PLAN_PERMISSION,
|
|
27703
|
-
description: `${config3.agent?.plan?.description ?? "Plan agent"} (OhMyOpenCode version)`,
|
|
27704
|
-
color: config3.agent?.plan?.color ?? "#6495ED"
|
|
28485
|
+
const agentConfig = {
|
|
28486
|
+
Sisyphus: builtinAgents.Sisyphus
|
|
27705
28487
|
};
|
|
27706
|
-
|
|
28488
|
+
if (builderEnabled) {
|
|
28489
|
+
const { name: _buildName, ...buildConfigWithoutName } = config3.agent?.build ?? {};
|
|
28490
|
+
const builderSisyphusOverride = pluginConfig.agents?.["Builder-Sisyphus"];
|
|
28491
|
+
const builderSisyphusBase = {
|
|
28492
|
+
...buildConfigWithoutName,
|
|
28493
|
+
prompt: BUILD_SYSTEM_PROMPT,
|
|
28494
|
+
permission: BUILD_PERMISSION,
|
|
28495
|
+
description: `${config3.agent?.build?.description ?? "Build agent"} (OhMyOpenCode version)`,
|
|
28496
|
+
color: config3.agent?.build?.color ?? "#32CD32"
|
|
28497
|
+
};
|
|
28498
|
+
agentConfig["Builder-Sisyphus"] = builderSisyphusOverride ? { ...builderSisyphusBase, ...builderSisyphusOverride } : builderSisyphusBase;
|
|
28499
|
+
}
|
|
28500
|
+
if (plannerEnabled) {
|
|
28501
|
+
const { name: _planName, ...planConfigWithoutName } = config3.agent?.plan ?? {};
|
|
28502
|
+
const plannerSisyphusOverride = pluginConfig.agents?.["Planner-Sisyphus"];
|
|
28503
|
+
const plannerSisyphusBase = {
|
|
28504
|
+
...planConfigWithoutName,
|
|
28505
|
+
prompt: PLAN_SYSTEM_PROMPT,
|
|
28506
|
+
permission: PLAN_PERMISSION,
|
|
28507
|
+
description: `${config3.agent?.plan?.description ?? "Plan agent"} (OhMyOpenCode version)`,
|
|
28508
|
+
color: config3.agent?.plan?.color ?? "#6495ED"
|
|
28509
|
+
};
|
|
28510
|
+
agentConfig["Planner-Sisyphus"] = plannerSisyphusOverride ? { ...plannerSisyphusBase, ...plannerSisyphusOverride } : plannerSisyphusBase;
|
|
28511
|
+
}
|
|
27707
28512
|
config3.agent = {
|
|
27708
|
-
|
|
27709
|
-
"Planner-Sisyphus": plannerSisyphusConfig,
|
|
28513
|
+
...agentConfig,
|
|
27710
28514
|
...Object.fromEntries(Object.entries(builtinAgents).filter(([k]) => k !== "Sisyphus")),
|
|
27711
28515
|
...userAgents,
|
|
27712
28516
|
...projectAgents,
|
|
27713
28517
|
...config3.agent,
|
|
27714
|
-
build: { ...config3.agent?.build, mode: "subagent" },
|
|
27715
|
-
plan: { ...config3.agent?.plan, mode: "subagent" }
|
|
28518
|
+
...replaceBuild ? { build: { ...config3.agent?.build, mode: "subagent" } } : {},
|
|
28519
|
+
...replacePlan ? { plan: { ...config3.agent?.plan, mode: "subagent" } } : {}
|
|
27716
28520
|
};
|
|
27717
28521
|
} else {
|
|
27718
28522
|
config3.agent = {
|