oh-my-opencode 0.1.28 → 0.1.30

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 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` 내용을 자동으로 주입합니다. 파일 디렉토리부터 프로젝트 루트까지 탐색하며, 디렉토리별 컨텍스트를 에이전트에게 제공합니다. Claude Code의 CLAUDE.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
 
package/README.md CHANGED
@@ -138,7 +138,17 @@ I believe in the right tool for the job. For your wallet's sake, use CLIProxyAPI
138
138
  - **Thinking Disabled Violation**: When thinking blocks exist but thinking is disabled → strips thinking blocks
139
139
  - **Empty Content Message**: When message has only thinking/meta blocks without actual content → injects "(interrupted)" text via filesystem
140
140
  - **Comment Checker**: Detects and reports unnecessary comments after code modifications. Smartly ignores valid patterns (BDD, directives, docstrings, shebangs) to keep the codebase clean from AI-generated artifacts.
141
- - **Directory AGENTS.md Injector**: Automatically injects `AGENTS.md` contents when reading files. Searches upward from the file's directory to project root, providing directory-level context to the agent. Inspired by Claude Code's CLAUDE.md feature.
141
+ - **Directory AGENTS.md Injector**: Automatically injects `AGENTS.md` contents when reading files. Searches upward from the file's directory to project root, collecting **all** `AGENTS.md` files along the path hierarchy. This enables nested, directory-specific instructions:
142
+ ```
143
+ project/
144
+ ├── AGENTS.md # Project-wide context
145
+ ├── src/
146
+ │ ├── AGENTS.md # src-specific context
147
+ │ └── components/
148
+ │ ├── AGENTS.md # Component-specific context
149
+ │ └── Button.tsx # Reading this injects ALL 3 AGENTS.md files
150
+ ```
151
+ When reading `Button.tsx`, the hook injects contexts in order: `project/AGENTS.md` → `src/AGENTS.md` → `components/AGENTS.md`. Each directory's context is injected only once per session. Inspired by Claude Code's CLAUDE.md feature.
142
152
 
143
153
  ### Agents
144
154
  - **oracle** (`openai/gpt-5.1`): The architect. Expert in code reviews and strategy. Uses GPT-5.1 for its unmatched logic and reasoning capabilities. Inspired by AmpCode.
@@ -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,2 @@
1
+ import type { ParsedTokenLimitError } from "./types";
2
+ export declare function parseAnthropicTokenLimitError(err: unknown): ParsedTokenLimitError | null;
@@ -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";
@@ -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,111 +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
- };
1795
- }
1796
1652
  // src/hooks/directory-agents-injector/index.ts
1797
1653
  import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
1798
1654
  import { dirname as dirname2, join as join7, resolve } from "path";
@@ -15909,7 +15765,7 @@ function isValidBinary(filePath) {
15909
15765
  return false;
15910
15766
  }
15911
15767
  }
15912
- function getPlatformPackageName2() {
15768
+ function getPlatformPackageName() {
15913
15769
  const platform = process.platform;
15914
15770
  const arch = process.arch;
15915
15771
  const platformMap = {
@@ -15938,7 +15794,7 @@ function findSgCliPathSync() {
15938
15794
  return sgPath;
15939
15795
  }
15940
15796
  } catch {}
15941
- const platformPkg = getPlatformPackageName2();
15797
+ const platformPkg = getPlatformPackageName();
15942
15798
  if (platformPkg) {
15943
15799
  try {
15944
15800
  const require2 = createRequire4(import.meta.url);
@@ -16004,37 +15860,9 @@ var CLI_LANGUAGES = [
16004
15860
  "tsx",
16005
15861
  "yaml"
16006
15862
  ];
16007
- var NAPI_LANGUAGES = ["html", "javascript", "tsx", "css", "typescript"];
16008
15863
  var DEFAULT_TIMEOUT_MS = 300000;
16009
15864
  var DEFAULT_MAX_OUTPUT_BYTES = 1 * 1024 * 1024;
16010
15865
  var DEFAULT_MAX_MATCHES = 500;
16011
- var LANG_EXTENSIONS = {
16012
- bash: [".bash", ".sh", ".zsh", ".bats"],
16013
- c: [".c", ".h"],
16014
- cpp: [".cpp", ".cc", ".cxx", ".hpp", ".hxx", ".h"],
16015
- csharp: [".cs"],
16016
- css: [".css"],
16017
- elixir: [".ex", ".exs"],
16018
- go: [".go"],
16019
- haskell: [".hs", ".lhs"],
16020
- html: [".html", ".htm"],
16021
- java: [".java"],
16022
- javascript: [".js", ".jsx", ".mjs", ".cjs"],
16023
- json: [".json"],
16024
- kotlin: [".kt", ".kts"],
16025
- lua: [".lua"],
16026
- nix: [".nix"],
16027
- php: [".php"],
16028
- python: [".py", ".pyi"],
16029
- ruby: [".rb", ".rake"],
16030
- rust: [".rs"],
16031
- scala: [".scala", ".sc"],
16032
- solidity: [".sol"],
16033
- swift: [".swift"],
16034
- typescript: [".ts", ".cts", ".mts"],
16035
- tsx: [".tsx"],
16036
- yaml: [".yml", ".yaml"]
16037
- };
16038
15866
 
16039
15867
  // src/tools/ast-grep/cli.ts
16040
15868
  var {spawn: spawn5 } = globalThis.Bun;
@@ -16200,91 +16028,6 @@ async function runSg(options) {
16200
16028
  };
16201
16029
  }
16202
16030
 
16203
- // src/tools/ast-grep/napi.ts
16204
- import { parse as parse5, Lang } from "@ast-grep/napi";
16205
- var LANG_MAP = {
16206
- html: Lang.Html,
16207
- javascript: Lang.JavaScript,
16208
- tsx: Lang.Tsx,
16209
- css: Lang.Css,
16210
- typescript: Lang.TypeScript
16211
- };
16212
- function parseCode(code, lang) {
16213
- const parseLang = LANG_MAP[lang];
16214
- if (!parseLang) {
16215
- const supportedLangs = NAPI_LANGUAGES.join(", ");
16216
- throw new Error(`Unsupported language for NAPI: "${lang}"
16217
- ` + `Supported languages: ${supportedLangs}
16218
-
16219
- ` + `Use ast_grep_search for other languages (25 supported via CLI).`);
16220
- }
16221
- return parse5(parseLang, code);
16222
- }
16223
- function findPattern(root, pattern) {
16224
- return root.root().findAll(pattern);
16225
- }
16226
- function nodeToRange(node) {
16227
- const range = node.range();
16228
- return {
16229
- start: { line: range.start.line, column: range.start.column },
16230
- end: { line: range.end.line, column: range.end.column }
16231
- };
16232
- }
16233
- function extractMetaVariablesFromPattern(pattern) {
16234
- const matches = pattern.match(/\$[A-Z_][A-Z0-9_]*/g) || [];
16235
- return [...new Set(matches.map((m) => m.slice(1)))];
16236
- }
16237
- function extractMetaVariables(node, pattern) {
16238
- const varNames = extractMetaVariablesFromPattern(pattern);
16239
- const result = [];
16240
- for (const name of varNames) {
16241
- const match = node.getMatch(name);
16242
- if (match) {
16243
- result.push({
16244
- name,
16245
- text: match.text(),
16246
- kind: String(match.kind())
16247
- });
16248
- }
16249
- }
16250
- return result;
16251
- }
16252
- function analyzeCode(code, lang, pattern, shouldExtractMetaVars) {
16253
- const root = parseCode(code, lang);
16254
- const matches = findPattern(root, pattern);
16255
- return matches.map((node) => ({
16256
- text: node.text(),
16257
- range: nodeToRange(node),
16258
- kind: String(node.kind()),
16259
- metaVariables: shouldExtractMetaVars ? extractMetaVariables(node, pattern) : []
16260
- }));
16261
- }
16262
- function transformCode(code, lang, pattern, rewrite) {
16263
- const root = parseCode(code, lang);
16264
- const matches = findPattern(root, pattern);
16265
- if (matches.length === 0) {
16266
- return { transformed: code, editCount: 0 };
16267
- }
16268
- const edits = matches.map((node) => {
16269
- const metaVars = extractMetaVariables(node, pattern);
16270
- let replacement = rewrite;
16271
- for (const mv of metaVars) {
16272
- replacement = replacement.replace(new RegExp(`\\$${mv.name}`, "g"), mv.text);
16273
- }
16274
- return node.replace(replacement);
16275
- });
16276
- const transformed = root.root().commitEdits(edits);
16277
- return { transformed, editCount: edits.length };
16278
- }
16279
- function getRootInfo(code, lang) {
16280
- const root = parseCode(code, lang);
16281
- const rootNode = root.root();
16282
- return {
16283
- kind: String(rootNode.kind()),
16284
- childCount: rootNode.children().length
16285
- };
16286
- }
16287
-
16288
16031
  // src/tools/ast-grep/utils.ts
16289
16032
  function formatSearchResult(result) {
16290
16033
  if (result.error) {
@@ -16338,36 +16081,6 @@ function formatReplaceResult(result, isDryRun) {
16338
16081
  return lines.join(`
