opencode-immune 1.0.49 → 1.0.52

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.
Files changed (2) hide show
  1. package/dist/plugin.js +92 -70
  2. package/package.json +2 -1
package/dist/plugin.js CHANGED
@@ -1,19 +1,19 @@
1
- "use strict";
2
1
  // .opencode/plugin.ts — opencode-immune plugin
3
2
  // Hybrid single-file architecture with factory functions, explicit state, error boundaries
4
3
  // See: memory-bank/creative/creative-plugin-architecture.md (Option C)
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const client_1 = require("@opencode-ai/sdk/v2/client");
7
- const promises_1 = require("fs/promises");
8
- const path_1 = require("path");
9
- const crypto_1 = require("crypto");
10
- const os_1 = require("os");
11
- const child_process_1 = require("child_process");
4
+ import { createOpencodeClient as createOpencodeClientV2 } from "@opencode-ai/sdk/v2/client";
5
+ import { appendFile, mkdir, readFile, unlink, writeFile, stat, rm, rename, readdir, copyFile } from "fs/promises";
6
+ import { join, dirname } from "path";
7
+ import { fileURLToPath } from "url";
8
+ import { createHash } from "crypto";
9
+ import { tmpdir } from "os";
10
+ import { execFile } from "child_process";
12
11
  // ═══════════════════════════════════════════════════════════════════════════════
13
12
  // PLUGIN VERSION CHECK
14
13
  // ═══════════════════════════════════════════════════════════════════════════════
15
- const PLUGIN_VERSION = "1.0.49";
14
+ const PLUGIN_VERSION = "1.0.52";
16
15
  const PLUGIN_PACKAGE_NAME = "opencode-immune";
