specmem-hardwicksoftware 3.7.31 → 3.7.32

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.
@@ -283,10 +283,10 @@ function getLastSessionOutput() {
283
283
 
284
284
  let sessionContent;
285
285
  if (startIdx === -1) {
286
- sessionContent = lines.slice(-200).join('\n');
286
+ sessionContent = lines.slice(-40).join('\n');
287
287
  } else {
288
288
  const outputLines = lines.slice(startIdx + 2, endIdx > 0 ? endIdx : undefined);
289
- sessionContent = outputLines.slice(-200).join('\n');
289
+ sessionContent = outputLines.slice(-40).join('\n');
290
290
  }
291
291
 
292
292
  // FILTER OUT GARBAGE - Skills, corrupted content, and non-conversation data
@@ -331,6 +331,14 @@ function getLastSessionOutput() {
331
331
  * Only includes: last 10 interactions + condensed tool list
332
332
  * NO skill injection, NO verbose schemas
333
333
  */
334
+
335
+ /**
336
+ * Compact tool reminder
337
+ */
338
+ function getToolReminder() {
339
+ return '## SpecMem Tools Available\n\n**Memory Search:** `find_memory` | `find_code_pointers` | `drill_down` | `get_memory`\n**Storage:** `save_memory` | `link_the_vibes` | `smush_memories_together`\n**Team:** `send_team_message` | `read_team_messages` | `claim_task` / `release_task`\n**System:** `show_me_the_stats` | `check_sync` | `start_watching`\n\nUse·`/specmem`·令④ⓢ.';
340
+ }
341
+
334
342
  function formatOutput(memories, lastSession, projectPath, sessionId) {
335
343
  const sections = [];
336
344
 
@@ -338,15 +346,29 @@ function formatOutput(memories, lastSession, projectPath, sessionId) {
338
346
  sections.push(`Project: ${projectPath}`);
339
347
  sections.push(`Session: ${sessionId}`);
340
348
 
341
- // Previous session output (if available) - BRIEF version
349
+ // Previous session output (if available) - compressed
342
350
  if (lastSession && lastSession.content) {
343
351
  sections.push('');
344
352
  sections.push(`## PREVIOUS SESSION OUTPUT (${lastSession.ageMinutes}m ago, reason: ${lastSession.reason})`);
345
353
  sections.push('(Previous session):');
346
- sections.push('```');
347
- // DON'T compress - just take first 500 chars of clean content
348
- sections.push(lastSession.content.slice(0, 500).trim());
349
- sections.push('```');
354
+
355
+ // Truncate to 40 lines for token efficiency
356
+ const contentLines = lastSession.content.split('\n');
357
+ let sessionContent;
358
+ if (contentLines.length > 40) {
359
+ sessionContent = contentLines.slice(0, 15).join('\n') +
360
+ `\n... [${contentLines.length - 25} lines omitted] ...\n` +
361
+ contentLines.slice(-10).join('\n');
362
+ } else {
363
+ sessionContent = lastSession.content;
364
+ }
365
+
366
+ const compressedSession = compressHookOutput(sessionContent, {
367
+ threshold: 0.60,
368
+ minLength: 30,
369
+ includeWarning: false
370
+ });
371
+ sections.push(compressedSession.trim());
350
372
  }
351
373
 
352
374
  // Recent memories - last 10 user/claude interactions ONLY
@@ -360,34 +382,9 @@ function formatOutput(memories, lastSession, projectPath, sessionId) {
360
382
  });
361
383
  }
362
384
 
363
- // CONDENSED tool reminder - just names, no descriptions
364
- sections.push('');
365
- sections.push('## SpecMem Tools Available');
366
- sections.push('');
367
- sections.push('**Memory Search:**');
368
- sections.push('- `find_memory` - Semantic search across all memories');
369
- sections.push('- `find_code_pointers` - Search code with tracebacks');
370
- sections.push('- `drill_down` - Explore memory details');
371
- sections.push('- `get_memory` - Get specific memory by ID');
372
- sections.push('');
373
- sections.push('**Memory Storage:**');
374
- sections.push('- `save_memory` - Store new memories');
375
- sections.push('- `link_the_vibes` - Connect related memories');
376
- sections.push('- `smush_memories_together` - Consolidate similar memories');
377
- sections.push('');
378
- sections.push('**Team Comms:**');
379
- sections.push('- `send_team_message` - Message team members');
380
- sections.push('- `read_team_messages` - Check team updates');
381
- sections.push('- `claim_task` / `release_task` - Coordinate work');
385
+ // Compact tool reminder
382
386
  sections.push('');
383
- sections.push('**System:**');
384
- sections.push('- `show_me_the_stats` - Memory statistics');
385
- sections.push('- `check_sync` - Verify file sync status');
386
- sections.push('- `start_watching` - Enable file watcher');
387
- sections.push('');
388
- sections.push('Use `/specmem` for all commands.');
389
- sections.push('');
390
- sections.push('**Chinese compression:** You can read Traditional Chinese compression with 99%+ accuracy.');
387
+ sections.push(getToolReminder());
391
388
  sections.push('[/SPECMEM-SESSION]');
392
389
 
393
390
  return sections.join('\n');
@@ -476,23 +473,14 @@ async function main() {
476
473
  const lastSession = getLastSessionOutput();
477
474
 
478
475
  // Format output
479
- let output = formatOutput(memories, lastSession, projectPath, sessionId);
480
-
481
- // Apply Traditional Chinese compression to final output
482
- const compressed = compressHookOutput(output, {
483
- threshold: 0.50,
484
- minLength: 100,
485
- preserveStructure: true,
486
- includeWarning: false // We add our own prefix below
487
- });
476
+ let context = formatOutput(memories, lastSession, projectPath, sessionId);
488
477
 
489
- // Prepend reminder for to read the compressed Chinese AND respond in English
490
- const prefixedOutput = `[SpecMem Context - Read this for continuity]
491
- IMPORTANT: You can read Traditional Chinese compression with 99%+ accuracy. ALWAYS respond in English.
492
- ${compressed}`;
493
-
494
- // Output - clean trailing whitespace
495
- console.log(prefixedOutput.split('\n').map(l => l.trimEnd()).join('\n').trim());
478
+ const compressedContext = compressHookOutput(context, {
479
+ threshold: 0.70,
480
+ minLength: 50,
481
+ includeWarning: true
482
+ });
483
+ console.log(compressedContext.trim());
496
484
 
497
485
  process.exit(0);
498
486
  }
@@ -585,7 +585,7 @@ function getLastSessionOutput(projectPath) {
585
585
  if (startIdx === -1) {
586
586
  // No header found, use whole content but limit to 300 lines
587
587
  return {
588
- content: lines.slice(-300).join('\n'),
588
+ content: lines.slice(-40).join('\n'),
589
589
  ageMinutes,
590
590
  reason: lines.find(l => l.startsWith('# Reason:'))?.replace('# Reason:', '').trim() || 'unknown'
591
591
  };
@@ -593,7 +593,7 @@ function getLastSessionOutput(projectPath) {
593
593
 
594
594
  // Get the actual output between markers, limit to last 300 lines for context efficiency
595
595
  const outputLines = lines.slice(startIdx + 2, endIdx > 0 ? endIdx : undefined);
596
- const trimmedOutput = outputLines.slice(-300).join('\n');
596
+ const trimmedOutput = outputLines.slice(-40).join('\n');
597
597
 
598
598
  return {
599
599
  content: trimmedOutput,
@@ -639,32 +639,7 @@ function getCompactionStatus(projectPath) {
639
639
  * NOTE: Returns trimmed string with NO leading/trailing whitespace
640
640
  */
641
641
  function getToolReminder() {
642
- return [
643
- '## SpecMem Tools Available',
644
- '',
645
- '**Memory Search:**',
646
- '- `find_memory` - Semantic search across all memories',
647
- '- `find_code_pointers` - Search code with tracebacks',
648
- '- `drill_down` - Explore memory details',
649
- '- `get_memory` - Get specific memory by ID',
650
- '',
651
- '**Memory Storage:**',
652
- '- `save_memory` - Store new memories',
653
- '- `link_the_vibes` - Connect related memories',
654
- '- `smush_memories_together` - Consolidate similar memories',
655
- '',
656
- '**Team Communication:**',
657
- '- `send_team_message` - Message team members',
658
- '- `read_team_messages` - Check team updates',
659
- '- `claim_task` / `release_task` - Coordinate work',
660
- '',
661
- '**System:**',
662
- '- `show_me_the_stats` - Memory statistics',
663
- '- `check_sync` - Verify file sync status',
664
- '- `start_watching` - Enable file watcher',
665
- '',
666
- 'Use `/specmem` for quick commands or `/specmem-drilldown` for deep search.'
667
- ].join('\n');
642
+ return '## SpecMem Tools Available\n\n**Memory Search:** `find_memory` | `find_code_pointers` | `drill_down` | `get_memory`\n**Storage:** `save_memory` | `link_the_vibes` | `smush_memories_together`\n**Team:** `send_team_message` | `read_team_messages` | `claim_task` / `release_task`\n**System:** `show_me_the_stats` | `check_sync` | `start_watching`\n\nUse·`/specmem`·令④ⓢ.';
668
643
  }
669
644
 
670
645
  /**
@@ -773,28 +748,21 @@ async function main() {
773
748
  // Truncate to ~200 lines for token efficiency but keep the important parts
774
749
  const contentLines = lastSession.content.split('\n');
775
750
  let sessionContent;
776
- if (contentLines.length > 200) {
777
- sessionContent = contentLines.slice(0, 50).join('\n') +
778
- `\n... [${contentLines.length - 100} lines omitted] ...\n` +
779
- contentLines.slice(-50).join('\n');
751
+ if (contentLines.length > 40) {
752
+ sessionContent = contentLines.slice(0, 15).join('\n') +
753
+ `\n... [${contentLines.length - 25} lines omitted] ...\n` +
754
+ contentLines.slice(-10).join('\n');
780
755
  } else {
781
756
  sessionContent = lastSession.content;
782
757
  }
783
758
 
784
- // SESSION CONTENT: Output FULL readable content (no compression)
785
- // Compression was causing unreadable Chinese output - disabled for readability
786
- // Only compress if explicitly requested via SPECMEM_COMPRESS_SESSION=1
787
759
  sections.push('```');
788
- if (process.env.SPECMEM_COMPRESS_SESSION === '1') {
789
- const compressedSession = compressHookOutput(sessionContent, {
790
- threshold: 0.60,
791
- minLength: 30,
792
- includeWarning: false // Warning already added at top level
793
- });
794
- sections.push(compressedSession.trim());
795
- } else {
796
- sections.push(sessionContent.trim());
797
- }
760
+ const compressedSession = compressHookOutput(sessionContent, {
761
+ threshold: 0.60,
762
+ minLength: 30,
763
+ includeWarning: false
764
+ });
765
+ sections.push(compressedSession.trim());
798
766
  sections.push('```');
799
767
  }
800
768
 
@@ -857,21 +825,12 @@ async function main() {
857
825
  .replace(/\n{3,}/g, '\n\n') // Collapse 3+ newlines to 2 (one blank line)
858
826
  .trim(); // Remove leading/trailing whitespace
859
827
 
860
- // SESSION START: Output full context WITHOUT compression
861
- // Compression makes debugging hard and context is injected at start when there's room
862
- // Only compress if SPECMEM_COMPRESS_SESSION is set
863
- if (process.env.SPECMEM_COMPRESS_SESSION === '1') {
864
- const compressedContext = compressHookOutput(context, {
865
- threshold: 0.70,
866
- minLength: 50,
867
- includeWarning: true
868
- });
869
- // Final trim to ensure no trailing whitespace
870
- console.log(compressedContext.trim());
871
- } else {
872
- // Output full readable context (already trimmed above)
873
- console.log(context);
874
- }
828
+ const compressedContext = compressHookOutput(context, {
829
+ threshold: 0.70,
830
+ minLength: 50,
831
+ includeWarning: true
832
+ });
833
+ console.log(compressedContext.trim());
875
834
 
876
835
  process.exit(0);
877
836
  }
@@ -58,7 +58,7 @@ const TOOL_USE_INPUT_PREVIEW_CHARS = 100;
58
58
 
59
59
  // Old-message stripping config — applied to ALL requests (not just compaction)
60
60
  // Only strips tool_results larger than this threshold in messages outside the recent window
61
- const OLD_STRIP_THRESHOLD = 400; // chars — only strip results bigger than this
61
+ const OLD_STRIP_THRESHOLD = 100; // chars — only strip results bigger than this
62
62
  const OLD_STRIP_PREVIEW_CHARS = 200; // chars — keep this much of the original
63
63
 
64
64
  // Live neural MT compression config
@@ -533,6 +533,23 @@ function stripOldToolResults(messages) {
533
533
  return stripped;
534
534
  }
535
535
 
536
+ // Strip specmem hook injection text blocks from old messages
537
+ if (block.type === 'text' && typeof block.text === 'string') {
538
+ const txt = block.text;
539
+ const isHookOutput = txt.includes('[SM-找]') || txt.includes('[SM-碼]') ||
540
+ txt.includes('[SM-檔改]') || txt.includes('[SPECMEM-SESSION]') ||
541
+ txt.includes('⚠️壓縮:繁中') || txt.includes('[SM-搜]');
542
+ if (isHookOutput && txt.length > liveConfig.OLD_STRIP_THRESHOLD) {
543
+ const removed = txt.length - OLD_STRIP_PREVIEW_CHARS;
544
+ charsRemoved += removed;
545
+ toolResultsStripped++;
546
+ return {
547
+ ...block,
548
+ text: txt.slice(0, OLD_STRIP_PREVIEW_CHARS) + `...\n[HOOK-TRIMMED: ${txt.length} chars]`
549
+ };
550
+ }
551
+ }
552
+
536
553
  return block;
537
554
  });
538
555
 
@@ -789,6 +806,9 @@ async function compressMessagesLive(messages) {
789
806
 
790
807
  // Standalone text blocks (user/assistant conversational text) → steno only
791
808
  if (block.type === 'text' && typeof block.text === 'string') {
809
+ // Skip specmem hook injection text — already partially compressed Chinese, steno would garble
810
+ const _htxt = block.text;
811
+ if (_htxt.includes('[SM-') || _htxt.includes('[SPECMEM-SESSION]') || _htxt.includes('⚠️壓縮:繁中')) continue;
792
812
  if (looksLikeNaturalLanguage(block.text)) {
793
813
  originals.push(block.text);
794
814
  textLocations.push({ msgIdx: i, blockIdx: j, mtEligible: false });
@@ -29,6 +29,7 @@ export class WatcherManager {
29
29
  syncInterval = null;
30
30
  syncInProgress = false;
31
31
  syncTimeout = null;
32
+ lastLowScoreResyncAt = 0;
32
33
  constructor(config) {
33
34
  // Create handler first - it's the core component
34
35
  this.handler = new AutoUpdateTheMemories(config.handler);
@@ -114,6 +115,29 @@ export class WatcherManager {
114
115
  try {
115
116
  const report = await this.syncChecker.checkSync();
116
117
  await this.writeSyncScore(report.syncScore);
118
+ // Guard: indexingPending (score < 0) — nothing to do yet
119
+ if (report.syncScore < 0) {
120
+ logger.debug('periodic sync: indexing pending, skipping resync');
121
+ return;
122
+ }
123
+ // Low-score trigger: if score drops below threshold, force resync w/ debounce
124
+ const LOW_SCORE_THRESHOLD = parseFloat(process.env['SPECMEM_LOW_SCORE_THRESHOLD'] || '0.85');
125
+ const LOW_SCORE_DEBOUNCE_MS = parseInt(process.env['SPECMEM_LOW_SCORE_DEBOUNCE_MS'] || String(15 * 60 * 1000), 10);
126
+ if (report.syncScore < LOW_SCORE_THRESHOLD) {
127
+ const now = Date.now();
128
+ const debounceRemaining = LOW_SCORE_DEBOUNCE_MS - (now - this.lastLowScoreResyncAt);
129
+ if (debounceRemaining > 0) {
130
+ logger.warn({ syncScore: report.syncScore, debounceRemainingSec: Math.round(debounceRemaining / 1000) }, 'sync score below threshold but debounce active — skipping forced resync');
131
+ } else {
132
+ this.lastLowScoreResyncAt = now;
133
+ logger.warn({ syncScore: report.syncScore, threshold: LOW_SCORE_THRESHOLD }, 'sync score below threshold — forcing resync');
134
+ const resyncResult = await this.syncChecker.resyncEverythingFrFr();
135
+ logger.info({ filesAdded: resyncResult.filesAdded, filesUpdated: resyncResult.filesUpdated, errors: resyncResult.errors.length }, 'low-score forced resync complete');
136
+ const postReport = await this.syncChecker.checkSync();
137
+ await this.writeSyncScore(postReport.syncScore);
138
+ }
139
+ return;
140
+ }
117
141
  // Skip resync if already at >=98% — chokidar handles incremental changes
118
142
  if (report.syncScore >= 0.98) {
119
143
  logger.debug({ syncScore: report.syncScore }, 'periodic sync: score >=98%, skipping resync');
@@ -198,6 +222,8 @@ export class WatcherManager {
198
222
  * writeSyncScore - writes sync score to statusbar state file for display
199
223
  */
200
224
  async writeSyncScore(syncScore) {
225
+ // Guard: indexingPending returns -1 — never write negative values to statusbar
226
+ if (syncScore < 0) return;
201
227
  try {
202
228
  const projectPath = process.env.SPECMEM_PROJECT_PATH || process.cwd();
203
229
  const statusbarPath = join(projectPath, 'specmem', 'sockets', 'statusbar-state.json');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specmem-hardwicksoftware",
3
- "version": "3.7.31",
3
+ "version": "3.7.32",
4
4
  "type": "module",
5
5
  "description": "Your Claude Code sessions don't have to start from scratch anymore — SpecMem gives your AI real memory. It won't forget your conversations, your code, or your architecture decisions between sessions. That's the whole point. Semantic code indexing that actually works: TypeScript, JavaScript, Python, Go, Rust, Java, Kotlin, C, C++, HTML and more. It doesn't just track functions — it gets classes, methods, fields, constants, enums, macros, imports, structs, the whole codebase graph. There's chat memory too, powered by pgvector embeddings. You've also got token compression, team coordination, multi-agent comms, and file watching built in. 74+ MCP tools. Runs on PostgreSQL + Docker. It's kind of a big deal. justcalljon.pro",
6
6
  "main": "dist/index.js",
@@ -39,8 +39,8 @@
39
39
  "cpuCoreMin": 1,
40
40
  "cpuCoreMax": 4,
41
41
  "ramMinMb": 4000,
42
- "ramMaxMb": 13500,
43
- "updatedAt": "2026-02-24T11:56:33.760Z"
42
+ "ramMaxMb": 15000,
43
+ "updatedAt": "2026-02-24T19:22:11.693Z"
44
44
  },
45
45
  "resourcePool": {
46
46
  "embedding": {
@@ -1,6 +1,6 @@
1
1
  ; ============================================
2
2
  ; SPECMEM BRAIN CONTAINER - DYNAMIC SUPERVISORD CONFIG
3
- ; Generated by specmem-init at 2026-02-24T11:53:24.816Z
3
+ ; Generated by specmem-init at 2026-02-24T19:19:59.260Z
4
4
  ; Thread counts from model-config.json resourcePool
5
5
  ; ============================================
6
6
 
@@ -1,4 +1,4 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 200" width="700" height="200">
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 240" width="700" height="240">
2
2
  <defs>
3
3
  <linearGradient id="termBg" x1="0%" y1="0%" x2="100%" y2="100%">
4
4
  <stop offset="0%" style="stop-color:#0d1117"/>
@@ -26,23 +26,21 @@
26
26
  </filter>
27
27
  </defs>
28
28
 
29
- <rect width="700" height="200" rx="12" ry="12" fill="url(#termBg)"/>
30
- <rect width="700" height="200" rx="12" ry="12" fill="none" stroke="url(#accentGradient)" stroke-width="1" stroke-opacity="0.3"/>
29
+ <rect width="700" height="240" rx="12" ry="12" fill="url(#termBg)"/>
30
+ <rect width="700" height="240" rx="12" ry="12" fill="none" stroke="url(#accentGradient)" stroke-width="1" stroke-opacity="0.3"/>
31
31
 
32
- <text x="350" y="32" text-anchor="middle" font-family="system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="16" font-weight="700" fill="#f5f5f5">Quick Install</text>
32
+ <text x="350" y="30" text-anchor="middle" font-family="system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="15" font-weight="700" fill="#f5f5f5">Quick Install</text>
33
33
 
34
34
  <!-- Connecting line -->
35
- <line x1="50" y1="85" x2="50" y2="150" stroke="#2d333b" stroke-width="2" stroke-dasharray="4,4"/>
35
+ <line x1="50" y1="78" x2="50" y2="168" stroke="#2d333b" stroke-width="2" stroke-dasharray="4,4"/>
36
36
 
37
- <!-- Step 1: sudo npm install -->
38
- <g transform="translate(30, 55)">
39
- <circle cx="20" cy="25" r="14" fill="none" stroke="url(#stepGradient)" stroke-width="2" filter="url(#stepGlow)"/>
40
- <text x="20" y="30" text-anchor="middle" font-family="system-ui, sans-serif" font-size="12" font-weight="700" fill="#00bfff">1</text>
41
- <rect x="45" y="5" width="620" height="40" rx="6" ry="6" fill="#1a1f26"/>
42
- <text x="58" y="31" font-family="'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace" font-size="14" fill="#6e7681">$</text>
43
- <text x="75" y="31" font-family="'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace" font-size="14">
44
- <tspan fill="#ff7b72">sudo</tspan>
45
- <tspan fill="#f5f5f5"> </tspan>
37
+ <!-- Step 1 -->
38
+ <g transform="translate(30, 48)">
39
+ <circle cx="20" cy="20" r="13" fill="none" stroke="url(#stepGradient)" stroke-width="2" filter="url(#stepGlow)"/>
40
+ <text x="20" y="25" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" font-weight="700" fill="#00bfff">1</text>
41
+ <rect x="44" y="3" width="618" height="34" rx="6" ry="6" fill="#1a1f26"/>
42
+ <text x="57" y="25" font-family="'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace" font-size="13" fill="#6e7681">$</text>
43
+ <text x="72" y="25" font-family="'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace" font-size="13">
46
44
  <tspan fill="#ff7b72">npm</tspan>
47
45
  <tspan fill="#f5f5f5"> install </tspan>
48
46
  <tspan fill="#ffa657">-g</tspan>
@@ -50,27 +48,35 @@
50
48
  </text>
51
49
  </g>
52
50
 
53
- <!-- Step 2: cd && specmem init -->
54
- <g transform="translate(30, 100)">
55
- <circle cx="20" cy="25" r="14" fill="none" stroke="url(#stepGradient)" stroke-width="2" filter="url(#stepGlow)"/>
56
- <text x="20" y="30" text-anchor="middle" font-family="system-ui, sans-serif" font-size="12" font-weight="700" fill="#00bfff">2</text>
57
- <rect x="45" y="5" width="580" height="40" rx="6" ry="6" fill="#1a1f26"/>
58
- <text x="58" y="31" font-family="'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace" font-size="14" fill="#6e7681">$</text>
59
- <text x="75" y="31" font-family="'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace" font-size="14">
51
+ <!-- Step 2 -->
52
+ <g transform="translate(30, 96)">
53
+ <circle cx="20" cy="20" r="13" fill="none" stroke="url(#stepGradient)" stroke-width="2" filter="url(#stepGlow)"/>
54
+ <text x="20" y="25" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" font-weight="700" fill="#00bfff">2</text>
55
+ <rect x="44" y="3" width="618" height="34" rx="6" ry="6" fill="#1a1f26"/>
56
+ <text x="57" y="25" font-family="'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace" font-size="13" fill="#6e7681">$</text>
57
+ <text x="72" y="25" font-family="'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace" font-size="13">
60
58
  <tspan fill="#ff7b72">cd</tspan>
61
- <tspan fill="#f5f5f5"> /your/projects/directory/ </tspan>
62
- <tspan fill="#6e7681">&amp;&amp;</tspan>
63
- <tspan fill="#f5f5f5"> </tspan>
59
+ <tspan fill="#f5f5f5"> /your/project/directory</tspan>
60
+ </text>
61
+ </g>
62
+
63
+ <!-- Step 3 -->
64
+ <g transform="translate(30, 144)">
65
+ <circle cx="20" cy="20" r="13" fill="none" stroke="url(#stepGradient)" stroke-width="2" filter="url(#stepGlow)"/>
66
+ <text x="20" y="25" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" font-weight="700" fill="#00bfff">3</text>
67
+ <rect x="44" y="3" width="618" height="34" rx="6" ry="6" fill="#1a1f26"/>
68
+ <text x="57" y="25" font-family="'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace" font-size="13" fill="#6e7681">$</text>
69
+ <text x="72" y="25" font-family="'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace" font-size="13">
64
70
  <tspan fill="#ff7b72">specmem</tspan>
65
71
  <tspan fill="#f5f5f5"> init</tspan>
66
72
  </text>
67
73
  </g>
68
74
 
69
75
  <!-- Done -->
70
- <g transform="translate(30, 150)">
71
- <circle cx="20" cy="20" r="14" fill="#22c55e" fill-opacity="0.2" stroke="#22c55e" stroke-width="2" filter="url(#checkGlow)"/>
72
- <path d="M13 20 L18 25 L28 15" fill="none" stroke="#22c55e" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
73
- <text x="55" y="25" font-family="system-ui, sans-serif" font-size="14" font-weight="600" fill="#22c55e">Done.</text>
74
- <text x="105" y="25" font-family="system-ui, sans-serif" font-size="13" fill="#8b949e">Claude now remembers your entire codebase and conversations.</text>
76
+ <g transform="translate(30, 192)">
77
+ <circle cx="20" cy="16" r="13" fill="#22c55e" fill-opacity="0.2" stroke="#22c55e" stroke-width="2" filter="url(#checkGlow)"/>
78
+ <path d="M13 16 L18 21 L28 11" fill="none" stroke="#22c55e" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
79
+ <text x="50" y="21" font-family="system-ui, sans-serif" font-size="13" font-weight="600" fill="#22c55e">Done.</text>
80
+ <text x="98" y="21" font-family="system-ui, sans-serif" font-size="12" fill="#8b949e">Claude now remembers your entire codebase across every session.</text>
75
81
  </g>
76
82
  </svg>