opencode-immune 1.0.27 → 1.0.29
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/dist/plugin.js +98 -12
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -41,9 +41,10 @@ async function getPluginVersion() {
|
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
43
|
* Check npm registry for latest version. Warn if current is outdated.
|
|
44
|
+
* Stores update message in state for injection into system prompt.
|
|
44
45
|
* Non-blocking, fire-and-forget — never delays plugin startup.
|
|
45
46
|
*/
|
|
46
|
-
async function checkPluginUpdate() {
|
|
47
|
+
async function checkPluginUpdate(state) {
|
|
47
48
|
try {
|
|
48
49
|
const currentVersion = await getPluginVersion();
|
|
49
50
|
const controller = new AbortController();
|
|
@@ -55,8 +56,10 @@ async function checkPluginUpdate() {
|
|
|
55
56
|
const data = (await res.json());
|
|
56
57
|
const latest = data.version;
|
|
57
58
|
if (latest && latest !== currentVersion) {
|
|
58
|
-
|
|
59
|
-
`
|
|
59
|
+
state.pluginUpdateMessage =
|
|
60
|
+
`[PLUGIN UPDATE] opencode-immune ${currentVersion} → ${latest} is available. ` +
|
|
61
|
+
`Please inform the user: a plugin update is available. They should restart opencode to get the latest version.`;
|
|
62
|
+
console.warn(`[opencode-immune] Plugin update available: ${currentVersion} → ${latest}.`);
|
|
60
63
|
}
|
|
61
64
|
else if (latest) {
|
|
62
65
|
console.log(`[opencode-immune] Plugin version ${currentVersion} is up to date.`);
|
|
@@ -83,6 +86,7 @@ function createState(input) {
|
|
|
83
86
|
autoResumeAttempted: false,
|
|
84
87
|
cycleCount: 0,
|
|
85
88
|
commitPending: false,
|
|
89
|
+
pluginUpdateMessage: null,
|
|
86
90
|
};
|
|
87
91
|
}
|
|
88
92
|
const ULTRAWORK_AGENT = "0-ultrawork";
|
|
@@ -955,6 +959,11 @@ function createSystemTransform(state) {
|
|
|
955
959
|
// Clear after injection to avoid repeated hints
|
|
956
960
|
state.lastEditAttempt = null;
|
|
957
961
|
}
|
|
962
|
+
// Plugin update notification — inject once into first system prompt
|
|
963
|
+
if (state.pluginUpdateMessage) {
|
|
964
|
+
output.system.push(state.pluginUpdateMessage);
|
|
965
|
+
// Keep showing until opencode restarts (don't clear)
|
|
966
|
+
}
|
|
958
967
|
};
|
|
959
968
|
}
|
|
960
969
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -1268,8 +1277,77 @@ const PRE_COMMIT_MARKER = "0-ULTRAWORK: PRE_COMMIT";
|
|
|
1268
1277
|
const CYCLE_COMPLETE_MARKER = "0-ULTRAWORK: CYCLE_COMPLETE";
|
|
1269
1278
|
const NEXT_TASK_PATTERN = /Next task:\s*(.+)/;
|
|
1270
1279
|
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.`;
|
|
1271
1291
|
/**
|
|
1272
|
-
*
|
|
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 a new 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) {
|
|
1314
|
+
try {
|
|
1315
|
+
const commitPrompt = await loadCommitPrompt();
|
|
1316
|
+
// Create a dedicated session for the commit
|
|
1317
|
+
const createResult = await state.input.client.session.create({
|
|
1318
|
+
body: {
|
|
1319
|
+
title: "Auto-commit (ultrawork cycle)",
|
|
1320
|
+
},
|
|
1321
|
+
});
|
|
1322
|
+
const sessionData = createResult?.data;
|
|
1323
|
+
const commitSessionID = sessionData?.id;
|
|
1324
|
+
if (!commitSessionID) {
|
|
1325
|
+
console.error("[opencode-immune] Smart commit: failed to create session.");
|
|
1326
|
+
return false;
|
|
1327
|
+
}
|
|
1328
|
+
console.log(`[opencode-immune] Smart commit: session created ${commitSessionID}, sending commit prompt...`);
|
|
1329
|
+
// Send commit prompt and wait for completion (sync prompt, not async)
|
|
1330
|
+
await state.input.client.session.prompt({
|
|
1331
|
+
body: {
|
|
1332
|
+
parts: [
|
|
1333
|
+
{
|
|
1334
|
+
type: "text",
|
|
1335
|
+
text: commitPrompt,
|
|
1336
|
+
},
|
|
1337
|
+
],
|
|
1338
|
+
},
|
|
1339
|
+
path: { id: commitSessionID },
|
|
1340
|
+
});
|
|
1341
|
+
console.log("[opencode-immune] Smart commit: completed.");
|
|
1342
|
+
return true;
|
|
1343
|
+
}
|
|
1344
|
+
catch (err) {
|
|
1345
|
+
console.error("[opencode-immune] Smart commit failed:", err);
|
|
1346
|
+
return false;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Helper: run git commit in the project directory (fallback).
|
|
1273
1351
|
* Uses execFile for safety (no shell injection).
|
|
1274
1352
|
* Returns true if commit succeeded, false otherwise.
|
|
1275
1353
|
*/
|
|
@@ -1327,12 +1405,16 @@ function createTextCompleteHandler(state) {
|
|
|
1327
1405
|
if (text.includes(PRE_COMMIT_MARKER) && !text.includes(CYCLE_COMPLETE_MARKER)) {
|
|
1328
1406
|
if (!state.commitPending) {
|
|
1329
1407
|
state.commitPending = true;
|
|
1330
|
-
console.log("[opencode-immune] Multi-Cycle: PRE_COMMIT detected (standalone), running
|
|
1408
|
+
console.log("[opencode-immune] Multi-Cycle: PRE_COMMIT detected (standalone), running smart commit...");
|
|
1331
1409
|
try {
|
|
1332
|
-
await
|
|
1410
|
+
const ok = await runSmartCommit(state);
|
|
1411
|
+
if (!ok) {
|
|
1412
|
+
console.log("[opencode-immune] Multi-Cycle: smart commit failed, falling back to git commit...");
|
|
1413
|
+
await runGitCommit(state.input.directory, "chore: ultrawork cycle auto-commit");
|
|
1414
|
+
}
|
|
1333
1415
|
}
|
|
1334
1416
|
catch (err) {
|
|
1335
|
-
console.error("[opencode-immune] Multi-Cycle:
|
|
1417
|
+
console.error("[opencode-immune] Multi-Cycle: commit failed (standalone):", err);
|
|
1336
1418
|
}
|
|
1337
1419
|
finally {
|
|
1338
1420
|
state.commitPending = false;
|
|
@@ -1345,13 +1427,17 @@ function createTextCompleteHandler(state) {
|
|
|
1345
1427
|
// Step 1: Always commit first (CYCLE_COMPLETE implies end of cycle)
|
|
1346
1428
|
if (!state.commitPending) {
|
|
1347
1429
|
state.commitPending = true;
|
|
1348
|
-
console.log("[opencode-immune] Multi-Cycle: CYCLE_COMPLETE detected, running
|
|
1430
|
+
console.log("[opencode-immune] Multi-Cycle: CYCLE_COMPLETE detected, running smart commit first...");
|
|
1349
1431
|
try {
|
|
1350
|
-
await
|
|
1351
|
-
|
|
1432
|
+
const ok = await runSmartCommit(state);
|
|
1433
|
+
if (!ok) {
|
|
1434
|
+
console.log("[opencode-immune] Multi-Cycle: smart commit failed, falling back to git commit...");
|
|
1435
|
+
await runGitCommit(state.input.directory, "chore: ultrawork cycle auto-commit");
|
|
1436
|
+
}
|
|
1437
|
+
console.log("[opencode-immune] Multi-Cycle: commit completed before new cycle.");
|
|
1352
1438
|
}
|
|
1353
1439
|
catch (err) {
|
|
1354
|
-
console.error("[opencode-immune] Multi-Cycle:
|
|
1440
|
+
console.error("[opencode-immune] Multi-Cycle: commit failed (continuing anyway):", err);
|
|
1355
1441
|
}
|
|
1356
1442
|
finally {
|
|
1357
1443
|
state.commitPending = false;
|
|
@@ -1449,7 +1535,7 @@ function createMultiCycleHandler(state) {
|
|
|
1449
1535
|
async function server(input) {
|
|
1450
1536
|
const state = createState(input);
|
|
1451
1537
|
// ── Plugin version check (non-blocking, fire-and-forget) ──
|
|
1452
|
-
checkPluginUpdate().catch(() => { });
|
|
1538
|
+
checkPluginUpdate(state).catch(() => { });
|
|
1453
1539
|
// ── Harness auto-sync (non-blocking, fire-and-forget) ──
|
|
1454
1540
|
// Runs in background so it doesn't delay plugin initialization.
|
|
1455
1541
|
// If sync fails, plugin continues normally with existing config.
|