opencode-immune 1.0.85 → 1.0.87

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.
@@ -5518,31 +5518,6 @@ async function fileHash(filePath) {
5518
5518
  return "";
5519
5519
  }
5520
5520
  }
5521
- async function fileExists(filePath) {
5522
- try {
5523
- await stat(filePath);
5524
- return true;
5525
- } catch {
5526
- return false;
5527
- }
5528
- }
5529
- async function getHarnessIntegrityIssue(directory) {
5530
- try {
5531
- const configPath = join(directory, "opencode.json");
5532
- const raw = await readFile(configPath, "utf-8");
5533
- const config = JSON.parse(raw);
5534
- const plugins = Array.isArray(config.plugin) ? config.plugin : [];
5535
- const usesLoader = plugins.includes(".opencode/plugin-loader.cjs") || plugins.includes(".opencode\\plugin-loader.cjs");
5536
- if (!usesLoader) return null;
5537
- const loaderPath = join(directory, ".opencode", "plugin-loader.cjs");
5538
- if (!await fileExists(loaderPath)) {
5539
- return "opencode.json references .opencode/plugin-loader.cjs but the file is missing";
5540
- }
5541
- } catch {
5542
- return null;
5543
- }
5544
- return null;
5545
- }
5546
5521
  function parseImmunePluginSpec(value) {
5547
5522
  const trimmed = value.trim();
5548
5523
  const match = trimmed.match(/^opencode-immune(?:@(.+))?$/);
@@ -5651,19 +5626,13 @@ async function syncHarness(state) {
5651
5626
  const release = await fetchLatestHarnessRelease(state.input.directory, repo, token);
5652
5627
  if (!release) return;
5653
5628
  const localVersion = await readLocalHarnessVersion(state.input.directory);
5654
- const integrityIssue = await getHarnessIntegrityIssue(state.input.directory);
5655
- if (localVersion === release.tagName && !integrityIssue) {
5629
+ if (localVersion === release.tagName) {
5656
5630
  pluginLog.info(
5657
5631
  `[opencode-immune] Harness sync: already up to date (${release.tagName})`
5658
5632
  );
5659
5633
  return;
5660
5634
  }
5661
- if (localVersion === release.tagName && integrityIssue) {
5662
- pluginLog.warn(
5663
- `[opencode-immune] Harness sync: integrity issue detected for ${release.tagName}: ${integrityIssue}. Reinstalling harness files from release.`
5664
- );
5665
- }
5666
- if (localVersion && localVersion !== release.tagName) {
5635
+ if (localVersion) {
5667
5636
  const versionOrder = compareHarnessVersions(localVersion, release.tagName);
5668
5637
  if (versionOrder === null) {
5669
5638
  pluginLog.warn(
@@ -5919,7 +5888,7 @@ Router action: retry the SAME pipeline slot once by creating a NEW Task call to
5919
5888
  if (state.lastEditAttempt) {
5920
5889
  const edit = state.lastEditAttempt;
5921
5890
  output.system.push(
5922
- `[Edit Error Recovery] Previous edit failed for "${edit.filePath}". The oldString was not found in the file. Read the file first to get the correct content, then retry the edit with the exact oldString from the file.`
5891
+ `[File Modification Recovery] Previous ${edit.tool} operation failed for "${edit.filePath}". Read the file first to get the correct content, then retry with the available file modification tool. If using edit, use the exact oldString from the file. If using apply_patch, build the patch from the current file content.`
5923
5892
  );
5924
5893
  state.lastEditAttempt = null;
5925
5894
  }
@@ -5928,19 +5897,37 @@ Router action: retry the SAME pipeline slot once by creating a NEW Task call to
5928
5897
  }
5929
5898
  };
5930
5899
  }
5900
+ function isFileModificationTool(tool) {
5901
+ return tool === "edit" || tool === "write" || tool === "apply_patch";
5902
+ }
5903
+ function getPatchPath(patchText) {
5904
+ const match = patchText.match(/^\*\*\* (?:Add|Update|Delete) File: (.+)$/m);
5905
+ return match?.[1]?.trim() || "patch";
5906
+ }
5907
+ function getFileModificationPath(tool, args) {
5908
+ if (tool === "apply_patch") return getPatchPath(args?.patchText ?? "");
5909
+ return args?.filePath ?? "unknown";
5910
+ }
5911
+ function getFileModificationContent(tool, args) {
5912
+ if (tool === "edit") return args?.newString ?? "";
5913
+ if (tool === "write") return args?.content ?? "";
5914
+ if (tool === "apply_patch") return args?.patchText ?? "";
5915
+ return "";
5916
+ }
5931
5917
  function createRalphLoopToolAfter(state) {
5932
5918
  return async (input, output) => {
5933
- if (input.tool !== "edit") return;
5919
+ if (!isFileModificationTool(input.tool)) return;
5934
5920
  const outputText = typeof output.output === "string" ? output.output : "";
5935
5921
  if (outputText.includes("oldString not found") || outputText.includes("Found multiple matches")) {
5936
5922
  state.lastEditAttempt = {
5937
- filePath: input.args?.filePath ?? "unknown",
5923
+ filePath: getFileModificationPath(input.tool, input.args),
5938
5924
  oldString: input.args?.oldString ?? "",
5939
- newString: input.args?.newString ?? "",
5925
+ newString: getFileModificationContent(input.tool, input.args),
5926
+ tool: input.tool,
5940
5927
  timestamp: Date.now()
5941
5928
  };
5942
5929
  pluginLog.warn(
5943
- `[opencode-immune] Ralph Loop: Edit failed for "${state.lastEditAttempt.filePath}". Recovery hint will be injected in next system transform.`
5930
+ `[opencode-immune] Ralph Loop: ${input.tool} failed for "${state.lastEditAttempt.filePath}". Recovery hint will be injected in next system transform.`
5944
5931
  );
5945
5932
  } else {
5946
5933
  state.lastEditAttempt = null;
@@ -5982,8 +5969,8 @@ var EMOJI_PATTERN = /[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\
5982
5969
  var TODO_PATTERN = /\b(TODO|FIXME|HACK|XXX)\b/i;
5983
5970
  function createCommentCheckerToolAfter(state) {
5984
5971
  return async (input, _output) => {
5985
- if (input.tool !== "edit" && input.tool !== "write") return;
5986
- const content = input.tool === "edit" ? input.args?.newString ?? "" : input.args?.content ?? "";
5972
+ if (!isFileModificationTool(input.tool)) return;
5973
+ const content = getFileModificationContent(input.tool, input.args);
5987
5974
  if (!content) return;
5988
5975
  if (EMOJI_PATTERN.test(content)) {
5989
5976
  pluginLog.warn(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-immune",
3
- "version": "1.0.85",
3
+ "version": "1.0.87",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin: session recovery, auto-retry, multi-cycle automation, context monitoring",
6
6
  "exports": {