oh-my-opencode 0.1.25 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +10 -0
- package/README.md +10 -0
- package/dist/hooks/grep-output-truncator.d.ts +12 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/pulse-monitor.d.ts +10 -0
- package/dist/index.js +257 -19
- package/dist/tools/ast-grep/index.d.ts +8 -8
- package/dist/tools/ast-grep/tools.d.ts +12 -12
- package/dist/tools/index.d.ts +8 -8
- package/dist/tools/lsp/client.d.ts +5 -1
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -151,12 +151,22 @@ OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
|
|
|
151
151
|
- **frontend-ui-ux-engineer** (`google/gemini-3-pro-preview`): 개발자로 전향한 디자이너라는 설정을 갖고 있습니다. 멋진 UI를 만듭니다. 아름답고 창의적인 UI 코드를 생성하는 데 탁월한 Gemini를 사용합니다.
|
|
152
152
|
- **document-writer** (`google/gemini-3-pro-preview`): 기술 문서 전문가라는 설정을 갖고 있습니다. Gemini 는 문학가입니다. 글을 기가막히게 씁니다.
|
|
153
153
|
|
|
154
|
+
각 에이전트는 메인 에이전트가 알아서 호출하지만, 명시적으로 요청할 수도 있습니다:
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
@oracle 한테 이 부분 설계 고민하고서 아키텍쳐 제안을 부탁해줘
|
|
158
|
+
@librarian 한테 이 부분 어떻게 구현돼있길래 자꾸 안에서 동작이 바뀌는지 알려달라고 해줘
|
|
159
|
+
@explore 한테 이 기능 정책 알려달라고 해줘
|
|
160
|
+
```
|
|
161
|
+
|
|
154
162
|
에이전트의 모델, 프롬프트, 권한은 `oh-my-opencode.json`에서 커스텀할 수 있습니다. 자세한 내용은 [설정](#설정)을 참고하세요.
|
|
155
163
|
|
|
156
164
|
### Tools
|
|
157
165
|
|
|
158
166
|
#### 내장 LSP Tools
|
|
159
167
|
|
|
168
|
+
당신이 에디터에서 사용하는 그 기능을 다른 에이전트들은 사용하지 못합니다. Oh My OpenCode 는 당신만의 그 도구를 LLM Agent 에게 쥐어줍니다. 리팩토링하고, 탐색하고, 분석하는 모든 작업을 OpenCode 의 설정값을 그대로 사용하여 지원합니다.
|
|
169
|
+
|
|
160
170
|
[OpenCode 는 LSP 를 제공하지만](https://opencode.ai/docs/lsp/), 오로지 분석용으로만 제공합니다. 탐색과 리팩토링을 위한 도구는 OpenCode 와 동일한 스펙과 설정으로 Oh My OpenCode 가 제공합니다.
|
|
161
171
|
|
|
162
172
|
- **lsp_hover**: 위치의 타입 정보, 문서, 시그니처 가져오기
|
package/README.md
CHANGED
|
@@ -146,12 +146,22 @@ I believe in the right tool for the job. For your wallet's sake, use CLIProxyAPI
|
|
|
146
146
|
- **frontend-ui-ux-engineer** (`google/gemini-3-pro-preview`): A designer turned developer. Creates stunning UIs. Uses Gemini because its creativity and UI code generation are superior.
|
|
147
147
|
- **document-writer** (`google/gemini-3-pro-preview`): A technical writing expert. Gemini is a wordsmith; it writes prose that flows naturally.
|
|
148
148
|
|
|
149
|
+
Each agent is automatically invoked by the main agent, but you can also explicitly request them:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
@oracle Please think through the design of this part and suggest an architecture.
|
|
153
|
+
@librarian Tell me how this is implemented — why does the behavior keep changing internally?
|
|
154
|
+
@explore Tell me about the policy for this feature.
|
|
155
|
+
```
|
|
156
|
+
|
|
149
157
|
Agent models, prompts, and permissions can be customized via `oh-my-opencode.json`. See [Configuration](#configuration) for details.
|
|
150
158
|
|
|
151
159
|
### Tools
|
|
152
160
|
|
|
153
161
|
#### Built-in LSP Tools
|
|
154
162
|
|
|
163
|
+
The features you use in your editor—other agents cannot access them. Oh My OpenCode hands those very tools to your LLM Agent. Refactoring, navigation, and analysis are all supported using the same OpenCode configuration.
|
|
164
|
+
|
|
155
165
|
[OpenCode provides LSP](https://opencode.ai/docs/lsp/), but only for analysis. Oh My OpenCode equips you with navigation and refactoring tools matching the same specification.
|
|
156
166
|
|
|
157
167
|
- **lsp_hover**: Get type info, docs, signatures at position
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
+
export declare function createGrepOutputTruncatorHook(ctx: PluginInput): {
|
|
3
|
+
"tool.execute.after": (input: {
|
|
4
|
+
tool: string;
|
|
5
|
+
sessionID: string;
|
|
6
|
+
callID: string;
|
|
7
|
+
}, output: {
|
|
8
|
+
title: string;
|
|
9
|
+
output: string;
|
|
10
|
+
metadata: unknown;
|
|
11
|
+
}) => Promise<void>;
|
|
12
|
+
};
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -3,3 +3,5 @@ export { createContextWindowMonitorHook } from "./context-window-monitor";
|
|
|
3
3
|
export { createSessionNotification } from "./session-notification";
|
|
4
4
|
export { createSessionRecoveryHook } from "./session-recovery";
|
|
5
5
|
export { createCommentCheckerHooks } from "./comment-checker";
|
|
6
|
+
export { createGrepOutputTruncatorHook } from "./grep-output-truncator";
|
|
7
|
+
export { createPulseMonitorHook } from "./pulse-monitor";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
+
export declare function createPulseMonitorHook(ctx: PluginInput): {
|
|
3
|
+
event: (input: {
|
|
4
|
+
event: any;
|
|
5
|
+
}) => Promise<void>;
|
|
6
|
+
"tool.execute.before": () => Promise<void>;
|
|
7
|
+
"tool.execute.after": (input: {
|
|
8
|
+
sessionID: string;
|
|
9
|
+
}) => Promise<void>;
|
|
10
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -602,18 +602,13 @@ function createBuiltinAgents(disabledAgents = [], agentOverrides = {}) {
|
|
|
602
602
|
return result;
|
|
603
603
|
}
|
|
604
604
|
// src/hooks/todo-continuation-enforcer.ts
|
|
605
|
-
var CONTINUATION_PROMPT = `[SYSTEM REMINDER - TODO
|
|
605
|
+
var CONTINUATION_PROMPT = `[SYSTEM REMINDER - TODO CONTINUATION]
|
|
606
606
|
|
|
607
|
-
|
|
607
|
+
Incomplete tasks remain in your todo list. Continue working on the next pending task.
|
|
608
608
|
|
|
609
|
-
|
|
610
|
-
-
|
|
611
|
-
-
|
|
612
|
-
- Work honestly and diligently to finish every task
|
|
613
|
-
- Do NOT ask for permission to continue - just proceed with the work
|
|
614
|
-
- Mark each task as completed as soon as you finish it
|
|
615
|
-
|
|
616
|
-
Resume your work NOW.`;
|
|
609
|
+
- Proceed without asking for permission
|
|
610
|
+
- Mark each task complete when finished
|
|
611
|
+
- Do not stop until all tasks are done`;
|
|
617
612
|
function detectInterrupt(error) {
|
|
618
613
|
if (!error)
|
|
619
614
|
return false;
|
|
@@ -694,7 +689,7 @@ function createTodoContinuationEnforcer(ctx) {
|
|
|
694
689
|
type: "text",
|
|
695
690
|
text: `${CONTINUATION_PROMPT}
|
|
696
691
|
|
|
697
|
-
[Status: ${incomplete.length}/${todos.length}
|
|
692
|
+
[Status: ${todos.length - incomplete.length}/${todos.length} completed, ${incomplete.length} remaining]`
|
|
698
693
|
}
|
|
699
694
|
]
|
|
700
695
|
},
|
|
@@ -1566,6 +1561,208 @@ ${result.message}`;
|
|
|
1566
1561
|
debugLog3("CLI: no comments detected");
|
|
1567
1562
|
}
|
|
1568
1563
|
}
|
|
1564
|
+
// src/hooks/grep-output-truncator.ts
|
|
1565
|
+
var ANTHROPIC_ACTUAL_LIMIT2 = 200000;
|
|
1566
|
+
var CHARS_PER_TOKEN_ESTIMATE = 4;
|
|
1567
|
+
var TARGET_MAX_TOKENS = 50000;
|
|
1568
|
+
function estimateTokens(text) {
|
|
1569
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN_ESTIMATE);
|
|
1570
|
+
}
|
|
1571
|
+
function truncateToTokenLimit(output, maxTokens) {
|
|
1572
|
+
const currentTokens = estimateTokens(output);
|
|
1573
|
+
if (currentTokens <= maxTokens) {
|
|
1574
|
+
return { result: output, truncated: false };
|
|
1575
|
+
}
|
|
1576
|
+
const lines = output.split(`
|
|
1577
|
+
`);
|
|
1578
|
+
if (lines.length <= 3) {
|
|
1579
|
+
const maxChars = maxTokens * CHARS_PER_TOKEN_ESTIMATE;
|
|
1580
|
+
return {
|
|
1581
|
+
result: output.slice(0, maxChars) + `
|
|
1582
|
+
|
|
1583
|
+
[Output truncated due to context window limit]`,
|
|
1584
|
+
truncated: true
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
const headerLines = lines.slice(0, 3);
|
|
1588
|
+
const contentLines = lines.slice(3);
|
|
1589
|
+
const headerText = headerLines.join(`
|
|
1590
|
+
`);
|
|
1591
|
+
const headerTokens = estimateTokens(headerText);
|
|
1592
|
+
const availableTokens = maxTokens - headerTokens - 50;
|
|
1593
|
+
if (availableTokens <= 0) {
|
|
1594
|
+
return {
|
|
1595
|
+
result: headerText + `
|
|
1596
|
+
|
|
1597
|
+
[Content truncated due to context window limit]`,
|
|
1598
|
+
truncated: true
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
let resultLines = [];
|
|
1602
|
+
let currentTokenCount = 0;
|
|
1603
|
+
for (const line of contentLines) {
|
|
1604
|
+
const lineTokens = estimateTokens(line + `
|
|
1605
|
+
`);
|
|
1606
|
+
if (currentTokenCount + lineTokens > availableTokens) {
|
|
1607
|
+
break;
|
|
1608
|
+
}
|
|
1609
|
+
resultLines.push(line);
|
|
1610
|
+
currentTokenCount += lineTokens;
|
|
1611
|
+
}
|
|
1612
|
+
const truncatedContent = [...headerLines, ...resultLines].join(`
|
|
1613
|
+
`);
|
|
1614
|
+
const removedCount = contentLines.length - resultLines.length;
|
|
1615
|
+
return {
|
|
1616
|
+
result: truncatedContent + `
|
|
1617
|
+
|
|
1618
|
+
[${removedCount} more lines truncated due to context window limit]`,
|
|
1619
|
+
truncated: true
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
function createGrepOutputTruncatorHook(ctx) {
|
|
1623
|
+
const GREP_TOOLS = ["safe_grep", "Grep"];
|
|
1624
|
+
const toolExecuteAfter = async (input, output) => {
|
|
1625
|
+
if (!GREP_TOOLS.includes(input.tool))
|
|
1626
|
+
return;
|
|
1627
|
+
const { sessionID } = input;
|
|
1628
|
+
try {
|
|
1629
|
+
const response = await ctx.client.session.messages({
|
|
1630
|
+
path: { id: sessionID }
|
|
1631
|
+
});
|
|
1632
|
+
const messages = response.data ?? response;
|
|
1633
|
+
const assistantMessages = messages.filter((m) => m.info.role === "assistant").map((m) => m.info);
|
|
1634
|
+
if (assistantMessages.length === 0)
|
|
1635
|
+
return;
|
|
1636
|
+
const totalInputTokens = assistantMessages.reduce((sum, m) => {
|
|
1637
|
+
const inputTokens = m.tokens?.input ?? 0;
|
|
1638
|
+
const cacheReadTokens = m.tokens?.cache?.read ?? 0;
|
|
1639
|
+
return sum + inputTokens + cacheReadTokens;
|
|
1640
|
+
}, 0);
|
|
1641
|
+
const remainingTokens = ANTHROPIC_ACTUAL_LIMIT2 - totalInputTokens;
|
|
1642
|
+
const maxOutputTokens = Math.min(remainingTokens * 0.5, TARGET_MAX_TOKENS);
|
|
1643
|
+
if (maxOutputTokens <= 0) {
|
|
1644
|
+
output.output = "[Output suppressed - context window exhausted]";
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
const { result, truncated } = truncateToTokenLimit(output.output, maxOutputTokens);
|
|
1648
|
+
if (truncated) {
|
|
1649
|
+
output.output = result;
|
|
1650
|
+
}
|
|
1651
|
+
} catch {}
|
|
1652
|
+
};
|
|
1653
|
+
return {
|
|
1654
|
+
"tool.execute.after": toolExecuteAfter
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
// src/hooks/pulse-monitor.ts
|
|
1658
|
+
function createPulseMonitorHook(ctx) {
|
|
1659
|
+
const STANDARD_TIMEOUT = 5 * 60 * 1000;
|
|
1660
|
+
const THINKING_TIMEOUT = 5 * 60 * 1000;
|
|
1661
|
+
const CHECK_INTERVAL = 5 * 1000;
|
|
1662
|
+
let lastHeartbeat = Date.now();
|
|
1663
|
+
let isMonitoring = false;
|
|
1664
|
+
let currentSessionID = null;
|
|
1665
|
+
let monitorTimer = null;
|
|
1666
|
+
let isThinking = false;
|
|
1667
|
+
const startMonitoring = (sessionID) => {
|
|
1668
|
+
if (currentSessionID !== sessionID) {
|
|
1669
|
+
currentSessionID = sessionID;
|
|
1670
|
+
isThinking = false;
|
|
1671
|
+
}
|
|
1672
|
+
lastHeartbeat = Date.now();
|
|
1673
|
+
if (!isMonitoring) {
|
|
1674
|
+
isMonitoring = true;
|
|
1675
|
+
if (monitorTimer)
|
|
1676
|
+
clearInterval(monitorTimer);
|
|
1677
|
+
monitorTimer = setInterval(async () => {
|
|
1678
|
+
if (!isMonitoring || !currentSessionID)
|
|
1679
|
+
return;
|
|
1680
|
+
const timeSinceLastHeartbeat = Date.now() - lastHeartbeat;
|
|
1681
|
+
const currentTimeout = isThinking ? THINKING_TIMEOUT : STANDARD_TIMEOUT;
|
|
1682
|
+
if (timeSinceLastHeartbeat > currentTimeout) {
|
|
1683
|
+
await recoverStalledSession(currentSessionID, timeSinceLastHeartbeat, isThinking);
|
|
1684
|
+
}
|
|
1685
|
+
}, CHECK_INTERVAL);
|
|
1686
|
+
}
|
|
1687
|
+
};
|
|
1688
|
+
const stopMonitoring = () => {
|
|
1689
|
+
isMonitoring = false;
|
|
1690
|
+
if (monitorTimer) {
|
|
1691
|
+
clearInterval(monitorTimer);
|
|
1692
|
+
monitorTimer = null;
|
|
1693
|
+
}
|
|
1694
|
+
};
|
|
1695
|
+
const updateHeartbeat = (isThinkingUpdate) => {
|
|
1696
|
+
if (isMonitoring) {
|
|
1697
|
+
lastHeartbeat = Date.now();
|
|
1698
|
+
if (isThinkingUpdate !== undefined) {
|
|
1699
|
+
isThinking = isThinkingUpdate;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
};
|
|
1703
|
+
const recoverStalledSession = async (sessionID, stalledDuration, wasThinking) => {
|
|
1704
|
+
stopMonitoring();
|
|
1705
|
+
try {
|
|
1706
|
+
const durationSec = Math.round(stalledDuration / 1000);
|
|
1707
|
+
const typeStr = wasThinking ? "Thinking" : "Standard";
|
|
1708
|
+
await ctx.client.tui.showToast({
|
|
1709
|
+
body: {
|
|
1710
|
+
title: "Pulse Monitor: Cardiac Arrest",
|
|
1711
|
+
message: `Session stalled (${typeStr}) for ${durationSec}s. Defibrillating...`,
|
|
1712
|
+
variant: "error",
|
|
1713
|
+
duration: 5000
|
|
1714
|
+
}
|
|
1715
|
+
}).catch(() => {});
|
|
1716
|
+
await ctx.client.session.abort({ path: { id: sessionID } }).catch(() => {});
|
|
1717
|
+
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
1718
|
+
await ctx.client.session.prompt({
|
|
1719
|
+
path: { id: sessionID },
|
|
1720
|
+
body: { parts: [{ type: "text", text: "The connection was unstable and stalled. Please continue from where you left off." }] },
|
|
1721
|
+
query: { directory: ctx.directory }
|
|
1722
|
+
});
|
|
1723
|
+
startMonitoring(sessionID);
|
|
1724
|
+
} catch (err) {
|
|
1725
|
+
console.error("[PulseMonitor] Recovery failed:", err);
|
|
1726
|
+
stopMonitoring();
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
return {
|
|
1730
|
+
event: async (input) => {
|
|
1731
|
+
const { event } = input;
|
|
1732
|
+
const props = event.properties;
|
|
1733
|
+
if (event.type === "session.updated" || event.type === "message.part.updated") {
|
|
1734
|
+
const sessionID = props?.info?.id || props?.sessionID;
|
|
1735
|
+
if (sessionID) {
|
|
1736
|
+
if (!isMonitoring)
|
|
1737
|
+
startMonitoring(sessionID);
|
|
1738
|
+
let thinkingUpdate = undefined;
|
|
1739
|
+
if (event.type === "message.part.updated") {
|
|
1740
|
+
const part = props?.part;
|
|
1741
|
+
if (part) {
|
|
1742
|
+
const THINKING_TYPES2 = ["thinking", "redacted_thinking", "reasoning"];
|
|
1743
|
+
if (THINKING_TYPES2.includes(part.type)) {
|
|
1744
|
+
thinkingUpdate = true;
|
|
1745
|
+
} else if (part.type === "text" || part.type === "tool_use") {
|
|
1746
|
+
thinkingUpdate = false;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
updateHeartbeat(thinkingUpdate);
|
|
1751
|
+
}
|
|
1752
|
+
} else if (event.type === "session.idle" || event.type === "session.error" || event.type === "session.stopped") {
|
|
1753
|
+
stopMonitoring();
|
|
1754
|
+
}
|
|
1755
|
+
},
|
|
1756
|
+
"tool.execute.before": async () => {
|
|
1757
|
+
stopMonitoring();
|
|
1758
|
+
},
|
|
1759
|
+
"tool.execute.after": async (input) => {
|
|
1760
|
+
if (input.sessionID) {
|
|
1761
|
+
startMonitoring(input.sessionID);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1569
1766
|
// src/features/terminal/title.ts
|
|
1570
1767
|
var STATUS_ICONS = {
|
|
1571
1768
|
ready: "",
|
|
@@ -1774,7 +1971,21 @@ var EXT_TO_LANG = {
|
|
|
1774
1971
|
".svelte": "svelte",
|
|
1775
1972
|
".astro": "astro",
|
|
1776
1973
|
".yaml": "yaml",
|
|
1777
|
-
".yml": "yaml"
|
|
1974
|
+
".yml": "yaml",
|
|
1975
|
+
".json": "json",
|
|
1976
|
+
".jsonc": "jsonc",
|
|
1977
|
+
".html": "html",
|
|
1978
|
+
".htm": "html",
|
|
1979
|
+
".css": "css",
|
|
1980
|
+
".scss": "scss",
|
|
1981
|
+
".less": "less",
|
|
1982
|
+
".sh": "shellscript",
|
|
1983
|
+
".bash": "shellscript",
|
|
1984
|
+
".zsh": "shellscript",
|
|
1985
|
+
".fish": "fish",
|
|
1986
|
+
".md": "markdown",
|
|
1987
|
+
".tf": "terraform",
|
|
1988
|
+
".tfvars": "terraform"
|
|
1778
1989
|
};
|
|
1779
1990
|
// src/tools/lsp/config.ts
|
|
1780
1991
|
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
|
|
@@ -2067,6 +2278,7 @@ class LSPClient {
|
|
|
2067
2278
|
openedFiles = new Set;
|
|
2068
2279
|
stderrBuffer = [];
|
|
2069
2280
|
processExited = false;
|
|
2281
|
+
diagnosticsStore = new Map;
|
|
2070
2282
|
constructor(root, server) {
|
|
2071
2283
|
this.root = root;
|
|
2072
2284
|
this.server = server;
|
|
@@ -2191,7 +2403,11 @@ stderr: ${stderr}` : ""));
|
|
|
2191
2403
|
this.buffer = this.buffer.slice(end);
|
|
2192
2404
|
try {
|
|
2193
2405
|
const msg = JSON.parse(content);
|
|
2194
|
-
if ("
|
|
2406
|
+
if ("method" in msg && !("id" in msg)) {
|
|
2407
|
+
if (msg.method === "textDocument/publishDiagnostics" && msg.params?.uri) {
|
|
2408
|
+
this.diagnosticsStore.set(msg.params.uri, msg.params.diagnostics ?? []);
|
|
2409
|
+
}
|
|
2410
|
+
} else if ("id" in msg && "method" in msg) {
|
|
2195
2411
|
this.handleServerRequest(msg.id, msg.method, msg.params);
|
|
2196
2412
|
} else if ("id" in msg && this.pending.has(msg.id)) {
|
|
2197
2413
|
const handler = this.pending.get(msg.id);
|
|
@@ -2253,9 +2469,15 @@ ${msg}`);
|
|
|
2253
2469
|
\r
|
|
2254
2470
|
${msg}`);
|
|
2255
2471
|
}
|
|
2256
|
-
handleServerRequest(id, method,
|
|
2472
|
+
handleServerRequest(id, method, params) {
|
|
2257
2473
|
if (method === "workspace/configuration") {
|
|
2258
|
-
|
|
2474
|
+
const items = params?.items ?? [];
|
|
2475
|
+
const result = items.map((item) => {
|
|
2476
|
+
if (item.section === "json")
|
|
2477
|
+
return { validate: { enable: true } };
|
|
2478
|
+
return {};
|
|
2479
|
+
});
|
|
2480
|
+
this.respond(id, result);
|
|
2259
2481
|
} else if (method === "client/registerCapability") {
|
|
2260
2482
|
this.respond(id, null);
|
|
2261
2483
|
} else if (method === "window/workDoneProgress/create") {
|
|
@@ -2317,7 +2539,9 @@ ${msg}`);
|
|
|
2317
2539
|
...this.server.initialization
|
|
2318
2540
|
});
|
|
2319
2541
|
this.notify("initialized");
|
|
2320
|
-
this.notify("workspace/didChangeConfiguration", {
|
|
2542
|
+
this.notify("workspace/didChangeConfiguration", {
|
|
2543
|
+
settings: { json: { validate: { enable: true } } }
|
|
2544
|
+
});
|
|
2321
2545
|
await new Promise((r) => setTimeout(r, 300));
|
|
2322
2546
|
}
|
|
2323
2547
|
async openFile(filePath) {
|
|
@@ -2375,11 +2599,18 @@ ${msg}`);
|
|
|
2375
2599
|
}
|
|
2376
2600
|
async diagnostics(filePath) {
|
|
2377
2601
|
const absPath = resolve(filePath);
|
|
2602
|
+
const uri = `file://${absPath}`;
|
|
2378
2603
|
await this.openFile(absPath);
|
|
2379
2604
|
await new Promise((r) => setTimeout(r, 500));
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2605
|
+
try {
|
|
2606
|
+
const result = await this.send("textDocument/diagnostic", {
|
|
2607
|
+
textDocument: { uri }
|
|
2608
|
+
});
|
|
2609
|
+
if (result && typeof result === "object" && "items" in result) {
|
|
2610
|
+
return result;
|
|
2611
|
+
}
|
|
2612
|
+
} catch {}
|
|
2613
|
+
return { items: this.diagnosticsStore.get(uri) ?? [] };
|
|
2383
2614
|
}
|
|
2384
2615
|
async prepareRename(filePath, line, character) {
|
|
2385
2616
|
const absPath = resolve(filePath);
|
|
@@ -2427,6 +2658,7 @@ ${msg}`);
|
|
|
2427
2658
|
this.proc?.kill();
|
|
2428
2659
|
this.proc = null;
|
|
2429
2660
|
this.processExited = true;
|
|
2661
|
+
this.diagnosticsStore.clear();
|
|
2430
2662
|
}
|
|
2431
2663
|
}
|
|
2432
2664
|
// src/tools/lsp/utils.ts
|
|
@@ -16495,7 +16727,9 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
16495
16727
|
const todoContinuationEnforcer = createTodoContinuationEnforcer(ctx);
|
|
16496
16728
|
const contextWindowMonitor = createContextWindowMonitorHook(ctx);
|
|
16497
16729
|
const sessionRecovery = createSessionRecoveryHook(ctx);
|
|
16730
|
+
const pulseMonitor = createPulseMonitorHook(ctx);
|
|
16498
16731
|
const commentChecker = createCommentCheckerHooks();
|
|
16732
|
+
const grepOutputTruncator = createGrepOutputTruncatorHook(ctx);
|
|
16499
16733
|
updateTerminalTitle({ sessionId: "main" });
|
|
16500
16734
|
const pluginConfig = loadPluginConfig(ctx.directory);
|
|
16501
16735
|
let mainSessionID;
|
|
@@ -16521,6 +16755,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
16521
16755
|
event: async (input) => {
|
|
16522
16756
|
await todoContinuationEnforcer(input);
|
|
16523
16757
|
await contextWindowMonitor.event(input);
|
|
16758
|
+
await pulseMonitor.event(input);
|
|
16524
16759
|
const { event } = input;
|
|
16525
16760
|
const props = event.properties;
|
|
16526
16761
|
if (event.type === "session.created") {
|
|
@@ -16603,6 +16838,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
16603
16838
|
}
|
|
16604
16839
|
},
|
|
16605
16840
|
"tool.execute.before": async (input, output) => {
|
|
16841
|
+
await pulseMonitor["tool.execute.before"]();
|
|
16606
16842
|
await commentChecker["tool.execute.before"](input, output);
|
|
16607
16843
|
if (input.sessionID === mainSessionID) {
|
|
16608
16844
|
updateTerminalTitle({
|
|
@@ -16615,6 +16851,8 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
16615
16851
|
}
|
|
16616
16852
|
},
|
|
16617
16853
|
"tool.execute.after": async (input, output) => {
|
|
16854
|
+
await pulseMonitor["tool.execute.after"](input);
|
|
16855
|
+
await grepOutputTruncator["tool.execute.after"](input, output);
|
|
16618
16856
|
await contextWindowMonitor["tool.execute.after"](input, output);
|
|
16619
16857
|
await commentChecker["tool.execute.after"](input, output);
|
|
16620
16858
|
if (input.sessionID === mainSessionID) {
|
|
@@ -20,11 +20,11 @@ export declare const builtinTools: {
|
|
|
20
20
|
swift: "swift";
|
|
21
21
|
elixir: "elixir";
|
|
22
22
|
yaml: "yaml";
|
|
23
|
-
|
|
23
|
+
json: "json";
|
|
24
|
+
html: "html";
|
|
24
25
|
css: "css";
|
|
26
|
+
bash: "bash";
|
|
25
27
|
haskell: "haskell";
|
|
26
|
-
html: "html";
|
|
27
|
-
json: "json";
|
|
28
28
|
kotlin: "kotlin";
|
|
29
29
|
nix: "nix";
|
|
30
30
|
scala: "scala";
|
|
@@ -37,7 +37,7 @@ export declare const builtinTools: {
|
|
|
37
37
|
};
|
|
38
38
|
execute(args: {
|
|
39
39
|
pattern: string;
|
|
40
|
-
lang: "typescript" | "csharp" | "rust" | "php" | "python" | "javascript" | "go" | "c" | "cpp" | "java" | "ruby" | "lua" | "swift" | "elixir" | "yaml" | "
|
|
40
|
+
lang: "typescript" | "csharp" | "rust" | "php" | "python" | "javascript" | "go" | "c" | "cpp" | "java" | "ruby" | "lua" | "swift" | "elixir" | "yaml" | "json" | "html" | "css" | "bash" | "haskell" | "kotlin" | "nix" | "scala" | "solidity" | "tsx";
|
|
41
41
|
paths?: string[] | undefined;
|
|
42
42
|
globs?: string[] | undefined;
|
|
43
43
|
context?: number | undefined;
|
|
@@ -64,11 +64,11 @@ export declare const builtinTools: {
|
|
|
64
64
|
swift: "swift";
|
|
65
65
|
elixir: "elixir";
|
|
66
66
|
yaml: "yaml";
|
|
67
|
-
|
|
67
|
+
json: "json";
|
|
68
|
+
html: "html";
|
|
68
69
|
css: "css";
|
|
70
|
+
bash: "bash";
|
|
69
71
|
haskell: "haskell";
|
|
70
|
-
html: "html";
|
|
71
|
-
json: "json";
|
|
72
72
|
kotlin: "kotlin";
|
|
73
73
|
nix: "nix";
|
|
74
74
|
scala: "scala";
|
|
@@ -82,7 +82,7 @@ export declare const builtinTools: {
|
|
|
82
82
|
execute(args: {
|
|
83
83
|
pattern: string;
|
|
84
84
|
rewrite: string;
|
|
85
|
-
lang: "typescript" | "csharp" | "rust" | "php" | "python" | "javascript" | "go" | "c" | "cpp" | "java" | "ruby" | "lua" | "swift" | "elixir" | "yaml" | "
|
|
85
|
+
lang: "typescript" | "csharp" | "rust" | "php" | "python" | "javascript" | "go" | "c" | "cpp" | "java" | "ruby" | "lua" | "swift" | "elixir" | "yaml" | "json" | "html" | "css" | "bash" | "haskell" | "kotlin" | "nix" | "scala" | "solidity" | "tsx";
|
|
86
86
|
paths?: string[] | undefined;
|
|
87
87
|
globs?: string[] | undefined;
|
|
88
88
|
dryRun?: boolean | undefined;
|
|
@@ -18,11 +18,11 @@ export declare const ast_grep_search: {
|
|
|
18
18
|
swift: "swift";
|
|
19
19
|
elixir: "elixir";
|
|
20
20
|
yaml: "yaml";
|
|
21
|
-
|
|
21
|
+
json: "json";
|
|
22
|
+
html: "html";
|
|
22
23
|
css: "css";
|
|
24
|
+
bash: "bash";
|
|
23
25
|
haskell: "haskell";
|
|
24
|
-
html: "html";
|
|
25
|
-
json: "json";
|
|
26
26
|
kotlin: "kotlin";
|
|
27
27
|
nix: "nix";
|
|
28
28
|
scala: "scala";
|
|
@@ -35,7 +35,7 @@ export declare const ast_grep_search: {
|
|
|
35
35
|
};
|
|
36
36
|
execute(args: {
|
|
37
37
|
pattern: string;
|
|
38
|
-
lang: "typescript" | "csharp" | "rust" | "php" | "python" | "javascript" | "go" | "c" | "cpp" | "java" | "ruby" | "lua" | "swift" | "elixir" | "yaml" | "
|
|
38
|
+
lang: "typescript" | "csharp" | "rust" | "php" | "python" | "javascript" | "go" | "c" | "cpp" | "java" | "ruby" | "lua" | "swift" | "elixir" | "yaml" | "json" | "html" | "css" | "bash" | "haskell" | "kotlin" | "nix" | "scala" | "solidity" | "tsx";
|
|
39
39
|
paths?: string[] | undefined;
|
|
40
40
|
globs?: string[] | undefined;
|
|
41
41
|
context?: number | undefined;
|
|
@@ -62,11 +62,11 @@ export declare const ast_grep_replace: {
|
|
|
62
62
|
swift: "swift";
|
|
63
63
|
elixir: "elixir";
|
|
64
64
|
yaml: "yaml";
|
|
65
|
-
|
|
65
|
+
json: "json";
|
|
66
|
+
html: "html";
|
|
66
67
|
css: "css";
|
|
68
|
+
bash: "bash";
|
|
67
69
|
haskell: "haskell";
|
|
68
|
-
html: "html";
|
|
69
|
-
json: "json";
|
|
70
70
|
kotlin: "kotlin";
|
|
71
71
|
nix: "nix";
|
|
72
72
|
scala: "scala";
|
|
@@ -80,7 +80,7 @@ export declare const ast_grep_replace: {
|
|
|
80
80
|
execute(args: {
|
|
81
81
|
pattern: string;
|
|
82
82
|
rewrite: string;
|
|
83
|
-
lang: "typescript" | "csharp" | "rust" | "php" | "python" | "javascript" | "go" | "c" | "cpp" | "java" | "ruby" | "lua" | "swift" | "elixir" | "yaml" | "
|
|
83
|
+
lang: "typescript" | "csharp" | "rust" | "php" | "python" | "javascript" | "go" | "c" | "cpp" | "java" | "ruby" | "lua" | "swift" | "elixir" | "yaml" | "json" | "html" | "css" | "bash" | "haskell" | "kotlin" | "nix" | "scala" | "solidity" | "tsx";
|
|
84
84
|
paths?: string[] | undefined;
|
|
85
85
|
globs?: string[] | undefined;
|
|
86
86
|
dryRun?: boolean | undefined;
|
|
@@ -98,8 +98,8 @@ export declare const ast_grep_analyze: {
|
|
|
98
98
|
lang: import("zod").ZodEnum<{
|
|
99
99
|
typescript: "typescript";
|
|
100
100
|
javascript: "javascript";
|
|
101
|
-
css: "css";
|
|
102
101
|
html: "html";
|
|
102
|
+
css: "css";
|
|
103
103
|
tsx: "tsx";
|
|
104
104
|
}>;
|
|
105
105
|
pattern: import("zod").ZodOptional<import("zod").ZodString>;
|
|
@@ -107,7 +107,7 @@ export declare const ast_grep_analyze: {
|
|
|
107
107
|
};
|
|
108
108
|
execute(args: {
|
|
109
109
|
code: string;
|
|
110
|
-
lang: "typescript" | "javascript" | "
|
|
110
|
+
lang: "typescript" | "javascript" | "html" | "css" | "tsx";
|
|
111
111
|
pattern?: string | undefined;
|
|
112
112
|
extractMetaVars?: boolean | undefined;
|
|
113
113
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
@@ -119,8 +119,8 @@ export declare const ast_grep_transform: {
|
|
|
119
119
|
lang: import("zod").ZodEnum<{
|
|
120
120
|
typescript: "typescript";
|
|
121
121
|
javascript: "javascript";
|
|
122
|
-
css: "css";
|
|
123
122
|
html: "html";
|
|
123
|
+
css: "css";
|
|
124
124
|
tsx: "tsx";
|
|
125
125
|
}>;
|
|
126
126
|
pattern: import("zod").ZodString;
|
|
@@ -128,7 +128,7 @@ export declare const ast_grep_transform: {
|
|
|
128
128
|
};
|
|
129
129
|
execute(args: {
|
|
130
130
|
code: string;
|
|
131
|
-
lang: "typescript" | "javascript" | "
|
|
131
|
+
lang: "typescript" | "javascript" | "html" | "css" | "tsx";
|
|
132
132
|
pattern: string;
|
|
133
133
|
rewrite: string;
|
|
134
134
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -171,11 +171,11 @@ export declare const builtinTools: {
|
|
|
171
171
|
swift: "swift";
|
|
172
172
|
elixir: "elixir";
|
|
173
173
|
yaml: "yaml";
|
|
174
|
-
|
|
174
|
+
json: "json";
|
|
175
|
+
html: "html";
|
|
175
176
|
css: "css";
|
|
177
|
+
bash: "bash";
|
|
176
178
|
haskell: "haskell";
|
|
177
|
-
html: "html";
|
|
178
|
-
json: "json";
|
|
179
179
|
kotlin: "kotlin";
|
|
180
180
|
nix: "nix";
|
|
181
181
|
scala: "scala";
|
|
@@ -188,7 +188,7 @@ export declare const builtinTools: {
|
|
|
188
188
|
};
|
|
189
189
|
execute(args: {
|
|
190
190
|
pattern: string;
|
|
191
|
-
lang: "typescript" | "csharp" | "rust" | "php" | "python" | "javascript" | "go" | "c" | "cpp" | "java" | "ruby" | "lua" | "swift" | "elixir" | "yaml" | "
|
|
191
|
+
lang: "typescript" | "csharp" | "rust" | "php" | "python" | "javascript" | "go" | "c" | "cpp" | "java" | "ruby" | "lua" | "swift" | "elixir" | "yaml" | "json" | "html" | "css" | "bash" | "haskell" | "kotlin" | "nix" | "scala" | "solidity" | "tsx";
|
|
192
192
|
paths?: string[] | undefined;
|
|
193
193
|
globs?: string[] | undefined;
|
|
194
194
|
context?: number | undefined;
|
|
@@ -215,11 +215,11 @@ export declare const builtinTools: {
|
|
|
215
215
|
swift: "swift";
|
|
216
216
|
elixir: "elixir";
|
|
217
217
|
yaml: "yaml";
|
|
218
|
-
|
|
218
|
+
json: "json";
|
|
219
|
+
html: "html";
|
|
219
220
|
css: "css";
|
|
221
|
+
bash: "bash";
|
|
220
222
|
haskell: "haskell";
|
|
221
|
-
html: "html";
|
|
222
|
-
json: "json";
|
|
223
223
|
kotlin: "kotlin";
|
|
224
224
|
nix: "nix";
|
|
225
225
|
scala: "scala";
|
|
@@ -233,7 +233,7 @@ export declare const builtinTools: {
|
|
|
233
233
|
execute(args: {
|
|
234
234
|
pattern: string;
|
|
235
235
|
rewrite: string;
|
|
236
|
-
lang: "typescript" | "csharp" | "rust" | "php" | "python" | "javascript" | "go" | "c" | "cpp" | "java" | "ruby" | "lua" | "swift" | "elixir" | "yaml" | "
|
|
236
|
+
lang: "typescript" | "csharp" | "rust" | "php" | "python" | "javascript" | "go" | "c" | "cpp" | "java" | "ruby" | "lua" | "swift" | "elixir" | "yaml" | "json" | "html" | "css" | "bash" | "haskell" | "kotlin" | "nix" | "scala" | "solidity" | "tsx";
|
|
237
237
|
paths?: string[] | undefined;
|
|
238
238
|
globs?: string[] | undefined;
|
|
239
239
|
dryRun?: boolean | undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ResolvedServer } from "./config";
|
|
2
|
+
import type { Diagnostic } from "./types";
|
|
2
3
|
declare class LSPServerManager {
|
|
3
4
|
private static instance;
|
|
4
5
|
private clients;
|
|
@@ -26,6 +27,7 @@ export declare class LSPClient {
|
|
|
26
27
|
private openedFiles;
|
|
27
28
|
private stderrBuffer;
|
|
28
29
|
private processExited;
|
|
30
|
+
private diagnosticsStore;
|
|
29
31
|
constructor(root: string, server: ResolvedServer);
|
|
30
32
|
start(): Promise<void>;
|
|
31
33
|
private startReading;
|
|
@@ -44,7 +46,9 @@ export declare class LSPClient {
|
|
|
44
46
|
references(filePath: string, line: number, character: number, includeDeclaration?: boolean): Promise<unknown>;
|
|
45
47
|
documentSymbols(filePath: string): Promise<unknown>;
|
|
46
48
|
workspaceSymbols(query: string): Promise<unknown>;
|
|
47
|
-
diagnostics(filePath: string): Promise<
|
|
49
|
+
diagnostics(filePath: string): Promise<{
|
|
50
|
+
items: Diagnostic[];
|
|
51
|
+
}>;
|
|
48
52
|
prepareRename(filePath: string, line: number, character: number): Promise<unknown>;
|
|
49
53
|
rename(filePath: string, line: number, character: number, newName: string): Promise<unknown>;
|
|
50
54
|
codeAction(filePath: string, startLine: number, startChar: number, endLine: number, endChar: number, only?: string[]): Promise<unknown>;
|