16
+ const PLUGIN_DIRNAME = dirname(fileURLToPath(import.meta.url));
17
17
  /**
18
18
  * Read plugin version from package.json at runtime.
19
19
  * Falls back to PLUGIN_VERSION constant if read fails.
@@ -24,12 +24,12 @@ async function getPluginVersion() {
24
24
  // dist/plugin.js → ../package.json
25
25
  // Also try direct path for when loaded from npm cache.
26
26
  const candidates = [
27
- (0, path_1.join)(__dirname, "..", "package.json"),
28
- (0, path_1.join)(__dirname, "package.json"),
27
+ join(PLUGIN_DIRNAME, "..", "package.json"),
28
+ join(PLUGIN_DIRNAME, "package.json"),
29
29
  ];
30
30
  for (const pkgPath of candidates) {
31
31
  try {
32
- const content = await (0, promises_1.readFile)(pkgPath, "utf-8");
32
+ const content = await readFile(pkgPath, "utf-8");
33
33
  const pkg = JSON.parse(content);
34
34
  if (pkg.version)
35
35
  return pkg.version;
@@ -77,7 +77,7 @@ function createState(input) {
77
77
  const { client: _client, ...runtimeInput } = input;
78
78
  return {
79
79
  input: runtimeInput,
80
- client: (0, client_1.createOpencodeClient)({
80
+ client: createOpencodeClientV2({
81
81
  baseUrl: input.serverUrl.toString(),
82
82
  directory: input.directory,
83
83
  }),
@@ -87,8 +87,8 @@ function createState(input) {
87
87
  providerRetryWatchdogs: new Map(),
88
88
  childFallbackRequests: new Map(),
89
89
  sessionErrorRetryCount: new Map(),
90
- ultraworkMarkerPath: (0, path_1.join)(input.directory, ".opencode", "state", "ultrawork-active.json"),
91
- diagnosticsLogPath: (0, path_1.join)(input.directory, ".opencode", "state", "opencode-immune-debug.log"),
90
+ ultraworkMarkerPath: join(input.directory, ".opencode", "state", "ultrawork-active.json"),
91
+ diagnosticsLogPath: join(input.directory, ".opencode", "state", "opencode-immune-debug.log"),
92
92
  lastEditAttempt: null,
93
93
  toolCallCount: 0,
94
94
  todoWriteUsed: false,
@@ -122,6 +122,7 @@ const DIAGNOSTIC_LOG_MAX_BYTES = 5 * 1024 * 1024;
122
122
  let activeLogDirectory = null;
123
123
  const MANAGED_SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1000;
124
124
  const PROVIDER_RETRY_WATCHDOG_MS = 30_000;
125
+ const RETRY_PROMPT_DELIVERY_ATTEMPTS = 3;
125
126
  const CHILD_FALLBACK_REQUEST_TTL_MS = 10 * 60 * 1000;
126
127
  const RATE_LIMIT_FALLBACK_MODEL = {
127
128
  providerID: "codexsale",
@@ -154,7 +155,7 @@ async function createManagedUltraworkSession(state, title) {
154
155
  return sessionID;
155
156
  }
156
157
  async function promptManagedSession(state, sessionID, text, options = {}) {
157
- await state.client.session.promptAsync({
158
+ const result = await state.client.session.promptAsync({
158
159
  directory: state.input.directory,
159
160
  sessionID,
160
161
  ...(options.model ? { model: options.model } : {}),
@@ -166,6 +167,9 @@ async function promptManagedSession(state, sessionID, text, options = {}) {
166
167
  },
167
168
  ],
168
169
  });
170
+ if (result.error || !result.response.ok) {
171
+ throw new Error(`prompt_async failed with status ${result.response.status}: ${JSON.stringify(result.error ?? null)}`);
172
+ }
169
173
  }
170
174
  async function abortManagedSession(state, sessionID) {
171
175
  await state.client.session.abort({
@@ -188,11 +192,11 @@ function pruneExpiredManagedSessions(state, now = Date.now()) {
188
192
  }
189
193
  async function writeDiagnosticLog(state, event, data = {}) {
190
194
  try {
191
- const cacheDir = (0, path_1.join)(state.input.directory, ".opencode", "state");
192
- await (0, promises_1.mkdir)(cacheDir, { recursive: true });
195
+ const cacheDir = join(state.input.directory, ".opencode", "state");
196
+ await mkdir(cacheDir, { recursive: true });
193
197
  await rotateDiagnosticLogIfNeeded(state.diagnosticsLogPath);
194
198
  const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data });
195
- await (0, promises_1.appendFile)(state.diagnosticsLogPath, `${line}\n`, "utf-8");
199
+ await appendFile(state.diagnosticsLogPath, `${line}\n`, "utf-8");
196
200
  }
197
201
  catch {
198
202
  // diagnostics must never affect runtime behavior
@@ -200,12 +204,12 @@ async function writeDiagnosticLog(state, event, data = {}) {
200
204
  }
201
205
  async function rotateDiagnosticLogIfNeeded(logPath) {
202
206
  try {
203
- const current = await (0, promises_1.stat)(logPath);
207
+ const current = await stat(logPath);
204
208
  if (current.size < DIAGNOSTIC_LOG_MAX_BYTES)
205
209
  return;
206
210
  const rotatedPath = `${logPath}.1`;
207
- await (0, promises_1.rm)(rotatedPath, { force: true });
208
- await (0, promises_1.rename)(logPath, rotatedPath);
211
+ await rm(rotatedPath, { force: true });
212
+ await rename(logPath, rotatedPath);
209
213
  }
210
214
  catch {
211
215
  // missing log or rotation failure must never affect runtime behavior
@@ -228,10 +232,10 @@ function writePluginLog(state, level, message, extra = {}) {
228
232
  });
229
233
  }
230
234
  function writePluginLogForDirectory(directory, level, message, extra = {}) {
231
- const diagnosticsLogPath = (0, path_1.join)(directory, ".opencode", "state", "opencode-immune-debug.log");
235
+ const diagnosticsLogPath = join(directory, ".opencode", "state", "opencode-immune-debug.log");
232
236
  void (async () => {
233
237
  try {
234
- await (0, promises_1.mkdir)((0, path_1.dirname)(diagnosticsLogPath), { recursive: true });
238
+ await mkdir(dirname(diagnosticsLogPath), { recursive: true });
235
239
  await rotateDiagnosticLogIfNeeded(diagnosticsLogPath);
236
240
  const line = JSON.stringify({
237
241
  ts: new Date().toISOString(),
@@ -239,7 +243,7 @@ function writePluginLogForDirectory(directory, level, message, extra = {}) {
239
243
  message,
240
244
  ...extra,
241
245
  });
242
- await (0, promises_1.appendFile)(diagnosticsLogPath, `${line}\n`, "utf-8");
246
+ await appendFile(diagnosticsLogPath, `${line}\n`, "utf-8");
243
247
  }
244
248
  catch {
245
249
  // file logging must never affect runtime behavior
@@ -263,13 +267,13 @@ const pluginLog = {
263
267
  // ── Ultrawork Marker File ──
264
268
  async function writeUltraworkMarker(state) {
265
269
  try {
266
- const dir = (0, path_1.join)(state.input.directory, ".opencode", "state");
267
- await (0, promises_1.mkdir)(dir, { recursive: true });
270
+ const dir = join(state.input.directory, ".opencode", "state");
271
+ await mkdir(dir, { recursive: true });
268
272
  const payload = JSON.stringify({
269
273
  active: true,
270
274
  updatedAt: new Date().toISOString(),
271
275
  });
272
- await (0, promises_1.writeFile)(state.ultraworkMarkerPath, payload, "utf-8");
276
+ await writeFile(state.ultraworkMarkerPath, payload, "utf-8");
273
277
  }
274
278
  catch {
275
279
  // marker write must never affect runtime
@@ -277,7 +281,7 @@ async function writeUltraworkMarker(state) {
277
281
  }
278
282
  async function clearUltraworkMarker(state) {
279
283
  try {
280
- await (0, promises_1.unlink)(state.ultraworkMarkerPath);
284
+ await unlink(state.ultraworkMarkerPath);
281
285
  }
282
286
  catch {
283
287
  // file may not exist — that's fine
@@ -285,7 +289,7 @@ async function clearUltraworkMarker(state) {
285
289
  }
286
290
  async function isUltraworkMarkerActive(state) {
287
291
  try {
288
- const raw = await (0, promises_1.readFile)(state.ultraworkMarkerPath, "utf-8");
292
+ const raw = await readFile(state.ultraworkMarkerPath, "utf-8");
289
293
  const parsed = JSON.parse(raw);
290
294
  return parsed?.active === true;
291
295
  }
@@ -673,12 +677,30 @@ function scheduleManagedSessionRetry(state, sessionID, options) {
673
677
  abortBeforePrompt: options.abortBeforePrompt,
674
678
  });
675
679
  }
676
- catch {
680
+ catch (err) {
677
681
  if (options.countAgainstBudget) {
678
682
  state.sessionErrorRetryCount.set(sessionID, Math.max((state.sessionErrorRetryCount.get(sessionID) ?? 1) - 1, 0));
679
683
  }
684
+ const deliveryAttempt = options.deliveryAttempt ?? 1;
685
+ await writeDiagnosticLog(state, "session-retry:prompt-delivery-failed", {
686
+ sessionID,
687
+ reason: options.reason,
688
+ deliveryAttempt,
689
+ error: err instanceof Error ? err.message : String(err),
690
+ });
680
691
  writePluginLog(state, "warn", `[opencode-immune] Retry prompt failed for session ${sessionID}. ` +
681
- `Will wait for the next retry signal.`);
692
+ (deliveryAttempt < RETRY_PROMPT_DELIVERY_ATTEMPTS
693
+ ? `Retrying delivery attempt ${deliveryAttempt + 1}/${RETRY_PROMPT_DELIVERY_ATTEMPTS}.`
694
+ : `Delivery attempts exhausted; will wait for the next retry signal.`));
695
+ if (deliveryAttempt < RETRY_PROMPT_DELIVERY_ATTEMPTS) {
696
+ scheduleManagedSessionRetry(state, sessionID, {
697
+ ...options,
698
+ delayMs: Math.min(options.delayMs * 2, 15_000),
699
+ countAgainstBudget: false,
700
+ abortBeforePrompt: false,
701
+ deliveryAttempt: deliveryAttempt + 1,
702
+ });
703
+ }
682
704
  }
683
705
  }, options.delayMs);
684
706
  state.sessionRetryTimers.set(sessionID, timer);
@@ -742,8 +764,8 @@ function compositeChatMessage(handlers) {
742
764
  */
