backpass 0.1.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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +406 -0
  3. package/bin/backpass.js +4 -0
  4. package/package.json +62 -0
  5. package/src/acpx.js +576 -0
  6. package/src/agents.js +389 -0
  7. package/src/analyze.js +289 -0
  8. package/src/apply/lavish.js +128 -0
  9. package/src/apply/terminal.js +119 -0
  10. package/src/apply/writer.js +101 -0
  11. package/src/bootstrap.js +74 -0
  12. package/src/cli.js +261 -0
  13. package/src/commands/analyze.js +88 -0
  14. package/src/commands/apply.js +103 -0
  15. package/src/commands/bootstrap.js +172 -0
  16. package/src/commands/init.js +59 -0
  17. package/src/commands/propose.js +136 -0
  18. package/src/commands/run.js +95 -0
  19. package/src/commands/scan.js +90 -0
  20. package/src/commands/status.js +143 -0
  21. package/src/commands/usage.js +25 -0
  22. package/src/config.js +249 -0
  23. package/src/diff.js +305 -0
  24. package/src/discovery/adapters/claude.js +77 -0
  25. package/src/discovery/adapters/codex.js +162 -0
  26. package/src/discovery/adapters/cursor-cli.js +109 -0
  27. package/src/discovery/adapters/cursor-ide.js +130 -0
  28. package/src/discovery/adapters/grok.js +107 -0
  29. package/src/discovery/adapters/opencode.js +151 -0
  30. package/src/discovery/adapters/pi.js +87 -0
  31. package/src/discovery/adapters/shared.js +195 -0
  32. package/src/discovery/adapters/sqlite.js +50 -0
  33. package/src/discovery/association.js +100 -0
  34. package/src/discovery/index.js +226 -0
  35. package/src/discovery/self.js +62 -0
  36. package/src/distill.js +182 -0
  37. package/src/fold.js +214 -0
  38. package/src/gap-ledger.js +174 -0
  39. package/src/logger.js +74 -0
  40. package/src/memory.js +244 -0
  41. package/src/progress.js +29 -0
  42. package/src/prompts/analysis.md +48 -0
  43. package/src/prompts/annotate.md +48 -0
  44. package/src/prompts/synthesis.md +98 -0
  45. package/src/prompts.js +36 -0
  46. package/src/proposal.js +430 -0
  47. package/src/redact.js +36 -0
  48. package/src/repo.js +118 -0
  49. package/src/sample.js +99 -0
  50. package/src/skills.js +207 -0
  51. package/src/state.js +202 -0
  52. package/src/subprocess.js +47 -0
  53. package/src/synthesize.js +287 -0
  54. package/src/tokens.js +48 -0
  55. package/src/tui/index.js +336 -0
  56. package/src/tui/render.js +487 -0
  57. package/src/tui/term.js +130 -0
  58. package/src/tui/theme.js +111 -0
  59. package/src/workspace.js +162 -0
  60. package/templates/apply.html +928 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kun Chen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,406 @@
