opencode-self-improvement 0.2.0 → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-self-improvement",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Durable memory for opencode: remember facts, decisions, and commands across sessions, plus a one-command per-repo setup (/repo-init).",
5
5
  "main": "index.ts",
6
6
  "exports": {
package/src/inject.ts CHANGED
@@ -147,18 +147,23 @@ export async function storeVersion(): Promise<string> {
147
147
  }
148
148
 
149
149
  // Trim an injection block to `budget` characters at WHOLE-line boundaries:
150
- // keep lines while the joined length stays <= budget, drop the rest, never cut
151
- // mid-line. Returns "" when even the header line exceeds the budget (the block
152
- // is not worth injecting).
150
+ // keep every line that still fits (skipping over-long ones), never cut
151
+ // mid-line. Returns "" when even the header line exceeds the budget, and ""
152
+ // when NOTHING but header lines survived — a dangling "## Relevant durable
153
+ // memory" over zero memories is noise, not memory (bug seen live: a store
154
+ // whose top matches all exceed the budget injected exactly the 26-char header).
153
155
  export function capToBudget(block: string, budget: number): string {
154
- const kept: string[] = [];
155
- let len = 0;
156
- for (const line of block.split("\n")) {
157
- const add = kept.length ? line.length + 1 : line.length;
158
- if (len + add > budget) break;
156
+ const lines = block.split("\n");
157
+ // The header gives the block its meaning; if it does not fit, nothing does.
158
+ if (lines[0].length > budget) return "";
159
+ const kept: string[] = [lines[0]];
160
+ let len = lines[0].length;
161
+ for (const line of lines.slice(1)) {
162
+ if (len + line.length + 1 > budget) continue; // skip over-long lines, keep scanning
159
163
  kept.push(line);
160
- len += add;
164
+ len += line.length + 1;
161
165
  }
166
+ if (kept.every((l) => l.startsWith("## "))) return "";
162
167
  return kept.join("\n");
163
168
  }
164
169
 
package/src/resolver.ts CHANGED
@@ -54,6 +54,30 @@ export function balancedParen(text: string, openIdx: number): { inner: string; e
54
54
 
55
55
  const CALL_RE = /([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+)\s*\(/g;
56
56
 
57
+ // Last dotted segment is a known file extension: `package.json(committed by…)`
58
+ // is a file reference followed by parenthetical prose, not a call to a method
59
+ // named `json`. This class produced 9 false drift_ref_stale_incoming blocks in
60
+ // live use before the guard existed.
61
+ const FILE_EXT = new Set([
62
+ "json", "jsonc", "jsonl", "logfmt", "md", "mdx", "ts", "tsx", "js", "jsx",
63
+ "mjs", "cjs", "py", "go", "rs", "rb", "java", "kt", "sh", "bash", "zsh",
64
+ "yaml", "yml", "toml", "ini", "conf", "cfg", "txt", "csv", "html", "htm",
65
+ "css", "scss", "xml", "sql", "tf", "lock", "log", "env",
66
+ ]);
67
+
68
+ // Parenthetical PROSE is not a code call signature. A code-like arg list is
69
+ // empty or contains code punctuation ({ } ( ) : = , . < > quotes); the markers
70
+ // [ ] @ / never appear in the arg snippets drift detection cares about, and
71
+ // bare multi-word English with no code punctuation ("main-agent review fix")
72
+ // is prose.
73
+ function looksProseArgs(inner: string): boolean {
74
+ const s = inner.trim();
75
+ if (!s) return false;
76
+ if (/[[\]@/]/.test(s)) return true;
77
+ if (/[{}():=",.<>'"`]/.test(s)) return false;
78
+ return s.split(/\s+/).length >= 2;
79
+ }
80
+
57
81
  export function extractCallForms(content: string): CallForm[] {
58
82
  const forms: CallForm[] = [];
59
83
  CALL_RE.lastIndex = 0;
@@ -65,6 +89,8 @@ export function extractCallForms(content: string): CallForm[] {
65
89
  if (!paren) continue;
66
90
  const parts = symbol.split(".");
67
91
  const name = parts[parts.length - 1];
92
+ if (FILE_EXT.has(name.toLowerCase())) continue;
93
+ if (looksProseArgs(paren.inner)) continue;
68
94
  const ownerPath = parts.slice(0, -1).join(".");
69
95
  forms.push({
70
96
  symbol,