@codemem/opencode-plugin 0.22.4 → 0.24.0

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.
@@ -15,7 +15,7 @@ import {
15
15
 
16
16
  const TRUTHY_VALUES = ["1", "true", "yes"];
17
17
  const DISABLED_VALUES = ["0", "false", "off"];
18
- const PINNED_BACKEND_VERSION = "0.22.4";
18
+ const PINNED_BACKEND_VERSION = "0.24.0";
19
19
  const COMPAT_CHECK_DELAY_MS = 1500;
20
20
  const COMPAT_CHECK_CACHE_TTL_MS = 5 * 60 * 1000;
21
21
 
@@ -161,6 +161,111 @@ const appendWorkingSetFileArgs = (args, workingSetFiles) => {
161
161
  return args;
162
162
  };
163
163
 
164
+ const buildInjectQuery = ({ firstPrompt, lastPromptText, projectName, filesModified }) => {
165
+ const parts = [];
166
+
167
+ if (firstPrompt && String(firstPrompt).trim()) {
168
+ parts.push(String(firstPrompt).trim());
169
+ }
170
+
171
+ if (
172
+ lastPromptText
173
+ && String(lastPromptText).trim()
174
+ && String(lastPromptText).trim() !== String(firstPrompt || "").trim()
175
+ && String(lastPromptText).trim().length > 5
176
+ ) {
177
+ parts.push(String(lastPromptText).trim());
178
+ }
179
+
180
+ if (projectName) {
181
+ parts.push(String(projectName));
182
+ }
183
+
184
+ const recentFiles = Array.from(filesModified || [])
185
+ .slice(-5)
186
+ .map((filePath) => String(filePath || "").split("/").pop())
187
+ .filter(Boolean)
188
+ .join(" ");
189
+ if (recentFiles) {
190
+ parts.push(recentFiles);
191
+ }
192
+
193
+ if (parts.length === 0) {
194
+ return "recent work";
195
+ }
196
+
197
+ const query = parts.join(" ");
198
+ return query.length > 500 ? query.slice(0, 500) : query;
199
+ };
200
+
201
+ const buildPackArgs = ({ query, filesModified, injectLimit, injectTokenBudget }) => {
202
+ const workingSetFiles = Array.from(filesModified || [])
203
+ .slice(-8)
204
+ .map((value) => String(value || "").trim())
205
+ .filter(Boolean);
206
+ const args = ["pack", query];
207
+ if (injectLimit !== null && Number.isFinite(injectLimit) && injectLimit > 0) {
208
+ args.push("--limit", String(injectLimit));
209
+ }
210
+ if (
211
+ injectTokenBudget !== null
212
+ && Number.isFinite(injectTokenBudget)
213
+ && injectTokenBudget > 0
214
+ ) {
215
+ args.push("--token-budget", String(injectTokenBudget));
216
+ }
217
+ return appendWorkingSetFileArgs(args, workingSetFiles);
218
+ };
219
+
220
+ const applyInjectedContextToOutput = async ({
221
+ injectEnabled,
222
+ input,
223
+ output,
224
+ injectedSessions,
225
+ injectionToastShown,
226
+ showToast,
227
+ resolveInjectQuery,
228
+ buildInjectedContext,
229
+ }) => {
230
+ if (!injectEnabled) {
231
+ return false;
232
+ }
233
+
234
+ const query = resolveInjectQuery();
235
+ const cached = injectedSessions.get(input.sessionID);
236
+ let contextText = cached?.text || "";
237
+
238
+ if (!contextText || cached?.query !== query) {
239
+ const injected = await buildInjectedContext(query);
240
+ if (injected?.text) {
241
+ injectedSessions.set(input.sessionID, {
242
+ query,
243
+ text: injected.text,
244
+ metrics: injected.metrics || null,
245
+ });
246
+ contextText = injected.text;
247
+
248
+ if (!injectionToastShown.has(input.sessionID) && showToast) {
249
+ injectionToastShown.add(input.sessionID);
250
+ try {
251
+ await showToast(buildInjectionToastMessage(injected.metrics));
252
+ } catch {
253
+ // best-effort only
254
+ }
255
+ }
256
+ }
257
+ }
258
+
259
+ if (!contextText) {
260
+ return false;
261
+ }
262
+ if (!Array.isArray(output.system)) {
263
+ output.system = [];
264
+ }
265
+ output.system.push(contextText);
266
+ return true;
267
+ };
268
+
164
269
  const mapOpencodeEventTypeToAdapterType = (eventType) => {
165
270
  if (eventType === "user_prompt") {
166
271
  return "prompt";
@@ -1335,63 +1440,13 @@ export const OpencodeMemPlugin = async ({
1335
1440
  await showToast(`${message}. Suggested action: ${guidance.action}`, "warning");
1336
1441
  };
1337
1442
 
1338
- const resolveInjectQuery = () => {
1339
- const parts = [];
1340
-
1341
- // First prompt captures session intent (most stable signal)
1342
- if (sessionContext.firstPrompt && sessionContext.firstPrompt.trim()) {
1343
- parts.push(sessionContext.firstPrompt.trim());
1344
- }
1345
-
1346
- // Latest prompt adds current focus (skip if same as first, or trivial)
1347
- if (
1348
- lastPromptText &&
1349
- lastPromptText.trim() &&
1350
- lastPromptText.trim() !== (sessionContext.firstPrompt || "").trim() &&
1351
- lastPromptText.trim().length > 5
1352
- ) {
1353
- parts.push(lastPromptText.trim());
1354
- }
1355
-
1356
- // Project name for scoping
1357
- const projectName = resolveProjectName(project, cwd);
1358
- if (projectName) {
1359
- parts.push(projectName);
1360
- }
1361
-
1362
- // Recently modified files signal what area of the codebase we're in
1363
- if (sessionContext.filesModified.size > 0) {
1364
- const recentFiles = Array.from(sessionContext.filesModified)
1365
- .slice(-5)
1366
- .map((f) => f.split("/").pop())
1367
- .join(" ");
1368
- parts.push(recentFiles);
1369
- }
1370
-
1371
- if (parts.length === 0) {
1372
- return "recent work";
1373
- }
1374
-
1375
- // Cap total length to avoid overly long CLI args
1376
- const query = parts.join(" ");
1377
- return query.length > 500 ? query.slice(0, 500) : query;
1378
- };
1379
-
1380
- const buildPackArgs = (query) => {
1381
- const workingSetFiles = Array.from(sessionContext.filesModified)
1382
- .slice(-8)
1383
- .map((value) => String(value || "").trim())
1384
- .filter(Boolean);
1385
- const args = ["pack", query];
1386
- if (injectLimit !== null && Number.isFinite(injectLimit) && injectLimit > 0) {
1387
- args.push("--limit", String(injectLimit));
1388
- }
1389
- if (injectTokenBudget !== null && Number.isFinite(injectTokenBudget) && injectTokenBudget > 0) {
1390
- args.push("--token-budget", String(injectTokenBudget));
1391
- }
1392
- appendWorkingSetFileArgs(args, workingSetFiles);
1393
- return args;
1394
- };
1443
+ const resolveInjectQuery = () =>
1444
+ buildInjectQuery({
1445
+ firstPrompt: sessionContext.firstPrompt,
1446
+ lastPromptText,
1447
+ projectName: resolveProjectName(project, cwd),
1448
+ filesModified: sessionContext.filesModified,
1449
+ });
1395
1450
 
1396
1451
  const parsePackText = (stdout) => {
1397
1452
  if (!stdout || !stdout.trim()) {
@@ -1424,7 +1479,12 @@ export const OpencodeMemPlugin = async ({
1424
1479
  };
1425
1480
 
1426
1481
  const buildInjectedContext = async (query) => {
1427
- const packArgs = buildPackArgs(query);
1482
+ const packArgs = buildPackArgs({
1483
+ query,
1484
+ filesModified: sessionContext.filesModified,
1485
+ injectLimit,
1486
+ injectTokenBudget,
1487
+ });
1428
1488
  const result = await runCli(packArgs);
1429
1489
  if (!result || result.exitCode !== 0) {
1430
1490
  const exitCode = result?.exitCode ?? "unknown";
@@ -1681,9 +1741,6 @@ export const OpencodeMemPlugin = async ({
1681
1741
 
1682
1742
  return {
1683
1743
  "experimental.chat.system.transform": async (input, output) => {
1684
- if (!injectEnabled) {
1685
- return;
1686
- }
1687
1744
  const query = resolveInjectQuery();
1688
1745
  if (debug) {
1689
1746
  await logLine(
@@ -1692,40 +1749,25 @@ export const OpencodeMemPlugin = async ({
1692
1749
  } tui_toast=${Boolean(client.tui?.showToast)}`
1693
1750
  );
1694
1751
  }
1695
- const cached = injectedSessions.get(input.sessionID);
1696
- let contextText = cached?.text || "";
1697
- if (!contextText || cached?.query !== query) {
1698
- const injected = await buildInjectedContext(query);
1699
- if (injected?.text) {
1700
- injectedSessions.set(input.sessionID, {
1701
- query,
1702
- text: injected.text,
1703
- metrics: injected.metrics || null,
1704
- });
1705
- contextText = injected.text;
1706
-
1707
- if (!injectionToastShown.has(input.sessionID) && client.tui?.showToast) {
1708
- injectionToastShown.add(input.sessionID);
1709
- try {
1710
- await client.tui.showToast({
1711
- body: {
1712
- message: buildInjectionToastMessage(injected.metrics),
1713
- variant: "info",
1714
- },
1715
- });
1716
- } catch (toastErr) {
1717
- // best-effort only
1718
- }
1752
+ await applyInjectedContextToOutput({
1753
+ injectEnabled,
1754
+ input,
1755
+ output,
1756
+ injectedSessions,
1757
+ injectionToastShown,
1758
+ showToast: client.tui?.showToast
1759
+ ? async (message) => {
1760
+ await client.tui.showToast({
1761
+ body: {
1762
+ message,
1763
+ variant: "info",
1764
+ },
1765
+ });
1719
1766
  }
1720
- }
1721
- }
1722
- if (!contextText) {
1723
- return;
1724
- }
1725
- if (!Array.isArray(output.system)) {
1726
- output.system = [];
1727
- }
1728
- output.system.push(contextText);
1767
+ : null,
1768
+ resolveInjectQuery,
1769
+ buildInjectedContext,
1770
+ });
1729
1771
  },
1730
1772
  event: async ({ event }) => {
1731
1773
  const eventType = event?.type || "unknown";
@@ -2009,6 +2051,9 @@ export const __testUtils = {
2009
2051
  inferProjectFromCwd,
2010
2052
  normalizeProjectLabel,
2011
2053
  resolveProjectName,
2054
+ buildInjectQuery,
2055
+ buildPackArgs,
2056
+ applyInjectedContextToOutput,
2012
2057
  buildRunnerArgs,
2013
2058
  appendWorkingSetFileArgs,
2014
2059
  extractApplyPatchPaths,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemem/opencode-plugin",
3
- "version": "0.22.4",
3
+ "version": "0.24.0",
4
4
  "description": "CodeMem plugin for OpenCode",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -18,7 +18,7 @@
18
18
  ".opencode/lib/"
19
19
  ],
20
20
  "dependencies": {
21
- "@opencode-ai/plugin": "1.3.13"
21
+ "@opencode-ai/plugin": "1.4.0"
22
22
  },
23
23
  "repository": {
24
24
  "type": "git",
@@ -29,6 +29,9 @@
29
29
  "bugs": {
30
30
  "url": "https://github.com/kunickiaj/codemem/issues"
31
31
  },
32
+ "engines": {
33
+ "node": ">=24"
34
+ },
32
35
  "license": "MIT",
33
36
  "publishConfig": {
34
37
  "access": "public"