16339
16082
  `);
16340
16083
  }
16341
- function formatAnalyzeResult(results, extractedMetaVars) {
16342
- if (results.length === 0) {
16343
- return "No matches found";
16344
- }
16345
- const lines = [`Found ${results.length} match(es):
16346
- `];
16347
- for (const result of results) {
16348
- const loc = `L${result.range.start.line + 1}:${result.range.start.column + 1}`;
16349
- lines.push(`[${loc}] (${result.kind})`);
16350
- lines.push(` ${result.text}`);
16351
- if (extractedMetaVars && result.metaVariables.length > 0) {
16352
- lines.push(" Meta-variables:");
16353
- for (const mv of result.metaVariables) {
16354
- lines.push(` $${mv.name} = "${mv.text}" (${mv.kind})`);
16355
- }
16356
- }
16357
- lines.push("");
16358
- }
16359
- return lines.join(`
16360
- `);
16361
- }
16362
- function formatTransformResult(original, transformed, editCount) {
16363
- if (editCount === 0) {
16364
- return "No matches found to transform";
16365
- }
16366
- return `Transformed (${editCount} edit(s)):
16367
- \`\`\`
16368
- ${transformed}
16369
- \`\`\``;
16370
- }
16371
16084
 
16372
16085
  // src/tools/ast-grep/tools.ts
