backpass 0.1.2 → 0.1.3

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 CHANGED
@@ -80,15 +80,15 @@ backpass apply # review each edit, accept or reject, then write
80
80
 
81
81
  backpass reads the local transcript stores of seven harnesses directly. No API, no upload.
82
82
 
83
- | Harness | Store | Repo tie |
84
- | -------------- | ---------------------------------------------- | --------------------------------------- |
85
- | **claude** | `~/.claude/projects/<munged-cwd>/<uuid>.jsonl` | per-line `cwd` |
86
- | **codex** | `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl` | `cwd` + recorded `git.repository_url` |
87
- | **pi** | `~/.pi/agent/sessions/<escaped-cwd>/*.jsonl` | session-header `cwd` |
88
- | **opencode** | `~/.local/share/opencode/opencode.db` (sqlite) | `session.directory` |
89
- | **grok** | `~/.grok/sessions/<encoded-cwd>/<uuid>/` | `summary.json` `cwd` + `git_remotes` |
90
- | **cursor CLI** | `~/.cursor/chats/<md5(cwd)>/<uuid>/` | `meta.json` `cwd` |
91
- | **hermes** | `~/.hermes/state.db` (sqlite) | CLI prompt cwd / ACP `model_config.cwd` |
83
+ | Harness | Store | Repo tie |
84
+ | -------------- | ---------------------------------------------- | --------------------------------------------------- |
85
+ | **claude** | `~/.claude/projects/<munged-cwd>/<uuid>.jsonl` | per-line `cwd` |
86
+ | **codex** | `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl` | `cwd` + recorded `git.repository_url` |
87
+ | **pi** | `~/.pi/agent/sessions/<escaped-cwd>/*.jsonl` | session-header `cwd` |
88
+ | **opencode** | `~/.local/share/opencode/opencode.db` (sqlite) | `session.directory` |
89
+ | **grok** | `~/.grok/sessions/<encoded-cwd>/<uuid>/` | `summary.json` `cwd` + `git_remotes` |
90
+ | **cursor CLI** | `~/.cursor/chats/<md5(cwd)>/<uuid>/` | `meta.json` `cwd` |
91
+ | **hermes** | `~/.hermes/state.db` (sqlite) | session cwd, with CLI prompt / ACP config fallbacks |
92
92
 
93
93
  Hermes collection includes CLI and ACP sessions only. Gateway, cron, and WhatsApp sessions
94
94
  are excluded because their recorded cwd belongs to the shared gateway process, not a project.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backpass",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
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",
@@ -7,15 +7,15 @@ import { openReadOnly, safeJsonParse } from "./sqlite.js";
7
7
  * hermes: ~/.hermes/state.db (sqlite)
8
8
  *
9
9
  * Observed schema version 13 (upstream is 26; SELECT named columns so additive
10
- * columns are tolerated). Hermes has no cwd, git branch, or git remote column.
11
- *
12
- * Association is recovered only for source in ('cli', 'acp'):
10
+ * columns are tolerated). v26 adds sessions.cwd; CLI rows leave system_prompt
11
+ * NULL and store the project path there. ACP still snapshots cwd on
12
+ * model_config. Prefer the cwd column when present, then the v13 paths:
13
13
  * acp - model_config.cwd when it is an absolute path
14
14
  * cli - first `Current working directory:` / `Working directory:` line in
15
15
  * system_prompt
16
- * Gateway / cron / whatsapp sessions are skipped: their prompt cwd is the
17
- * gateway process cwd, not a project, and would pin unrelated sessions to one
18
- * repo. Sessions with no recoverable cwd are skipped.
16
+ * Gateway / cron / whatsapp sessions are skipped even if sessions.cwd is set:
17
+ * their path is the gateway process cwd, not a project, and would pin
18
+ * unrelated sessions to one repo. Sessions with no recoverable cwd are skipped.
19
19
  *
20
20
  * Timestamps are epoch seconds; backpass uses milliseconds (x1000).
21
21
  * Structured content uses a `\x00json:` prefix; node:sqlite truncates TEXT at
@@ -62,17 +62,25 @@ function cwdFromPrompt(prompt) {
62
62
  }
63
63
 
64
64
  function recoverCwd(row, source) {
65
+ if (looksAbsolute(row.cwd)) return row.cwd;
65
66
  if (source === "acp") return cwdFromConfig(row.model_config);
66
67
  if (source === "cli") return cwdFromPrompt(row.system_prompt);
67
68
  return null;
68
69
  }
69
70
 
70
- function activeMessageFilter(db, alias = "") {
71
- const hasActive = db
72
- .prepare("PRAGMA table_info(messages)")
71
+ function tableHasColumn(db, table, column) {
72
+ return db
73
+ .prepare(`PRAGMA table_info(${table})`)
73
74
  .all()
74
- .some((column) => column.name === "active");
75
- return hasActive ? ` AND ${alias}active = 1` : "";
75
+ .some((entry) => entry.name === column);
76
+ }
77
+
78
+ function activeMessageFilter(db, alias = "") {
79
+ return tableHasColumn(db, "messages", "active") ? ` AND ${alias}active = 1` : "";
80
+ }
81
+
82
+ function sessionCwdSelect(db) {
83
+ return tableHasColumn(db, "sessions", "cwd") ? ", s.cwd" : "";
76
84
  }
77
85
 
78
86
  /**
@@ -87,10 +95,11 @@ export async function discover({ cutoffMs } = {}) {
87
95
  const cutoffSec = cutoffMs == null ? null : cutoffMs / 1000;
88
96
  try {
89
97
  const activeFilter = activeMessageFilter(db, "m.");
98
+ const cwdSelect = sessionCwdSelect(db);
90
99
  const rows = db
91
100
  .prepare(
92
101
  `SELECT s.id, s.source, s.model, s.model_config, s.system_prompt, s.title,
93
- s.started_at, s.ended_at,
102
+ s.started_at, s.ended_at${cwdSelect},
94
103
  MAX(s.started_at,
95
104
  COALESCE(s.ended_at, s.started_at),
96
105
  COALESCE((SELECT MAX(m.timestamp)