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.
- package/CHANGELOG.md +22 -0
- package/README.md +12 -0
- package/claude-hooks/bash-call-enforcer.cjs +140 -0
- package/claude-hooks/settings.json +33 -0
- package/claude-hooks/specmem-drilldown-hook.cjs +49 -2
- package/claude-hooks/specmem-drilldown-hook.js +49 -2
- package/claude-hooks/specmem-drilldown-hook.js.bak +495 -0
- package/claude-hooks/specmem-precompact.cjs +13 -36
- package/claude-hooks/specmem-precompact.js +3 -7
- package/claude-hooks/specmem-session-start.cjs +38 -50
- package/claude-hooks/specmem-session-start.js +19 -60
- package/dist/mcp/compactionProxy.js +21 -1
- package/dist/watcher/index.js +26 -0
- package/package.json +1 -1
- package/specmem/model-config.json +2 -2
- package/specmem/supervisord.conf +1 -1
- package/svg-sections/readme-install.svg +35 -29
- package/svg-sections/readme-whats-new.svg +120 -114
|
@@ -283,10 +283,10 @@ function getLastSessionOutput() {
|
|
|
283
283
|
|
|
284
284
|
let sessionContent;
|
|
285
285
|
if (startIdx === -1) {
|
|
286
|
-
sessionContent = lines.slice(-
|
|
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(-
|
|
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) -
|
|
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
|
-
|
|
347
|
-
//
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
//
|
|
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(
|
|
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
|
|
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
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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(-
|
|
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(-
|
|
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 >
|
|
777
|
-
sessionContent = contentLines.slice(0,
|
|
778
|
-
`\n... [${contentLines.length -
|
|
779
|
-
contentLines.slice(-
|
|
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
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
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
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
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 =
|
|
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 });
|
package/dist/watcher/index.js
CHANGED
|
@@ -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.
|
|
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",
|
package/specmem/supervisord.conf
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
; ============================================
|
|
2
2
|
; SPECMEM BRAIN CONTAINER - DYNAMIC SUPERVISORD CONFIG
|
|
3
|
-
; Generated by specmem-init at 2026-02-
|
|
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
|
|
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="
|
|
30
|
-
<rect width="700" height="
|
|
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 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="
|
|
35
|
+
<line x1="50" y1="78" x2="50" y2="168" stroke="#2d333b" stroke-width="2" stroke-dasharray="4,4"/>
|
|
36
36
|
|
|
37
|
-
<!-- Step 1
|
|
38
|
-
<g transform="translate(30,
|
|
39
|
-
<circle cx="20" cy="
|
|
40
|
-
<text x="20" y="
|
|
41
|
-
<rect x="
|
|
42
|
-
<text x="
|
|
43
|
-
<text x="
|
|
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
|
|
54
|
-
<g transform="translate(30,
|
|
55
|
-
<circle cx="20" cy="
|
|
56
|
-
<text x="20" y="
|
|
57
|
-
<rect x="
|
|
58
|
-
<text x="
|
|
59
|
-
<text x="
|
|
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/
|
|
62
|
-
|
|
63
|
-
|
|
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,
|
|
71
|
-
<circle cx="20" cy="
|
|
72
|
-
<path d="M13
|
|
73
|
-
<text x="
|
|
74
|
-
<text x="
|
|
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>
|