backpass 0.1.4 → 0.1.6
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/README.md +5 -0
- package/package.json +1 -1
- package/src/apply/lavish.js +13 -2
- package/src/discovery/adapters/claude.js +17 -7
package/README.md
CHANGED
|
@@ -90,6 +90,11 @@ backpass reads the local transcript stores of seven harnesses directly. No API,
|
|
|
90
90
|
| **cursor CLI** | `~/.cursor/chats/<md5(cwd)>/<uuid>/` | `meta.json` `cwd` |
|
|
91
91
|
| **hermes** | `~/.hermes/state.db` (sqlite) | session cwd, with CLI prompt / ACP config fallbacks |
|
|
92
92
|
|
|
93
|
+
Claude collection covers `$CLAUDE_CONFIG_DIR/projects` alongside the default store, so a
|
|
94
|
+
relocated config dir does not hide its sessions. The variable is read from backpass's own
|
|
95
|
+
environment: if you reach that profile through an alias that only prefixes `claude`, set it
|
|
96
|
+
for the backpass run too (`CLAUDE_CONFIG_DIR=~/.claude-work backpass`, or export it).
|
|
97
|
+
|
|
93
98
|
Hermes collection includes CLI and ACP sessions only. Gateway, cron, and WhatsApp sessions
|
|
94
99
|
are excluded because their recorded cwd belongs to the shared gateway process, not a project.
|
|
95
100
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backpass",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"packageManager": "pnpm@11.5.0",
|
|
5
5
|
"description": "Gradient descent for your agent memory - analyzes past agent session transcripts and proposes evidence-backed edits to AGENTS.md / CLAUDE.md",
|
|
6
6
|
"type": "module",
|
package/src/apply/lavish.js
CHANGED
|
@@ -78,7 +78,12 @@ export function parseDecisions(text, editIds) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
export async function openApplySurface(file) {
|
|
81
|
-
|
|
81
|
+
// `backpass apply` is itself the human asking for the review surface, so a session the
|
|
82
|
+
// reviewer ended from the browser in an earlier run has to be reopened, not refused.
|
|
83
|
+
// lavish-axi keys sessions by file path and `ended_by: "user"` is sticky, so without
|
|
84
|
+
// `--reopen` it exits 0 and prints the dead session's URL - which apply would then hand
|
|
85
|
+
// the reviewer as if it were live. On a live or agent-ended session the flag is a no-op.
|
|
86
|
+
const result = await runLavish([file, "--reopen", "--no-open"]);
|
|
82
87
|
if (result.spawnError && result.spawnError.code === "ENOENT") {
|
|
83
88
|
throw new UserError(
|
|
84
89
|
`${LAVISH_BIN} not found on PATH`,
|
|
@@ -133,7 +138,13 @@ export async function pollDecisions(file, editIds, { delayMs = POLL_RETRY_DELAY_
|
|
|
133
138
|
const decisions = parseDecisions(text, editIds);
|
|
134
139
|
if (decisions) return decisions;
|
|
135
140
|
|
|
136
|
-
|
|
141
|
+
// lavish-axi reports an end through the session fields, never in prose: `status: ended`
|
|
142
|
+
// when nothing was queued, `session_ended: true` alongside the final `status: feedback`.
|
|
143
|
+
// Scope these fields to the leading session metadata block so field-like feedback or
|
|
144
|
+
// diagnostics cannot end the wait. parseDecisions has already run, so a `Send & End`
|
|
145
|
+
// carrying a vector still applies.
|
|
146
|
+
const session = /^session:\s*\r?\n((?:[ \t]+[^\r\n]*(?:\r?\n|$))*)/m.exec(text)?.[1] || "";
|
|
147
|
+
if (/^\s*status:\s*ended\b/m.test(session) || /^\s*session_ended:\s*true\b/m.test(session)) {
|
|
137
148
|
warn("review session ended without a decision vector - nothing applied");
|
|
138
149
|
return null;
|
|
139
150
|
}
|
|
@@ -19,23 +19,33 @@ import {
|
|
|
19
19
|
* directory name is a lossy munge of the cwd (slashes and dots both become dashes),
|
|
20
20
|
* so it is only used to narrow the search - the per-line `cwd` is the authority.
|
|
21
21
|
* No git remote is recorded, so a deleted worktree can only reach tier 3.
|
|
22
|
+
*
|
|
23
|
+
* `CLAUDE_CONFIG_DIR` relocates the whole config dir, and it is commonly set per
|
|
24
|
+
* invocation (a shell alias for a work profile), which splits a machine's sessions across
|
|
25
|
+
* two stores rather than moving them - so both roots are scanned. The variable is read
|
|
26
|
+
* from this process's environment; an alias that only prefixes `claude` never reaches it.
|
|
22
27
|
*/
|
|
23
28
|
|
|
24
29
|
const HEADER_LINES = 40;
|
|
25
30
|
|
|
26
31
|
export const name = "claude";
|
|
27
32
|
|
|
28
|
-
export function
|
|
29
|
-
|
|
33
|
+
export function storeRoots() {
|
|
34
|
+
const roots = [home(".claude", "projects")];
|
|
35
|
+
const configured = process.env.CLAUDE_CONFIG_DIR;
|
|
36
|
+
if (configured) roots.push(path.join(configured, "projects"));
|
|
37
|
+
return [...new Set(roots)];
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
export function enumerate() {
|
|
33
41
|
const out = [];
|
|
34
|
-
for (const
|
|
35
|
-
for (const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
for (const root of storeRoots()) {
|
|
43
|
+
for (const dir of listDirs(root)) {
|
|
44
|
+
for (const file of listFiles(dir, ".jsonl")) {
|
|
45
|
+
const stat = statOrNull(file);
|
|
46
|
+
if (!stat) continue;
|
|
47
|
+
out.push({ key: file, path: file, mtimeMs: stat.mtimeMs, bytes: stat.size });
|
|
48
|
+
}
|
|
39
49
|
}
|
|
40
50
|
}
|
|
41
51
|
return out;
|