@stitchdb/cli 0.9.0 → 0.10.1

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.js +53 -9
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -629,13 +629,28 @@ async function cmdHook(args) {
629
629
  const stitch = client(cfg);
630
630
  const projectTag = (threadName.split('/')[0] || threadName).toLowerCase();
631
631
  const baseUrl = cfg.baseUrl || 'https://db.stitchdb.com';
632
- const [thread, memHits, workspaces, fileSummaries, aboutMems] = await Promise.all([
632
+ // Adaptive sizing scale what we inject by how much the project
633
+ // actually has. A 5k-memory pool surfacing 8 random memories is
634
+ // noise; better to inject 0 and tell Claude to `recall` on demand.
635
+ const [thread, allProjectMems, workspaces, fileSummaries, aboutMems] = await Promise.all([
633
636
  stitch.thread(threadName).recall({ last: 5 }).catch(() => ({ thread_id: '', recent: [], semantic: [] })),
634
- stitch.recall(projectTag, { k: 8 }).catch(() => []),
637
+ stitch.list({ limit: 1 }).catch(() => []), // probe the pool size cheaply
635
638
  stitch.workspaces.list().catch(() => []),
636
639
  stitch.list({ limit: 12 }).then((all) => all.filter((m) => m.tags.some((t) => t.startsWith('file:')))).catch(() => []),
637
640
  stitch.list({ tag: 'workspace:about', limit: 1 }).catch(() => []),
638
641
  ]);
642
+ // Estimate the workspace's total memory count from a single page
643
+ // header. We just need to know "small / medium / huge" — exact
644
+ // count is overkill. (D1 doesn't expose count in list — approximate
645
+ // by listing 200 once and capping.)
646
+ const sample = await stitch.list({ limit: 200 }).catch(() => []);
647
+ const poolSize = Array.isArray(sample) ? sample.length : 0;
648
+ // Tier the injection: tiny / small / medium / huge.
649
+ const memSliceK = poolSize <= 50 ? 8 : poolSize < 500 ? 8 : 0;
650
+ const turnSliceK = (thread.recent?.length ?? 0) < 5 ? 5 : (thread.recent?.length ?? 0) < 100 ? 5 : 2;
651
+ const memHits = memSliceK > 0
652
+ ? await stitch.recall(projectTag, { k: memSliceK }).catch(() => [])
653
+ : [];
639
654
  // Look up the current workspace using the client's resolved id; fall
640
655
  // back to first if resolveWorkspace failed (shouldn't normally).
641
656
  const currentWsId = await stitch.resolveWorkspace().catch(() => null);
@@ -655,8 +670,15 @@ async function cmdHook(args) {
655
670
  catch { /* ignore */ }
656
671
  const lines = [];
657
672
  lines.push('<stitch-context>');
658
- lines.push(`Project: ${threadName} · Workspace: ${currentWs?.name || '(unknown)'} · Stitch MCP tools: recall, remember, recall_global, remember_global, thread_recall, thread_append, workspace_setup, file_summary, file_summary_save.`);
673
+ lines.push(`Project: ${threadName} · Workspace: ${currentWs?.name || '(unknown)'} · ${poolSize} memories indexed · Stitch MCP tools: recall, remember, recall_global, remember_global, thread_recall, thread_append, workspace_setup, file_summary, file_summary_save.`);
659
674
  lines.push('');
675
+ // Huge-pool mode: don't pre-load any memories. Tell Claude what's
676
+ // available and that it should pull what's relevant on demand.
677
+ if (poolSize >= 200 && memHits.length === 0) {
678
+ lines.push('### Memory pool');
679
+ lines.push(`This project has ${poolSize}+ memories. Don't try to load them all — call \`recall(query)\` whenever the user references prior decisions, code patterns, or preferences. Cheaper and more relevant than reading them upfront.`);
680
+ lines.push('');
681
+ }
660
682
  // Nudge the AI to set a meaningful workspace name once.
661
683
  if (currentWs?.name === 'default') {
662
684
  lines.push('### ⚠ Workspace is still named "default"');
@@ -683,7 +705,13 @@ async function cmdHook(args) {
683
705
  for (const m of sortedMems) {
684
706
  const isAuto = Array.isArray(m.tags) && m.tags.includes('auto');
685
707
  const txt = String(m.content || '').replace(/\n+/g, ' ').slice(0, 350);
686
- lines.push(`- **[${m.kind}${isAuto ? '·auto' : ''}]** ${txt}`);
708
+ // Source receipt: when this memory came from a distilled thread,
709
+ // mark the source so the agent knows it can call thread_recall on
710
+ // that thread for verbatim context behind the fact.
711
+ const src = m.source_thread_id
712
+ ? ` _(from thread, ${m.source_turn_ids?.length || 0} turns)_`
713
+ : '';
714
+ lines.push(`- **[${m.kind}${isAuto ? '·auto' : ''}]** ${txt}${src}`);
687
715
  }
688
716
  lines.push('');
689
717
  }
@@ -699,13 +727,17 @@ async function cmdHook(args) {
699
727
  }
700
728
  if (thread.recent && thread.recent.length > 0) {
701
729
  lines.push('### Most recent turns (continue from here)');
702
- for (const t of thread.recent.slice(-5)) {
730
+ for (const t of thread.recent.slice(-turnSliceK)) {
703
731
  const txt = String(t.content || '').replace(/\n+/g, ' ').slice(0, 300);
704
732
  lines.push(`- **${t.role}**: ${txt}`);
705
733
  }
706
734
  lines.push('');
707
735
  }
708
- lines.push('Call `recall` for project memory, `recall_global` for cross-project user prefs, `thread_recall` for older turns, `file_summary` BEFORE reading any non-trivial file. Save user-level habits/preferences with `remember_global`; project facts with `remember`.');
736
+ lines.push('## How to use this memory layer');
737
+ lines.push('- The user mentions prior work, decisions, or "what we discussed" → call `recall(query)` (or `thread_recall(thread, semantic: ...)` for verbatim turns) BEFORE answering. Don\'t guess from the snippets above; they\'re a teaser, not the truth.');
738
+ lines.push('- About to open a non-trivial file → call `file_summary(path)` first. If hash matches the cached summary, you can skip the full read entirely.');
739
+ lines.push('- Learn a durable user-level habit ("I always use Postgres", "commit before reverting") → save with `remember_global` so it surfaces across every project. Project-specific facts → `remember`.');
740
+ lines.push('- A surfaced memory has `_(from thread, N turns)_` next to it → those turns are queryable via `thread_recall`. Reach for them when challenged on a fact.');
709
741
  lines.push('</stitch-context>');
710
742
  process.stdout.write(lines.join('\n'));
711
743
  }
@@ -1238,13 +1270,25 @@ async function cmdDistill(args) {
1238
1270
  bumpDistillCooldown(thread);
1239
1271
  return;
1240
1272
  }
1241
- // Push each to Stitch as a memory with auto:true tag. No project:* tag —
1242
- // the workspace itself is the project identity.
1273
+ // Push each to Stitch as a memory with auto:true tag, plus receipts:
1274
+ // the source thread + the IDs of the turns this distill batch covered,
1275
+ // and a short excerpt from the most recent user turn so the dashboard /
1276
+ // recall surface can preview the conversational context behind the fact
1277
+ // without an extra round-trip.
1278
+ const sourceTurnIds = recall.recent.map((t) => t.id);
1279
+ const lastUserTurn = [...recall.recent].reverse().find((t) => t.role === 'user');
1280
+ const sourceExcerpt = lastUserTurn ? String(lastUserTurn.content || '').slice(0, 300) : undefined;
1243
1281
  let saved = 0;
1244
1282
  for (const m of memories) {
1245
1283
  try {
1246
1284
  const tags = ['auto', 'auto:distill', `thread:${thread}`, ...(m.tags || [])];
1247
- await stitch.remember(m.content, { kind: m.kind, tags });
1285
+ await stitch.remember(m.content, {
1286
+ kind: m.kind,
1287
+ tags,
1288
+ source_thread_id: recall.thread_id,
1289
+ source_turn_ids: sourceTurnIds,
1290
+ source_excerpt: sourceExcerpt,
1291
+ });
1248
1292
  saved++;
1249
1293
  }
1250
1294
  catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stitchdb/cli",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "description": "Stitch CLI — manage memory + run agents from your terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "license": "MIT",
17
17
  "engines": { "node": ">=20" },
18
18
  "dependencies": {
19
- "@stitchdb/agent": "^0.2.0"
19
+ "@stitchdb/agent": "^0.3.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "typescript": "^5.4.0",