get-claudia 1.54.0 → 1.54.2
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/index.js +198 -95
- package/package.json +1 -1
- package/template-v2/.mcp.json.example +14 -10
- package/template-v2/CLAUDE.md +25 -19
package/bin/index.js
CHANGED
|
@@ -149,6 +149,7 @@ class ProgressRenderer {
|
|
|
149
149
|
case 'error': return `${colors.red}!${colors.reset}`;
|
|
150
150
|
case 'active': return `${colors.cyan}${this.spinnerChars[this.spinnerFrame]}${colors.reset}`;
|
|
151
151
|
case 'skipped': return `${colors.dim}○${colors.reset}`;
|
|
152
|
+
case 'cascade': return `${colors.dim}·${colors.reset}`;
|
|
152
153
|
default: return `${colors.dim}░${colors.reset}`;
|
|
153
154
|
}
|
|
154
155
|
}
|
|
@@ -156,7 +157,7 @@ class ProgressRenderer {
|
|
|
156
157
|
getCompletedCount() {
|
|
157
158
|
return STEPS.filter(s => {
|
|
158
159
|
const st = this.states[s.id].state;
|
|
159
|
-
return st === 'done' || st === 'warn' || st === 'skipped';
|
|
160
|
+
return st === 'done' || st === 'warn' || st === 'skipped' || st === 'cascade';
|
|
160
161
|
}).length;
|
|
161
162
|
}
|
|
162
163
|
|
|
@@ -175,7 +176,7 @@ class ProgressRenderer {
|
|
|
175
176
|
for (const step of STEPS) {
|
|
176
177
|
const { state, detail } = this.states[step.id];
|
|
177
178
|
const icon = this.getIcon(state);
|
|
178
|
-
const label = state === 'skipped'
|
|
179
|
+
const label = (state === 'skipped' || state === 'cascade')
|
|
179
180
|
? `${colors.dim}${step.label}${colors.reset}`
|
|
180
181
|
: step.label;
|
|
181
182
|
const detailStr = detail
|
|
@@ -183,7 +184,7 @@ class ProgressRenderer {
|
|
|
183
184
|
: '';
|
|
184
185
|
// Pad label to 20 chars for alignment
|
|
185
186
|
const paddedLabel = step.label.padEnd(20);
|
|
186
|
-
lines.push(` ${icon} ${state === 'skipped' ? colors.dim + paddedLabel + colors.reset : paddedLabel}${detailStr}`);
|
|
187
|
+
lines.push(` ${icon} ${(state === 'skipped' || state === 'cascade') ? colors.dim + paddedLabel + colors.reset : paddedLabel}${detailStr}`);
|
|
187
188
|
}
|
|
188
189
|
|
|
189
190
|
lines.push('');
|
|
@@ -209,10 +210,11 @@ class ProgressRenderer {
|
|
|
209
210
|
if (supportsInPlace) return; // handled by render()
|
|
210
211
|
const step = STEPS.find(s => s.id === stepId);
|
|
211
212
|
if (!step) return;
|
|
212
|
-
if (state === 'done' || state === 'warn' || state === 'error' || state === 'skipped') {
|
|
213
|
+
if (state === 'done' || state === 'warn' || state === 'error' || state === 'skipped' || state === 'cascade') {
|
|
213
214
|
const icon = state === 'done' ? '✓' :
|
|
214
215
|
state === 'warn' ? '○' :
|
|
215
|
-
state === 'error' ? '!' :
|
|
216
|
+
state === 'error' ? '!' :
|
|
217
|
+
state === 'cascade' ? '·' : '-';
|
|
216
218
|
console.log(` ${icon} ${step.label}${detail ? ' ' + detail : ''}`);
|
|
217
219
|
}
|
|
218
220
|
}
|
|
@@ -277,6 +279,77 @@ async function installOllama() {
|
|
|
277
279
|
});
|
|
278
280
|
}
|
|
279
281
|
|
|
282
|
+
// ─── Python helpers ─────────────────────────────────────────────────────
|
|
283
|
+
|
|
284
|
+
/** Check if Python 3.10+ is available. Returns the command name or null. */
|
|
285
|
+
async function isPythonInstalled() {
|
|
286
|
+
for (const cmd of ['python3', 'python']) {
|
|
287
|
+
const ver = await new Promise((resolve) => {
|
|
288
|
+
const proc = spawn(cmd, ['--version'], { stdio: 'pipe', timeout: 5000 });
|
|
289
|
+
let stdout = '';
|
|
290
|
+
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
291
|
+
proc.on('close', () => resolve(stdout.trim()));
|
|
292
|
+
proc.on('error', () => resolve(''));
|
|
293
|
+
});
|
|
294
|
+
const match = ver.match(/Python (\d+)\.(\d+)/);
|
|
295
|
+
if (match && (parseInt(match[1]) > 3 || (parseInt(match[1]) === 3 && parseInt(match[2]) >= 10))) {
|
|
296
|
+
return cmd;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Install Python automatically.
|
|
304
|
+
* macOS: uses brew if available
|
|
305
|
+
* Linux: tries apt, dnf, pacman
|
|
306
|
+
* Windows: skip (requires manual install from python.org)
|
|
307
|
+
*/
|
|
308
|
+
async function installPython() {
|
|
309
|
+
if (isWindows) return false;
|
|
310
|
+
|
|
311
|
+
if (process.platform === 'darwin') {
|
|
312
|
+
const hasBrew = await new Promise((resolve) => {
|
|
313
|
+
const proc = spawn('which', ['brew'], { stdio: 'pipe', timeout: 5000 });
|
|
314
|
+
proc.on('close', (code) => resolve(code === 0));
|
|
315
|
+
proc.on('error', () => resolve(false));
|
|
316
|
+
});
|
|
317
|
+
if (hasBrew) {
|
|
318
|
+
return new Promise((resolve) => {
|
|
319
|
+
const proc = spawn('brew', ['install', 'python@3.12'], {
|
|
320
|
+
stdio: 'pipe', timeout: 300000
|
|
321
|
+
});
|
|
322
|
+
proc.on('close', (code) => resolve(code === 0));
|
|
323
|
+
proc.on('error', () => resolve(false));
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Linux: try apt, dnf, pacman
|
|
330
|
+
for (const [pm, args] of [
|
|
331
|
+
['apt-get', ['install', '-y', 'python3', 'python3-venv']],
|
|
332
|
+
['dnf', ['install', '-y', 'python3']],
|
|
333
|
+
['pacman', ['-S', '--noconfirm', 'python']],
|
|
334
|
+
]) {
|
|
335
|
+
const hasPm = await new Promise((resolve) => {
|
|
336
|
+
const proc = spawn('which', [pm], { stdio: 'pipe', timeout: 5000 });
|
|
337
|
+
proc.on('close', (code) => resolve(code === 0));
|
|
338
|
+
proc.on('error', () => resolve(false));
|
|
339
|
+
});
|
|
340
|
+
if (hasPm) {
|
|
341
|
+
return new Promise((resolve) => {
|
|
342
|
+
const proc = spawn('sudo', [pm, ...args], {
|
|
343
|
+
stdio: 'pipe', timeout: 300000
|
|
344
|
+
});
|
|
345
|
+
proc.on('close', (code) => resolve(code === 0));
|
|
346
|
+
proc.on('error', () => resolve(false));
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
|
|
280
353
|
/**
|
|
281
354
|
* Start the Ollama service and wait for it to respond.
|
|
282
355
|
* On macOS: open the Ollama app or run `ollama serve` in background.
|
|
@@ -548,6 +621,7 @@ async function main() {
|
|
|
548
621
|
|
|
549
622
|
// Run CLI-based setup (no Python daemon needed)
|
|
550
623
|
let memoryOk = false;
|
|
624
|
+
let rootCause = null;
|
|
551
625
|
|
|
552
626
|
try {
|
|
553
627
|
// Step 1: Environment -- check Node.js version, detect/install/start Ollama
|
|
@@ -689,30 +763,19 @@ async function main() {
|
|
|
689
763
|
? join(daemonVenvDir, 'Scripts', 'pip')
|
|
690
764
|
: join(daemonVenvDir, 'bin', 'pip');
|
|
691
765
|
|
|
692
|
-
// Phase 1: Find Python 3.10+
|
|
693
|
-
let pythonCmd =
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
proc.on('close', () => resolve(stdout.trim()));
|
|
700
|
-
proc.on('error', () => resolve(''));
|
|
701
|
-
});
|
|
702
|
-
const match = ver.match(/Python (\d+)\.(\d+)/);
|
|
703
|
-
if (match) {
|
|
704
|
-
const major = parseInt(match[1], 10);
|
|
705
|
-
const minor = parseInt(match[2], 10);
|
|
706
|
-
if (major > 3 || (major === 3 && minor >= 10)) {
|
|
707
|
-
pythonCmd = cmd;
|
|
708
|
-
break;
|
|
709
|
-
}
|
|
710
|
-
}
|
|
766
|
+
// Phase 1: Find Python 3.10+ (auto-install if missing)
|
|
767
|
+
let pythonCmd = await isPythonInstalled();
|
|
768
|
+
|
|
769
|
+
if (!pythonCmd) {
|
|
770
|
+
renderer.update('daemon', 'active', 'installing Python...');
|
|
771
|
+
const installed = await installPython();
|
|
772
|
+
if (installed) pythonCmd = await isPythonInstalled();
|
|
711
773
|
}
|
|
712
774
|
|
|
713
775
|
if (!pythonCmd) {
|
|
714
776
|
renderer.update('daemon', 'warn', 'Python 3.10+ not found');
|
|
715
777
|
if (!supportsInPlace) renderer.appendLine('daemon', 'warn', 'Python 3.10+ not found');
|
|
778
|
+
rootCause = { step: 'daemon', issue: 'python' };
|
|
716
779
|
} else {
|
|
717
780
|
// Phase 2: Create venv if it doesn't exist
|
|
718
781
|
if (!existsSync(venvPython)) {
|
|
@@ -726,6 +789,7 @@ async function main() {
|
|
|
726
789
|
if (!venvCreated) {
|
|
727
790
|
renderer.update('daemon', 'warn', 'venv creation failed');
|
|
728
791
|
if (!supportsInPlace) renderer.appendLine('daemon', 'warn', 'venv creation failed');
|
|
792
|
+
rootCause = rootCause || { step: 'daemon', issue: 'venv' };
|
|
729
793
|
}
|
|
730
794
|
}
|
|
731
795
|
|
|
@@ -746,6 +810,7 @@ async function main() {
|
|
|
746
810
|
} else {
|
|
747
811
|
renderer.update('daemon', 'warn', 'pip install failed');
|
|
748
812
|
if (!supportsInPlace) renderer.appendLine('daemon', 'warn', 'pip install failed');
|
|
813
|
+
rootCause = rootCause || { step: 'daemon', issue: 'pip' };
|
|
749
814
|
}
|
|
750
815
|
}
|
|
751
816
|
|
|
@@ -763,6 +828,7 @@ async function main() {
|
|
|
763
828
|
daemonOk = false;
|
|
764
829
|
renderer.update('daemon', 'warn', 'daemon import failed');
|
|
765
830
|
if (!supportsInPlace) renderer.appendLine('daemon', 'warn', 'import failed');
|
|
831
|
+
rootCause = rootCause || { step: 'daemon', issue: 'import' };
|
|
766
832
|
}
|
|
767
833
|
}
|
|
768
834
|
|
|
@@ -820,59 +886,71 @@ async function main() {
|
|
|
820
886
|
}
|
|
821
887
|
|
|
822
888
|
// MCP Config step: verify .mcp.json is correct and check stdio server count
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
renderer.
|
|
827
|
-
if (!supportsInPlace) renderer.appendLine('mcp', 'done', 'claudia-memory configured');
|
|
828
|
-
} else if (mcpCheckResult.hasDaemon && mcpCheckResult.stdioCount > 1) {
|
|
829
|
-
renderer.update('mcp', 'warn', `${mcpCheckResult.stdioCount} stdio servers (only 1 reliable)`);
|
|
830
|
-
if (!supportsInPlace) renderer.appendLine('mcp', 'warn', `${mcpCheckResult.stdioCount} stdio servers`);
|
|
889
|
+
if (rootCause?.step === 'daemon') {
|
|
890
|
+
const cascadeMsg = rootCause.issue === 'python' ? 'needs Python first' : 'needs daemon first';
|
|
891
|
+
renderer.update('mcp', 'cascade', cascadeMsg);
|
|
892
|
+
if (!supportsInPlace) renderer.appendLine('mcp', 'cascade', cascadeMsg);
|
|
831
893
|
} else {
|
|
832
|
-
renderer.update('mcp', '
|
|
833
|
-
|
|
894
|
+
renderer.update('mcp', 'active', 'checking .mcp.json...');
|
|
895
|
+
const mcpCheckResult = checkMcpConfig(targetPath);
|
|
896
|
+
if (mcpCheckResult.hasDaemon && mcpCheckResult.stdioCount <= 1) {
|
|
897
|
+
renderer.update('mcp', 'done', `claudia-memory configured${mcpCheckResult.stdioCount === 1 ? '' : ' (no stdio servers?)'}`);
|
|
898
|
+
if (!supportsInPlace) renderer.appendLine('mcp', 'done', 'claudia-memory configured');
|
|
899
|
+
} else if (mcpCheckResult.hasDaemon && mcpCheckResult.stdioCount > 1) {
|
|
900
|
+
renderer.update('mcp', 'warn', `${mcpCheckResult.stdioCount} stdio servers (only 1 reliable)`);
|
|
901
|
+
if (!supportsInPlace) renderer.appendLine('mcp', 'warn', `${mcpCheckResult.stdioCount} stdio servers`);
|
|
902
|
+
} else {
|
|
903
|
+
renderer.update('mcp', 'warn', 'claudia-memory not in .mcp.json');
|
|
904
|
+
if (!supportsInPlace) renderer.appendLine('mcp', 'warn', 'daemon not configured');
|
|
905
|
+
}
|
|
834
906
|
}
|
|
835
907
|
|
|
836
908
|
// Vault step: handled below
|
|
837
909
|
|
|
838
910
|
// Health Check: check daemon health endpoint or verify daemon can import
|
|
839
|
-
|
|
840
|
-
|
|
911
|
+
if (rootCause?.step === 'daemon') {
|
|
912
|
+
const cascadeMsg = rootCause.issue === 'python' ? 'needs Python first' : 'needs daemon first';
|
|
913
|
+
renderer.update('health', 'cascade', cascadeMsg);
|
|
914
|
+
if (!supportsInPlace) renderer.appendLine('health', 'cascade', cascadeMsg);
|
|
915
|
+
} else {
|
|
916
|
+
renderer.update('health', 'active', 'verifying...');
|
|
917
|
+
let healthOk = false;
|
|
841
918
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
919
|
+
// Try the standalone daemon's health endpoint first (port 3848)
|
|
920
|
+
try {
|
|
921
|
+
const healthResp = await fetch('http://localhost:3848/status', {
|
|
922
|
+
signal: AbortSignal.timeout(3000),
|
|
923
|
+
});
|
|
924
|
+
if (healthResp.ok) {
|
|
925
|
+
const healthData = await healthResp.json();
|
|
926
|
+
healthOk = healthData.status === 'healthy' || healthData.status === 'degraded';
|
|
927
|
+
}
|
|
928
|
+
} catch {
|
|
929
|
+
// Standalone daemon not running -- that's OK, check daemon importability instead
|
|
850
930
|
}
|
|
851
|
-
} catch {
|
|
852
|
-
// Standalone daemon not running -- that's OK, check daemon importability instead
|
|
853
|
-
}
|
|
854
931
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
932
|
+
// Fallback: verify the daemon can at least be imported
|
|
933
|
+
if (!healthOk && daemonOk && existsSync(venvPython)) {
|
|
934
|
+
healthOk = await new Promise((resolve) => {
|
|
935
|
+
const proc = spawn(venvPython, ['-c', 'from claudia_memory.database import Database; print("ok")'], {
|
|
936
|
+
stdio: 'pipe',
|
|
937
|
+
timeout: 10000
|
|
938
|
+
});
|
|
939
|
+
proc.on('close', (code) => resolve(code === 0));
|
|
940
|
+
proc.on('error', () => resolve(false));
|
|
861
941
|
});
|
|
862
|
-
|
|
863
|
-
proc.on('error', () => resolve(false));
|
|
864
|
-
});
|
|
865
|
-
}
|
|
942
|
+
}
|
|
866
943
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
944
|
+
if (healthOk) {
|
|
945
|
+
renderer.update('health', 'done', 'system healthy');
|
|
946
|
+
if (!supportsInPlace) renderer.appendLine('health', 'done', 'system healthy');
|
|
947
|
+
} else if (daemonOk) {
|
|
948
|
+
renderer.update('health', 'warn', 'daemon installed, standalone not running');
|
|
949
|
+
if (!supportsInPlace) renderer.appendLine('health', 'warn', 'standalone not running');
|
|
950
|
+
} else {
|
|
951
|
+
renderer.update('health', 'warn', 'check CLAUDE.md for troubleshooting');
|
|
952
|
+
if (!supportsInPlace) renderer.appendLine('health', 'warn', 'check manually');
|
|
953
|
+
}
|
|
876
954
|
}
|
|
877
955
|
|
|
878
956
|
memoryOk = daemonOk || hasExistingDb;
|
|
@@ -891,7 +969,7 @@ async function main() {
|
|
|
891
969
|
// Vault step, then completion
|
|
892
970
|
runVaultStep(renderer, () => {
|
|
893
971
|
renderer.render();
|
|
894
|
-
showCompletion(targetDir, isCurrentDir, memoryOk);
|
|
972
|
+
showCompletion(targetDir, isCurrentDir, memoryOk, rootCause);
|
|
895
973
|
});
|
|
896
974
|
|
|
897
975
|
// ── Vault step ──
|
|
@@ -951,8 +1029,8 @@ async function main() {
|
|
|
951
1029
|
renderer.update('vault', 'done', 'configured');
|
|
952
1030
|
if (!supportsInPlace) renderer.appendLine('vault', 'done', 'configured');
|
|
953
1031
|
} else {
|
|
954
|
-
renderer.update('vault', '
|
|
955
|
-
if (!supportsInPlace) renderer.appendLine('vault', '
|
|
1032
|
+
renderer.update('vault', 'skipped', 'Obsidian not installed (optional)');
|
|
1033
|
+
if (!supportsInPlace) renderer.appendLine('vault', 'skipped', 'Obsidian not installed (optional)');
|
|
956
1034
|
}
|
|
957
1035
|
|
|
958
1036
|
callback(obsidianDetected);
|
|
@@ -960,42 +1038,67 @@ async function main() {
|
|
|
960
1038
|
|
|
961
1039
|
// ── Completion block ──
|
|
962
1040
|
|
|
963
|
-
function showCompletion(targetDir, isCurrentDir, memoryInstalled) {
|
|
964
|
-
const
|
|
1041
|
+
function showCompletion(targetDir, isCurrentDir, memoryInstalled, failureCause) {
|
|
1042
|
+
const rerunCmd = isCurrentDir ? 'npx get-claudia .' : `cd ${targetDir} && npx get-claudia .`;
|
|
1043
|
+
const launchCmd = isCurrentDir ? 'claude' : `cd ${targetDir} && claude`;
|
|
965
1044
|
|
|
966
1045
|
console.log('');
|
|
967
1046
|
console.log(`${colors.dim}${'━'.repeat(46)}${colors.reset}`);
|
|
968
1047
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
{
|
|
973
|
-
|
|
974
|
-
{
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
for (const c of components) {
|
|
979
|
-
if (c.ok && !c.warn) {
|
|
980
|
-
console.log(` ${colors.green}✓${colors.reset} ${c.name}`);
|
|
981
|
-
} else if (c.warn) {
|
|
982
|
-
console.log(` ${colors.yellow}⚠${colors.reset} ${c.name} ${colors.dim}${c.warn}${colors.reset}`);
|
|
983
|
-
} else {
|
|
984
|
-
console.log(` ${colors.yellow}○${colors.reset} ${c.name} ${colors.dim}(not ready)${colors.reset}`);
|
|
985
|
-
}
|
|
1048
|
+
if (memoryInstalled && !failureCause) {
|
|
1049
|
+
// Everything worked
|
|
1050
|
+
console.log('');
|
|
1051
|
+
console.log(` ${colors.green}Ready to go!${colors.reset}`);
|
|
1052
|
+
console.log('');
|
|
1053
|
+
console.log(` ${colors.bold}Next:${colors.reset} Open Claude Code:`);
|
|
1054
|
+
console.log(` ${colors.cyan}${launchCmd}${colors.reset}`);
|
|
1055
|
+
console.log('');
|
|
1056
|
+
return;
|
|
986
1057
|
}
|
|
987
1058
|
|
|
1059
|
+
// Something needs fixing
|
|
1060
|
+
console.log('');
|
|
1061
|
+
console.log(` ${colors.boldYellow}Almost there!${colors.reset} One thing to fix:`);
|
|
988
1062
|
console.log('');
|
|
989
|
-
console.log(` ${colors.bold}Next:${colors.reset} Open Claude Code:`);
|
|
990
|
-
console.log(` ${colors.cyan}${cdCmd}claude${colors.reset}`);
|
|
991
1063
|
|
|
992
|
-
if (
|
|
993
|
-
console.log(
|
|
994
|
-
|
|
995
|
-
|
|
1064
|
+
if (failureCause?.issue === 'python') {
|
|
1065
|
+
console.log(` ${colors.bold}→ Install Python 3.10+:${colors.reset}`);
|
|
1066
|
+
if (process.platform === 'darwin') {
|
|
1067
|
+
const hasBrew = existsSync('/opt/homebrew/bin/brew') || existsSync('/usr/local/bin/brew');
|
|
1068
|
+
if (hasBrew) {
|
|
1069
|
+
console.log(` ${colors.cyan}brew install python@3.12${colors.reset}`);
|
|
1070
|
+
} else {
|
|
1071
|
+
console.log(` ${colors.dim}Install Homebrew first:${colors.reset}`);
|
|
1072
|
+
console.log(` ${colors.cyan}/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"${colors.reset}`);
|
|
1073
|
+
console.log('');
|
|
1074
|
+
console.log(` ${colors.dim}Then:${colors.reset}`);
|
|
1075
|
+
console.log(` ${colors.cyan}brew install python@3.12${colors.reset}`);
|
|
1076
|
+
}
|
|
1077
|
+
} else if (isWindows) {
|
|
1078
|
+
console.log(` ${colors.cyan}https://www.python.org/downloads/${colors.reset}`);
|
|
1079
|
+
} else {
|
|
1080
|
+
console.log(` ${colors.cyan}sudo apt install python3 python3-venv${colors.reset} ${colors.dim}(Debian/Ubuntu)${colors.reset}`);
|
|
1081
|
+
console.log(` ${colors.cyan}sudo dnf install python3${colors.reset} ${colors.dim}(Fedora/RHEL)${colors.reset}`);
|
|
1082
|
+
}
|
|
1083
|
+
} else if (failureCause?.issue === 'venv') {
|
|
1084
|
+
console.log(` ${colors.bold}→ Python venv creation failed.${colors.reset}`);
|
|
1085
|
+
console.log(` ${colors.dim}Try: python3 -m ensurepip && python3 -m venv ~/.claudia/daemon/venv${colors.reset}`);
|
|
1086
|
+
} else if (failureCause?.issue === 'pip') {
|
|
1087
|
+
console.log(` ${colors.bold}→ Daemon package install failed.${colors.reset}`);
|
|
1088
|
+
console.log(` ${colors.dim}Check your internet connection and try again.${colors.reset}`);
|
|
1089
|
+
} else if (failureCause?.issue === 'import') {
|
|
1090
|
+
console.log(` ${colors.bold}→ Daemon installed but won't load.${colors.reset}`);
|
|
1091
|
+
console.log(` ${colors.dim}Try: rm -rf ~/.claudia/daemon/venv && re-run setup.${colors.reset}`);
|
|
1092
|
+
} else {
|
|
1093
|
+
console.log(` ${colors.bold}→ Memory daemon not ready.${colors.reset}`);
|
|
996
1094
|
}
|
|
997
1095
|
|
|
998
1096
|
console.log('');
|
|
1097
|
+
console.log(` ${colors.bold}Then finish setup:${colors.reset}`);
|
|
1098
|
+
console.log(` ${colors.cyan}${rerunCmd}${colors.reset}`);
|
|
1099
|
+
console.log('');
|
|
1100
|
+
console.log(` ${colors.dim}Stuck? Copy this message into any AI chat and ask for help.${colors.reset}`);
|
|
1101
|
+
console.log('');
|
|
999
1102
|
}
|
|
1000
1103
|
}
|
|
1001
1104
|
|
|
@@ -1021,7 +1124,7 @@ function restoreMcpServers(targetPath) {
|
|
|
1021
1124
|
|
|
1022
1125
|
// Path 1: Restore from _disabled_mcpServers stash (older migration format)
|
|
1023
1126
|
if (config._disabled_mcpServers) {
|
|
1024
|
-
const toRestore = ['claudia-memory', 'claudia_memory'
|
|
1127
|
+
const toRestore = ['claudia-memory', 'claudia_memory'];
|
|
1025
1128
|
for (const key of toRestore) {
|
|
1026
1129
|
if (config._disabled_mcpServers[key] && !config.mcpServers[key]) {
|
|
1027
1130
|
const serverConfig = { ...config._disabled_mcpServers[key] };
|
package/package.json
CHANGED
|
@@ -6,15 +6,19 @@
|
|
|
6
6
|
"_description": "Claudia memory system with vector search",
|
|
7
7
|
"_setup": "Auto-configured by the installer (npx get-claudia). The installer creates a Python venv at ~/.claudia/daemon/venv/ and sets the correct command path in .mcp.json automatically."
|
|
8
8
|
},
|
|
9
|
-
"
|
|
10
|
-
"command": "
|
|
11
|
-
"args": ["-
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
"
|
|
9
|
+
"google_workspace": {
|
|
10
|
+
"command": "uvx",
|
|
11
|
+
"args": ["workspace-mcp", "--tool-tier", "core"],
|
|
12
|
+
"env": {
|
|
13
|
+
"GOOGLE_OAUTH_CLIENT_ID": "",
|
|
14
|
+
"GOOGLE_OAUTH_CLIENT_SECRET": ""
|
|
15
|
+
},
|
|
16
|
+
"_setup": "Google Workspace MCP: Gmail, Calendar, Drive, Docs, Sheets, Tasks, and more in one server. Requires Google Cloud OAuth credentials. See Google Integration Setup in CLAUDE.md. Tool tiers: core (43 tools), extended (83), complete (111). Start with core.",
|
|
17
|
+
"_tiers": {
|
|
18
|
+
"core": "Gmail, Calendar, Drive, Contacts (43 tools, recommended default)",
|
|
19
|
+
"extended": "Adds Docs, Sheets, Tasks, Chat (83 tools)",
|
|
20
|
+
"complete": "All services including Slides, Forms, Apps Script (111 tools)"
|
|
21
|
+
}
|
|
18
22
|
},
|
|
19
23
|
"rube": {
|
|
20
24
|
"type": "http",
|
|
@@ -29,7 +33,7 @@
|
|
|
29
33
|
|
|
30
34
|
"_notes": {
|
|
31
35
|
"memory": "Claudia's memory is powered by the claudia-memory daemon (Python MCP server). It provides ~33 tools for semantic search, pattern detection, and relationship tracking. The installer (npx get-claudia) automatically sets up the daemon in a Python venv at ~/.claudia/daemon/venv/ and configures .mcp.json.",
|
|
32
|
-
"
|
|
36
|
+
"google_workspace": "Google Workspace uses the workspace-mcp server (taylorwilsdon/google_workspace_mcp). One server covers Gmail, Calendar, Drive, Docs, Sheets, Tasks, and more. Tool tiers control context usage. See Google Integration Setup in CLAUDE.md.",
|
|
33
37
|
"rube": "Rube (by Composio) connects 500+ apps through one HTTP MCP connection. Each user creates their own free Rube account at rube.app. See the Rube section in CLAUDE.md for setup and troubleshooting.",
|
|
34
38
|
"security": "Each user authenticates with their own accounts and credentials. OAuth tokens are stored locally on your machine, never shared.",
|
|
35
39
|
"not_included": {
|
package/template-v2/CLAUDE.md
CHANGED
|
@@ -319,12 +319,10 @@ I adapt to whatever tools are available. When you ask me to do something that ne
|
|
|
319
319
|
|
|
320
320
|
**Obsidian vault:** My memory syncs to an Obsidian vault at `~/.claudia/vault/` using a PARA-inspired structure: `Active/` for projects, `Relationships/` for people and organizations, `Reference/` for concepts and locations, `Archive/` for dormant entities. Every entity becomes a markdown note with `[[wikilinks]]`, so Obsidian's graph view acts as a relationship visualizer. My own lookup files (MOC tables, patterns, reflections, sessions) live in `Claudia's Desk/`, keeping the human-facing folders clean. The vault syncs on-demand via `claudia vault sync`. SQLite remains the source of truth; the vault is a read projection.
|
|
321
321
|
|
|
322
|
-
**
|
|
322
|
+
**Google Workspace (MCP):** Google Workspace is provided by the workspace-mcp server ([taylorwilsdon/google_workspace_mcp](https://github.com/taylorwilsdon/google_workspace_mcp)). One server covers Gmail, Calendar, Drive, Docs, Sheets, Tasks, Contacts, and more. Tool tiers control how many tools are exposed: `--tool-tier core` (43 tools, default), `--tool-tier extended` (83 tools), or `--tool-tier complete` (111 tools). When connected, I have access to Gmail, Calendar, Drive, Docs, Sheets, Tasks, and more. I use these tools naturally when you ask me to check email, send messages, look at your calendar, search Drive, or work with documents.
|
|
323
323
|
|
|
324
324
|
If the MCP tools aren't responding or you see authentication errors, the user needs to set up their Google Cloud credentials. See the **Google Integration Setup** section below.
|
|
325
325
|
|
|
326
|
-
**Alternative:** CLI commands (`claudia gmail search`, `claudia calendar list`, etc.) are also available as a fallback. These require separate authentication via `claudia google login`.
|
|
327
|
-
|
|
328
326
|
**Rube (500+ Apps):** Rube (by Composio) is an optional MCP aggregator that connects Claudia to hundreds of apps through a single server. Each user creates their own free Rube account, connects the apps they want via one-click OAuth, and Claudia gets access to all of them through one MCP connection.
|
|
329
327
|
|
|
330
328
|
If a user asks about connecting apps, integrations, or any of the services listed below, proactively suggest Rube as the easiest path. Walk them through setup step by step.
|
|
@@ -348,7 +346,7 @@ If a user asks about connecting apps, integrations, or any of the services liste
|
|
|
348
346
|
| **Calendar** | Google Calendar, Outlook Calendar, Calendly |
|
|
349
347
|
| **And 500+ more** | Browse the full list at [rube.app](https://rube.app) |
|
|
350
348
|
|
|
351
|
-
**External integrations** (
|
|
349
|
+
**External integrations** (Google Workspace, Rube, Brave Search) are optional add-ons that extend what I can see and do. I work fully without them. The core value is relationships and context.
|
|
352
350
|
|
|
353
351
|
### Rube Setup (Guide Users Through This)
|
|
354
352
|
|
|
@@ -403,33 +401,41 @@ The MCP tools from Rube will have names like `SLACK_SEND_MESSAGE`, `NOTION_CREAT
|
|
|
403
401
|
| Rate limited | Rube has usage limits on the free tier. The user may need to upgrade at rube.app/pricing. |
|
|
404
402
|
| Want to disconnect an app | Go to Rube dashboard and disconnect the app there. No Claudia config changes needed. |
|
|
405
403
|
|
|
406
|
-
**Rube vs.
|
|
404
|
+
**Rube vs. workspace-mcp:** Rube works alongside (not instead of) the workspace-mcp server. Workspace-mcp gives a direct connection with no intermediary but requires Google Cloud setup. Rube gives one setup for everything but routes data through Composio servers. Both can coexist. If a user has both workspace-mcp and Rube's Google apps connected, prefer the direct workspace-mcp tools.
|
|
407
405
|
|
|
408
406
|
### Google Integration Setup
|
|
409
407
|
|
|
410
|
-
|
|
408
|
+
The workspace-mcp server requires your own Google Cloud credentials. Each user sets this up once:
|
|
411
409
|
|
|
412
410
|
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
413
411
|
2. Create a new project (or select an existing one)
|
|
414
|
-
3. Enable the
|
|
412
|
+
3. Enable the APIs you need:
|
|
415
413
|
- Go to APIs & Services > Library
|
|
416
|
-
- Search for
|
|
417
|
-
-
|
|
414
|
+
- Search for and enable: **Gmail API**, **Google Calendar API**, **Google Drive API**, **Google Docs API**, **Google Sheets API**, **Google Tasks API**, **People API** (Contacts)
|
|
415
|
+
- You can enable more later as needed
|
|
418
416
|
4. Create OAuth credentials:
|
|
419
417
|
- Go to APIs & Services > Credentials
|
|
420
418
|
- Click "Create Credentials" > "OAuth client ID"
|
|
421
419
|
- If prompted, configure the consent screen first (External, add your email as test user)
|
|
422
420
|
- Application type: **Desktop app**
|
|
423
|
-
- Click Create
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
421
|
+
- Click Create
|
|
422
|
+
- Copy the **Client ID** and **Client Secret**
|
|
423
|
+
5. Add credentials to `.mcp.json`:
|
|
424
|
+
- Open `.mcp.json` in the project root
|
|
425
|
+
- Find the `google_workspace` server entry
|
|
426
|
+
- Set the `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` environment variables:
|
|
427
|
+
```json
|
|
428
|
+
"env": {
|
|
429
|
+
"GOOGLE_CLIENT_ID": "your-client-id.apps.googleusercontent.com",
|
|
430
|
+
"GOOGLE_CLIENT_SECRET": "your-client-secret"
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
6. Choose your tool tier:
|
|
434
|
+
- The default is `--tool-tier core` (43 tools), which covers most needs
|
|
435
|
+
- For more capabilities, change to `--tool-tier extended` (83 tools) or `--tool-tier complete` (111 tools) in the server args
|
|
436
|
+
7. Restart Claude Code. On first run, the server opens your browser for Google sign-in. Tokens are stored locally for future sessions.
|
|
437
|
+
|
|
438
|
+
**Migrating from the old setup:** If you previously used separate Gmail and Calendar MCP servers, the same GCP project works. Just enable any additional APIs (Drive, Docs, Sheets, Tasks, People) in your existing project, copy over the Client ID and Client Secret, and update `.mcp.json` to use the `google_workspace` server entry instead of the old individual entries.
|
|
433
439
|
|
|
434
440
|
---
|
|
435
441
|
|