1
+ <h1 align="center">backpass</h1>
2
+ <p align="center">
3
+ <a href="https://github.com/kunchenguid/backpass/actions/workflows/ci.yml"
4
+ ><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/kunchenguid/backpass/ci.yml?style=flat-square&label=ci"
5
+ /></a>
6
+ <a href="https://github.com/kunchenguid/backpass/actions/workflows/release-please.yml"
7
+ ><img alt="Release" src="https://img.shields.io/github/actions/workflow/status/kunchenguid/backpass/release-please.yml?style=flat-square&label=release"
8
+ /></a>
9
+ <a href="https://www.npmjs.com/package/backpass"
10
+ ><img alt="npm" src="https://img.shields.io/npm/v/backpass?style=flat-square"
11
+ /></a>
12
+ <a href="https://img.shields.io/badge/platform-macOS%20%7C%20Linux-blue?style=flat-square"
13
+ ><img alt="Platform" src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux-blue?style=flat-square"
14
+ /></a>
15
+ <a href="https://x.com/kunchenguid"
16
+ ><img alt="X" src="https://img.shields.io/badge/X-@kunchenguid-black?style=flat-square"
17
+ /></a>
18
+ </p>
19
+
20
+ <h3 align="center">Gradient descent for your agent memory.</h3>
21
+
22
+ Your `AGENTS.md` is a set of weights. Every agent session is a forward pass. The
23
+ transcript that session leaves on disk is the loss signal - and today nothing reads it.
24
+ The loop only closes when a human happens to remember a failure and edits the file by hand.
25
+
26
+ `backpass` closes it. It finds the agent sessions that actually ran in your repo, reads
27
+ what happened in them, and proposes evidence-backed edits to your memory file - under a
28
+ token budget, gated by you.
29
+
30
+ - **Local-first** - Reads the transcript stores of six agent harnesses directly from disk.
31
+ No API, no upload; transcripts never leave your machine except into an agent you already
32
+ authenticated, and obvious secrets are redacted before they do.
33
+ - **Evidence-gated** - Every proposed edit carries verbatim quotes from real sessions, a
34
+ new instruction needs evidence from at least two independent sessions, and one run
35
+ proposes at most five edits. Small, noisy, repeated steps - not a rewrite.
36
+ - **Human in the loop** - Analysis never writes. `backpass apply` is the only writing
37
+ command, and it shows each edit with its evidence for you to accept or reject.
38
+
39
+ ```
40
+ AGENTS.md / CLAUDE.md (the weights)
41
+ → agent session (forward pass)
42
+ → transcript on disk (loss signal)
43
+ → backpass: collect samples, distill, calculate loss, aggregate gradients
44
+ → backpass: gradient descent (diffs + skill extractions)
45
+ → you accept or reject (the human gate)
46
+ → back to the weights
47
+ ```
48
+
49
+ One run is one gradient step: at most five edits, and a new instruction needs evidence
50
+ from at least two independent sessions.
51
+
52
+ ## Quick Start
53
+
54
+ ```sh
55
+ npm install -g backpass
56
+ # or run it without installing
57
+ npx backpass
58
+ ```
59
+
60
+ Requires **Node >= 22.5** and [`acpx`](https://github.com/openclaw/acpx) on your PATH.
61
+
62
+ backpass has **no API keys of its own**. Every model call goes through acpx to a harness
63
+ you have already authenticated.
64
+
65
+ ```sh
66
+ cd your-repo
67
+ backpass init # write .backpassrc.json, exclude .backpass/ via .git/info/exclude
68
+ backpass # collect samples → calculate loss → aggregate gradients → gradient descent (never writes)
69
+ backpass apply # review each edit, accept or reject, then write
70
+ ```
71
+
72
+ ## How It Works
73
+
74
+ ### 1. Collect samples - which sessions belong to this repo
75
+
76
+ backpass reads the local transcript stores of six harnesses directly. No API, no upload.
77
+
78
+ | Harness | Store | Repo tie |
79
+ | -------------- | ---------------------------------------------- | ------------------------------------- |
80
+ | **claude** | `~/.claude/projects/<munged-cwd>/<uuid>.jsonl` | per-line `cwd` |
81
+ | **codex** | `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl` | `cwd` + recorded `git.repository_url` |
82
+ | **pi** | `~/.pi/agent/sessions/<escaped-cwd>/*.jsonl` | session-header `cwd` |
83
+ | **opencode** | `~/.local/share/opencode/opencode.db` (sqlite) | `session.directory` |
84
+ | **grok** | `~/.grok/sessions/<encoded-cwd>/<uuid>/` | `summary.json` `cwd` + `git_remotes` |
85
+ | **cursor CLI** | `~/.cursor/chats/<md5(cwd)>/<uuid>/` | `meta.json` `cwd` |
86
+
87
+ Association runs in three tiers:
88
+
89
+ 1. **Tier 1 - deterministic.** The session's cwd is (or sits inside) one of this repo's
90
+ worktrees.
91
+ 2. **Tier 2 - deterministic, survives deletion.** A git remote recorded in the transcript
92
+ matches one of the repo's remotes. This is how codex and grok stay attributable long
93
+ after the worktree is gone.
94
+ 3. **Tier 3 - best-effort.** A dead path whose last segment is the repo's directory name,
95
+ or one matching a glob you configured. Labelled as such, and excluded by `--strict`.
96
+
97
+ Collection is incremental. Codex alone can hold 10,000+ rollouts, so verdicts are cached in
98
+ `.backpass/scan-cache.json` by path, mtime and size - re-scans cost only the new files.
99
+ A harness whose store is missing or has drifted into an unrecognised shape produces a
100
+ warning and is skipped; the run continues. backpass's own loss and gradient-descent calls land
101
+ in these same stores under the repo's cwd; every prompt it sends is tagged, and tagged
102
+ sessions are excluded from the corpus (the `SELF` column in `backpass scan`).
103
+
104
+ ```sh
105
+ backpass scan --since 7d --strict
106
+ ```
107
+
108
+ ### 2. Distillation - cheap first
109
+
110
+ Raw transcripts are mostly tool-call noise; a megabyte of session is a few thousand tokens
111
+ of actual signal. Before any model sees anything, backpass reduces each session
112
+ deterministically: user and assistant turns verbatim, each tool call collapsed to one line
113
+ (`tool: Bash "npm test" -> 1 failing`), tool output truncated, injected harness scaffolding
114
+ dropped, secrets redacted. Typical reduction is **96-99%**.
115
+
116
+ The distilled trace ends with the path to the raw transcript, so the analysis agent can
117
+ open the original when - and only when - a specific claim needs it.
118
+
119
+ ### 3. Calculate loss - one cheap call per transcript
120
+
121
+ Calculating loss costs one call per transcript, so the set is capped first: past `maxTranscripts`
122
+ (default 100, `--max-transcripts`) a **recency-weighted sample** is analyzed instead of
123
+ everything. Each transcript's weight halves every `sampleHalfLife` (default 14d), and the
124
+ sample is drawn without replacement, so recent sessions are almost always kept and old
125
+ ones stay represented in proportion. When this happens the run says so on stderr
126
+ (`discovered 340 transcript(s), analyzing a recency-weighted sample of 100`); pass
127
+ `--max-transcripts all` to analyze every transcript, or `--seed <n>` to reproduce a sample.
128
+
129
+ Each distilled trace goes to a cheap model with the memory file and a rubric. It returns
130
+ strict JSON: which instructions helped, which were violated, and what mistakes no current
131
+ instruction covers.
132
+
133
+ **Every claim must carry a verbatim quote.** Quoteless items are discarded - the single
134
+ most important defence against a model confabulating influence. Negative evidence (a
135
+ visible violation) is weighted highest.
136
+
137
+ Results are cached per transcript, keyed to both the transcript's content _and_ the memory
138
+ file's hash: edit the weights and the evidence correctly re-computes; change nothing and
139
+ the next run is free.
140
+
141
+ ### 4. Aggregate gradients - deterministic, no model
142
+
143
+ Evidence is grouped by instruction, giving each one a positive/negative count and a
144
+ **relevance** figure: the share of analyzed sessions in which it mattered at all. Duplicate
145
+ gaps across sessions are clustered, and clusters seen in fewer than `minGapEvidence`
146
+ sessions (default 2) are dropped. One bad session never rewrites the weights.
147
+
148
+ Those sessions are counted across runs, not per run: every gap sighting is kept in
149
+ `.backpass/gap-ledger.json` by gap and session, so a gap seen in one session today and in
150
+ another session next week graduates on the later run. The same session never counts twice,
151
+ a sighting retires once the memory file gains an instruction that covers it, and a session's
152
+ sightings expire after `gapLedgerMaxAge` (default 90d). Until a gap corroborates it stays
153
+ out of the proposal entirely.
154
+
155
+ ### 5. Gradient descent - one session, native edits
156
+
157
+ A single high-reasoning session turns the aggregated gradients into concrete edits: ADD,
158
+ REMOVE, REWRITE, or EXTRACT→SKILL. The agent does not describe edits for backpass to
159
+ splice in - it makes them, with its harness's own file tools, in a **staging copy** of the
160
+ memory file under `.backpass/synthesis/` (the repo itself is read-only to it, for
161
+ grounding). backpass then diffs the copy against the original and shows the agent the
162
+ measured changes by id; the agent annotates each one with a title, rationale, and the
163
+ verbatim evidence behind it. Nothing textual is ever taken from the model: every hunk's
164
+ text is copied out of your file by construction, so an edit can never "not appear" in it.
165
+ Then mechanical gates run, and they are not negotiable:
166
+
167
+ - at most `maxEditsPerRun` edits (the learning rate). By default the cap is adaptive: 5
168
+ when the file is near or under budget, and in a shrink plan (file over budget) one edit
169
+ per ~40 tokens of overage, capped at 20, so badly overgrown files recover in fewer runs.
170
+ An explicit `--max-edits` or config value always pins it.
171
+ - every measured change belongs to exactly one annotated edit - an unexplained change
172
+ is a violation, so is an edit that names no change
173
+ - new instructions need evidence from `minGapEvidence` distinct sessions (an edit that
174
+ only adds text is a new instruction, whatever the model calls it)
175
+ - every edit carries a verbatim quote
176
+ - the post-edit file must fit the budget, measured on the staged file
177
+
178
+ A violation triggers a re-prompt naming the exact breach (at most two). If those also
179
+ fail, backpass **fails loudly** and saves the rejected proposal. It never silently
180
+ truncates. A harness that writes past the staging copy into the repo is an error, never
181
+ an apply.
182
+
183
+ Token deltas shown to you are measured by backpass from the actual text - never taken from
184
+ the model's own arithmetic.
185
+
186
+ ### 6. The budget - "model size"
187
+
188
+ Every always-loaded token is paid on every future session, forever, and instruction
189
+ following dilutes as the file grows. So the budget is the constraint the whole backward
190
+ pass optimizes under.
191
+
192
+ **Default: 5,000 estimated tokens (~20KB)** per always-loaded memory file, configurable.
193
+ The estimator is bytes/4 - harness-neutral, ±15%.
194
+
195
+ ```
196
+ AGENTS.md [###############.................] 2,412 / 5,000 tok · 63 instructions
197
+ ```
198
+
199
+ At or over budget, the synthesis prompt goes zero-sum: every addition must name the
200
+ removal or extraction that pays for it.
201
+
202
+ ### 7. Skills as overflow
203
+
204
+ A skill's description **is** its when-useful condition: the description is always loaded
205
+ and cheap, the body is free until the trigger fires. That makes extraction the release
206
+ valve for the budget.
207
+
208
+ | | Trigger fits one description line | Trigger not detectable |
209
+ | ------------------------------------------------ | --------------------------------- | ---------------------- |
210
+ | **Broad** (≥20% of sessions, or safety-critical) | memory file | memory file |
211
+ | **Conditional / narrow** | **skill** | deletion candidate |
212
+
213
+ "Matters in N% of sessions" is measured, not guessed - it falls straight out of the
214
+ aggregate-gradients stage. A 640-token procedure relevant to 4% of sessions becomes a 35-token description
215
+ line, and backpass reports the arithmetic: `−611 tok always-loaded, +35 tok description`.
216
+
217
+ Skill descriptions are weights too. If the evidence shows an agent lacked knowledge a
218
+ skill already contains, that is a _failed trigger_ - backpass proposes a description edit,
219
+ not duplicate content.
220
+
221
+ ### 8. Apply - the human gate
222
+
223
+ `backpass apply` is the only command that writes. It serves a review surface through
224
+ [`lavish-axi`](https://github.com/kunchenguid/lavish-axi): one card per edit with the diff,
225
+ the evidence quotes and their sources, a live budget gauge, and ACCEPT / REJECT.
226
+
227
+ The surface is a static template shipped in the package - the CLI injects one JSON payload,
228
+ so it is instant, deterministic, and identical every run. Nothing there is model-generated.
229
+
230
+ There is no DEFER button, and it isn't missing: **rejections are remembered.** A rejected
231
+ edit is not proposed again unless materially new evidence arrives.
232
+
233
+ ```sh
234
+ backpass apply --no-ui # same decision, in the terminal
235
+ backpass apply --dry-run # show what would be written
236
+ ```
237
+
238
+ ### 9. Which file is the weights
239
+
240
+ `memoryFiles` is an ordered list (default `["AGENTS.md", "CLAUDE.md"]`); the first one
241
+ that exists is the file a run optimizes, so **AGENTS.md is canonical**. Resolution is
242
+ pointer-aware:
243
+
244
+ - `CLAUDE.md` containing only `@AGENTS.md` (the standard import) is a pointer: optimizing
245
+ AGENTS.md covers both harness families and the pointer stays valid. Nothing to report.
246
+ - Two separate full files are a divergence hazard. backpass optimizes AGENTS.md, leaves
247
+ CLAUDE.md untouched, and warns each run until you consolidate: move CLAUDE.md's content
248
+ into AGENTS.md and make CLAUDE.md the one-line pointer.
249
+ - A repo with **no memory file** is bootstrapped on the first `backpass` run: a starter
250
+ AGENTS.md (purpose, an empty `## Learnings` section, a `## Maintaining this file`
251
+ section) plus a CLAUDE.md pointer. The starter is then run through the ordinary backward pass, so recurring gaps
252
+ from your real transcripts become its first evidence-backed instructions. With no
253
+ transcripts it is seeded from defaults alone and says so. Bootstrap only ever creates
254
+ files; review it with `git diff`.
255
+
256
+ ## CLI Reference
257
+
258
+ | Command | What it does |
259
+ | ------------------ | ---------------------------------------------------------------------------------------- |
260
+ | `backpass` | collect samples → calculate loss → aggregate gradients → gradient descent. Never writes. |
261
+ | `backpass scan` | collect samples only: the transcript table with a confidence column |
262
+ | `backpass analyze` | calculate loss: the tier-1 pass over pending transcripts |
263
+ | `backpass propose` | aggregate gradients + gradient descent: the tier-2 pass from cached evidence |
264
+ | `backpass apply` | review and write the accepted edits |
265
+ | `backpass status` | cache state, failed transcripts, budget bars |
266
+ | `backpass init` | write `.backpassrc.json`, exclude `.backpass/` locally |
267
+
268
+ Run `backpass --help` for the full flag list.
269
+
270
+ ### Live progress
271
+
272
+ On an interactive terminal the default run renders a live progress view: the budget gauge,
273
+ a stage rail (collect samples → calculate loss → aggregate gradients → gradient descent),
274
+ per-store collection counts, one lane
275
+ per analysis job with its distillation receipt, and a running evidence tally. It draws to
276
+ stderr only and collapses into the plain line summary when the run ends, so scrollback and
277
+ piped output are identical to a run without it.
278
+
279
+ The view never gets in the way of automation: no TTY, `NO_COLOR`, `CI`, `--quiet`, `--json`,
280
+ or a terminal under 60 columns all mean plain lines, unchanged. Truecolor terminals get the
281
+ backpass theme; everything else falls back to the nearest ANSI-16 colors. The ink set adapts
282
+ to light backgrounds automatically (queried via OSC 11, `COLORFGBG` as fallback); force one
283
+ with `--theme dark|light` or `"theme"` in `.backpassrc.json`.
284
+
285
+ ### Two-tier models
286
+
287
+ Cheap analysis, smart synthesis. Both go through acpx, so backpass uses the harnesses you
288
+ already have - and by default it works out which ones those are. Each pass has an ordered
289
+ ladder of candidates, and the first one that is installed, logged in, and serves the model
290
+ wins:
291
+
292
+ | pass | effort | 1st | 2nd | 3rd |
293
+ | --------- | ------ | -------------------------------------- | ---------------------------- | --------------------------------- |
294
+ | analysis | medium | `gpt-5.6-luna` via pi, opencode, codex | `claude-sonnet-5` via claude | `grok-4.6` via pi, opencode, grok |
295
+ | synthesis | high | `gpt-5.6-sol` via pi, opencode, codex | `claude-opus-5` via claude | `grok-4.6` via pi, opencode, grok |
296
+
297
+ Each candidate is checked with a ~1.5s zero-token acpx probe (claude via `claude auth status`,
298
+ because its adapter accepts sessions while logged out); verdicts are cached in
299
+ `.backpass/agent-probe-cache.json` for 12h (30min for negatives) and re-probed with `--force`.
300
+ The probe is a filter, not a promise: if the chosen harness answers `AUTH_REQUIRED` or rejects
301
+ the model mid-run, backpass falls through to the next candidate and says so. When a whole
302
+ ladder is exhausted the error lists every candidate with what to run to fix it.
303
+
304
+ Bare model ids are resolved against what each adapter advertises (`openai-codex/gpt-5.6-luna`
305
+ on pi, `openai/gpt-5.6-luna` on opencode, `gpt-5.6-luna` on codex), so nothing is hardcoded
306
+ per harness. Ladders are ordinary config - reorder or shorten them under `"ladders"`.
307
+
308
+ Pinning an agent skips its ladder entirely:
309
+
310
+ ```sh
311
+ backpass \
312
+ --analysis-agent codex --analysis-model gpt-5.5 --analysis-effort low \
313
+ --synthesis-agent claude --synthesis-model claude-opus-5 --synthesis-effort high
314
+ ```
315
+
316
+ `--no-auto-agent` pins the pre-ladder defaults (codex / claude). If an adapter does not
317
+ advertise reasoning effort, backpass says so in the run report rather than pretending it
318
+ applied.
319
+
320
+ ### Configuration
321
+
322
+ `.backpassrc.json` in the repo root, layered over `~/.config/backpass/config.json`, with
323
+ CLI flags on top:
324
+
325
+ ```json
326
+ {
327
+ "memoryFiles": ["AGENTS.md"],
328
+ "budgetTokens": 5000,
329
+ "skillsDir": ".agents/skills",
330
+ "maxEditsPerRun": null,
331
+ "minGapEvidence": 2,
332
+ "gapLedgerMaxAge": "90d",
333
+ "maxTranscripts": 100,
334
+ "sampleHalfLife": "14d",
335
+ "analysis": { "agent": null, "model": null, "effort": null },
336
+ "synthesis": { "agent": null, "model": null, "effort": null },
337
+ "ladders": {
338
+ "analysis": [
339
+ { "model": "gpt-5.6-luna", "agents": ["pi", "opencode", "codex"] },
340
+ { "model": "claude-sonnet-5", "agents": ["claude"] },
341
+ { "model": "grok-4.6", "agents": ["pi", "opencode", "grok"] }
342
+ ],
343
+ "synthesis": [
344
+ { "model": "gpt-5.6-sol", "agents": ["pi", "opencode", "codex"] },
345
+ { "model": "claude-opus-5", "agents": ["claude"] },
346
+ { "model": "grok-4.6", "agents": ["pi", "opencode", "grok"] }
347
+ ]
348
+ },
349
+ "discovery": {
350
+ "harnesses": ["claude", "codex", "pi", "opencode", "grok", "cursor"],
351
+ "since": "30d",
352
+ "worktreeGlobs": [],
353
+ "minUserTurns": 2
354
+ },
355
+ "jobs": 4
356
+ }
357
+ ```
358
+
359
+ ### State
360
+
361
+ Everything mutable lives in `.backpass/`, kept out of git via the repo's local exclude
362
+ (`.git/info/exclude`, written by `backpass init`) rather than the tracked `.gitignore`:
363
+
364
+ ```
365
+ .backpass/
366
+ scan-cache.json collect-samples verdicts by path + mtime + size
367
+ evidence/<id>.json per-transcript loss
368
+ evidence-summary.json aggregated gradients
369
+ proposal.json the latest gradient-descent step
370
+ synthesis/ the staging copy the gradient-descent agent edited (memory file + skills)
371
+ prompts/ the exact prompts of the last run
372
+ agent-probe-cache.json which harnesses were available and logged in, and when
373
+ rejections.json edits you turned down, and the evidence behind them
374
+ gap-ledger.json gap sightings by gap and session, accumulated across runs
375
+ apply/apply.html the rendered review surface
376
+ ```
377
+
378
+ ## Limitations
379
+
380
+ - **Causal attribution is genuinely hard.** A model can confabulate influence. The
381
+ mitigations are structural - mandatory verbatim quotes, the two-session rule, negative
382
+ evidence weighted highest, and a human gate - but read the evidence, not just the title.
383
+ - **Transcript formats are undocumented** and can change without notice. Each adapter is
384
+ pinned by a golden fixture and fails soft.
385
+ - **Cursor IDE is deferred to v1.1.** Its composer→workspace link is version-dependent;
386
+ `--include-cursor-ide` enables a best-effort pass, but it is not a v1 guarantee.
387
+ - Global memory (`~/.claude/CLAUDE.md`) is treated as context, never an edit target.
388
+ - Paths are verified on macOS and Linux.
389
+
390
+ ## Development
391
+
392
+ ```sh
393
+ git clone https://github.com/kunchenguid/backpass.git
394
+ cd backpass
395
+ pnpm install --frozen-lockfile
396
+ ```
397
+
398
+ ```sh
399
+ pnpm run check # Run all verification commands
400
+ pnpm test # Run node:test tests
401
+ pnpm run lint # Run ESLint
402
+ pnpm run format:check # Check Prettier formatting
403
+ pnpm run typecheck # Run TypeScript checkJs validation
404
+ ```
405
+
406
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the contributor workflow.
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { main } from "../src/cli.js";
3
+
4
+ process.exitCode = await main(process.argv.slice(2));
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "backpass",
3
+ "version": "0.1.0",
4
+ "packageManager": "pnpm@11.5.0",
5
+ "description": "Gradient descent for your agent memory - analyzes past agent session transcripts and proposes evidence-backed edits to AGENTS.md / CLAUDE.md",
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "author": "Kun Chen",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/kunchenguid/backpass.git"
12
+ },
13
+ "homepage": "https://github.com/kunchenguid/backpass#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/kunchenguid/backpass/issues"
16
+ },
17
+ "keywords": [
18
+ "agents",
19
+ "agent-memory",
20
+ "AGENTS.md",
21
+ "CLAUDE.md",
22
+ "transcripts",
23
+ "acpx",
24
+ "cli",
25
+ "llm"
26
+ ],
27
+ "bin": {
28
+ "backpass": "./bin/backpass.js"
29
+ },
30
+ "files": [
31
+ "bin",
32
+ "src",
33
+ "templates",
34
+ "LICENSE",
35
+ "README.md"
36
+ ],
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "scripts": {
41
+ "check": "npm run lint && npm run format:check && npm run typecheck && npm test",
42
+ "start": "node bin/backpass.js",
43
+ "lint": "eslint bin src test",
44
+ "format": "prettier --write .",
45
+ "format:check": "prettier --check .",
46
+ "test": "node --test \"test/**/*.test.js\"",
47
+ "typecheck": "tsc --noEmit"
48
+ },
49
+ "dependencies": {},
50
+ "devDependencies": {
51
+ "@eslint/js": "^10.0.1",
52
+ "@types/node": "^25.6.2",
53
+ "eslint": "^10.3.0",
54
+ "eslint-config-prettier": "^10.1.8",
55
+ "globals": "^17.6.0",
56
+ "prettier": "^3.8.3",
57
+ "typescript": "^5.9.3"
58
+ },
59
+ "engines": {
60
+ "node": ">=22.5.0"
61
+ }
62
+ }