16373
16086
  function showOutputToUser(context, output) {
@@ -16459,72 +16172,6 @@ var ast_grep_replace = tool({
16459
16172
  }
16460
16173
  }
16461
16174
  });
16462
- var ast_grep_languages = tool({
16463
- description: "List all supported languages for ast-grep tools with their file extensions. " + "Use this to determine valid language options.",
16464
- args: {},
16465
- execute: async (_args, context) => {
16466
- const lines = [`Supported Languages (${CLI_LANGUAGES.length}):`];
16467
- for (const lang of CLI_LANGUAGES) {
16468
- const exts = LANG_EXTENSIONS[lang]?.join(", ") || "";
16469
- lines.push(` ${lang}: ${exts}`);
16470
- }
16471
- lines.push("");
16472
- lines.push(`NAPI (in-memory) languages: ${NAPI_LANGUAGES.join(", ")}`);
16473
- const output = lines.join(`
16474
- `);
16475
- showOutputToUser(context, output);
16476
- return output;
16477
- }
16478
- });
16479
- var ast_grep_analyze = tool({
16480
- 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.",
16481
- args: {
16482
- code: tool.schema.string().describe("Source code to analyze"),
16483
- lang: tool.schema.enum(NAPI_LANGUAGES).describe("Language (html, javascript, tsx, css, typescript)"),
16484
- pattern: tool.schema.string().optional().describe("Pattern to find (omit for root structure)"),
16485
- extractMetaVars: tool.schema.boolean().optional().describe("Extract meta-variable bindings (default: true)")
16486
- },
16487
- execute: async (args, context) => {
16488
- try {
16489
- if (!args.pattern) {
16490
- const info = getRootInfo(args.code, args.lang);
16491
- const output2 = `Root kind: ${info.kind}
16492
- Children: ${info.childCount}`;
16493
- showOutputToUser(context, output2);
16494
- return output2;
16495
- }
16496
- const results = analyzeCode(args.code, args.lang, args.pattern, args.extractMetaVars !== false);
16497
- const output = formatAnalyzeResult(results, args.extractMetaVars !== false);
16498
- showOutputToUser(context, output);
16499
- return output;
16500
- } catch (e) {
16501
- const output = `Error: ${e instanceof Error ? e.message : String(e)}`;
16502
- showOutputToUser(context, output);
16503
- return output;
16504
- }
16505
- }
16506
- });
16507
- var ast_grep_transform = tool({
16508
- description: "Transform code in-memory using AST-aware rewriting. " + "Only for: html, javascript, tsx, css, typescript. " + "Returns transformed code without writing to filesystem.",
16509
- args: {
16510
- code: tool.schema.string().describe("Source code to transform"),
16511
- lang: tool.schema.enum(NAPI_LANGUAGES).describe("Language"),
16512
- pattern: tool.schema.string().describe("Pattern to match"),
16513
- rewrite: tool.schema.string().describe("Replacement (can use $VAR from pattern)")
16514
- },
16515
- execute: async (args, context) => {
16516
- try {
16517
- const { transformed, editCount } = transformCode(args.code, args.lang, args.pattern, args.rewrite);
16518
- const output = formatTransformResult(args.code, transformed, editCount);
16519
- showOutputToUser(context, output);
16520
- return output;
16521
- } catch (e) {
16522
- const output = `Error: ${e instanceof Error ? e.message : String(e)}`;
16523
- showOutputToUser(context, output);
16524
- return output;
16525
- }
16526
- }
16527
- });
16528
16175
  // src/tools/grep/cli.ts