743
765
  async function parseTasksFile(directory) {
744
766
  try {
745
- const tasksPath = (0, path_1.join)(directory, "memory-bank", "tasks.md");
746
- const content = await (0, promises_1.readFile)(tasksPath, "utf-8");
767
+ const tasksPath = join(directory, "memory-bank", "tasks.md");
768
+ const content = await readFile(tasksPath, "utf-8");
747
769
  // Check for active task
748
770
  if (!content.includes("## Active Task") ||
749
771
  content.includes("No active tasks")) {
@@ -815,7 +837,7 @@ const DEFAULT_HARNESS_REPO = "gendoor/opencode-immune-harness";
815
837
  async function parseDotEnv(filePath) {
816
838
  const result = {};
817
839
  try {
818
- const content = await (0, promises_1.readFile)(filePath, "utf-8");
840
+ const content = await readFile(filePath, "utf-8");
819
841
  for (const line of content.split("\n")) {
820
842
  const trimmed = line.trim();
821
843
  if (!trimmed || trimmed.startsWith("#"))
@@ -849,13 +871,13 @@ async function resolveEnvValue(directory, key) {
849
871
  if (process.env[key])
850
872
  return process.env[key];
851
873
  // 2. Per-project .env
852
- const projectEnv = await parseDotEnv((0, path_1.join)(directory, ".env"));
874
+ const projectEnv = await parseDotEnv(join(directory, ".env"));
853
875
  if (projectEnv[key])
854
876
  return projectEnv[key];
855
877
  // 3. Global config
856
878
  const home = process.env.HOME || process.env.USERPROFILE || "";
857
879
  if (home) {
858
- const globalEnv = await parseDotEnv((0, path_1.join)(home, ".config", "opencode-immune", ".env"));
880
+ const globalEnv = await parseDotEnv(join(home, ".config", "opencode-immune", ".env"));
859
881
  if (globalEnv[key])
860
882
  return globalEnv[key];
861
883
  }
@@ -913,8 +935,8 @@ async function fetchLatestHarnessRelease(directory, repo, token) {
913
935
  */
914
936
  async function readLocalHarnessVersion(directory) {
915
937
  try {
916
- const versionPath = (0, path_1.join)(directory, ".opencode", HARNESS_VERSION_FILE);
917
- const content = await (0, promises_1.readFile)(versionPath, "utf-8");
938
+ const versionPath = join(directory, ".opencode", HARNESS_VERSION_FILE);
939
+ const content = await readFile(versionPath, "utf-8");
918
940
  return content.trim() || null;
919
941
  }
920
942
  catch {
@@ -938,8 +960,8 @@ async function downloadHarnessAsset(assetUrl, token) {
938
960
  throw new Error(`Download failed: ${resp.status} ${resp.statusText}`);
939
961
  }
940
962
  const buffer = Buffer.from(await resp.arrayBuffer());
941
- const tempPath = (0, path_1.join)((0, os_1.tmpdir)(), `harness-${Date.now()}.tar.gz`);
942
- await (0, promises_1.writeFile)(tempPath, buffer);
963
+ const tempPath = join(tmpdir(), `harness-${Date.now()}.tar.gz`);
964
+ await writeFile(tempPath, buffer);
943
965
  return tempPath;
944
966
  }
945
967
  /**
@@ -948,7 +970,7 @@ async function downloadHarnessAsset(assetUrl, token) {
948
970
  */
949
971
  function extractTarGz(archivePath, destDir) {
950
972
  return new Promise((resolve, reject) => {
951
- (0, child_process_1.execFile)("tar", ["xzf", archivePath, "-C", destDir], (err) => {
973
+ execFile("tar", ["xzf", archivePath, "-C", destDir], (err) => {
952
974
  if (err)
953
975
  reject(err);
954
976
  else
@@ -963,22 +985,22 @@ function extractTarGz(archivePath, destDir) {
963
985
  */
964
986
  async function copyDirRecursive(src, dest, skipRootFiles, rootDest) {
965
987
  const effectiveRoot = rootDest ?? dest;
966
- const entries = await (0, promises_1.readdir)(src, { withFileTypes: true });
967
- await (0, promises_1.mkdir)(dest, { recursive: true });
988
+ const entries = await readdir(src, { withFileTypes: true });
989
+ await mkdir(dest, { recursive: true });
968
990
  for (const entry of entries) {
969
991
  // Skip files only at the root destination level
970
992
  if (skipRootFiles && dest === effectiveRoot && entry.name === ".gitignore") {
971
993
  pluginLog.info(`[opencode-immune] Harness sync: skipping root .gitignore`);
972
994
  continue;
973
995
  }
974
- const srcPath = (0, path_1.join)(src, entry.name);
975
- const destPath = (0, path_1.join)(dest, entry.name);
996
+ const srcPath = join(src, entry.name);
997
+ const destPath = join(dest, entry.name);
976
998
  if (entry.isDirectory()) {
977
999
  await copyDirRecursive(srcPath, destPath, skipRootFiles, effectiveRoot);
978
1000
  }
979
1001
  else {
980
- await (0, promises_1.mkdir)((0, path_1.dirname)(destPath), { recursive: true });
981
- await (0, promises_1.copyFile)(srcPath, destPath);
1002
+ await mkdir(dirname(destPath), { recursive: true });
1003
+ await copyFile(srcPath, destPath);
982
1004
  }
983
1005
  }
984
1006
  }
@@ -987,8 +1009,8 @@ async function copyDirRecursive(src, dest, skipRootFiles, rootDest) {
987
1009
  */
988
1010
  async function fileHash(filePath) {
989
1011
  try {
990
- const content = await (0, promises_1.readFile)(filePath);
991
- return (0, crypto_1.createHash)("sha256").update(content).digest("hex");
1012
+ const content = await readFile(filePath);
1013
+ return createHash("sha256").update(content).digest("hex");
992
1014
  }
993
1015
  catch {
994
1016
  return "";
@@ -1030,13 +1052,13 @@ async function syncHarness(state) {
1030
1052
  }
1031
1053
  pluginLog.info(`[opencode-immune] Harness sync: updating ${localVersion ?? "(none)"} → ${release.tagName}`);
1032
1054
  // 3. Hash opencode.json before update
1033
- const configPath = (0, path_1.join)(state.input.directory, "opencode.json");
1055
+ const configPath = join(state.input.directory, "opencode.json");
1034
1056
  const hashBefore = await fileHash(configPath);
1035
1057
  // 4. Download asset
1036
1058
  const archivePath = await downloadHarnessAsset(release.assetUrl, token);
1037
1059
  // 5. Extract to temp dir
1038
- const extractDir = (0, path_1.join)((0, os_1.tmpdir)(), `harness-extract-${Date.now()}`);
1039
- await (0, promises_1.mkdir)(extractDir, { recursive: true });
1060
+ const extractDir = join(tmpdir(), `harness-extract-${Date.now()}`);
1061
+ await mkdir(extractDir, { recursive: true });
1040
1062
  try {
1041
1063
  await extractTarGz(archivePath, extractDir);
1042
1064
  // 6. Copy extracted files to project root (skip .gitignore — project owns its own)
@@ -1044,9 +1066,9 @@ async function syncHarness(state) {
1044
1066
  const SKIP_ROOT_FILES = new Set([".gitignore"]);
1045
1067
  await copyDirRecursive(extractDir, state.input.directory, SKIP_ROOT_FILES);
1046
1068
  // 7. Write version marker
1047
- const versionDir = (0, path_1.join)(state.input.directory, ".opencode");
1048
- await (0, promises_1.mkdir)(versionDir, { recursive: true });
1049
- await (0, promises_1.writeFile)((0, path_1.join)(versionDir, HARNESS_VERSION_FILE), release.tagName + "\n", "utf-8");
1069
+ const versionDir = join(state.input.directory, ".opencode");
1070
+ await mkdir(versionDir, { recursive: true });
1071
+ await writeFile(join(versionDir, HARNESS_VERSION_FILE), release.tagName + "\n", "utf-8");
1050
1072
  // 8. Check if opencode.json changed
1051
1073
  const hashAfter = await fileHash(configPath);
1052
1074
  if (hashBefore && hashAfter && hashBefore !== hashAfter) {
@@ -1063,11 +1085,11 @@ async function syncHarness(state) {
1063
1085
  finally {
1064
1086
  // 9. Cleanup temp files
1065
1087
  try {
1066
- await (0, promises_1.unlink)(archivePath);
1088
+ await unlink(archivePath);
1067
1089
  }
1068
1090
  catch { /* ignore */ }
1069
1091
  try {
1070
- await (0, promises_1.rm)(extractDir, { recursive: true, force: true });
1092
+ await rm(extractDir, { recursive: true, force: true });
1071
1093
  }
1072
1094
  catch { /* ignore */ }
1073
1095
  }
@@ -1598,22 +1620,22 @@ const ALL_CYCLES_COMPLETE_MARKER = "0-ULTRAWORK: ALL_CYCLES_COMPLETE";
1598
1620
  * Skips silently if progress.md doesn't exist or is trivially empty.
1599
1621
  */
1600
1622
  async function archiveProgress(directory) {
1601
- const progressPath = (0, path_1.join)(directory, "memory-bank", "progress.md");
1623
+ const progressPath = join(directory, "memory-bank", "progress.md");
1602
1624
  try {
1603
- const content = await (0, promises_1.readFile)(progressPath, "utf-8");
1625
+ const content = await readFile(progressPath, "utf-8");
1604
1626
  // Skip if empty or trivially empty
1605
1627
  if (!content.trim() || content.trim() === "# Progress") {
1606
1628
  pluginLog.info("[opencode-immune] Archive progress: nothing to archive (empty).");
1607
1629
  return;
1608
1630
  }
1609
- const archiveDir = (0, path_1.join)(directory, "memory-bank", "archive");
1610
- await (0, promises_1.mkdir)(archiveDir, { recursive: true });
1631
+ const archiveDir = join(directory, "memory-bank", "archive");
1632
+ await mkdir(archiveDir, { recursive: true });
1611
1633
  const now = new Date();
1612
1634
  const dateStr = now.toISOString().slice(0, 10); // YYYY-MM-DD
1613
1635
  const ts = Math.floor(now.getTime() / 1000);
1614
1636
  const archiveName = `progress-${dateStr}-${ts}.md`;
1615
- const archivePath = (0, path_1.join)(archiveDir, archiveName);
1616
- await (0, promises_1.rename)(progressPath, archivePath);
1637
+ const archivePath = join(archiveDir, archiveName);
1638
+ await rename(progressPath, archivePath);
1617
1639
  pluginLog.info(`[opencode-immune] Archive progress: moved to ${archiveName}`);
1618
1640
  }
1619
1641
  catch (err) {
@@ -1664,17 +1686,17 @@ async function buildCommitMessage(directory, diffStat) {
1664
1686
  function runGitCommit(directory) {
1665
1687
  return new Promise((resolve) => {
1666
1688
  // Stage all changes
1667
- (0, child_process_1.execFile)("git", ["add", "-A"], { cwd: directory }, (addErr) => {
1689
+ execFile("git", ["add", "-A"], { cwd: directory }, (addErr) => {
1668
1690
  if (addErr) {
1669
1691
  pluginLog.error("[opencode-immune] git add failed:", addErr.message);
1670
1692
  resolve(false);
1671
1693
  return;
1672
1694
  }
1673
1695
  // Get diff stat for commit body
1674
- (0, child_process_1.execFile)("git", ["diff", "--cached", "--stat"], { cwd: directory }, async (_diffErr, diffOut) => {
1696
+ execFile("git", ["diff", "--cached", "--stat"], { cwd: directory }, async (_diffErr, diffOut) => {
1675
1697
  const stat = (diffOut ?? "").trim();
1676
1698
  const message = await buildCommitMessage(directory, stat);
1677
- (0, child_process_1.execFile)("git", ["commit", "-m", message], { cwd: directory }, (commitErr, stdout, stderr) => {
1699
+ execFile("git", ["commit", "-m", message], { cwd: directory }, (commitErr, stdout, stderr) => {
1678
1700
  if (commitErr) {
1679
1701
  if (stderr?.includes("nothing to commit") || stdout?.includes("nothing to commit")) {
1680
1702
  pluginLog.info("[opencode-immune] git commit: nothing to commit (clean tree).");
@@ -1897,8 +1919,8 @@ async function server(input) {
1897
1919
  else {
1898
1920
  // No active task — check if backlog has pending work to start a new cycle
1899
1921
  try {
1900
- const backlogPath = (0, path_1.join)(state.input.directory, "memory-bank", "backlog.md");
1901
- const backlogContent = await (0, promises_1.readFile)(backlogPath, "utf-8");
1922
+ const backlogPath = join(state.input.directory, "memory-bank", "backlog.md");
1923
+ const backlogContent = await readFile(backlogPath, "utf-8");
1902
1924
  const hasPendingTasks = /- \[ \]/.test(backlogContent);
1903
1925
  if (hasPendingTasks) {
1904
1926
  state.autoResumeAttempted = true;
@@ -1960,7 +1982,7 @@ async function server(input) {
1960
1982
  "permission.ask": withErrorBoundary(state, "permission.ask", createPermissionAskHandler(state)),
1961
1983
  };
1962
1984
  }
1963
- exports.default = {
1985
+ export default {
1964
1986
  id: "opencode-immune",
1965
1987
  server,
1966
1988
  };
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "opencode-immune",
3
- "version": "1.0.49",
3
+ "version": "1.0.52",
4
+ "type": "module",
4
5
  "description": "OpenCode plugin: session recovery, auto-retry, multi-cycle automation, context monitoring",
5
6
  "exports": {
6
7
  "./server": "./dist/plugin.js"