agent-file-stash 0.2.5 → 0.3.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 (3) hide show
  1. package/README.md +16 -0
  2. package/dist/cli.mjs +72 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -112,6 +112,22 @@ filestash status:
112
112
  Tokens saved (total): ~53,851
113
113
  ```
114
114
 
115
+ When each project keeps its own stash (for example with `FILESTASH_DIR=.vscode/file-stash`), sum all of them with `--all`. It scans the given directory (default: home) for `.file-stash/` and `file-stash/` folders:
116
+
117
+ ```bash
118
+ npx agent-file-stash status --all ~/Projects
119
+ ```
120
+
121
+ ```
122
+ filestash status (3 databases under /Users/me/Projects):
123
+ ~ 252,340 tokens 112 files /Users/me/Projects/api
124
+ ~ 45,157 tokens 128 files /Users/me/Projects/web
125
+ ~ 7,043 tokens 18 files /Users/me/Projects/legacy
126
+ ~ 304,540 tokens 258 files TOTAL
127
+ ```
128
+
129
+ Savings come from re-reads within a session (unchanged files and diffs). A new session always receives full content, since the file is not in its context yet.
130
+
115
131
  #### `help`
116
132
 
117
133
  ```bash
package/dist/cli.mjs CHANGED
@@ -456,8 +456,8 @@ function createStash(config) {
456
456
  }
457
457
 
458
458
  // packages/cli/src/index.ts
459
- import { resolve as resolve4, join as join2 } from "node:path";
460
- import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync } from "node:fs";
459
+ import { resolve as resolve4, join as join3 } from "node:path";
460
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync } from "node:fs";
461
461
  import { homedir } from "node:os";
462
462
 
463
463
  // packages/cli/src/mcp.ts
