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.
- package/README.md +16 -0
- package/dist/cli.mjs +72 -11
- 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
|
|
460
|
-
import { existsSync as
|
|
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 =
|
|
682
|
-
if (!
|
|
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 ||
|
|
761
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || join3(home, ".config");
|
|
705
762
|
const targets = [
|
|
706
763
|
{
|
|
707
764
|
name: "Claude Code",
|
|
708
|
-
path:
|
|
765
|
+
path: join3(home, ".claude.json"),
|
|
709
766
|
key: "mcpServers",
|
|
710
767
|
entry: mcpServersEntry
|
|
711
768
|
},
|
|
712
769
|
{
|
|
713
770
|
name: "Cursor",
|
|
714
|
-
path:
|
|
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:
|
|
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 =
|
|
728
|
-
if (!
|
|
784
|
+
const dir = join3(target.path, "..");
|
|
785
|
+
if (!existsSync4(dir)) continue;
|
|
729
786
|
let config = {};
|
|
730
|
-
if (
|
|
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.
|
|
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",
|