liteagents 2.22.1 → 2.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +203 -1
  2. package/README.md +7 -5
  3. package/package.json +1 -1
  4. package/packages/ampcode/commands/branch-review.md +180 -14
  5. package/packages/ampcode/commands/docs-builder/docs-builder.cjs +47 -2
  6. package/packages/ampcode/commands/refactor.md +71 -3
  7. package/packages/ampcode/commands/release.md +49 -9
  8. package/packages/ampcode/commands/remember/AGENT_RULES.md +12 -3
  9. package/packages/ampcode/commands/remember/friction.cjs +32 -3
  10. package/packages/ampcode/commands/remember.md +40 -7
  11. package/packages/ampcode/commands/ship.md +16 -0
  12. package/packages/claude/commands/branch-review.md +180 -14
  13. package/packages/claude/commands/docs-builder/docs-builder.cjs +47 -2
  14. package/packages/claude/commands/refactor.md +71 -3
  15. package/packages/claude/commands/release.md +49 -9
  16. package/packages/claude/commands/remember/AGENT_RULES.md +12 -3
  17. package/packages/claude/commands/remember/friction.cjs +32 -3
  18. package/packages/claude/commands/remember.md +40 -7
  19. package/packages/claude/commands/ship.md +16 -0
  20. package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/package-lock.json +19 -18
  21. package/packages/droid/commands/branch-review.md +180 -14
  22. package/packages/droid/commands/docs-builder/docs-builder.cjs +47 -2
  23. package/packages/droid/commands/refactor.md +71 -3
  24. package/packages/droid/commands/release.md +49 -9
  25. package/packages/droid/commands/remember/AGENT_RULES.md +12 -3
  26. package/packages/droid/commands/remember/friction.cjs +32 -3
  27. package/packages/droid/commands/remember.md +40 -7
  28. package/packages/droid/commands/ship.md +16 -0
  29. package/packages/opencode/command/branch-review.md +180 -14
  30. package/packages/opencode/command/docs-builder/docs-builder.cjs +47 -2
  31. package/packages/opencode/command/refactor.md +71 -3
  32. package/packages/opencode/command/release.md +49 -9
  33. package/packages/opencode/command/remember/AGENT_RULES.md +12 -3
  34. package/packages/opencode/command/remember/friction.cjs +32 -3
  35. package/packages/opencode/command/remember.md +40 -7
  36. package/packages/opencode/command/ship.md +16 -0
