@stitchdb/cli 0.7.3 → 0.7.4
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/dist/cli.js +52 -28
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -490,9 +490,11 @@ async function cmdHook(args) {
|
|
|
490
490
|
await handlePreReadHook(cfg, event, cwd || process.cwd()).catch(() => { });
|
|
491
491
|
return;
|
|
492
492
|
}
|
|
493
|
-
// ── PostToolUse on Read:
|
|
494
|
-
|
|
495
|
-
|
|
493
|
+
// ── PostToolUse on Read/Edit/Write/MultiEdit: keep the file-summary
|
|
494
|
+
// cache fresh. After an edit we re-hash the on-disk file and
|
|
495
|
+
// re-summarise, so a stale "old version" memory never lingers.
|
|
496
|
+
if (eventName === 'PostToolUse' && ['Read', 'Edit', 'Write', 'MultiEdit'].includes(String(event?.tool_name))) {
|
|
497
|
+
await handlePostFileChangeHook(cfg, event, cwd || process.cwd()).catch(() => { });
|
|
496
498
|
return;
|
|
497
499
|
}
|
|
498
500
|
// ── SessionStart: self-heal hook config + inject prior context ─────────
|
|
@@ -735,13 +737,14 @@ function lastAssistantTextFromTranscript(transcriptPath) {
|
|
|
735
737
|
const FILE_SUMMARY_MIN_BYTES = 500; // skip tiny files
|
|
736
738
|
const FILE_SUMMARY_MAX_BYTES = 200_000; // skip absolutely huge files
|
|
737
739
|
const FILE_SUMMARY_COOLDOWN_MS = 5 * 60_000; // per-file: don't re-summarize more than once per 5 min
|
|
738
|
-
const FILE_SUMMARY_PROMPT = `You are summarising a single source file for an AI coding assistant cache.
|
|
740
|
+
const FILE_SUMMARY_PROMPT = `You are summarising a single source file for an AI coding assistant's persistent project cache. Other agents will read this summary before deciding whether to open the file in full, so it must enable a real mental model of the project — not just describe the file in isolation.
|
|
739
741
|
|
|
740
|
-
Return ONLY a 2-
|
|
741
|
-
|
|
742
|
-
|
|
742
|
+
Return ONLY a 2-4 sentence summary that covers:
|
|
743
|
+
1. PURPOSE — what this file does, its key exported symbols / routes / commands / classes worth knowing, any non-obvious invariant or pattern.
|
|
744
|
+
2. CONNECTIONS — concrete other files/modules this depends on (real import paths, real symbol names) AND who/what typically depends on this. Use real names from the file content; don't generalise.
|
|
745
|
+
3. WHEN TO OPEN — what kind of task forces an edit here vs a peek. (e.g. "edit this when adding a new auth provider; safe to skip for purely UI changes").
|
|
743
746
|
|
|
744
|
-
|
|
747
|
+
Be dense and specific. No preamble. No "this file ...". No markdown fences. No prose around it. Plain text only.
|
|
745
748
|
|
|
746
749
|
File path: {{PATH}}
|
|
747
750
|
|
|
@@ -820,47 +823,64 @@ async function handlePreReadHook(cfg, event, cwd) {
|
|
|
820
823
|
const payload = { hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: note } };
|
|
821
824
|
process.stdout.write(JSON.stringify(payload));
|
|
822
825
|
}
|
|
823
|
-
// PostToolUse
|
|
824
|
-
//
|
|
825
|
-
|
|
826
|
+
// PostToolUse handler for tools that observe-or-modify a single file
|
|
827
|
+
// (Read, Edit, Write, MultiEdit). Kicks off a background `claude -p` to
|
|
828
|
+
// (re-)summarise this file unless we already have an up-to-date cached
|
|
829
|
+
// summary for this exact hash. Fire-and-forget.
|
|
830
|
+
//
|
|
831
|
+
// Read flow: file content same as cache → no-op. Different → re-summarise.
|
|
832
|
+
// Edit flow: file content always different from cache → re-summarise. The
|
|
833
|
+
// existing summary is replaced by cmdSummarizeFile so no stale
|
|
834
|
+
// memory persists.
|
|
835
|
+
// Write/MultiEdit: same as Edit.
|
|
836
|
+
//
|
|
837
|
+
// A 30 s "min-interval" floor on top of the hash-keyed cooldown prevents a
|
|
838
|
+
// flurry of edits within seconds from spawning N parallel claude -p calls
|
|
839
|
+
// for the same file.
|
|
840
|
+
async function handlePostFileChangeHook(cfg, event, cwd) {
|
|
826
841
|
const filePath = String(event?.tool_input?.file_path || '');
|
|
827
842
|
if (!filePath)
|
|
828
843
|
return;
|
|
829
844
|
const rel = relPathFor(cwd, filePath);
|
|
830
|
-
|
|
831
|
-
//
|
|
832
|
-
let body =
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
return;
|
|
839
|
-
}
|
|
845
|
+
// After Edit/Write the on-disk content is what we want; the hook payload
|
|
846
|
+
// may also carry it, but reading from disk is always correct.
|
|
847
|
+
let body = '';
|
|
848
|
+
try {
|
|
849
|
+
body = fs.readFileSync(filePath, 'utf8');
|
|
850
|
+
}
|
|
851
|
+
catch {
|
|
852
|
+
return;
|
|
840
853
|
}
|
|
841
|
-
if (!body
|
|
854
|
+
if (!body)
|
|
855
|
+
return;
|
|
856
|
+
// Tiny files: not worth a summary. Huge files: skip; would exhaust context.
|
|
857
|
+
if (body.length < FILE_SUMMARY_MIN_BYTES)
|
|
842
858
|
return;
|
|
843
859
|
if (body.length > FILE_SUMMARY_MAX_BYTES)
|
|
844
860
|
return;
|
|
845
861
|
const fullHash = await sha256Hex(body);
|
|
846
862
|
const hashPrefix = fullHash.slice(0, 16);
|
|
847
|
-
// Per-file cooldown so a hot-edited file doesn't re-summarize on every read.
|
|
848
863
|
const state = loadFileSummaryState();
|
|
849
864
|
const last = state.files[rel];
|
|
865
|
+
// Hash-keyed dedupe: same content as last spawn → nothing to do.
|
|
850
866
|
if (last && last.hash === hashPrefix && Date.now() - last.lastSummarizedAt < FILE_SUMMARY_COOLDOWN_MS)
|
|
851
867
|
return;
|
|
868
|
+
// Min-interval floor: regardless of hash change, don't spawn more often
|
|
869
|
+
// than once per 30 s for the same file (catches edit flurries).
|
|
870
|
+
const MIN_RESPAWN_MS = 30_000;
|
|
871
|
+
if (last && Date.now() - last.lastSummarizedAt < MIN_RESPAWN_MS)
|
|
872
|
+
return;
|
|
873
|
+
// Optimisation: confirm the API view is also stale before spending tokens.
|
|
852
874
|
const stitch = client(cfg);
|
|
853
875
|
const cached = await lookupFileSummary(stitch, rel);
|
|
854
876
|
if (cached && cached.hash === hashPrefix) {
|
|
855
|
-
// Already summarized this exact version — refresh cooldown and bail.
|
|
856
877
|
state.files[rel] = { hash: hashPrefix, lastSummarizedAt: Date.now() };
|
|
857
878
|
saveFileSummaryState(state);
|
|
858
879
|
return;
|
|
859
880
|
}
|
|
860
|
-
// Mark BEFORE spawning so
|
|
881
|
+
// Mark BEFORE spawning so overlapping events don't fire N spawns.
|
|
861
882
|
state.files[rel] = { hash: hashPrefix, lastSummarizedAt: Date.now() };
|
|
862
883
|
saveFileSummaryState(state);
|
|
863
|
-
// Spawn detached: stitch _summarize-file <abs-path> <hash> <rel>
|
|
864
884
|
try {
|
|
865
885
|
const cliPath = process.argv[1] || (await import('node:url')).fileURLToPath(import.meta.url);
|
|
866
886
|
const child = spawn(process.argv[0], [cliPath, '_summarize-file', filePath, hashPrefix, rel], {
|
|
@@ -1688,13 +1708,17 @@ const STITCH_SESSION_START_HOOK = {
|
|
|
1688
1708
|
matcher: '*',
|
|
1689
1709
|
hooks: [{ type: 'command', command: 'stitch _hook SessionStart' }],
|
|
1690
1710
|
};
|
|
1691
|
-
// File summary cache:
|
|
1711
|
+
// File summary cache:
|
|
1712
|
+
// • PreToolUse fires only on Read (cached summary surfaces before the read).
|
|
1713
|
+
// • PostToolUse fires on Read, Edit, Write, MultiEdit — Edit/Write/MultiEdit
|
|
1714
|
+
// invalidate any prior summary so a stale "old version" memory never
|
|
1715
|
+
// lingers. The matcher is a regex (Claude Code accepts pipe alternation).
|
|
1692
1716
|
const STITCH_PRE_READ_HOOK = {
|
|
1693
1717
|
matcher: 'Read',
|
|
1694
1718
|
hooks: [{ type: 'command', command: 'stitch _hook' }],
|
|
1695
1719
|
};
|
|
1696
1720
|
const STITCH_POST_READ_HOOK = {
|
|
1697
|
-
matcher: 'Read',
|
|
1721
|
+
matcher: 'Read|Edit|Write|MultiEdit',
|
|
1698
1722
|
hooks: [{ type: 'command', command: 'stitch _hook' }],
|
|
1699
1723
|
};
|
|
1700
1724
|
function mergeHook(existing, entry) {
|