16529
16176
  var {spawn: spawn6 } = globalThis.Bun;
16530
16177
 
@@ -17076,7 +16723,6 @@ var OhMyOpenCodePlugin = async (ctx) => {
17076
16723
  const todoContinuationEnforcer = createTodoContinuationEnforcer(ctx);
17077
16724
  const contextWindowMonitor = createContextWindowMonitorHook(ctx);
17078
16725
  const sessionRecovery = createSessionRecoveryHook(ctx);
17079
- const pulseMonitor = createPulseMonitorHook(ctx);
17080
16726
  const commentChecker = createCommentCheckerHooks();
17081
16727
  const grepOutputTruncator = createGrepOutputTruncatorHook(ctx);
17082
16728
  const directoryAgentsInjector = createDirectoryAgentsInjectorHook(ctx);
@@ -17105,7 +16751,6 @@ var OhMyOpenCodePlugin = async (ctx) => {
17105
16751
  event: async (input) => {
17106
16752
  await todoContinuationEnforcer(input);
17107
16753
  await contextWindowMonitor.event(input);
17108
- await pulseMonitor.event(input);
17109
16754
  await directoryAgentsInjector.event(input);
17110
16755
  const { event } = input;
17111
16756
  const props = event.properties;
@@ -17189,7 +16834,6 @@ var OhMyOpenCodePlugin = async (ctx) => {
17189
16834
  }
17190
16835
  },
17191
16836
  "tool.execute.before": async (input, output) => {
17192
- await pulseMonitor["tool.execute.before"]();
17193
16837
  await commentChecker["tool.execute.before"](input, output);
17194
16838
  if (input.sessionID === mainSessionID) {
17195
16839
  updateTerminalTitle({
@@ -17202,7 +16846,6 @@ var OhMyOpenCodePlugin = async (ctx) => {
17202
16846
  }
17203
16847
  },
17204
16848
  "tool.execute.after": async (input, output) => {
17205
- await pulseMonitor["tool.execute.after"](input);
17206
16849
  await grepOutputTruncator["tool.execute.after"](input, output);
17207
16850
  await contextWindowMonitor["tool.execute.after"](input, output);
17208
16851
  await commentChecker["tool.execute.after"](input, output);
@@ -5,8 +5,3 @@ export declare function createBuiltinMcps(disabledMcps?: McpName[]): Record<stri
5
5
  url: string;
6
6
  enabled: boolean;
7
7
  }>;
8
- export declare const builtinMcps: Record<"websearch_exa" | "context7", {
9
- type: "remote";
10
- url: string;
11
- enabled: boolean;
12
- }>;
@@ -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.28",
3
+ "version": "0.1.30",
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",
@@ -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
- };