@@ -1765,8 +1765,26 @@ function extractToolsFromTurn(event) {
1765
1765
  const toolName = block.name || 'unknown';
1766
1766
  tools.push({ tool: toolName, action: 'call' });
1767
1767
  } else if (block.type === 'tool_result') {
1768
- const result = String(block.content || '');
1769
- if (result.includes('Exit code 0')) {
1768
+ // The transcript marks a failed tool call with `is_error`; a success
1769
+ // carries no sentinel in the result text. Reading the text for
1770
+ // 'Exit code 0' matched 1 block in 2623 sampled from the real
1771
+ // corpus, so `result` was 'unknown' on 31 of 34 clusters — and the
1772
+ // case this field exists for is a CLAIMED success the user is
1773
+ // contradicting, which is the one that never fired. Content is also
1774
+ // sometimes an array of blocks (403/2623), which String() turned
1775
+ // into "[object Object]" so no pattern could match it.
1776
+ //
1777
+ // A block with no `is_error` stays unknown on purpose: those are
1778
+ // harness meta-results (skill/agent launches, question answers —
1779
+ // 560/2624 sampled) and calling them success would inflate the very
1780
+ // signal this field is here to detect.
1781
+ const raw = block.content;
1782
+ const result = Array.isArray(raw)
1783
+ ? raw.map(b => (b && typeof b === 'object' ? (b.text || '') : String(b || ''))).join('\n')
1784
+ : String(raw || '');
1785
+ if (block.is_error === true) {
1786
+ tools.push({ tool: 'result', action: 'error' });
1787
+ } else if (typeof block.is_error === 'boolean') {
1770
1788
  tools.push({ tool: 'result', action: 'success' });
1771
1789
  } else if (/Exit code [1-9]|Traceback|Error/.test(result)) {
1772
1790
  tools.push({ tool: 'result', action: 'error' });
@@ -2142,6 +2160,7 @@ function clusterCandidates(allCandidates, canonicalGroups) {
2142
2160
  errors: b.errors,
2143
2161
  peak: b.peak,
2144
2162
  texts: b.texts,
2163
+ files: [...b.files],
2145
2164
  preceding: b.preceding,
2146
2165
  anySelf: b.selfVotes > 0, // at least one self-correction → warn, LLM confirms target
2147
2166
  };
@@ -2166,7 +2185,7 @@ function clusterCandidates(allCandidates, canonicalGroups) {
2166
2185
  if (best) {
2167
2186
  cl = best;
2168
2187
  } else {
2169
- cl = { sig: new Set(), seedSig: new Set(ss.sig), shCount: new Map(), sessions: {}, signals: {}, contexts: [], errors: [], peaks: [], anySelf: false, preceding: null };
2188
+ cl = { sig: new Set(), seedSig: new Set(ss.sig), shCount: new Map(), sessions: {}, signals: {}, contexts: [], errors: [], peaks: [], anySelf: false, preceding: null, files: new Set() };
2170
2189
  clusters.push(cl);
2171
2190
  }
2172
2191
  for (const s of ss.sig) { cl.sig.add(s); cl.shCount.set(s, (cl.shCount.get(s) || 0) + 1); }
@@ -2178,6 +2197,15 @@ function clusterCandidates(allCandidates, canonicalGroups) {
2178
2197
  cl.peaks.push(ss.peak);
2179
2198
  if (ss.anySelf) cl.anySelf = true;
2180
2199
  if (ss.preceding && (!cl.preceding || (ss.preceding.action !== 'none' || ss.preceding.error))) cl.preceding = ss.preceding;
2200
+ // The file referents a cluster's sessions touched. Measured on a 34-cluster real
2201
+ // corpus as the one channel that actually separates clusters: file basenames gave 28
2202
+ // distinct signatures at a 3.7% collision rate, against 13 / 19.3% for the preceding
2203
+ // action+result pair and 10 / 25.8% for tool_sequence. Matching an incoming cluster
2204
+ // to a ledger entry otherwise runs on class_hints alone, and those ARE fragments of
2205
+ // the entry's own evidence quotes — one channel, where identity and proof are the
2206
+ // same strings. These paths are mechanical, so carrying them adds a second channel
2207
+ // without reintroducing an LLM distillation step.
2208
+ for (const f of ss.files) cl.files.add(f);
2181
2209
  }
2182
2210
 
2183
2211
  const out = clusters.map(cl => {
@@ -2251,6 +2279,7 @@ function clusterCandidates(allCandidates, canonicalGroups) {
2251
2279
  max_peak: peaks[peaks.length - 1],
2252
2280
  contexts: cl.contexts,
2253
2281
  errors: cl.errors,
2282
+ files: [...cl.files].sort().slice(0, 8), // referents: the discriminative match channel (see merge above)
2254
2283
  preceding: cl.preceding, // #4: agent action + result just before the reaction
2255
2284
  self_suspect: allSelf || cl.anySelf, // #3: a self-correction is present — LLM confirms target (advisory)
2256
2285
  top_keywords: topSh.slice(0, 10),
@@ -133,6 +133,14 @@ Reads all raw material (`.opencode/stash/*.md` + `.opencode/remember/friction/an
133
133
  Re-processing a stash whose episode is already filed must not create a near-duplicate
134
134
  pair. Every older episode is **folded, then deleted**: its lesson becomes a fact (handed
135
135
  to the rewrite above); the narrative is removed. No archive — git has the history.
136
+ **Specify the operation once.** The keep-10 rule is the rule; the set to remove is
137
+ *derived* from it, never supplied alongside it as a second list. Given both, an agent
138
+ applies both and removes their union — observed in the field: a run told to keep 10 and
139
+ handed a 5-entry delete list removed 7, and the 2 extras were never folded, so one
140
+ lesson left memory with nothing carrying it. **No episode is removed whose lesson has
141
+ not been folded into a fact first**, and the two sets must match: state the count
142
+ before, the count after, and name each episode removed. Removed-but-not-folded is a
143
+ defect to report, not a tidy-up.
136
144
  - **Antigens section**: only update from friction output (step 4)
137
145
  - Write merged result to `.opencode/remember/MEMORY.md` in the format under step 5.
138
146
 
@@ -276,7 +284,11 @@ Reads all raw material (`.opencode/stash/*.md` + `.opencode/remember/friction/an
276
284
  recurrence; a single occurrence has none to track yet. Friction re-scans every
277
285
  session log every run, so a later run matches it back to 2+ sessions and seeds it
278
286
  then — this does not change matching against an EXISTING entry, which is recurrence
279
- regardless of the matching cluster's own session count.
287
+ regardless of the matching cluster's own session count. **A match is not an
288
+ increment.** Whether it counts as a new conversation is decided in 4c by
289
+ `friction.cjs count`, which is a no-op when that session hash is already stored — so
290
+ several matches against one entry routinely produce zero increments, and that is
291
+ correct, not a miscount.
280
292
  - For `new:<theme>` groups with no ledger match: distinct conversations = distinct
281
293
  cluster indices in the group (within one classify batch, no two cluster indices
282
294
  share a session hash). `sessions < 2` → writes nothing. `sessions >= 2` → new entry,
@@ -346,18 +358,39 @@ Reads all raw material (`.opencode/stash/*.md` + `.opencode/remember/friction/an
346
358
  inline duplication is needed
347
359
  - If `.opencode/remember/AGENT_RULES.md` exists (bootstrapped in step 1), compose a second,
348
360
  independent section between `<!-- AGENT_RULES:START -->` and `<!-- AGENT_RULES:END -->`
349
- markers. Unlike MEMORY.md above, this is a **plain path pointer, never `@`-referenced**
350
- — an `@`-reference hot-loads the whole file into every session, and this is a standards
351
- guide to consult when designing/building something new, not hot context:
361
+ markers. Unlike MEMORY.md above, the file itself is **never `@`-referenced** — an
362
+ `@`-reference hot-loads all ~300 lines into every session, and it is a standards guide
363
+ to consult when designing/building something new, not hot context. The section carries
364
+ a path pointer plus exactly two inline rules: the ones that change what you TYPE, which
365
+ you cannot look up because you do not know you need them. Everything else stays behind
366
+ the pointer. Write the section verbatim, rules first:
352
367
  ```
353
368
  <!-- AGENT_RULES:START -->
369
+ **One writer per piece of state.** One function assigns each field; everything else
370
+ calls it. Grep who writes it before you write it — and if a write can land from a
371
+ callback, thread, or lifecycle, the reader must tell stale from fresh.
372
+
373
+ **Surgical changes only.** Touch what the task requires. Dead code, nits, bugs you
374
+ pass: if it's inside or affects the code you're already changing and the fix changes
375
+ no behavior, fix it and say so — otherwise report it and say what it costs to leave
376
+ it. A problem you don't fix goes in the report, never in a comment.
377
+
354
378
  Standards guide (read when designing/building something new, not hot context):
355
379
  .opencode/remember/AGENT_RULES.md
356
380
  <!-- AGENT_RULES:END -->
357
381
  ```
358
- - Each marker pair is independent: if AGENTS.md already has a given pair, replace the
359
- section between them; if not, append it at the end; if no AGENTS.md exists, create one
360
- containing whichever section(s) apply
382
+ - Each marker pair is independent: if AGENTS.md lacks a given pair, append it at the
383
+ end; if a given pair already exists, replace its content in place; if no AGENTS.md
384
+ exists, create one containing whichever section(s) apply.
385
+ - **An existing AGENT_RULES pair is left alone — bootstrap once, never overwrite.** The
386
+ block above is what to write when creating it, not a template to re-impose every run.
387
+ Users trim this section deliberately (a pointer-only variant is common), and rewriting
388
+ it silently re-adds text they removed, on every single run, forever. Observed in the
389
+ field: a run restored the inline rules into a AGENTS.md whose owner had cut them, and
390
+ the edit had to be reverted by hand. This matches how `AGENT_RULES.md` itself is
391
+ handled — bootstrapped once, never overwritten after.
392
+ - If an existing pair is present but its **path pointer** is missing or wrong, that is
393
+ load-bearing: **report it and stop**, do not silently rewrite the section around it.
361
394
 
362
395
  ```markdown
363
396
  # Project Memory
@@ -20,6 +20,22 @@ its exit code**. A check you did not run is a **fail**, never a pass. **N/A
20
20
  requires a stated reason** ("no build script in `package.json`") — N/A must
21
21
  never stand in for "didn't get to it."
22
22
 
23
+ **Capture the exit code of the command itself, never of a pipeline.** Run the
24
+ bare command, then read `$?` on the next line:
25
+
26
+ ```
27
+ node "$f" > /tmp/out 2>&1; e=$?
28
+ ```
29
+
30
+ `$?` after a pipe is the *last element's* status, so the common shape
31
+ `out=$(cmd 2>&1 | tail -1); echo "exit=$?"` reports `tail`'s success — `0` —
32
+ for a suite that exited `2`. Reproduced: a check printing "exit 2:
33
+ prerequisites missing" was recorded as a pass by exactly that loop. Nor does
34
+ `${PIPESTATUS[0]}` rescue it inside a command substitution; it is empty by
35
+ the time you read it. This is the rule the whole gate rests on, and the
36
+ piped form is the natural way to write a multi-suite loop, so it fails
37
+ silently and in the unsafe direction.
38
+
23
39
  ## Checklist
24
40
  - [ ] **Tests pass** — run the project's real test command (`npm test`,
25
41
  `pytest`, `go test ./...`, `cargo test`, `make test`).