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 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
 
@@ -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,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 getPlatformPackageName2() {
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 = getPlatformPackageName2();
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.29",
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.1",
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
- };