agent-file-stash 0.3.0 → 0.4.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 (2) hide show
  1. package/dist/cli.mjs +41 -0
  2. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -160,6 +160,11 @@ CREATE TABLE IF NOT EXISTS session_reads (
160
160
  PRIMARY KEY (session_id, path)
161
161
  );
162
162
 
163
+ CREATE TABLE IF NOT EXISTS sessions (
164
+ session_id TEXT PRIMARY KEY,
165
+ pid INTEGER NOT NULL
166
+ );
167
+
163
168
  CREATE TABLE IF NOT EXISTS stats (
164
169
  key TEXT PRIMARY KEY,
165
170
  value INTEGER NOT NULL DEFAULT 0
@@ -181,6 +186,15 @@ var HASH_LENGTH = 16;
181
186
  function contentHash(content) {
182
187
  return createHash("sha256").update(content).digest("hex").slice(0, HASH_LENGTH);
183
188
  }
189
+ function isProcessAlive(pid) {
190
+ if (!Number.isInteger(pid) || pid <= 0) return false;
191
+ try {
192
+ process.kill(pid, 0);
193
+ return true;
194
+ } catch (err) {
195
+ return err.code === "EPERM";
196
+ }
197
+ }
184
198
  function queryOne(db, sql, params = []) {
185
199
  const rows = db.prepare(sql).all(...params);
186
200
  return rows[0];
@@ -199,9 +213,35 @@ var StashStore = class {
199
213
  const { DatabaseSync } = await import("node:sqlite");
200
214
  this.db = new DatabaseSync(this.dbPath);
201
215
  this.db.exec("PRAGMA journal_mode=WAL");
216
+ this.db.exec("PRAGMA busy_timeout=5000");
202
217
  this.db.exec(SCHEMA);
218
+ this.registerSession(this.db);
219
+ this.pruneClosedSessions(this.db);
203
220
  this.initialized = true;
204
221
  }
222
+ registerSession(db) {
223
+ db.prepare("INSERT OR REPLACE INTO sessions (session_id, pid) VALUES (?, ?)").run(this.sessionId, process.pid);
224
+ }
225
+ pruneClosedSessions(db) {
226
+ db.exec("BEGIN IMMEDIATE");
227
+ try {
228
+ const sessions = db.prepare("SELECT session_id, pid FROM sessions").all();
229
+ for (const s of sessions) {
230
+ if (s.session_id !== this.sessionId && !isProcessAlive(s.pid)) {
231
+ db.prepare("DELETE FROM sessions WHERE session_id = ?").run(s.session_id);
232
+ }
233
+ }
234
+ db.exec("DELETE FROM session_reads WHERE session_id NOT IN (SELECT session_id FROM sessions)");
235
+ db.exec("DELETE FROM session_stats WHERE session_id NOT IN (SELECT session_id FROM sessions)");
236
+ db.exec(
237
+ "DELETE FROM file_versions WHERE NOT EXISTS (SELECT 1 FROM session_reads r WHERE r.path = file_versions.path AND r.hash = file_versions.hash)"
238
+ );
239
+ db.exec("COMMIT");
240
+ } catch (err) {
241
+ db.exec("ROLLBACK");
242
+ throw err;
243
+ }
244
+ }
205
245
  getDb() {
206
246
  if (!this.db) throw new Error("StashStore not initialized. Call init() first.");
207
247
  return this.db;
@@ -373,6 +413,7 @@ var StashStore = class {
373
413
  }
374
414
  async close() {
375
415
  if (this.db) {
416
+ this.db.prepare("DELETE FROM sessions WHERE session_id = ?").run(this.sessionId);
376
417
  this.db.close();
377
418
  this.db = null;
378
419
  this.initialized = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-file-stash",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "mcpName": "io.github.atilio-ts/agent-file-stash",
5
5
  "description": "File stash with diff tracking for AI coding agents. Drop-in replacement for file reads that saves tokens.",
6
6
  "type": "module",
@@ -55,7 +55,7 @@
55
55
  "license": "MIT",
56
56
  "repository": {
57
57
  "type": "git",
58
- "url": "https://github.com/atilio-ts/agent-file-stash"
58
+ "url": "git+https://github.com/atilio-ts/agent-file-stash.git"
59
59
  },
60
60
  "homepage": "https://github.com/atilio-ts/agent-file-stash",
61
61
  "packageManager": "pnpm@10.29.3"