specmem-hardwicksoftware 3.7.32 → 3.7.33
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/bin/specmem-cli.cjs +18 -33
- package/bootstrap.cjs +5 -1
- package/claude-hooks/agent-loading-hook.js +31 -47
- package/claude-hooks/subagent-loading-hook.js +1 -1
- package/claude-hooks/team-comms-enforcer.cjs +112 -93
- package/dist/config/configSync.js +4 -1
- package/dist/index.js +44 -1
- package/dist/init/claudeConfigInjector.js +4 -1
- package/dist/installer/autoInstall.js +4 -1
- package/dist/mcp/compactionProxy.js +627 -72
- package/dist/mcp/compactionProxyDaemon.js +18 -4
- package/dist/mcp/specMemServer.js +8 -0
- package/dist/watcher/index.js +16 -2
- package/package.json +1 -1
- package/scripts/deploy-hooks.cjs +4 -1
- package/scripts/specmem-init.cjs +31 -35
- package/scripts/specmem-uninstall.cjs +4 -1
- package/specmem/model-config.json +3 -3
- package/specmem/supervisord.conf +1 -1
- package/svg-sections/readme-install.svg +89 -53
|
@@ -26,6 +26,9 @@ import {
|
|
|
26
26
|
handleRequest,
|
|
27
27
|
log,
|
|
28
28
|
togglePause,
|
|
29
|
+
setPaused,
|
|
30
|
+
startReaper,
|
|
31
|
+
hasLiveProjects,
|
|
29
32
|
PROXY_PORT,
|
|
30
33
|
PORT_FILE,
|
|
31
34
|
PID_FILE,
|
|
@@ -37,9 +40,9 @@ import {
|
|
|
37
40
|
// Precondition checks
|
|
38
41
|
// ============================================================================
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
// If disabled, start in paused (passthrough) mode instead of exiting.
|
|
44
|
+
// The proxy must always be in the request path for seamless on/off toggling.
|
|
45
|
+
const startPaused = existsSync(DISABLED_FILE);
|
|
43
46
|
|
|
44
47
|
// ============================================================================
|
|
45
48
|
// Port collision check
|
|
@@ -73,6 +76,9 @@ const KYS_IDLE_THRESHOLD = 120000; // 2 minutes without Claude → exit
|
|
|
73
76
|
const KYS_GRACE_PERIOD = 60000; // 60s grace after startup
|
|
74
77
|
|
|
75
78
|
function isClaudeAlive() {
|
|
79
|
+
// Check registry first — much cheaper than pgrep
|
|
80
|
+
if (hasLiveProjects()) return true;
|
|
81
|
+
// Fallback to pgrep for cases where MCP didn't register (old versions)
|
|
76
82
|
try {
|
|
77
83
|
const result = execSync(
|
|
78
84
|
'pgrep -f "claude" 2>/dev/null',
|
|
@@ -122,7 +128,15 @@ async function main() {
|
|
|
122
128
|
server.listen(PROXY_PORT, '127.0.0.1', () => {
|
|
123
129
|
// Write port file on successful bind — MCP and bashrc use this to find us
|
|
124
130
|
writeFileSync(PORT_FILE, String(PROXY_PORT), { mode: 0o644 });
|
|
125
|
-
|
|
131
|
+
// If disabled flag existed at startup, start in passthrough mode
|
|
132
|
+
if (startPaused) {
|
|
133
|
+
setPaused(true);
|
|
134
|
+
log('info', `Daemon started in PASSTHROUGH mode (disabled flag): PID=${process.pid} port=${PROXY_PORT}`);
|
|
135
|
+
} else {
|
|
136
|
+
log('info', `Daemon started: PID=${process.pid} port=${PROXY_PORT}`);
|
|
137
|
+
}
|
|
138
|
+
// Start the stale project reaper
|
|
139
|
+
startReaper();
|
|
126
140
|
});
|
|
127
141
|
|
|
128
142
|
// --- Signal handlers ---
|
|
@@ -2072,6 +2072,14 @@ export class SpecMemServer {
|
|
|
2072
2072
|
catch (err) {
|
|
2073
2073
|
logger.warn({ error: err }, 'Error closing MCP server');
|
|
2074
2074
|
}
|
|
2075
|
+
// Step 6.5: Deregister from compaction proxy daemon
|
|
2076
|
+
try {
|
|
2077
|
+
stopCompactionProxy();
|
|
2078
|
+
logger.debug('Compaction proxy deregistered');
|
|
2079
|
+
}
|
|
2080
|
+
catch (err) {
|
|
2081
|
+
logger.debug({ error: err }, 'Could not deregister from compaction proxy');
|
|
2082
|
+
}
|
|
2075
2083
|
// FIX 6.10 + CRIT-2: unhandledRejection/uncaughtException use once() so auto-remove.
|
|
2076
2084
|
// Only remove SIGINT/SIGTERM/SIGHUP which may still be registered.
|
|
2077
2085
|
if (this._processHandlers) {
|
package/dist/watcher/index.js
CHANGED
|
@@ -30,6 +30,7 @@ export class WatcherManager {
|
|
|
30
30
|
syncInProgress = false;
|
|
31
31
|
syncTimeout = null;
|
|
32
32
|
lastLowScoreResyncAt = 0;
|
|
33
|
+
lastLowScoreResyncScore = null; // track score at last resync to detect drops
|
|
33
34
|
constructor(config) {
|
|
34
35
|
// Create handler first - it's the core component
|
|
35
36
|
this.handler = new AutoUpdateTheMemories(config.handler);
|
|
@@ -120,21 +121,34 @@ export class WatcherManager {
|
|
|
120
121
|
logger.debug('periodic sync: indexing pending, skipping resync');
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
123
|
-
// Low-score trigger: if score
|
|
124
|
+
// Low-score trigger: only force resync if score DROPPED by >=10% since last resync
|
|
125
|
+
// This prevents infinite resync loops when score is stuck below threshold
|
|
124
126
|
const LOW_SCORE_THRESHOLD = parseFloat(process.env['SPECMEM_LOW_SCORE_THRESHOLD'] || '0.85');
|
|
127
|
+
const LOW_SCORE_DROP_THRESHOLD = parseFloat(process.env['SPECMEM_LOW_SCORE_DROP_THRESHOLD'] || '0.10');
|
|
125
128
|
const LOW_SCORE_DEBOUNCE_MS = parseInt(process.env['SPECMEM_LOW_SCORE_DEBOUNCE_MS'] || String(15 * 60 * 1000), 10);
|
|
126
129
|
if (report.syncScore < LOW_SCORE_THRESHOLD) {
|
|
130
|
+
// First time seeing low score — always resync
|
|
131
|
+
// After that, only resync if score dropped by >=10% from the post-resync score
|
|
132
|
+
const scoreDrop = this.lastLowScoreResyncScore !== null
|
|
133
|
+
? this.lastLowScoreResyncScore - report.syncScore
|
|
134
|
+
: Infinity; // first time = always trigger
|
|
135
|
+
if (scoreDrop < LOW_SCORE_DROP_THRESHOLD) {
|
|
136
|
+
logger.warn({ syncScore: report.syncScore, lastResyncScore: this.lastLowScoreResyncScore, scoreDrop }, 'sync score below threshold but has not dropped by >=10% since last resync — skipping');
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
127
139
|
const now = Date.now();
|
|
128
140
|
const debounceRemaining = LOW_SCORE_DEBOUNCE_MS - (now - this.lastLowScoreResyncAt);
|
|
129
141
|
if (debounceRemaining > 0) {
|
|
130
142
|
logger.warn({ syncScore: report.syncScore, debounceRemainingSec: Math.round(debounceRemaining / 1000) }, 'sync score below threshold but debounce active — skipping forced resync');
|
|
131
143
|
} else {
|
|
132
144
|
this.lastLowScoreResyncAt = now;
|
|
133
|
-
logger.warn({ syncScore: report.syncScore, threshold: LOW_SCORE_THRESHOLD }, 'sync score below threshold — forcing resync');
|
|
145
|
+
logger.warn({ syncScore: report.syncScore, threshold: LOW_SCORE_THRESHOLD, scoreDrop }, 'sync score below threshold with >=10% drop — forcing resync');
|
|
134
146
|
const resyncResult = await this.syncChecker.resyncEverythingFrFr();
|
|
135
147
|
logger.info({ filesAdded: resyncResult.filesAdded, filesUpdated: resyncResult.filesUpdated, errors: resyncResult.errors.length }, 'low-score forced resync complete');
|
|
136
148
|
const postReport = await this.syncChecker.checkSync();
|
|
137
149
|
await this.writeSyncScore(postReport.syncScore);
|
|
150
|
+
// Track the post-resync score so we can detect future drops
|
|
151
|
+
this.lastLowScoreResyncScore = postReport.syncScore;
|
|
138
152
|
}
|
|
139
153
|
return;
|
|
140
154
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specmem-hardwicksoftware",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.33",
|
|
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/scripts/deploy-hooks.cjs
CHANGED
|
@@ -1014,7 +1014,10 @@ function configureMCP() {
|
|
|
1014
1014
|
console.log(`${C.dim}Backed up to ${backupPath}${C.reset}`);
|
|
1015
1015
|
}
|
|
1016
1016
|
|
|
1017
|
-
|
|
1017
|
+
// ATOMIC WRITE: write to temp then rename to prevent corruption
|
|
1018
|
+
const tmpClaudePath = CLAUDE_JSON + '.tmp.' + process.pid;
|
|
1019
|
+
fs.writeFileSync(tmpClaudePath, JSON.stringify(claudeJson, null, 2));
|
|
1020
|
+
fs.renameSync(tmpClaudePath, CLAUDE_JSON);
|
|
1018
1021
|
console.log(`${C.green}✓${C.reset} Configured MCP server: specmem for project ${PROJECT_PATH}`);
|
|
1019
1022
|
|
|
1020
1023
|
// ACK verification - re-read and verify
|
package/scripts/specmem-init.cjs
CHANGED
|
@@ -1292,53 +1292,50 @@ fi
|
|
|
1292
1292
|
}
|
|
1293
1293
|
|
|
1294
1294
|
// ── Step 3b: Add ANTHROPIC_BASE_URL proxy routing to bashrc ─────────────
|
|
1295
|
-
//
|
|
1296
|
-
//
|
|
1297
|
-
// file written by the proxy on startup. Only activates if proxy has run.
|
|
1295
|
+
// Always routes Claude Code API calls through the compaction proxy.
|
|
1296
|
+
// Proxy runs in passthrough mode when disabled — seamless on/off toggling.
|
|
1298
1297
|
// Does NOT touch ANTHROPIC_API_KEY — only sets the base URL.
|
|
1299
1298
|
const proxyMarker = '# specmem-proxy-env';
|
|
1299
|
+
const defaultProxyPort = process.env.COMPACTION_PROXY_PORT || '4080';
|
|
1300
1300
|
let hasProxyFix = false;
|
|
1301
1301
|
try {
|
|
1302
1302
|
if (fs.existsSync(bashrcPath)) {
|
|
1303
1303
|
const currentBashrc = fs.readFileSync(bashrcPath, 'utf8');
|
|
1304
1304
|
hasProxyFix = currentBashrc.includes(proxyMarker);
|
|
1305
|
+
// If old conditional version exists, replace it with the unconditional one
|
|
1306
|
+
if (hasProxyFix && currentBashrc.includes('.compaction-proxy-port')) {
|
|
1307
|
+
const cleaned = currentBashrc.replace(/\n?# specmem-proxy-env\n(?:# [^\n]*\n)*if \[ -f "\$HOME\/\.claude\/\.compaction-proxy-port" \];[\s\S]*?fi\n?/g, '');
|
|
1308
|
+
fs.writeFileSync(bashrcPath, cleaned);
|
|
1309
|
+
hasProxyFix = false; // Will re-add the new version below
|
|
1310
|
+
initLog('Replaced old conditional proxy bashrc block with unconditional version');
|
|
1311
|
+
}
|
|
1305
1312
|
}
|
|
1306
1313
|
} catch (e) { /* no .bashrc */ }
|
|
1307
1314
|
|
|
1308
1315
|
if (!hasProxyFix) {
|
|
1309
1316
|
const proxyFix = `
|
|
1310
1317
|
${proxyMarker}
|
|
1311
|
-
# SpecMem:
|
|
1312
|
-
|
|
1313
|
-
if [ -f "$HOME/.claude/.compaction-proxy-port" ]; then
|
|
1314
|
-
_specmem_proxy_port="$(cat "$HOME/.claude/.compaction-proxy-port" 2>/dev/null)"
|
|
1315
|
-
if [ -n "$_specmem_proxy_port" ]; then
|
|
1316
|
-
export ANTHROPIC_BASE_URL="http://127.0.0.1:$_specmem_proxy_port"
|
|
1317
|
-
fi
|
|
1318
|
-
unset _specmem_proxy_port
|
|
1319
|
-
fi
|
|
1318
|
+
# SpecMem: Always route API calls through compaction proxy (passthrough when disabled)
|
|
1319
|
+
export ANTHROPIC_BASE_URL="http://127.0.0.1:\${COMPACTION_PROXY_PORT:-${defaultProxyPort}}"
|
|
1320
1320
|
`;
|
|
1321
1321
|
try {
|
|
1322
1322
|
fs.appendFileSync(bashrcPath, proxyFix);
|
|
1323
1323
|
result.proxyEnvFixed = true;
|
|
1324
1324
|
result.fixed = true;
|
|
1325
|
-
initLog('Added ANTHROPIC_BASE_URL proxy routing to .bashrc');
|
|
1325
|
+
initLog('Added unconditional ANTHROPIC_BASE_URL proxy routing to .bashrc');
|
|
1326
1326
|
} catch (e) {
|
|
1327
1327
|
initLog('Failed to write .bashrc proxy fix', e);
|
|
1328
1328
|
}
|
|
1329
1329
|
}
|
|
1330
1330
|
|
|
1331
|
-
//
|
|
1332
|
-
|
|
1333
|
-
const
|
|
1334
|
-
if (
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
process.env.ANTHROPIC_BASE_URL = `http://127.0.0.1:${port}`;
|
|
1338
|
-
initLog(`Set ANTHROPIC_BASE_URL=http://127.0.0.1:${port} for current session`);
|
|
1339
|
-
}
|
|
1331
|
+
// Always set for current session — proxy is always in the path
|
|
1332
|
+
{
|
|
1333
|
+
const proxyUrl = `http://127.0.0.1:${defaultProxyPort}`;
|
|
1334
|
+
if (process.env.ANTHROPIC_BASE_URL !== proxyUrl) {
|
|
1335
|
+
process.env.ANTHROPIC_BASE_URL = proxyUrl;
|
|
1336
|
+
initLog(`Set ANTHROPIC_BASE_URL=${proxyUrl} for current session`);
|
|
1340
1337
|
}
|
|
1341
|
-
}
|
|
1338
|
+
}
|
|
1342
1339
|
|
|
1343
1340
|
// ── Step 4: Set TERM for current session ────────────────────────────────
|
|
1344
1341
|
if (result.needed && !term.includes('256color')) {
|
|
@@ -7266,16 +7263,9 @@ async function launchScreenSessions(projectPath, ui) {
|
|
|
7266
7263
|
// Uses screen hardcopy to tmpfs on-demand instead of continuous logging
|
|
7267
7264
|
// -h 5000 sets scrollback buffer to 5000 lines for hardcopy capture
|
|
7268
7265
|
const claudeBin = getClaudeBinary();
|
|
7269
|
-
//
|
|
7270
|
-
|
|
7271
|
-
const
|
|
7272
|
-
let proxyEnv = '';
|
|
7273
|
-
try {
|
|
7274
|
-
if (fs.existsSync(portFile)) {
|
|
7275
|
-
const port = fs.readFileSync(portFile, 'utf8').trim();
|
|
7276
|
-
if (port) proxyEnv = `ANTHROPIC_BASE_URL="http://127.0.0.1:${port}" `;
|
|
7277
|
-
}
|
|
7278
|
-
} catch (e) { /* non-fatal */ }
|
|
7266
|
+
// Always set ANTHROPIC_BASE_URL — proxy runs in passthrough mode when disabled
|
|
7267
|
+
const proxyPort = process.env.COMPACTION_PROXY_PORT || '4080';
|
|
7268
|
+
const proxyEnv = `ANTHROPIC_BASE_URL="http://127.0.0.1:${proxyPort}" `;
|
|
7279
7269
|
execSync(`screen -h 5000 -dmS ${claudeSession} bash -c 'cd "${projectPath}" && ${proxyEnv}SPECMEM_DASHBOARD=1 "${claudeBin}" 2>&1; exec bash'`, { stdio: 'ignore' });
|
|
7280
7270
|
await sleep(300);
|
|
7281
7271
|
|
|
@@ -8843,7 +8833,10 @@ CREATE INDEX IF NOT EXISTS idx_embedding_queue_project ON embedding_queue (proje
|
|
|
8843
8833
|
};
|
|
8844
8834
|
claudeJson.projects[projectPath].hasTrustDialogAccepted = true;
|
|
8845
8835
|
|
|
8846
|
-
|
|
8836
|
+
// ATOMIC WRITE: write to temp then rename to prevent corruption
|
|
8837
|
+
const tmpPath1 = claudeJsonPath + '.tmp.' + process.pid;
|
|
8838
|
+
fs.writeFileSync(tmpPath1, JSON.stringify(claudeJson, null, 2));
|
|
8839
|
+
fs.renameSync(tmpPath1, claudeJsonPath);
|
|
8847
8840
|
initLog('[SETUP] ✓ MCP server configured in ~/.claude.json for ' + projectPath);
|
|
8848
8841
|
} catch (e) {
|
|
8849
8842
|
initLog('[SETUP] ⚠ Failed to configure MCP in .claude.json: ' + e.message);
|
|
@@ -10555,7 +10548,10 @@ priority=999
|
|
|
10555
10548
|
};
|
|
10556
10549
|
claudeJson.projects[projectPath].hasTrustDialogAccepted = true;
|
|
10557
10550
|
|
|
10558
|
-
|
|
10551
|
+
// ATOMIC WRITE: write to temp then rename to prevent corruption
|
|
10552
|
+
const tmpPath2 = claudeJsonPath + '.tmp.' + process.pid;
|
|
10553
|
+
fs.writeFileSync(tmpPath2, JSON.stringify(claudeJson, null, 2));
|
|
10554
|
+
fs.renameSync(tmpPath2, claudeJsonPath);
|
|
10559
10555
|
initLog('[CONTAINER] ✓ MCP server configured in ~/.claude.json for ' + projectPath);
|
|
10560
10556
|
ui.setStatus('MCP configured');
|
|
10561
10557
|
|
|
@@ -119,7 +119,10 @@ function cleanClaudeConfigs() {
|
|
|
119
119
|
if (changed) {
|
|
120
120
|
const backup = claudeJsonPath + '.pre-uninstall.' + Date.now();
|
|
121
121
|
fs.copyFileSync(claudeJsonPath, backup);
|
|
122
|
-
|
|
122
|
+
// ATOMIC WRITE: write to temp then rename to prevent corruption
|
|
123
|
+
const tmpPath = claudeJsonPath + '.tmp.' + process.pid;
|
|
124
|
+
fs.writeFileSync(tmpPath, JSON.stringify(claudeJson, null, 2));
|
|
125
|
+
fs.renameSync(tmpPath, claudeJsonPath);
|
|
123
126
|
console.log(` ${c.green}✓${c.reset} Removed SpecMem MCP server from .claude.json`);
|
|
124
127
|
console.log(` ${c.dim} Backup: ${backup}${c.reset}`);
|
|
125
128
|
}
|
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
},
|
|
36
36
|
"resources": {
|
|
37
37
|
"cpuMin": 10,
|
|
38
|
-
"cpuMax":
|
|
38
|
+
"cpuMax": 30,
|
|
39
39
|
"cpuCoreMin": 1,
|
|
40
40
|
"cpuCoreMax": 4,
|
|
41
41
|
"ramMinMb": 4000,
|
|
42
42
|
"ramMaxMb": 15000,
|
|
43
|
-
"updatedAt": "2026-02-
|
|
43
|
+
"updatedAt": "2026-02-24T20:01:48.721Z"
|
|
44
44
|
},
|
|
45
45
|
"resourcePool": {
|
|
46
46
|
"embedding": {
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
},
|
|
96
96
|
"heavyOps": {
|
|
97
97
|
"enabled": true,
|
|
98
|
-
"enabledAt": "2026-02-
|
|
98
|
+
"enabledAt": "2026-02-26T14:18:50.747Z",
|
|
99
99
|
"originalBatchSize": 32,
|
|
100
100
|
"batchSizeMultiplier": 2,
|
|
101
101
|
"throttleReduction": 0.2
|
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-26T14:42:59.603Z
|
|
4
4
|
; Thread counts from model-config.json resourcePool
|
|
5
5
|
; ============================================
|
|
6
6
|
|
|
@@ -1,82 +1,118 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 420" width="800" height="420">
|
|
2
2
|
<defs>
|
|
3
|
-
<linearGradient id="
|
|
3
|
+
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
4
4
|
<stop offset="0%" style="stop-color:#0d1117"/>
|
|
5
5
|
<stop offset="100%" style="stop-color:#161b22"/>
|
|
6
6
|
</linearGradient>
|
|
7
|
-
<linearGradient id="
|
|
7
|
+
<linearGradient id="cardGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
8
|
+
<stop offset="0%" style="stop-color:#1c2128;stop-opacity:0.9"/>
|
|
9
|
+
<stop offset="100%" style="stop-color:#21262d;stop-opacity:0.85"/>
|
|
10
|
+
</linearGradient>
|
|
11
|
+
<linearGradient id="titleGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
8
12
|
<stop offset="0%" style="stop-color:#00bfff"/>
|
|
9
|
-
<stop offset="
|
|
13
|
+
<stop offset="50%" style="stop-color:#a855f7"/>
|
|
14
|
+
<stop offset="100%" style="stop-color:#00bfff"/>
|
|
10
15
|
</linearGradient>
|
|
11
|
-
<linearGradient id="
|
|
16
|
+
<linearGradient id="cyanPurple" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
12
17
|
<stop offset="0%" style="stop-color:#00bfff"/>
|
|
13
18
|
<stop offset="100%" style="stop-color:#a855f7"/>
|
|
14
19
|
</linearGradient>
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
<feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
|
|
20
|
-
</filter>
|
|
21
|
-
<filter id="stepGlow" x="-50%" y="-50%" width="200%" height="200%">
|
|
22
|
-
<feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur"/>
|
|
23
|
-
<feFlood flood-color="#00bfff" flood-opacity="0.3"/>
|
|
24
|
-
<feComposite in2="blur" operator="in"/>
|
|
25
|
-
<feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
|
|
26
|
-
</filter>
|
|
20
|
+
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
21
|
+
<stop offset="0%" style="stop-color:#22c55e"/>
|
|
22
|
+
<stop offset="100%" style="stop-color:#4ade80"/>
|
|
23
|
+
</linearGradient>
|
|
27
24
|
</defs>
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
<rect width="
|
|
26
|
+
<!-- Background -->
|
|
27
|
+
<rect width="800" height="420" rx="16" ry="16" fill="url(#bgGrad)"/>
|
|
28
|
+
<rect width="800" height="420" rx="16" ry="16" fill="none" stroke="url(#cyanPurple)" stroke-width="1" stroke-opacity="0.25"/>
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
<line x1="50" y1="78" x2="50" y2="168" stroke="#2d333b" stroke-width="2" stroke-dasharray="4,4"/>
|
|
30
|
+
<!-- Title -->
|
|
31
|
+
<text x="400" y="38" text-anchor="middle" font-family="system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="22" font-weight="700" fill="url(#titleGrad)">Quick Install</text>
|
|
32
|
+
<text x="400" y="60" text-anchor="middle" font-family="system-ui, sans-serif" font-size="12" fill="#8b949e">3 steps. No root required. Works on Debian, Ubuntu, Mint, Kali.</text>
|
|
36
33
|
|
|
37
34
|
<!-- Step 1 -->
|
|
38
|
-
<g transform="translate(30,
|
|
39
|
-
<
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
<text x="
|
|
35
|
+
<g transform="translate(30, 78)">
|
|
36
|
+
<rect width="740" height="72" rx="12" fill="url(#cardGrad)" stroke="#30363d" stroke-width="1"/>
|
|
37
|
+
<rect width="740" height="3" rx="1.5" fill="url(#cyanPurple)"/>
|
|
38
|
+
<!-- Step badge -->
|
|
39
|
+
<circle cx="36" cy="42" r="16" fill="none" stroke="#00bfff" stroke-width="1.5" stroke-opacity="0.6"/>
|
|
40
|
+
<text x="36" y="47" text-anchor="middle" font-family="system-ui, sans-serif" font-size="14" font-weight="700" fill="#00bfff">1</text>
|
|
41
|
+
<!-- Command -->
|
|
42
|
+
<rect x="64" y="20" width="660" height="32" rx="6" fill="#0d1117" stroke="#21262d" stroke-width="1"/>
|
|
43
|
+
<text x="78" y="40" font-family="'SF Mono', 'Fira Code', Consolas, monospace" font-size="13" fill="#6e7681">$</text>
|
|
44
|
+
<text x="94" y="40" font-family="'SF Mono', 'Fira Code', Consolas, monospace" font-size="13">
|
|
44
45
|
<tspan fill="#ff7b72">npm</tspan>
|
|
45
|
-
<tspan fill="#
|
|
46
|
+
<tspan fill="#e6edf3"> install </tspan>
|
|
46
47
|
<tspan fill="#ffa657">-g</tspan>
|
|
47
|
-
<tspan fill="#
|
|
48
|
+
<tspan fill="#e6edf3"> specmem-hardwicksoftware</tspan>
|
|
48
49
|
</text>
|
|
50
|
+
<text x="64" y="65" font-family="system-ui, sans-serif" font-size="10" fill="#6e7681">Install globally from npm</text>
|
|
49
51
|
</g>
|
|
50
52
|
|
|
53
|
+
<!-- Connector line -->
|
|
54
|
+
<line x1="66" y1="150" x2="66" y2="168" stroke="#30363d" stroke-width="2" stroke-dasharray="3,3"/>
|
|
55
|
+
|
|
51
56
|
<!-- Step 2 -->
|
|
52
|
-
<g transform="translate(30,
|
|
53
|
-
<
|
|
54
|
-
<
|
|
55
|
-
<
|
|
56
|
-
<text x="
|
|
57
|
-
<
|
|
57
|
+
<g transform="translate(30, 168)">
|
|
58
|
+
<rect width="740" height="72" rx="12" fill="url(#cardGrad)" stroke="#30363d" stroke-width="1"/>
|
|
59
|
+
<rect width="740" height="3" rx="1.5" fill="url(#cyanPurple)"/>
|
|
60
|
+
<circle cx="36" cy="42" r="16" fill="none" stroke="#a855f7" stroke-width="1.5" stroke-opacity="0.6"/>
|
|
61
|
+
<text x="36" y="47" text-anchor="middle" font-family="system-ui, sans-serif" font-size="14" font-weight="700" fill="#a855f7">2</text>
|
|
62
|
+
<rect x="64" y="20" width="660" height="32" rx="6" fill="#0d1117" stroke="#21262d" stroke-width="1"/>
|
|
63
|
+
<text x="78" y="40" font-family="'SF Mono', 'Fira Code', Consolas, monospace" font-size="13" fill="#6e7681">$</text>
|
|
64
|
+
<text x="94" y="40" font-family="'SF Mono', 'Fira Code', Consolas, monospace" font-size="13">
|
|
58
65
|
<tspan fill="#ff7b72">cd</tspan>
|
|
59
|
-
<tspan fill="#
|
|
66
|
+
<tspan fill="#e6edf3"> /your/project/directory</tspan>
|
|
60
67
|
</text>
|
|
68
|
+
<text x="64" y="65" font-family="system-ui, sans-serif" font-size="10" fill="#6e7681">Navigate to your project root</text>
|
|
61
69
|
</g>
|
|
62
70
|
|
|
71
|
+
<!-- Connector line -->
|
|
72
|
+
<line x1="66" y1="240" x2="66" y2="258" stroke="#30363d" stroke-width="2" stroke-dasharray="3,3"/>
|
|
73
|
+
|
|
63
74
|
<!-- Step 3 -->
|
|
64
|
-
<g transform="translate(30,
|
|
65
|
-
<
|
|
66
|
-
<
|
|
67
|
-
<
|
|
68
|
-
<text x="
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
<g transform="translate(30, 258)">
|
|
76
|
+
<rect width="740" height="72" rx="12" fill="url(#cardGrad)" stroke="#22c55e" stroke-width="1.5"/>
|
|
77
|
+
<rect width="740" height="3" rx="1.5" fill="url(#greenGrad)"/>
|
|
78
|
+
<circle cx="36" cy="42" r="16" fill="none" stroke="#22c55e" stroke-width="1.5" stroke-opacity="0.6"/>
|
|
79
|
+
<text x="36" y="47" text-anchor="middle" font-family="system-ui, sans-serif" font-size="14" font-weight="700" fill="#4ade80">3</text>
|
|
80
|
+
<rect x="64" y="20" width="660" height="32" rx="6" fill="#0d1117" stroke="#21262d" stroke-width="1"/>
|
|
81
|
+
<text x="78" y="40" font-family="'SF Mono', 'Fira Code', Consolas, monospace" font-size="13" fill="#6e7681">$</text>
|
|
82
|
+
<text x="94" y="40" font-family="'SF Mono', 'Fira Code', Consolas, monospace" font-size="13">
|
|
83
|
+
<tspan fill="#4ade80">specmem</tspan>
|
|
84
|
+
<tspan fill="#e6edf3"> init</tspan>
|
|
72
85
|
</text>
|
|
86
|
+
<text x="64" y="65" font-family="system-ui, sans-serif" font-size="10" fill="#6e7681">One-time setup — models, DB, hooks, everything</text>
|
|
73
87
|
</g>
|
|
74
88
|
|
|
75
|
-
<!-- Done -->
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
<!-- Done bar -->
|
|
90
|
+
<rect x="30" y="350" width="740" height="52" rx="12" fill="#22c55e" fill-opacity="0.08" stroke="#22c55e" stroke-width="1" stroke-opacity="0.3"/>
|
|
91
|
+
<!-- Checkmark -->
|
|
92
|
+
<circle cx="66" cy="376" r="12" fill="none" stroke="#22c55e" stroke-width="2"/>
|
|
93
|
+
<path d="M60 376 L64 381 L74 370" fill="none" stroke="#4ade80" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
94
|
+
<text x="90" y="372" font-family="system-ui, sans-serif" font-size="13" font-weight="600" fill="#4ade80">Done.</text>
|
|
95
|
+
<text x="90" y="389" font-family="system-ui, sans-serif" font-size="11" fill="#6e7681">Claude now has full semantic memory of your codebase — persistent across every session.</text>
|
|
96
|
+
|
|
97
|
+
<!-- OS logos (right side of done bar) -->
|
|
98
|
+
<!-- Ubuntu circle pattern -->
|
|
99
|
+
<circle cx="600" cy="376" r="10" fill="none" stroke="#E95420" stroke-width="1.5"/>
|
|
100
|
+
<circle cx="600" cy="376" r="3" fill="#E95420"/>
|
|
101
|
+
<circle cx="600" cy="366" r="2" fill="#E95420"/>
|
|
102
|
+
<circle cx="591" cy="381" r="2" fill="#E95420"/>
|
|
103
|
+
<circle cx="609" cy="381" r="2" fill="#E95420"/>
|
|
104
|
+
|
|
105
|
+
<!-- Debian swirl (simplified) -->
|
|
106
|
+
<circle cx="630" cy="376" r="10" fill="none" stroke="#A80030" stroke-width="1.5"/>
|
|
107
|
+
<path d="M630 368 C636 368 638 374 634 379 C630 384 624 382 622 378" fill="none" stroke="#D70751" stroke-width="2" stroke-linecap="round"/>
|
|
108
|
+
<circle cx="630" cy="376" r="2" fill="#D70751"/>
|
|
109
|
+
|
|
110
|
+
<!-- Mint LM badge -->
|
|
111
|
+
<rect x="647" y="366" width="20" height="20" rx="5" fill="none" stroke="#87CF3E" stroke-width="1.5"/>
|
|
112
|
+
<text x="657" y="380" text-anchor="middle" font-family="system-ui, sans-serif" font-size="8" font-weight="700" fill="#87CF3E">LM</text>
|
|
113
|
+
|
|
114
|
+
<!-- Kali dragon shape (simplified) -->
|
|
115
|
+
<path d="M688 366 L696 374 L694 380 L700 388 L688 382 L676 388 L682 380 L680 374 Z" fill="none" stroke="#557C94" stroke-width="1.5" stroke-linejoin="round"/>
|
|
116
|
+
|
|
117
|
+
<text x="710" y="374" font-family="system-ui, sans-serif" font-size="9" fill="#484f58">+ more</text>
|
|
82
118
|
</svg>
|