oh-my-opencode 2.4.1 → 2.4.2

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.
@@ -9,6 +9,9 @@ interface ToolExecuteOutput {
9
9
  output: string;
10
10
  metadata: unknown;
11
11
  }
12
+ interface ToolExecuteBeforeOutput {
13
+ args: unknown;
14
+ }
12
15
  interface EventInput {
13
16
  event: {
14
17
  type: string;
@@ -16,6 +19,7 @@ interface EventInput {
16
19
  };
17
20
  }
18
21
  export declare function createDirectoryAgentsInjectorHook(ctx: PluginInput): {
22
+ "tool.execute.before": (input: ToolExecuteInput, output: ToolExecuteBeforeOutput) => Promise<void>;
19
23
  "tool.execute.after": (input: ToolExecuteInput, output: ToolExecuteOutput) => Promise<void>;
20
24
  event: ({ event }: EventInput) => Promise<void>;
21
25
  };
@@ -9,6 +9,9 @@ interface ToolExecuteOutput {
9
9
  output: string;
10
10
  metadata: unknown;
11
11
  }
12
+ interface ToolExecuteBeforeOutput {
13
+ args: unknown;
14
+ }
12
15
  interface EventInput {
13
16
  event: {
14
17
  type: string;
@@ -16,6 +19,7 @@ interface EventInput {
16
19
  };
17
20
  }
18
21
  export declare function createDirectoryReadmeInjectorHook(ctx: PluginInput): {
22
+ "tool.execute.before": (input: ToolExecuteInput, output: ToolExecuteBeforeOutput) => Promise<void>;
19
23
  "tool.execute.after": (input: ToolExecuteInput, output: ToolExecuteOutput) => Promise<void>;
20
24
  event: ({ event }: EventInput) => Promise<void>;
21
25
  };
@@ -9,6 +9,9 @@ interface ToolExecuteOutput {
9
9
  output: string;
10
10
  metadata: unknown;
11
11
  }
12
+ interface ToolExecuteBeforeOutput {
13
+ args: unknown;
14
+ }
12
15
  interface EventInput {
13
16
  event: {
14
17
  type: string;
@@ -16,6 +19,7 @@ interface EventInput {
16
19
  };
17
20
  }
18
21
  export declare function createRulesInjectorHook(ctx: PluginInput): {
22
+ "tool.execute.before": (input: ToolExecuteInput, output: ToolExecuteBeforeOutput) => Promise<void>;
19
23
  "tool.execute.after": (input: ToolExecuteInput, output: ToolExecuteOutput) => Promise<void>;
20
24
  event: ({ event }: EventInput) => Promise<void>;
21
25
  };
package/dist/index.js CHANGED
@@ -3271,8 +3271,7 @@ function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory
3271
3271
  config = { ...config, prompt: config.prompt + envContext };
3272
3272
  }
3273
3273
  if (override) {
3274
- const { model: _, ...restOverride } = override;
3275
- config = mergeAgentConfig(config, restOverride);
3274
+ config = mergeAgentConfig(config, override);
3276
3275
  }
3277
3276
  result[name] = config;
3278
3277
  }
@@ -4921,18 +4920,19 @@ function clearInjectedPaths(sessionID) {
4921
4920
  // src/hooks/directory-agents-injector/index.ts
4922
4921
  function createDirectoryAgentsInjectorHook(ctx) {
4923
4922
  const sessionCaches = new Map;
4923
+ const pendingBatchReads = new Map;
4924
4924
  function getSessionCache(sessionID) {
4925
4925
  if (!sessionCaches.has(sessionID)) {
4926
4926
  sessionCaches.set(sessionID, loadInjectedPaths(sessionID));
4927
4927
  }
4928
4928
  return sessionCaches.get(sessionID);
4929
4929
  }
4930
- function resolveFilePath2(title) {
4931
- if (!title)
4930
+ function resolveFilePath2(path4) {
4931
+ if (!path4)
4932
4932
  return null;
4933
- if (title.startsWith("/"))
4934
- return title;
4935
- return resolve2(ctx.directory, title);
4933
+ if (path4.startsWith("/"))
4934
+ return path4;
4935
+ return resolve2(ctx.directory, path4);
4936
4936
  }
4937
4937
  function findAgentsMdUp(startDir) {
4938
4938
  const found = [];
@@ -4953,35 +4953,59 @@ function createDirectoryAgentsInjectorHook(ctx) {
4953
4953
  }
4954
4954
  return found.reverse();
4955
4955
  }
4956
- const toolExecuteAfter = async (input, output) => {
4957
- if (input.tool.toLowerCase() !== "read")
4958
- return;
4959
- const filePath = resolveFilePath2(output.title);
4960
- if (!filePath)
4956
+ function processFilePathForInjection(filePath, sessionID, output) {
4957
+ const resolved = resolveFilePath2(filePath);
4958
+ if (!resolved)
4961
4959
  return;
4962
- const dir = dirname2(filePath);
4963
- const cache = getSessionCache(input.sessionID);
4960
+ const dir = dirname2(resolved);
4961
+ const cache = getSessionCache(sessionID);
4964
4962
  const agentsPaths = findAgentsMdUp(dir);
4965
- const toInject = [];
4966
4963
  for (const agentsPath of agentsPaths) {
4967
4964
  const agentsDir = dirname2(agentsPath);
4968
4965
  if (cache.has(agentsDir))
4969
4966
  continue;
4970
4967
  try {
4971
4968
  const content = readFileSync5(agentsPath, "utf-8");
4972
- toInject.push({ path: agentsPath, content });
4969
+ output.output += `
4970
+
4971
+ [Directory Context: ${agentsPath}]
4972
+ ${content}`;
4973
4973
  cache.add(agentsDir);
4974
4974
  } catch {}
4975
4975
  }
4976
- if (toInject.length === 0)
4976
+ saveInjectedPaths(sessionID, cache);
4977
+ }
4978
+ const toolExecuteBefore = async (input, output) => {
4979
+ if (input.tool.toLowerCase() !== "batch")
4977
4980
  return;
4978
- for (const { path: path4, content } of toInject) {
4979
- output.output += `
4980
-
4981
- [Directory Context: ${path4}]
4982
- ${content}`;
4981
+ const args = output.args;
4982
+ if (!args?.tool_calls)
4983
+ return;
4984
+ const readFilePaths = [];
4985
+ for (const call of args.tool_calls) {
4986
+ if (call.tool.toLowerCase() === "read" && call.parameters?.filePath) {
4987
+ readFilePaths.push(call.parameters.filePath);
4988
+ }
4989
+ }
4990
+ if (readFilePaths.length > 0) {
4991
+ pendingBatchReads.set(input.callID, readFilePaths);
4992
+ }
4993
+ };
4994
+ const toolExecuteAfter = async (input, output) => {
4995
+ const toolName = input.tool.toLowerCase();
4996
+ if (toolName === "read") {
4997
+ processFilePathForInjection(output.title, input.sessionID, output);
4998
+ return;
4999
+ }
5000
+ if (toolName === "batch") {
5001
+ const filePaths = pendingBatchReads.get(input.callID);
5002
+ if (filePaths) {
5003
+ for (const filePath of filePaths) {
5004
+ processFilePathForInjection(filePath, input.sessionID, output);
5005
+ }
5006
+ pendingBatchReads.delete(input.callID);
5007
+ }
4983
5008
  }
4984
- saveInjectedPaths(input.sessionID, cache);
4985
5009
  };
4986
5010
  const eventHandler = async ({ event }) => {
4987
5011
  const props = event.properties;
@@ -5001,6 +5025,7 @@ ${content}`;
5001
5025
  }
5002
5026
  };
5003
5027
  return {
5028
+ "tool.execute.before": toolExecuteBefore,
5004
5029
  "tool.execute.after": toolExecuteAfter,
5005
5030
  event: eventHandler
5006
5031
  };
@@ -5062,18 +5087,19 @@ function clearInjectedPaths2(sessionID) {
5062
5087
  // src/hooks/directory-readme-injector/index.ts
5063
5088
  function createDirectoryReadmeInjectorHook(ctx) {
5064
5089
  const sessionCaches = new Map;
5090
+ const pendingBatchReads = new Map;
5065
5091
  function getSessionCache(sessionID) {
5066
5092
  if (!sessionCaches.has(sessionID)) {
5067
5093
  sessionCaches.set(sessionID, loadInjectedPaths2(sessionID));
5068
5094
  }
5069
5095
  return sessionCaches.get(sessionID);
5070
5096
  }
5071
- function resolveFilePath2(title) {
5072
- if (!title)
5097
+ function resolveFilePath2(path4) {
5098
+ if (!path4)
5073
5099
  return null;
5074
- if (title.startsWith("/"))
5075
- return title;
5076
- return resolve3(ctx.directory, title);
5100
+ if (path4.startsWith("/"))
5101
+ return path4;
5102
+ return resolve3(ctx.directory, path4);
5077
5103
  }
5078
5104
  function findReadmeMdUp(startDir) {
5079
5105
  const found = [];
@@ -5094,35 +5120,59 @@ function createDirectoryReadmeInjectorHook(ctx) {
5094
5120
  }
5095
5121
  return found.reverse();
5096
5122
  }
5097
- const toolExecuteAfter = async (input, output) => {
5098
- if (input.tool.toLowerCase() !== "read")
5099
- return;
5100
- const filePath = resolveFilePath2(output.title);
5101
- if (!filePath)
5123
+ function processFilePathForInjection(filePath, sessionID, output) {
5124
+ const resolved = resolveFilePath2(filePath);
5125
+ if (!resolved)
5102
5126
  return;
5103
- const dir = dirname3(filePath);
5104
- const cache = getSessionCache(input.sessionID);
5127
+ const dir = dirname3(resolved);
5128
+ const cache = getSessionCache(sessionID);
5105
5129
  const readmePaths = findReadmeMdUp(dir);
5106
- const toInject = [];
5107
5130
  for (const readmePath of readmePaths) {
5108
5131
  const readmeDir = dirname3(readmePath);
5109
5132
  if (cache.has(readmeDir))
5110
5133
  continue;
5111
5134
  try {
5112
5135
  const content = readFileSync7(readmePath, "utf-8");
5113
- toInject.push({ path: readmePath, content });
5136
+ output.output += `
5137
+
5138
+ [Project README: ${readmePath}]
5139
+ ${content}`;
5114
5140
  cache.add(readmeDir);
5115
5141
  } catch {}
5116
5142
  }
5117
- if (toInject.length === 0)
5143
+ saveInjectedPaths2(sessionID, cache);
5144
+ }
5145
+ const toolExecuteBefore = async (input, output) => {
5146
+ if (input.tool.toLowerCase() !== "batch")
5118
5147
  return;
5119
- for (const { path: path4, content } of toInject) {
5120
- output.output += `
5121
-
5122
- [Project README: ${path4}]
5123
- ${content}`;
5148
+ const args = output.args;
5149
+ if (!args?.tool_calls)
5150
+ return;
5151
+ const readFilePaths = [];
5152
+ for (const call of args.tool_calls) {
5153
+ if (call.tool.toLowerCase() === "read" && call.parameters?.filePath) {
5154
+ readFilePaths.push(call.parameters.filePath);
5155
+ }
5156
+ }
5157
+ if (readFilePaths.length > 0) {
5158
+ pendingBatchReads.set(input.callID, readFilePaths);
5159
+ }
5160
+ };
5161
+ const toolExecuteAfter = async (input, output) => {
5162
+ const toolName = input.tool.toLowerCase();
5163
+ if (toolName === "read") {
5164
+ processFilePathForInjection(output.title, input.sessionID, output);
5165
+ return;
5166
+ }
5167
+ if (toolName === "batch") {
5168
+ const filePaths = pendingBatchReads.get(input.callID);
5169
+ if (filePaths) {
5170
+ for (const filePath of filePaths) {
5171
+ processFilePathForInjection(filePath, input.sessionID, output);
5172
+ }
5173
+ pendingBatchReads.delete(input.callID);
5174
+ }
5124
5175
  }
5125
- saveInjectedPaths2(input.sessionID, cache);
5126
5176
  };
5127
5177
  const eventHandler = async ({ event }) => {
5128
5178
  const props = event.properties;
@@ -5142,6 +5192,7 @@ ${content}`;
5142
5192
  }
5143
5193
  };
5144
5194
  return {
5195
+ "tool.execute.before": toolExecuteBefore,
5145
5196
  "tool.execute.after": toolExecuteAfter,
5146
5197
  event: eventHandler
5147
5198
  };
@@ -7740,29 +7791,28 @@ function clearInjectedRules(sessionID) {
7740
7791
  var TRACKED_TOOLS = ["read", "write", "edit", "multiedit"];
7741
7792
  function createRulesInjectorHook(ctx) {
7742
7793
  const sessionCaches = new Map;
7794
+ const pendingBatchFiles = new Map;
7743
7795
  function getSessionCache(sessionID) {
7744
7796
  if (!sessionCaches.has(sessionID)) {
7745
7797
  sessionCaches.set(sessionID, loadInjectedRules(sessionID));
7746
7798
  }
7747
7799
  return sessionCaches.get(sessionID);
7748
7800
  }
7749
- function resolveFilePath2(title) {
7750
- if (!title)
7801
+ function resolveFilePath2(path4) {
7802
+ if (!path4)
7751
7803
  return null;
7752
- if (title.startsWith("/"))
7753
- return title;
7754
- return resolve4(ctx.directory, title);
7804
+ if (path4.startsWith("/"))
7805
+ return path4;
7806
+ return resolve4(ctx.directory, path4);
7755
7807
  }
7756
- const toolExecuteAfter = async (input, output) => {
7757
- if (!TRACKED_TOOLS.includes(input.tool.toLowerCase()))
7758
- return;
7759
- const filePath = resolveFilePath2(output.title);
7760
- if (!filePath)
7808
+ function processFilePathForInjection(filePath, sessionID, output) {
7809
+ const resolved = resolveFilePath2(filePath);
7810
+ if (!resolved)
7761
7811
  return;
7762
- const projectRoot = findProjectRoot(filePath);
7763
- const cache2 = getSessionCache(input.sessionID);
7812
+ const projectRoot = findProjectRoot(resolved);
7813
+ const cache2 = getSessionCache(sessionID);
7764
7814
  const home = homedir10();
7765
- const ruleFileCandidates = findRuleFiles(projectRoot, home, filePath);
7815
+ const ruleFileCandidates = findRuleFiles(projectRoot, home, resolved);
7766
7816
  const toInject = [];
7767
7817
  for (const candidate of ruleFileCandidates) {
7768
7818
  if (isDuplicateByRealPath(candidate.realPath, cache2.realPaths))
@@ -7770,7 +7820,7 @@ function createRulesInjectorHook(ctx) {
7770
7820
  try {
7771
7821
  const rawContent = readFileSync10(candidate.path, "utf-8");
7772
7822
  const { metadata, body } = parseRuleFrontmatter(rawContent);
7773
- const matchResult = shouldApplyRule(metadata, filePath, projectRoot);
7823
+ const matchResult = shouldApplyRule(metadata, resolved, projectRoot);
7774
7824
  if (!matchResult.applies)
7775
7825
  continue;
7776
7826
  const contentHash = createContentHash(body);
@@ -7797,7 +7847,46 @@ function createRulesInjectorHook(ctx) {
7797
7847
  [Match: ${rule.matchReason}]
7798
7848
  ${rule.content}`;
7799
7849
  }
7800
- saveInjectedRules(input.sessionID, cache2);
7850
+ saveInjectedRules(sessionID, cache2);
7851
+ }
7852
+ function extractFilePathFromToolCall(call) {
7853
+ const params = call.parameters;
7854
+ return params?.filePath ?? params?.file_path ?? params?.path;
7855
+ }
7856
+ const toolExecuteBefore = async (input, output) => {
7857
+ if (input.tool.toLowerCase() !== "batch")
7858
+ return;
7859
+ const args = output.args;
7860
+ if (!args?.tool_calls)
7861
+ return;
7862
+ const filePaths = [];
7863
+ for (const call of args.tool_calls) {
7864
+ if (TRACKED_TOOLS.includes(call.tool.toLowerCase())) {
7865
+ const filePath = extractFilePathFromToolCall(call);
7866
+ if (filePath) {
7867
+ filePaths.push(filePath);
7868
+ }
7869
+ }
7870
+ }
7871
+ if (filePaths.length > 0) {
7872
+ pendingBatchFiles.set(input.callID, filePaths);
7873
+ }
7874
+ };
7875
+ const toolExecuteAfter = async (input, output) => {
7876
+ const toolName = input.tool.toLowerCase();
7877
+ if (TRACKED_TOOLS.includes(toolName)) {
7878
+ processFilePathForInjection(output.title, input.sessionID, output);
7879
+ return;
7880
+ }
7881
+ if (toolName === "batch") {
7882
+ const filePaths = pendingBatchFiles.get(input.callID);
7883
+ if (filePaths) {
7884
+ for (const filePath of filePaths) {
7885
+ processFilePathForInjection(filePath, input.sessionID, output);
7886
+ }
7887
+ pendingBatchFiles.delete(input.callID);
7888
+ }
7889
+ }
7801
7890
  };
7802
7891
  const eventHandler = async ({ event }) => {
7803
7892
  const props = event.properties;
@@ -7817,6 +7906,7 @@ ${rule.content}`;
7817
7906
  }
7818
7907
  };
7819
7908
  return {
7909
+ "tool.execute.before": toolExecuteBefore,
7820
7910
  "tool.execute.after": toolExecuteAfter,
7821
7911
  event: eventHandler
7822
7912
  };
@@ -27826,6 +27916,9 @@ var OhMyOpenCodePlugin = async (ctx) => {
27826
27916
  await claudeCodeHooks["tool.execute.before"](input, output);
27827
27917
  await nonInteractiveEnv?.["tool.execute.before"](input, output);
27828
27918
  await commentChecker?.["tool.execute.before"](input, output);
27919
+ await directoryAgentsInjector?.["tool.execute.before"]?.(input, output);
27920
+ await directoryReadmeInjector?.["tool.execute.before"]?.(input, output);
27921
+ await rulesInjector?.["tool.execute.before"]?.(input, output);
27829
27922
  if (input.tool === "task") {
27830
27923
  const args = output.args;
27831
27924
  const subagentType = args.subagent_type;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode",
3
- "version": "2.4.1",
3
+ "version": "2.4.2",
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",