@@ -669,6 +669,33 @@ Use this to verify filestash is working and see how many tokens it has saved.`
669
669
  process.on("SIGTERM", shutdown);
670
670
  }
671
671
 
672
+ // packages/cli/src/scan.ts
673
+ import { existsSync as existsSync3, readdirSync } from "node:fs";
674
+ import { join as join2 } from "node:path";
675
+ var STASH_DIR_NAMES = /* @__PURE__ */ new Set([".file-stash", "file-stash"]);
676
+ var SKIPPED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "build", "dist", "target", "bin", "obj"]);
677
+ var MAX_SCAN_DEPTH = 8;
678
+ function findStashDatabases(root, depth = 0) {
679
+ if (depth > MAX_SCAN_DEPTH) return [];
680
+ let entries;
681
+ try {
682
+ entries = readdirSync(root, { withFileTypes: true });
683
+ } catch {
684
+ return [];
685
+ }
686
+ const found = [];
687
+ for (const entry of entries) {
688
+ if (!entry.isDirectory() || SKIPPED_DIRS.has(entry.name)) continue;
689
+ const dir = join2(root, entry.name);
690
+ if (STASH_DIR_NAMES.has(entry.name) && existsSync3(join2(dir, "stash.db"))) {
691
+ found.push(join2(dir, "stash.db"));
692
+ continue;
693
+ }
694
+ found.push(...findStashDatabases(dir, depth + 1));
695
+ }
696
+ return found;
697
+ }
698
+
672
699
  // packages/cli/src/index.ts
673
700
  var _origEmitWarning = process.emitWarning;
674
701
  process.emitWarning = function(msg, ...args) {
@@ -678,8 +705,8 @@ process.emitWarning = function(msg, ...args) {
678
705
  var CLI_STATUS_SESSION = "cli-status";
679
706
  async function runStatus() {
680
707
  const stashDir = resolve4(process.env.FILESTASH_DIR ?? ".file-stash");
681
- const dbPath = join2(stashDir, "stash.db");
682
- if (!existsSync3(dbPath)) {
708
+ const dbPath = join3(stashDir, "stash.db");
709
+ if (!existsSync4(dbPath)) {
683
710
  console.log("No filestash database found. Run 'filestash serve' to start stashing.");
684
711
  process.exit(0);
685
712
  }
@@ -691,6 +718,36 @@ async function runStatus() {
691
718
  console.log(` Tokens saved (total): ~${stats.tokensSaved.toLocaleString()}`);
692
719
  await stash.close();
693
720
  }
721
+ async function runStatusAll(root) {
722
+ const databases = findStashDatabases(resolve4(root));
723
+ if (databases.length === 0) {
724
+ console.log(`No filestash databases found under ${resolve4(root)}.`);
725
+ return;
726
+ }
727
+ const rows = [];
728
+ for (const dbPath of databases) {
729
+ const { stash } = createStash({ dbPath, sessionId: CLI_STATUS_SESSION });
730
+ try {
731
+ await stash.init();
732
+ const stats = await stash.getStats();
733
+ rows.push({ project: resolve4(dbPath, "../..").replace(/\/\.vscode$/, ""), files: stats.filesTracked, tokens: stats.tokensSaved });
734
+ } catch (e) {
735
+ console.error(` skipped ${dbPath}: ${e instanceof Error ? e.message : String(e)}`);
736
+ } finally {
737
+ await stash.close();
738
+ }
739
+ }
740
+ rows.sort((a, b) => b.tokens - a.tokens);
741
+ const totalTokens = rows.reduce((sum, r) => sum + r.tokens, 0);
742
+ const totalFiles = rows.reduce((sum, r) => sum + r.files, 0);
743
+ console.log(`filestash status (${rows.length} databases under ${resolve4(root)}):`);
744
+ for (const r of rows) {
745
+ console.log(` ~${r.tokens.toLocaleString().padStart(10)} tokens ${String(r.files).padStart(5)} files ${r.project}`);
746
+ }
747
+ console.log(` ~${totalTokens.toLocaleString().padStart(10)} tokens ${String(totalFiles).padStart(5)} files TOTAL`);
748
+ console.log(`
749
+ Savings count re-reads within a session (unchanged files and diffs); a new session always receives full content.`);
750
+ }
694
751
  async function runInit() {
695
752
  const home = homedir();
696
753
  const mcpServersEntry = {
@@ -701,33 +758,33 @@ async function runInit() {
701
758
  type: "local",
702
759
  command: ["npx", "agent-file-stash", "serve"]
703
760
  };
704
- const xdgConfig = process.env.XDG_CONFIG_HOME || join2(home, ".config");
761
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join3(home, ".config");
705
762
  const targets = [
706
763
  {
707
764
  name: "Claude Code",
708
- path: join2(home, ".claude.json"),
765
+ path: join3(home, ".claude.json"),
709
766
  key: "mcpServers",
710
767
  entry: mcpServersEntry
711
768
  },
712
769
  {
713
770
  name: "Cursor",
714
- path: join2(home, ".cursor", "mcp.json"),
771
+ path: join3(home, ".cursor", "mcp.json"),
715
772
  key: "mcpServers",
716
773
  entry: mcpServersEntry
717
774
  },
718
775
  {
719
776
  name: "OpenCode",
720
- path: join2(xdgConfig, "opencode", "opencode.json"),
777
+ path: join3(xdgConfig, "opencode", "opencode.json"),
721
778
  key: "mcp",
722
779
  entry: opencodeMcpEntry
723
780
  }
724
781
  ];
725
782
  let configured = 0;
726
783
  for (const target of targets) {
727
- const dir = join2(target.path, "..");
728
- if (!existsSync3(dir)) continue;
784
+ const dir = join3(target.path, "..");
785
+ if (!existsSync4(dir)) continue;
729
786
  let config = {};
730
- if (existsSync3(target.path)) {
787
+ if (existsSync4(target.path)) {
731
788
  try {
732
789
  config = JSON.parse(readFileSync3(target.path, "utf-8"));
733
790
  } catch {
@@ -768,6 +825,8 @@ Usage:
768
825
  agent-file-stash init Auto-configure for your editor
769
826
  agent-file-stash serve Start the MCP server (default)
770
827
  agent-file-stash status Show stash statistics
828
+ agent-file-stash status --all [dir]
829
+ Sum statistics of every stash under dir (default: home)
771
830
  agent-file-stash help Show this help message
772
831
 
773
832
  Environment:
@@ -776,6 +835,8 @@ Environment:
776
835
  var command = process.argv[2];
777
836
  if (!command || command === "serve") {
778
837
  await startMcpServer();
838
+ } else if (command === "status" && process.argv[3] === "--all") {
839
+ await runStatusAll(process.argv[4] ?? homedir());
779
840
  } else if (command === "status") {
780
841
  await runStatus();
781
842
  } else if (command === "init") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-file-stash",
3
- "version": "0.2.5",
3
+ "version": "0.3.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",