opencode-immune 1.0.30 → 1.0.32

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 +46 -99
  2. package/package.json +1 -1
package/dist/plugin.js CHANGED
@@ -615,10 +615,12 @@ async function fetchLatestHarnessRelease(repo, token) {
615
615
  const tagName = data.tag_name;
616
616
  if (!tagName)
617
617
  return null;
618
- // Find the harness.tar.gz asset
619
- const asset = data.assets?.find((a) => a.name === "harness.tar.gz");
618
+ // Find the correct harness asset based on OS
619
+ const isWindows = process.platform === "win32";
620
+ const assetName = isWindows ? "harness-windows.tar.gz" : "harness.tar.gz";
621
+ const asset = data.assets?.find((a) => a.name === assetName);
620
622
  if (!asset) {
621
- console.warn(`[opencode-immune] Harness sync: release ${tagName} has no harness.tar.gz asset`);
623
+ console.warn(`[opencode-immune] Harness sync: release ${tagName} has no ${assetName} asset`);
622
624
  return null;
623
625
  }
624
626
  return {
@@ -684,17 +686,22 @@ function extractTarGz(archivePath, destDir) {
684
686
  /**
685
687
  * Recursively copy all files from src to dest, creating directories as needed.
686
688
  * Overwrites existing files.
689
+ * skipRootFiles: file names to skip ONLY when dest === rootDest (top-level copy target).
687
690
  */
688
- async function copyDirRecursive(src, dest, skipFiles) {
691
+ async function copyDirRecursive(src, dest, skipRootFiles, rootDest) {
692
+ const effectiveRoot = rootDest ?? dest;
689
693
  const entries = await (0, promises_1.readdir)(src, { withFileTypes: true });
690
694
  await (0, promises_1.mkdir)(dest, { recursive: true });
691
695
  for (const entry of entries) {
692
- if (skipFiles?.has(entry.name))
696
+ // Skip files only at the root destination level
697
+ if (skipRootFiles && dest === effectiveRoot && entry.name === ".gitignore") {
698
+ console.log(`[opencode-immune] Harness sync: skipping root .gitignore`);
693
699
  continue;
700
+ }
694
701
  const srcPath = (0, path_1.join)(src, entry.name);
695
702
  const destPath = (0, path_1.join)(dest, entry.name);
696
703
  if (entry.isDirectory()) {
697
- await copyDirRecursive(srcPath, destPath);
704
+ await copyDirRecursive(srcPath, destPath, skipRootFiles, effectiveRoot);
698
705
  }
699
706
  else {
700
707
  await (0, promises_1.mkdir)((0, path_1.dirname)(destPath), { recursive: true });
@@ -1277,92 +1284,40 @@ const PRE_COMMIT_MARKER = "0-ULTRAWORK: PRE_COMMIT";
1277
1284
  const CYCLE_COMPLETE_MARKER = "0-ULTRAWORK: CYCLE_COMPLETE";
1278
1285
  const NEXT_TASK_PATTERN = /Next task:\s*(.+)/;
1279
1286
  const ALL_CYCLES_COMPLETE_MARKER = "0-ULTRAWORK: ALL_CYCLES_COMPLETE";
1280
- // Default commit prompt — used when user's /commit command file is not found
1281
- const DEFAULT_COMMIT_PROMPT = `Review the staged changes using \`git diff --cached\` and create a commit.
1282
-
1283
- If there are no staged changes, check \`git status\` for unstaged changes and stage the relevant files first.
1284
-
1285
- The commit message MUST follow this structure:
1286
- 1. First line: short description (<50 chars) prefixed with feat:/fix:/refactor:
1287
- 2. Second line: empty
1288
- 3. Body: concise bullet points describing significant changes
1289
-
1290
- Create the commit using \`git commit -m\` with the properly formatted message.`;
1291
- /**
1292
- * Load the /commit command prompt from user's global config.
1293
- * Falls back to DEFAULT_COMMIT_PROMPT if file doesn't exist.
1294
- */
1295
- async function loadCommitPrompt() {
1296
- try {
1297
- const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
1298
- const commandPath = (0, path_1.join)(home, ".config", "opencode", "commands", "commit.md");
1299
- const content = await (0, promises_1.readFile)(commandPath, "utf-8");
1300
- // Strip YAML frontmatter (--- ... ---)
1301
- const stripped = content.replace(/^---[\s\S]*?---\s*/, "").trim();
1302
- return stripped || DEFAULT_COMMIT_PROMPT;
1303
- }
1304
- catch {
1305
- return DEFAULT_COMMIT_PROMPT;
1306
- }
1307
- }
1308
- /**
1309
- * Execute a commit by sending the /commit prompt to the current session.
1310
- * The agent will analyze the diff and create a proper commit message.
1311
- * Returns true if the prompt was sent successfully.
1312
- */
1313
- async function runSmartCommit(state, sessionID) {
1314
- try {
1315
- const commitPrompt = await loadCommitPrompt();
1316
- console.log(`[opencode-immune] Smart commit: sending commit prompt to session ${sessionID}...`);
1317
- // Send commit prompt to the SAME session (ultrawork already finished, session is free)
1318
- await state.input.client.session.prompt({
1319
- body: {
1320
- parts: [
1321
- {
1322
- type: "text",
1323
- text: commitPrompt,
1324
- },
1325
- ],
1326
- },
1327
- path: { id: sessionID },
1328
- });
1329
- console.log("[opencode-immune] Smart commit: completed.");
1330
- return true;
1331
- }
1332
- catch (err) {
1333
- console.error("[opencode-immune] Smart commit failed:", err);
1334
- return false;
1335
- }
1336
- }
1337
1287
  /**
1338
- * Helper: run git commit in the project directory (fallback).
1339
- * Uses execFile for safety (no shell injection).
1340
- * Returns true if commit succeeded, false otherwise.
1288
+ * Helper: run git add + diff-stat + commit with descriptive message.
1289
+ * Builds commit body from `git diff --cached --stat` so the commit
1290
+ * is not an opaque "auto-commit" but shows what changed.
1291
+ * Returns true if commit succeeded (or nothing to commit), false on error.
1341
1292
  */
1342
- function runGitCommit(directory, message) {
1293
+ function runGitCommit(directory) {
1343
1294
  return new Promise((resolve) => {
1344
- // Stage all changes first
1295
+ // Stage all changes
1345
1296
  (0, child_process_1.execFile)("git", ["add", "-A"], { cwd: directory }, (addErr) => {
1346
1297
  if (addErr) {
1347
1298
  console.error("[opencode-immune] git add failed:", addErr.message);
1348
1299
  resolve(false);
1349
1300
  return;
1350
1301
  }
1351
- // Then commit
1352
- (0, child_process_1.execFile)("git", ["commit", "-m", message], { cwd: directory }, (commitErr, stdout, stderr) => {
1353
- if (commitErr) {
1354
- // "nothing to commit" is not a real error
1355
- if (stderr?.includes("nothing to commit") || stdout?.includes("nothing to commit")) {
1356
- console.log("[opencode-immune] git commit: nothing to commit (clean tree).");
1357
- resolve(true);
1302
+ // Get diff stat for commit body
1303
+ (0, child_process_1.execFile)("git", ["diff", "--cached", "--stat"], { cwd: directory }, (_diffErr, diffOut) => {
1304
+ const stat = (diffOut ?? "").trim();
1305
+ const body = stat ? `\n\n${stat}` : "";
1306
+ const message = `chore: ultrawork cycle auto-commit${body}`;
1307
+ (0, child_process_1.execFile)("git", ["commit", "-m", message], { cwd: directory }, (commitErr, stdout, stderr) => {
1308
+ if (commitErr) {
1309
+ if (stderr?.includes("nothing to commit") || stdout?.includes("nothing to commit")) {
1310
+ console.log("[opencode-immune] git commit: nothing to commit (clean tree).");
1311
+ resolve(true);
1312
+ return;
1313
+ }
1314
+ console.error("[opencode-immune] git commit failed:", commitErr.message, stderr);
1315
+ resolve(false);
1358
1316
  return;
1359
1317
  }
1360
- console.error("[opencode-immune] git commit failed:", commitErr.message, stderr);
1361
- resolve(false);
1362
- return;
1363
- }
1364
- console.log("[opencode-immune] git commit succeeded:", stdout?.trim());
1365
- resolve(true);
1318
+ console.log("[opencode-immune] git commit succeeded:", stdout?.trim());
1319
+ resolve(true);
1320
+ });
1366
1321
  });
1367
1322
  });
1368
1323
  });
@@ -1391,18 +1346,14 @@ function createTextCompleteHandler(state) {
1391
1346
  }
1392
1347
  // ── PRE_COMMIT only (without CYCLE_COMPLETE in same part): run commit ──
1393
1348
  if (text.includes(PRE_COMMIT_MARKER) && !text.includes(CYCLE_COMPLETE_MARKER)) {
1394
- if (!state.commitPending && sessionID) {
1349
+ if (!state.commitPending) {
1395
1350
  state.commitPending = true;
1396
- console.log("[opencode-immune] Multi-Cycle: PRE_COMMIT detected (standalone), running smart commit...");
1351
+ console.log("[opencode-immune] Multi-Cycle: PRE_COMMIT detected (standalone), running git commit...");
1397
1352
  try {
1398
- const ok = await runSmartCommit(state, sessionID);
1399
- if (!ok) {
1400
- console.log("[opencode-immune] Multi-Cycle: smart commit failed, falling back to git commit...");
1401
- await runGitCommit(state.input.directory, "chore: ultrawork cycle auto-commit");
1402
- }
1353
+ await runGitCommit(state.input.directory);
1403
1354
  }
1404
1355
  catch (err) {
1405
- console.error("[opencode-immune] Multi-Cycle: commit failed (standalone):", err);
1356
+ console.error("[opencode-immune] Multi-Cycle: git commit failed (standalone):", err);
1406
1357
  }
1407
1358
  finally {
1408
1359
  state.commitPending = false;
@@ -1412,20 +1363,16 @@ function createTextCompleteHandler(state) {
1412
1363
  }
1413
1364
  // ── CYCLE_COMPLETE: self-contained sequence: commit → new session ──
1414
1365
  if (text.includes(CYCLE_COMPLETE_MARKER)) {
1415
- // Step 1: Always commit first in the current session
1416
- if (!state.commitPending && sessionID) {
1366
+ // Step 1: Always commit first
1367
+ if (!state.commitPending) {
1417
1368
  state.commitPending = true;
1418
- console.log("[opencode-immune] Multi-Cycle: CYCLE_COMPLETE detected, running smart commit first...");
1369
+ console.log("[opencode-immune] Multi-Cycle: CYCLE_COMPLETE detected, running git commit first...");
1419
1370
  try {
1420
- const ok = await runSmartCommit(state, sessionID);
1421
- if (!ok) {
1422
- console.log("[opencode-immune] Multi-Cycle: smart commit failed, falling back to git commit...");
1423
- await runGitCommit(state.input.directory, "chore: ultrawork cycle auto-commit");
1424
- }
1425
- console.log("[opencode-immune] Multi-Cycle: commit completed before new cycle.");
1371
+ await runGitCommit(state.input.directory);
1372
+ console.log("[opencode-immune] Multi-Cycle: git commit completed before new cycle.");
1426
1373
  }
1427
1374
  catch (err) {
1428
- console.error("[opencode-immune] Multi-Cycle: commit failed (continuing anyway):", err);
1375
+ console.error("[opencode-immune] Multi-Cycle: git commit failed (continuing anyway):", err);
1429
1376
  }
1430
1377
  finally {
1431
1378
  state.commitPending = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-immune",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
4
4
  "description": "OpenCode plugin: session recovery, auto-retry, multi-cycle automation, context monitoring",
5
5
  "exports": {
6
6
  "./server": "./dist/plugin.js"