oh-my-opencode 0.1.29 → 0.1.31
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 +11 -1
- package/dist/hooks/anthropic-auto-compact/executor.d.ts +3 -0
- package/dist/hooks/anthropic-auto-compact/index.d.ts +12 -0
- package/dist/hooks/anthropic-auto-compact/parser.d.ts +2 -0
- package/dist/hooks/anthropic-auto-compact/types.d.ts +12 -0
- package/dist/hooks/comment-checker/constants.d.ts +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/index.js +2 -363
- package/dist/tools/ast-grep/tools.d.ts +0 -47
- package/package.json +3 -2
- package/dist/hooks/pulse-monitor.d.ts +0 -10
package/README.ko.md
CHANGED
|
@@ -142,7 +142,17 @@ OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
|
|
|
142
142
|
- **Thinking Disabled Violation**: thinking 이 비활성화인데 thinking 블록이 있을 때 → thinking 블록 제거
|
|
143
143
|
- **Empty Content Message**: 메시지가 thinking/meta 블록만 있고 실제 내용이 없을 때 → 파일시스템을 통해 "(interrupted)" 텍스트 주입
|
|
144
144
|
- **Comment Checker**: 코드 수정 후 불필요한 주석을 감지하여 보고합니다. BDD 패턴, 지시어, 독스트링 등 유효한 주석은 똑똑하게 제외하고, AI가 남긴 흔적을 제거하여 코드를 깨끗하게 유지합니다.
|
|
145
|
-
- **Directory AGENTS.md Injector**: 파일을 읽을 때 `AGENTS.md` 내용을 자동으로 주입합니다. 파일 디렉토리부터 프로젝트 루트까지 탐색하며,
|
|
145
|
+
- **Directory AGENTS.md Injector**: 파일을 읽을 때 `AGENTS.md` 내용을 자동으로 주입합니다. 파일 디렉토리부터 프로젝트 루트까지 탐색하며, 경로 상의 **모든** `AGENTS.md` 파일을 수집합니다. 중첩된 디렉토리별 지침을 지원합니다:
|
|
146
|
+
```
|
|
147
|
+
project/
|
|
148
|
+
├── AGENTS.md # 프로젝트 전체 컨텍스트
|
|
149
|
+
├── src/
|
|
150
|
+
│ ├── AGENTS.md # src 전용 컨텍스트
|
|
151
|
+
│ └── components/
|
|
152
|
+
│ ├── AGENTS.md # 컴포넌트 전용 컨텍스트
|
|
153
|
+
│ └── Button.tsx # 이 파일을 읽으면 위 3개 AGENTS.md 모두 주입
|
|
154
|
+
```
|
|
155
|
+
`Button.tsx`를 읽으면 순서대로 주입됩니다: `project/AGENTS.md` → `src/AGENTS.md` → `components/AGENTS.md`. 각 디렉토리의 컨텍스트는 세션당 한 번만 주입됩니다. Claude Code의 CLAUDE.md 기능에서 영감을 받았습니다.
|
|
146
156
|
|
|
147
157
|
### Agents
|
|
148
158
|
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { AutoCompactState } from "./types";
|
|
2
|
+
export declare function getLastAssistant(sessionID: string, client: any, directory: string): Promise<Record<string, unknown> | null>;
|
|
3
|
+
export declare function executeCompact(sessionID: string, msg: Record<string, unknown>, autoCompactState: AutoCompactState, client: any, directory: string): Promise<void>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
+
export declare function createAnthropicAutoCompactHook(ctx: PluginInput): {
|
|
3
|
+
event: ({ event }: {
|
|
4
|
+
event: {
|
|
5
|
+
type: string;
|
|
6
|
+
properties?: unknown;
|
|
7
|
+
};
|
|
8
|
+
}) => Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
export type { AutoCompactState, ParsedTokenLimitError } from "./types";
|
|
11
|
+
export { parseAnthropicTokenLimitError } from "./parser";
|
|
12
|
+
export { executeCompact, getLastAssistant } from "./executor";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ParsedTokenLimitError {
|
|
2
|
+
currentTokens: number;
|
|
3
|
+
maxTokens: number;
|
|
4
|
+
requestId?: string;
|
|
5
|
+
errorType: string;
|
|
6
|
+
providerID?: string;
|
|
7
|
+
modelID?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface AutoCompactState {
|
|
10
|
+
pendingCompact: Set<string>;
|
|
11
|
+
errorDataBySession: Map<string, ParsedTokenLimitError>;
|
|
12
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const BDD_KEYWORDS: Set<string>;
|
|
2
2
|
export declare const TYPE_CHECKER_PREFIXES: string[];
|
|
3
|
-
export declare const HOOK_MESSAGE_HEADER = "COMMENT/DOCSTRING DETECTED - IMMEDIATE ACTION REQUIRED\n\nYour recent changes contain comments or docstrings, which triggered this hook.\nYou need to take immediate action. You must follow the conditions below.\n(Listed in priority order - you must always act according to this priority order)\n\nCRITICAL WARNING: This hook message MUST NEVER be ignored, even if you receive it multiple times.\nYou MUST provide corresponding explanation or action for EACH occurrence of this message.\nIgnoring this message or failing to respond appropriately is strictly prohibited.\n\nPRIORITY-BASED ACTION GUIDELINES:\n\n1. This is a comment/docstring that already existed before\n\t-> Explain to the user that this is an existing comment/docstring and proceed (justify it)\n\n2. This is a newly written comment: but it's in given, when, then format\n\t-> Tell the user it's a BDD comment and proceed (justify it)\n\t-> Note: This applies to comments only, not docstrings\n\n3. This is a newly written comment/docstring: but it's a necessary comment/docstring\n\t-> Tell the user why this comment/docstring is absolutely necessary and proceed (justify it)\n\t-> Examples of necessary comments: complex algorithms, security-related, performance optimization, regex, mathematical formulas\n\t-> Examples of necessary docstrings: public API documentation, complex module/class interfaces\n\t-> IMPORTANT: Most docstrings are unnecessary if the code is self-explanatory. Only keep truly essential ones.\n\n4. This is a newly written comment/docstring: but it's an unnecessary comment/docstring\n\t-> Apologize to the user and remove the comment/docstring.\n\t-> Make the code itself clearer so it can be understood without comments/docstrings.\n\t-> For verbose docstrings: refactor code to be self-documenting instead of adding lengthy explanations.\n\nMANDATORY REQUIREMENT: You must acknowledge this hook message and take one of the above actions.\nReview in the above priority order and take the corresponding action EVERY TIME this appears.\n\nDetected comments/docstrings:\n";
|
|
3
|
+
export declare const HOOK_MESSAGE_HEADER = "COMMENT/DOCSTRING DETECTED - IMMEDIATE ACTION REQUIRED\n\nYour recent changes contain comments or docstrings, which triggered this hook.\nYou need to take immediate action. You must follow the conditions below.\n(Listed in priority order - you must always act according to this priority order)\n\nCRITICAL WARNING: This hook message MUST NEVER be ignored, even if you receive it multiple times.\nYou MUST provide corresponding explanation or action for EACH occurrence of this message.\nIgnoring this message or failing to respond appropriately is strictly prohibited.\n\nPRIORITY-BASED ACTION GUIDELINES:\n\n1. This is a comment/docstring that already existed before\n\t-> Explain to the user that this is an existing comment/docstring and proceed (justify it)\n\n2. This is a newly written comment: but it's in given, when, then format\n\t-> Tell the user it's a BDD comment and proceed (justify it)\n\t-> Note: This applies to comments only, not docstrings\n\n3. This is a newly written comment/docstring: but it's a necessary comment/docstring\n\t-> Tell the user why this comment/docstring is absolutely necessary and proceed (justify it)\n\t-> Examples of necessary comments: complex algorithms, security-related, performance optimization, regex, mathematical formulas\n\t-> Examples of necessary docstrings: public API documentation, complex module/class interfaces\n\t-> IMPORTANT: Most docstrings are unnecessary if the code is self-explanatory. Only keep truly essential ones.\n\n4. This is a newly written comment/docstring: but it's an unnecessary comment/docstring\n\t-> Apologize to the user and remove the comment/docstring.\n\t-> Make the code itself clearer so it can be understood without comments/docstrings.\n\t-> For verbose docstrings: refactor code to be self-documenting instead of adding lengthy explanations.\n\nCODE SMELL WARNING: Using comments as visual separators (e.g., \"// =========\", \"# ---\", \"// *** Section ***\")\nis a code smell. If you need separators, your file is too long or poorly organized.\nRefactor into smaller modules or use proper code organization instead of comment-based section dividers.\n\nMANDATORY REQUIREMENT: You must acknowledge this hook message and take one of the above actions.\nReview in the above priority order and take the corresponding action EVERY TIME this appears.\n\nDetected comments/docstrings:\n";
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -4,6 +4,6 @@ export { createSessionNotification } from "./session-notification";
|
|
|
4
4
|
export { createSessionRecoveryHook } from "./session-recovery";
|
|
5
5
|
export { createCommentCheckerHooks } from "./comment-checker";
|
|
6
6
|
export { createGrepOutputTruncatorHook } from "./grep-output-truncator";
|
|
7
|
-
export { createPulseMonitorHook } from "./pulse-monitor";
|
|
8
7
|
export { createDirectoryAgentsInjectorHook } from "./directory-agents-injector";
|
|
9
8
|
export { createEmptyTaskResponseDetectorHook } from "./empty-task-response-detector";
|
|
9
|
+
export { createAnthropicAutoCompactHook } from "./anthropic-auto-compact";
|
package/dist/index.js
CHANGED
|
@@ -1336,18 +1336,6 @@ function debugLog2(...args) {
|
|
|
1336
1336
|
fs.appendFileSync(DEBUG_FILE2, msg);
|
|
1337
1337
|
}
|
|
1338
1338
|
}
|
|
1339
|
-
function getPlatformPackageName() {
|
|
1340
|
-
const platform = process.platform;
|
|
1341
|
-
const arch = process.arch;
|
|
1342
|
-
const platformMap = {
|
|
1343
|
-
"darwin-arm64": "@code-yeongyu/comment-checker-darwin-arm64",
|
|
1344
|
-
"darwin-x64": "@code-yeongyu/comment-checker-darwin-x64",
|
|
1345
|
-
"linux-arm64": "@code-yeongyu/comment-checker-linux-arm64",
|
|
1346
|
-
"linux-x64": "@code-yeongyu/comment-checker-linux-x64",
|
|
1347
|
-
"win32-x64": "@code-yeongyu/comment-checker-windows-x64"
|
|
1348
|
-
};
|
|
1349
|
-
return platformMap[`${platform}-${arch}`] ?? null;
|
|
1350
|
-
}
|
|
1351
1339
|
function getBinaryName2() {
|
|
1352
1340
|
return process.platform === "win32" ? "comment-checker.exe" : "comment-checker";
|
|
1353
1341
|
}
|
|
@@ -1365,33 +1353,6 @@ function findCommentCheckerPathSync() {
|
|
|
1365
1353
|
} catch {
|
|
1366
1354
|
debugLog2("main package not installed");
|
|
1367
1355
|
}
|
|
1368
|
-
const platformPkg = getPlatformPackageName();
|
|
1369
|
-
if (platformPkg) {
|
|
1370
|
-
try {
|
|
1371
|
-
const require2 = createRequire2(import.meta.url);
|
|
1372
|
-
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
1373
|
-
const pkgDir = dirname(pkgPath);
|
|
1374
|
-
const binaryPath = join4(pkgDir, "bin", binaryName);
|
|
1375
|
-
if (existsSync3(binaryPath)) {
|
|
1376
|
-
debugLog2("found binary in platform package:", binaryPath);
|
|
1377
|
-
return binaryPath;
|
|
1378
|
-
}
|
|
1379
|
-
} catch {
|
|
1380
|
-
debugLog2("platform package not installed:", platformPkg);
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
if (process.platform === "darwin") {
|
|
1384
|
-
const homebrewPaths = [
|
|
1385
|
-
"/opt/homebrew/bin/comment-checker",
|
|
1386
|
-
"/usr/local/bin/comment-checker"
|
|
1387
|
-
];
|
|
1388
|
-
for (const path2 of homebrewPaths) {
|
|
1389
|
-
if (existsSync3(path2)) {
|
|
1390
|
-
debugLog2("found binary via homebrew:", path2);
|
|
1391
|
-
return path2;
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
1356
|
const cachedPath = getCachedBinaryPath();
|
|
1396
1357
|
if (cachedPath) {
|
|
1397
1358
|
debugLog2("found binary in cache:", cachedPath);
|
|
@@ -1688,115 +1649,6 @@ function createGrepOutputTruncatorHook(ctx) {
|
|
|
1688
1649
|
"tool.execute.after": toolExecuteAfter
|
|
1689
1650
|
};
|
|
1690
1651
|
}
|
|
1691
|
-
// src/hooks/pulse-monitor.ts
|
|
1692
|
-
function createPulseMonitorHook(ctx) {
|
|
1693
|
-
const STANDARD_TIMEOUT = 5 * 60 * 1000;
|
|
1694
|
-
const THINKING_TIMEOUT = 5 * 60 * 1000;
|
|
1695
|
-
const CHECK_INTERVAL = 5 * 1000;
|
|
1696
|
-
let lastHeartbeat = Date.now();
|
|
1697
|
-
let isMonitoring = false;
|
|
1698
|
-
let currentSessionID = null;
|
|
1699
|
-
let monitorTimer = null;
|
|
1700
|
-
let isThinking = false;
|
|
1701
|
-
const startMonitoring = (sessionID) => {
|
|
1702
|
-
if (currentSessionID !== sessionID) {
|
|
1703
|
-
currentSessionID = sessionID;
|
|
1704
|
-
isThinking = false;
|
|
1705
|
-
}
|
|
1706
|
-
lastHeartbeat = Date.now();
|
|
1707
|
-
if (!isMonitoring) {
|
|
1708
|
-
isMonitoring = true;
|
|
1709
|
-
if (monitorTimer)
|
|
1710
|
-
clearInterval(monitorTimer);
|
|
1711
|
-
monitorTimer = setInterval(async () => {
|
|
1712
|
-
if (!isMonitoring || !currentSessionID)
|
|
1713
|
-
return;
|
|
1714
|
-
const timeSinceLastHeartbeat = Date.now() - lastHeartbeat;
|
|
1715
|
-
const currentTimeout = isThinking ? THINKING_TIMEOUT : STANDARD_TIMEOUT;
|
|
1716
|
-
if (timeSinceLastHeartbeat > currentTimeout) {
|
|
1717
|
-
await recoverStalledSession(currentSessionID, timeSinceLastHeartbeat, isThinking);
|
|
1718
|
-
}
|
|
1719
|
-
}, CHECK_INTERVAL);
|
|
1720
|
-
}
|
|
1721
|
-
};
|
|
1722
|
-
const stopMonitoring = () => {
|
|
1723
|
-
isMonitoring = false;
|
|
1724
|
-
if (monitorTimer) {
|
|
1725
|
-
clearInterval(monitorTimer);
|
|
1726
|
-
monitorTimer = null;
|
|
1727
|
-
}
|
|
1728
|
-
};
|
|
1729
|
-
const updateHeartbeat = (isThinkingUpdate) => {
|
|
1730
|
-
if (isMonitoring) {
|
|
1731
|
-
lastHeartbeat = Date.now();
|
|
1732
|
-
if (isThinkingUpdate !== undefined) {
|
|
1733
|
-
isThinking = isThinkingUpdate;
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
};
|
|
1737
|
-
const recoverStalledSession = async (sessionID, stalledDuration, wasThinking) => {
|
|
1738
|
-
stopMonitoring();
|
|
1739
|
-
try {
|
|
1740
|
-
const durationSec = Math.round(stalledDuration / 1000);
|
|
1741
|
-
const typeStr = wasThinking ? "Thinking" : "Standard";
|
|
1742
|
-
await ctx.client.tui.showToast({
|
|
1743
|
-
body: {
|
|
1744
|
-
title: "Pulse Monitor: Cardiac Arrest",
|
|
1745
|
-
message: `Session stalled (${typeStr}) for ${durationSec}s. Defibrillating...`,
|
|
1746
|
-
variant: "error",
|
|
1747
|
-
duration: 5000
|
|
1748
|
-
}
|
|
1749
|
-
}).catch(() => {});
|
|
1750
|
-
await ctx.client.session.abort({ path: { id: sessionID } }).catch(() => {});
|
|
1751
|
-
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
1752
|
-
await ctx.client.session.prompt({
|
|
1753
|
-
path: { id: sessionID },
|
|
1754
|
-
body: { parts: [{ type: "text", text: "The connection was unstable and stalled. Please continue from where you left off." }] },
|
|
1755
|
-
query: { directory: ctx.directory }
|
|
1756
|
-
});
|
|
1757
|
-
startMonitoring(sessionID);
|
|
1758
|
-
} catch (err) {
|
|
1759
|
-
console.error("[PulseMonitor] Recovery failed:", err);
|
|
1760
|
-
stopMonitoring();
|
|
1761
|
-
}
|
|
1762
|
-
};
|
|
1763
|
-
return {
|
|
1764
|
-
event: async (input) => {
|
|
1765
|
-
const { event } = input;
|
|
1766
|
-
const props = event.properties;
|
|
1767
|
-
if (event.type === "session.updated" || event.type === "message.part.updated") {
|
|
1768
|
-
const sessionID = props?.info?.id || props?.sessionID;
|
|
1769
|
-
if (sessionID) {
|
|
1770
|
-
if (!isMonitoring)
|
|
1771
|
-
startMonitoring(sessionID);
|
|
1772
|
-
let thinkingUpdate = undefined;
|
|
1773
|
-
if (event.type === "message.part.updated") {
|
|
1774
|
-
const part = props?.part;
|
|
1775
|
-
if (part) {
|
|
1776
|
-
const THINKING_TYPES2 = ["thinking", "redacted_thinking", "reasoning"];
|
|
1777
|
-
if (THINKING_TYPES2.includes(part.type)) {
|
|
1778
|
-
thinkingUpdate = true;
|
|
1779
|
-
} else if (part.type === "text" || part.type === "tool_use") {
|
|
1780
|
-
thinkingUpdate = false;
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
updateHeartbeat(thinkingUpdate);
|
|
1785
|
-
}
|
|
1786
|
-
} else if (event.type === "session.idle" || event.type === "session.error" || event.type === "session.stopped") {
|
|
1787
|
-
stopMonitoring();
|
|
1788
|
-
}
|
|
1789
|
-
},
|
|
1790
|
-
"tool.execute.before": async () => {
|
|
1791
|
-
stopMonitoring();
|
|
1792
|
-
},
|
|
1793
|
-
"tool.execute.after": async (input) => {
|
|
1794
|
-
if (input.sessionID && currentSessionID === input.sessionID) {
|
|
1795
|
-
lastHeartbeat = Date.now();
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
};
|
|
1799
|
-
}
|
|
1800
1652
|
// src/hooks/directory-agents-injector/index.ts
|
|
1801
1653
|
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
1802
1654
|
import { dirname as dirname2, join as join7, resolve } from "path";
|
|
@@ -15913,7 +15765,7 @@ function isValidBinary(filePath) {
|
|
|
15913
15765
|
return false;
|
|
15914
15766
|
}
|
|
15915
15767
|
}
|
|
15916
|
-
function
|
|
15768
|
+
function getPlatformPackageName() {
|
|
15917
15769
|
const platform = process.platform;
|
|
15918
15770
|
const arch = process.arch;
|
|
15919
15771
|
const platformMap = {
|
|
@@ -15942,7 +15794,7 @@ function findSgCliPathSync() {
|
|
|
15942
15794
|
return sgPath;
|
|
15943
15795
|
}
|
|
15944
15796
|
} catch {}
|
|
15945
|
-
const platformPkg =
|
|
15797
|
+
const platformPkg = getPlatformPackageName();
|
|
15946
15798
|
if (platformPkg) {
|
|
15947
15799
|
try {
|
|
15948
15800
|
const require2 = createRequire4(import.meta.url);
|
|
@@ -16008,37 +15860,9 @@ var CLI_LANGUAGES = [
|
|
|
16008
15860
|
"tsx",
|
|
16009
15861
|
"yaml"
|
|
16010
15862
|
];
|
|
16011
|
-
var NAPI_LANGUAGES = ["html", "javascript", "tsx", "css", "typescript"];
|
|
16012
15863
|
var DEFAULT_TIMEOUT_MS = 300000;
|
|
16013
15864
|
var DEFAULT_MAX_OUTPUT_BYTES = 1 * 1024 * 1024;
|
|
16014
15865
|
var DEFAULT_MAX_MATCHES = 500;
|
|
16015
|
-
var LANG_EXTENSIONS = {
|
|
16016
|
-
bash: [".bash", ".sh", ".zsh", ".bats"],
|
|
16017
|
-
c: [".c", ".h"],
|
|
16018
|
-
cpp: [".cpp", ".cc", ".cxx", ".hpp", ".hxx", ".h"],
|
|
16019
|
-
csharp: [".cs"],
|
|
16020
|
-
css: [".css"],
|
|
16021
|
-
elixir: [".ex", ".exs"],
|
|
16022
|
-
go: [".go"],
|
|
16023
|
-
haskell: [".hs", ".lhs"],
|
|
16024
|
-
html: [".html", ".htm"],
|
|
16025
|
-
java: [".java"],
|
|
16026
|
-
javascript: [".js", ".jsx", ".mjs", ".cjs"],
|
|
16027
|
-
json: [".json"],
|
|
16028
|
-
kotlin: [".kt", ".kts"],
|
|
16029
|
-
lua: [".lua"],
|
|
16030
|
-
nix: [".nix"],
|
|
16031
|
-
php: [".php"],
|
|
16032
|
-
python: [".py", ".pyi"],
|
|
16033
|
-
ruby: [".rb", ".rake"],
|
|
16034
|
-
rust: [".rs"],
|
|
16035
|
-
scala: [".scala", ".sc"],
|
|
16036
|
-
solidity: [".sol"],
|
|
16037
|
-
swift: [".swift"],
|
|
16038
|
-
typescript: [".ts", ".cts", ".mts"],
|
|
16039
|
-
tsx: [".tsx"],
|
|
16040
|
-
yaml: [".yml", ".yaml"]
|
|
16041
|
-
};
|
|
16042
15866
|
|
|
16043
15867
|
// src/tools/ast-grep/cli.ts
|
|
16044
15868
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
@@ -16204,91 +16028,6 @@ async function runSg(options) {
|
|
|
16204
16028
|
};
|
|
16205
16029
|
}
|
|
16206
16030
|
|
|
16207
|
-
// src/tools/ast-grep/napi.ts
|
|
16208
|
-
import { parse as parse5, Lang } from "@ast-grep/napi";
|
|
16209
|
-
var LANG_MAP = {
|
|
16210
|
-
html: Lang.Html,
|
|
16211
|
-
javascript: Lang.JavaScript,
|
|
16212
|
-
tsx: Lang.Tsx,
|
|
16213
|
-
css: Lang.Css,
|
|
16214
|
-
typescript: Lang.TypeScript
|
|
16215
|
-
};
|
|
16216
|
-
function parseCode(code, lang) {
|
|
16217
|
-
const parseLang = LANG_MAP[lang];
|
|
16218
|
-
if (!parseLang) {
|
|
16219
|
-
const supportedLangs = NAPI_LANGUAGES.join(", ");
|
|
16220
|
-
throw new Error(`Unsupported language for NAPI: "${lang}"
|
|
16221
|
-
` + `Supported languages: ${supportedLangs}
|
|
16222
|
-
|
|
16223
|
-
` + `Use ast_grep_search for other languages (25 supported via CLI).`);
|
|
16224
|
-
}
|
|
16225
|
-
return parse5(parseLang, code);
|
|
16226
|
-
}
|
|
16227
|
-
function findPattern(root, pattern) {
|
|
16228
|
-
return root.root().findAll(pattern);
|
|
16229
|
-
}
|
|
16230
|
-
function nodeToRange(node) {
|
|
16231
|
-
const range = node.range();
|
|
16232
|
-
return {
|
|
16233
|
-
start: { line: range.start.line, column: range.start.column },
|
|
16234
|
-
end: { line: range.end.line, column: range.end.column }
|
|
16235
|
-
};
|
|
16236
|
-
}
|
|
16237
|
-
function extractMetaVariablesFromPattern(pattern) {
|
|
16238
|
-
const matches = pattern.match(/\$[A-Z_][A-Z0-9_]*/g) || [];
|
|
16239
|
-
return [...new Set(matches.map((m) => m.slice(1)))];
|
|
16240
|
-
}
|
|
16241
|
-
function extractMetaVariables(node, pattern) {
|
|
16242
|
-
const varNames = extractMetaVariablesFromPattern(pattern);
|
|
16243
|
-
const result = [];
|
|
16244
|
-
for (const name of varNames) {
|
|
16245
|
-
const match = node.getMatch(name);
|
|
16246
|
-
if (match) {
|
|
16247
|
-
result.push({
|
|
16248
|
-
name,
|
|
16249
|
-
text: match.text(),
|
|
16250
|
-
kind: String(match.kind())
|
|
16251
|
-
});
|
|
16252
|
-
}
|
|
16253
|
-
}
|
|
16254
|
-
return result;
|
|
16255
|
-
}
|
|
16256
|
-
function analyzeCode(code, lang, pattern, shouldExtractMetaVars) {
|
|
16257
|
-
const root = parseCode(code, lang);
|
|
16258
|
-
const matches = findPattern(root, pattern);
|
|
16259
|
-
return matches.map((node) => ({
|
|
16260
|
-
text: node.text(),
|
|
16261
|
-
range: nodeToRange(node),
|
|
16262
|
-
kind: String(node.kind()),
|
|
16263
|
-
metaVariables: shouldExtractMetaVars ? extractMetaVariables(node, pattern) : []
|
|
16264
|
-
}));
|
|
16265
|
-
}
|
|
16266
|
-
function transformCode(code, lang, pattern, rewrite) {
|
|
16267
|
-
const root = parseCode(code, lang);
|
|
16268
|
-
const matches = findPattern(root, pattern);
|
|
16269
|
-
if (matches.length === 0) {
|
|
16270
|
-
return { transformed: code, editCount: 0 };
|
|
16271
|
-
}
|
|
16272
|
-
const edits = matches.map((node) => {
|
|
16273
|
-
const metaVars = extractMetaVariables(node, pattern);
|
|
16274
|
-
let replacement = rewrite;
|
|
16275
|
-
for (const mv of metaVars) {
|
|
16276
|
-
replacement = replacement.replace(new RegExp(`\\$${mv.name}`, "g"), mv.text);
|
|
16277
|
-
}
|
|
16278
|
-
return node.replace(replacement);
|
|
16279
|
-
});
|
|
16280
|
-
const transformed = root.root().commitEdits(edits);
|
|
16281
|
-
return { transformed, editCount: edits.length };
|
|
16282
|
-
}
|
|
16283
|
-
function getRootInfo(code, lang) {
|
|
16284
|
-
const root = parseCode(code, lang);
|
|
16285
|
-
const rootNode = root.root();
|
|
16286
|
-
return {
|
|
16287
|
-
kind: String(rootNode.kind()),
|
|
16288
|
-
childCount: rootNode.children().length
|
|
16289
|
-
};
|
|
16290
|
-
}
|
|
16291
|
-
|
|
16292
16031
|
// src/tools/ast-grep/utils.ts
|
|
16293
16032
|
function formatSearchResult(result) {
|
|
16294
16033
|
if (result.error) {
|
|
@@ -16342,36 +16081,6 @@ function formatReplaceResult(result, isDryRun) {
|
|
|
16342
16081
|
return lines.join(`
|
|
16343
16082
|
`);
|
|
16344
16083
|
}
|
|
16345
|
-
function formatAnalyzeResult(results, extractedMetaVars) {
|
|
16346
|
-
if (results.length === 0) {
|
|
16347
|
-
return "No matches found";
|
|
16348
|
-
}
|
|
16349
|
-
const lines = [`Found ${results.length} match(es):
|
|
16350
|
-
`];
|
|
16351
|
-
for (const result of results) {
|
|
16352
|
-
const loc = `L${result.range.start.line + 1}:${result.range.start.column + 1}`;
|
|
16353
|
-
lines.push(`[${loc}] (${result.kind})`);
|
|
16354
|
-
lines.push(` ${result.text}`);
|
|
16355
|
-
if (extractedMetaVars && result.metaVariables.length > 0) {
|
|
16356
|
-
lines.push(" Meta-variables:");
|
|
16357
|
-
for (const mv of result.metaVariables) {
|
|
16358
|
-
lines.push(` $${mv.name} = "${mv.text}" (${mv.kind})`);
|
|
16359
|
-
}
|
|
16360
|
-
}
|
|
16361
|
-
lines.push("");
|
|
16362
|
-
}
|
|
16363
|
-
return lines.join(`
|
|
16364
|
-
`);
|
|
16365
|
-
}
|
|
16366
|
-
function formatTransformResult(original, transformed, editCount) {
|
|
16367
|
-
if (editCount === 0) {
|
|
16368
|
-
return "No matches found to transform";
|
|
16369
|
-
}
|
|
16370
|
-
return `Transformed (${editCount} edit(s)):
|
|
16371
|
-
\`\`\`
|
|
16372
|
-
${transformed}
|
|
16373
|
-
\`\`\``;
|
|
16374
|
-
}
|
|
16375
16084
|
|
|
16376
16085
|
// src/tools/ast-grep/tools.ts
|
|
16377
16086
|
function showOutputToUser(context, output) {
|
|
@@ -16463,72 +16172,6 @@ var ast_grep_replace = tool({
|
|
|
16463
16172
|
}
|
|
16464
16173
|
}
|
|
16465
16174
|
});
|
|
16466
|
-
var ast_grep_languages = tool({
|
|
16467
|
-
description: "List all supported languages for ast-grep tools with their file extensions. " + "Use this to determine valid language options.",
|
|
16468
|
-
args: {},
|
|
16469
|
-
execute: async (_args, context) => {
|
|
16470
|
-
const lines = [`Supported Languages (${CLI_LANGUAGES.length}):`];
|
|
16471
|
-
for (const lang of CLI_LANGUAGES) {
|
|
16472
|
-
const exts = LANG_EXTENSIONS[lang]?.join(", ") || "";
|
|
16473
|
-
lines.push(` ${lang}: ${exts}`);
|
|
16474
|
-
}
|
|
16475
|
-
lines.push("");
|
|
16476
|
-
lines.push(`NAPI (in-memory) languages: ${NAPI_LANGUAGES.join(", ")}`);
|
|
16477
|
-
const output = lines.join(`
|
|
16478
|
-
`);
|
|
16479
|
-
showOutputToUser(context, output);
|
|
16480
|
-
return output;
|
|
16481
|
-
}
|
|
16482
|
-
});
|
|
16483
|
-
var ast_grep_analyze = tool({
|
|
16484
|
-
description: "Parse code and extract AST structure with pattern matching (in-memory). " + "Extracts meta-variable bindings. Only for: html, javascript, tsx, css, typescript. " + "Use for detailed code analysis without file I/O.",
|
|
16485
|
-
args: {
|
|
16486
|
-
code: tool.schema.string().describe("Source code to analyze"),
|
|
16487
|
-
lang: tool.schema.enum(NAPI_LANGUAGES).describe("Language (html, javascript, tsx, css, typescript)"),
|
|
16488
|
-
pattern: tool.schema.string().optional().describe("Pattern to find (omit for root structure)"),
|
|
16489
|
-
extractMetaVars: tool.schema.boolean().optional().describe("Extract meta-variable bindings (default: true)")
|
|
16490
|
-
},
|
|
16491
|
-
execute: async (args, context) => {
|
|
16492
|
-
try {
|
|
16493
|
-
if (!args.pattern) {
|
|
16494
|
-
const info = getRootInfo(args.code, args.lang);
|
|
16495
|
-
const output2 = `Root kind: ${info.kind}
|
|
16496
|
-
Children: ${info.childCount}`;
|
|
16497
|
-
showOutputToUser(context, output2);
|
|
16498
|
-
return output2;
|
|
16499
|
-
}
|
|
16500
|
-
const results = analyzeCode(args.code, args.lang, args.pattern, args.extractMetaVars !== false);
|
|
16501
|
-
const output = formatAnalyzeResult(results, args.extractMetaVars !== false);
|
|
16502
|
-
showOutputToUser(context, output);
|
|
16503
|
-
return output;
|
|
16504
|
-
} catch (e) {
|
|
16505
|
-
const output = `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
16506
|
-
showOutputToUser(context, output);
|
|
16507
|
-
return output;
|
|
16508
|
-
}
|
|
16509
|
-
}
|
|
16510
|
-
});
|
|
16511
|
-
var ast_grep_transform = tool({
|
|
16512
|
-
description: "Transform code in-memory using AST-aware rewriting. " + "Only for: html, javascript, tsx, css, typescript. " + "Returns transformed code without writing to filesystem.",
|
|
16513
|
-
args: {
|
|
16514
|
-
code: tool.schema.string().describe("Source code to transform"),
|
|
16515
|
-
lang: tool.schema.enum(NAPI_LANGUAGES).describe("Language"),
|
|
16516
|
-
pattern: tool.schema.string().describe("Pattern to match"),
|
|
16517
|
-
rewrite: tool.schema.string().describe("Replacement (can use $VAR from pattern)")
|
|
16518
|
-
},
|
|
16519
|
-
execute: async (args, context) => {
|
|
16520
|
-
try {
|
|
16521
|
-
const { transformed, editCount } = transformCode(args.code, args.lang, args.pattern, args.rewrite);
|
|
16522
|
-
const output = formatTransformResult(args.code, transformed, editCount);
|
|
16523
|
-
showOutputToUser(context, output);
|
|
16524
|
-
return output;
|
|
16525
|
-
} catch (e) {
|
|
16526
|
-
const output = `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
16527
|
-
showOutputToUser(context, output);
|
|
16528
|
-
return output;
|
|
16529
|
-
}
|
|
16530
|
-
}
|
|
16531
|
-
});
|
|
16532
16175
|
// src/tools/grep/cli.ts
|
|
16533
16176
|
var {spawn: spawn6 } = globalThis.Bun;
|
|
16534
16177
|
|
|
@@ -17080,7 +16723,6 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
17080
16723
|
const todoContinuationEnforcer = createTodoContinuationEnforcer(ctx);
|
|
17081
16724
|
const contextWindowMonitor = createContextWindowMonitorHook(ctx);
|
|
17082
16725
|
const sessionRecovery = createSessionRecoveryHook(ctx);
|
|
17083
|
-
const pulseMonitor = createPulseMonitorHook(ctx);
|
|
17084
16726
|
const commentChecker = createCommentCheckerHooks();
|
|
17085
16727
|
const grepOutputTruncator = createGrepOutputTruncatorHook(ctx);
|
|
17086
16728
|
const directoryAgentsInjector = createDirectoryAgentsInjectorHook(ctx);
|
|
@@ -17109,7 +16751,6 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
17109
16751
|
event: async (input) => {
|
|
17110
16752
|
await todoContinuationEnforcer(input);
|
|
17111
16753
|
await contextWindowMonitor.event(input);
|
|
17112
|
-
await pulseMonitor.event(input);
|
|
17113
16754
|
await directoryAgentsInjector.event(input);
|
|
17114
16755
|
const { event } = input;
|
|
17115
16756
|
const props = event.properties;
|
|
@@ -17193,7 +16834,6 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
17193
16834
|
}
|
|
17194
16835
|
},
|
|
17195
16836
|
"tool.execute.before": async (input, output) => {
|
|
17196
|
-
await pulseMonitor["tool.execute.before"]();
|
|
17197
16837
|
await commentChecker["tool.execute.before"](input, output);
|
|
17198
16838
|
if (input.sessionID === mainSessionID) {
|
|
17199
16839
|
updateTerminalTitle({
|
|
@@ -17206,7 +16846,6 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
17206
16846
|
}
|
|
17207
16847
|
},
|
|
17208
16848
|
"tool.execute.after": async (input, output) => {
|
|
17209
|
-
await pulseMonitor["tool.execute.after"](input);
|
|
17210
16849
|
await grepOutputTruncator["tool.execute.after"](input, output);
|
|
17211
16850
|
await contextWindowMonitor["tool.execute.after"](input, output);
|
|
17212
16851
|
await commentChecker["tool.execute.after"](input, output);
|
|
@@ -86,50 +86,3 @@ export declare const ast_grep_replace: {
|
|
|
86
86
|
dryRun?: boolean | undefined;
|
|
87
87
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
88
88
|
};
|
|
89
|
-
export declare const ast_grep_languages: {
|
|
90
|
-
description: string;
|
|
91
|
-
args: {};
|
|
92
|
-
execute(args: Record<string, never>, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
93
|
-
};
|
|
94
|
-
export declare const ast_grep_analyze: {
|
|
95
|
-
description: string;
|
|
96
|
-
args: {
|
|
97
|
-
code: import("zod").ZodString;
|
|
98
|
-
lang: import("zod").ZodEnum<{
|
|
99
|
-
typescript: "typescript";
|
|
100
|
-
javascript: "javascript";
|
|
101
|
-
html: "html";
|
|
102
|
-
css: "css";
|
|
103
|
-
tsx: "tsx";
|
|
104
|
-
}>;
|
|
105
|
-
pattern: import("zod").ZodOptional<import("zod").ZodString>;
|
|
106
|
-
extractMetaVars: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
107
|
-
};
|
|
108
|
-
execute(args: {
|
|
109
|
-
code: string;
|
|
110
|
-
lang: "typescript" | "javascript" | "html" | "css" | "tsx";
|
|
111
|
-
pattern?: string | undefined;
|
|
112
|
-
extractMetaVars?: boolean | undefined;
|
|
113
|
-
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
114
|
-
};
|
|
115
|
-
export declare const ast_grep_transform: {
|
|
116
|
-
description: string;
|
|
117
|
-
args: {
|
|
118
|
-
code: import("zod").ZodString;
|
|
119
|
-
lang: import("zod").ZodEnum<{
|
|
120
|
-
typescript: "typescript";
|
|
121
|
-
javascript: "javascript";
|
|
122
|
-
html: "html";
|
|
123
|
-
css: "css";
|
|
124
|
-
tsx: "tsx";
|
|
125
|
-
}>;
|
|
126
|
-
pattern: import("zod").ZodString;
|
|
127
|
-
rewrite: import("zod").ZodString;
|
|
128
|
-
};
|
|
129
|
-
execute(args: {
|
|
130
|
-
code: string;
|
|
131
|
-
lang: "typescript" | "javascript" | "html" | "css" | "tsx";
|
|
132
|
-
pattern: string;
|
|
133
|
-
rewrite: string;
|
|
134
|
-
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
135
|
-
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oh-my-opencode",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.31",
|
|
4
4
|
"description": "OpenCode plugin - custom agents (oracle, librarian) and enhanced features",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -44,13 +44,14 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@ast-grep/cli": "^0.40.0",
|
|
46
46
|
"@ast-grep/napi": "^0.40.0",
|
|
47
|
-
"@code-yeongyu/comment-checker": "^0.4.
|
|
47
|
+
"@code-yeongyu/comment-checker": "^0.4.4",
|
|
48
48
|
"@opencode-ai/plugin": "^1.0.7",
|
|
49
49
|
"xdg-basedir": "^5.1.0",
|
|
50
50
|
"zod": "^4.1.8"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"bun-types": "latest",
|
|
54
|
+
"oh-my-opencode": "^0.1.30",
|
|
54
55
|
"typescript": "^5.7.3"
|
|
55
56
|
},
|
|
56
57
|
"peerDependencies": {
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
};
|