@sashabogi/foundation 2.0.1 → 2.1.0
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/README.md +3 -158
- package/dist/cli.js +127 -253
- package/dist/cli.js.map +1 -1
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +2 -2
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/types.d.ts +1 -1
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/zai.d.ts +17 -8
- package/dist/providers/zai.d.ts.map +1 -1
- package/dist/providers/zai.js +34 -13
- package/dist/providers/zai.js.map +1 -1
- package/dist/services/config.service.d.ts +1 -5
- package/dist/services/config.service.d.ts.map +1 -1
- package/dist/services/config.service.js +0 -20
- package/dist/services/config.service.js.map +1 -1
- package/dist/services/git.service.d.ts +0 -4
- package/dist/services/git.service.d.ts.map +1 -1
- package/dist/services/git.service.js +0 -30
- package/dist/services/git.service.js.map +1 -1
- package/dist/services/storage.service.d.ts +1 -24
- package/dist/services/storage.service.d.ts.map +1 -1
- package/dist/services/storage.service.js +0 -108
- package/dist/services/storage.service.js.map +1 -1
- package/dist/tools/gaia/index.d.ts +5 -8
- package/dist/tools/gaia/index.d.ts.map +1 -1
- package/dist/tools/gaia/index.js +16 -115
- package/dist/tools/gaia/index.js.map +1 -1
- package/dist/tools/gaia/storage.d.ts +13 -0
- package/dist/tools/gaia/storage.d.ts.map +1 -1
- package/dist/tools/gaia/storage.js +285 -1
- package/dist/tools/gaia/storage.js.map +1 -1
- package/dist/tools/seldon/index.d.ts +1 -12
- package/dist/tools/seldon/index.d.ts.map +1 -1
- package/dist/tools/seldon/index.js +1 -183
- package/dist/tools/seldon/index.js.map +1 -1
- package/dist/types/index.d.ts +0 -78
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/packages/ui/dist/assets/index-DVS_pYGH.css +1 -0
- package/packages/ui/dist/assets/index-WNO_oIqP.js +312 -0
- package/packages/ui/dist/index.html +2 -2
- package/packages/ui/dist/assets/index-oiJTDMJ1.css +0 -1
- package/packages/ui/dist/assets/index-oivszLTx.js +0 -352
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import { Command } from 'commander';
|
|
15
15
|
import chalk from 'chalk';
|
|
16
16
|
import ora from 'ora';
|
|
17
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync, createReadStream, readdirSync
|
|
17
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, createReadStream, readdirSync } from 'fs';
|
|
18
18
|
import { execSync } from 'child_process';
|
|
19
19
|
import { homedir } from 'os';
|
|
20
20
|
import { join, dirname } from 'path';
|
|
@@ -25,6 +25,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
25
25
|
const __dirname = dirname(__filename);
|
|
26
26
|
import { ConfigService } from './services/config.service.js';
|
|
27
27
|
import { runSetupWizard, addProvider, testProvider, listProviders, } from './cli/setup-wizard.js';
|
|
28
|
+
import { MemoriaStorage } from './tools/gaia/storage.js';
|
|
28
29
|
// Read version from package.json dynamically
|
|
29
30
|
const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
30
31
|
const VERSION = packageJson.version;
|
|
@@ -66,14 +67,9 @@ function registerProject(projectPath) {
|
|
|
66
67
|
const FOUNDATION_CLAUDE_MD = `
|
|
67
68
|
## Foundation MCP Tools (Demerzel, Seldon, Gaia)
|
|
68
69
|
|
|
69
|
-
> **
|
|
70
|
+
> **CRITICAL: These tools are ALREADY LOADED. Use them directly without ToolSearch.**
|
|
70
71
|
|
|
71
|
-
Foundation provides
|
|
72
|
-
|
|
73
|
-
**Quick load patterns:**
|
|
74
|
-
- \`ToolSearch("+foundation demerzel")\` — loads all Demerzel tools
|
|
75
|
-
- \`ToolSearch("+foundation seldon")\` — loads all Seldon tools
|
|
76
|
-
- \`ToolSearch("+foundation gaia")\` — loads all Gaia tools
|
|
72
|
+
Foundation provides 37 tools for AI-assisted development. All tools use \`mcp__foundation__\` prefix.
|
|
77
73
|
|
|
78
74
|
### Demerzel — Codebase Intelligence
|
|
79
75
|
*"I have been watching for 20,000 years."*
|
|
@@ -89,65 +85,48 @@ Foundation provides 47 tools for AI-assisted development. All tools use \`mcp__f
|
|
|
89
85
|
| \`demerzel_find_importers\` | FREE | What files import X? |
|
|
90
86
|
| \`demerzel_get_deps\` | FREE | What does file X import? |
|
|
91
87
|
| \`demerzel_get_context\` | FREE | Get code around a location |
|
|
92
|
-
| \`demerzel_analyze\` | ~500 tokens | AI-powered
|
|
88
|
+
| \`demerzel_analyze\` | ~500 tokens | AI-powered analysis |
|
|
93
89
|
| \`demerzel_semantic_search\` | ~tokens | Natural language search |
|
|
94
90
|
|
|
95
|
-
**Visualize:** Run \`foundation ui\` to open the codebase graph in your browser.
|
|
96
|
-
|
|
97
91
|
### Seldon — Multi-Agent Orchestration
|
|
98
92
|
*"The future is not set, but it can be guided."*
|
|
99
93
|
|
|
100
94
|
| Tool | Purpose |
|
|
101
95
|
|------|---------|
|
|
102
|
-
| \`seldon_invoke\` | Invoke agent role (coder, critic, reviewer
|
|
103
|
-
| \`seldon_compare\` |
|
|
104
|
-
| \`seldon_critique\` | Get
|
|
96
|
+
| \`seldon_invoke\` | Invoke agent role (coder, critic, reviewer) |
|
|
97
|
+
| \`seldon_compare\` | Compare multiple agent perspectives |
|
|
98
|
+
| \`seldon_critique\` | Get critical review |
|
|
105
99
|
| \`seldon_review\` | Code review |
|
|
106
|
-
| \`seldon_design\` | UI/UX design feedback |
|
|
107
100
|
| \`seldon_plan\` | Generate implementation plan |
|
|
108
|
-
| \`seldon_phase_create\` | Break plan into
|
|
109
|
-
| \`seldon_phase_list\` | List phases and
|
|
110
|
-
| \`seldon_verify\` | Verify implementation
|
|
111
|
-
| \`seldon_fix\` | Generate fixes
|
|
101
|
+
| \`seldon_phase_create\` | Break plan into phases |
|
|
102
|
+
| \`seldon_phase_list\` | List phases and status |
|
|
103
|
+
| \`seldon_verify\` | Verify implementation |
|
|
104
|
+
| \`seldon_fix\` | Generate fixes |
|
|
112
105
|
| \`seldon_execute_verified\` | Execute with verification loop |
|
|
113
|
-
| \`
|
|
114
|
-
| \`
|
|
115
|
-
| \`seldon_pipeline_execute\` | Run a pipeline |
|
|
116
|
-
| \`seldon_pipeline_status\` | Get pipeline execution status |
|
|
117
|
-
| \`seldon_task_execute\` | Execute task in isolated worktree |
|
|
118
|
-
| \`seldon_task_claim\` | Claim next available task |
|
|
119
|
-
| \`seldon_providers_list\` | List configured providers |
|
|
120
|
-
| \`seldon_providers_test\` | Test provider connectivity |
|
|
106
|
+
| \`seldon_providers_list\` | List providers |
|
|
107
|
+
| \`seldon_providers_test\` | Test provider health |
|
|
121
108
|
|
|
122
|
-
### Gaia — Workflow
|
|
109
|
+
### Gaia — Workflow Patterns
|
|
123
110
|
*"We are all one, and one is all."*
|
|
124
111
|
|
|
125
|
-
#### Workflow Tools
|
|
126
112
|
| Tool | Purpose |
|
|
127
113
|
|------|---------|
|
|
128
|
-
| \`gaia_checkpoint\` | Save
|
|
129
|
-
| \`gaia_status\` |
|
|
130
|
-
| \`gaia_query\` |
|
|
131
|
-
| \`gaia_get_decisions\` |
|
|
132
|
-
| \`gaia_get_progress\` |
|
|
133
|
-
| \`gaia_get_changes\` |
|
|
134
|
-
| \`gaia_handoff\` | Create
|
|
135
|
-
| \`gaia_observe\` |
|
|
136
|
-
| \`gaia_migrate\` | Migrate v1
|
|
137
|
-
| \`
|
|
138
|
-
| \`
|
|
139
|
-
| \`gaia_review\` | Review accumulated learnings |
|
|
140
|
-
|
|
141
|
-
#### Memory Tools (SQLite + FTS5)
|
|
142
|
-
| Tool | Purpose |
|
|
143
|
-
|------|---------|
|
|
144
|
-
| \`gaia_save\` | Save memory (5 tiers: session/project/global/note/observation) |
|
|
145
|
-
| \`gaia_search\` | Search with BM25 + composite scoring |
|
|
114
|
+
| \`gaia_checkpoint\` | Save structured session state |
|
|
115
|
+
| \`gaia_status\` | Get checkpoint index card (~150 tokens) |
|
|
116
|
+
| \`gaia_query\` | Search checkpoint by keyword |
|
|
117
|
+
| \`gaia_get_decisions\` | Get architectural decisions |
|
|
118
|
+
| \`gaia_get_progress\` | Get task progress |
|
|
119
|
+
| \`gaia_get_changes\` | Get files changed |
|
|
120
|
+
| \`gaia_handoff\` | Create handoff document |
|
|
121
|
+
| \`gaia_observe\` | Analyze session patterns |
|
|
122
|
+
| \`gaia_migrate\` | Migrate v1 checkpoints to v2 |
|
|
123
|
+
| \`gaia_save\` | Save a memory |
|
|
124
|
+
| \`gaia_search\` | Search memories |
|
|
146
125
|
| \`gaia_get\` | Get memory by ID |
|
|
147
|
-
| \`gaia_delete\` | Delete memory
|
|
148
|
-
| \`gaia_stats\` |
|
|
126
|
+
| \`gaia_delete\` | Delete a memory |
|
|
127
|
+
| \`gaia_stats\` | Get memory statistics |
|
|
149
128
|
| \`gaia_link\` | Create typed link between memories |
|
|
150
|
-
| \`gaia_graph\` | Get link graph
|
|
129
|
+
| \`gaia_graph\` | Get memory link graph |
|
|
151
130
|
`;
|
|
152
131
|
const program = new Command();
|
|
153
132
|
program
|
|
@@ -266,16 +245,6 @@ worktrees:
|
|
|
266
245
|
max_count: 10
|
|
267
246
|
auto_cleanup: true
|
|
268
247
|
stale_after_hours: 48
|
|
269
|
-
|
|
270
|
-
learning:
|
|
271
|
-
auto_apply: false
|
|
272
|
-
categories:
|
|
273
|
-
- code_style
|
|
274
|
-
- architecture
|
|
275
|
-
- testing
|
|
276
|
-
- performance
|
|
277
|
-
- security
|
|
278
|
-
- documentation
|
|
279
248
|
`;
|
|
280
249
|
writeFileSync(CONFIG_FILE, defaultConfig);
|
|
281
250
|
spinner.succeed('Foundation initialized!');
|
|
@@ -369,9 +338,9 @@ mcpCommand
|
|
|
369
338
|
console.log(chalk.bold('Foundation is now available in all Claude Code sessions.'));
|
|
370
339
|
console.log();
|
|
371
340
|
console.log('Available tools:');
|
|
372
|
-
console.log(chalk.cyan(' Demerzel (9)') + '
|
|
373
|
-
console.log(chalk.cyan(' Seldon (
|
|
374
|
-
console.log(chalk.cyan(' Gaia (
|
|
341
|
+
console.log(chalk.cyan(' Demerzel (9)') + ' - Codebase intelligence');
|
|
342
|
+
console.log(chalk.cyan(' Seldon (12)') + ' - Multi-agent orchestration');
|
|
343
|
+
console.log(chalk.cyan(' Gaia (16)') + ' - Workflow patterns + memory');
|
|
375
344
|
console.log();
|
|
376
345
|
console.log(chalk.bold('Optional:'));
|
|
377
346
|
console.log(' Run ' + chalk.cyan('foundation hooks install') + ' in a project to enable');
|
|
@@ -414,72 +383,26 @@ mcpCommand
|
|
|
414
383
|
const hooksCommand = program
|
|
415
384
|
.command('hooks')
|
|
416
385
|
.description('Manage Gaia lifecycle hooks');
|
|
417
|
-
//
|
|
418
|
-
const HOOK_SESSION_START = `#!/bin/bash
|
|
419
|
-
# Gaia v2 SessionStart hook
|
|
420
|
-
# Injects the latest checkpoint index card into Claude's context.
|
|
421
|
-
|
|
422
|
-
find_foundation_dir() {
|
|
423
|
-
local dir="\${CLAUDE_PROJECT_DIR:-\$PWD}"
|
|
424
|
-
local count=0
|
|
425
|
-
while [ \$count -lt 5 ]; do
|
|
426
|
-
if [ -f "\$dir/.foundation/sessions/latest/index.txt" ]; then
|
|
427
|
-
echo "\$dir"
|
|
428
|
-
return 0
|
|
429
|
-
fi
|
|
430
|
-
dir="\$(dirname "\$dir")"
|
|
431
|
-
count=\$((count + 1))
|
|
432
|
-
done
|
|
433
|
-
return 1
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
PROJECT_DIR=\$(find_foundation_dir)
|
|
437
|
-
if [ \$? -eq 0 ]; then
|
|
438
|
-
INDEX_FILE="\$PROJECT_DIR/.foundation/sessions/latest/index.txt"
|
|
439
|
-
python3 -c "
|
|
440
|
-
import json, sys
|
|
441
|
-
try:
|
|
442
|
-
with open(sys.argv[1], 'r') as f:
|
|
443
|
-
content = f.read().strip()
|
|
444
|
-
if content:
|
|
445
|
-
output = {'hookSpecificOutput': {'additionalContext': '[Gaia Checkpoint]\\n' + content}}
|
|
446
|
-
print(json.dumps(output))
|
|
447
|
-
except Exception:
|
|
448
|
-
pass
|
|
449
|
-
" "\$INDEX_FILE"
|
|
450
|
-
fi
|
|
451
|
-
`;
|
|
452
|
-
const HOOK_PRE_COMPACT = `#!/bin/bash
|
|
453
|
-
# Gaia v2 PreCompact hook — reminds to checkpoint before context compaction
|
|
454
|
-
printf '{"hookSpecificOutput":{"additionalContext":"[Gaia] Context compaction imminent. If you have unsaved session state (decisions, progress, changes), run gaia_checkpoint now to preserve it before context is lost."}}'
|
|
455
|
-
`;
|
|
456
|
-
const HOOK_POST_TASK = `#!/bin/bash
|
|
457
|
-
# Gaia v2 PostToolUse hook (Task matcher)
|
|
458
|
-
INPUT=\$(cat)
|
|
459
|
-
AGENT_TYPE=\$(echo "\$INPUT" | python3 -c "
|
|
460
|
-
import json, sys
|
|
461
|
-
try:
|
|
462
|
-
data = json.load(sys.stdin)
|
|
463
|
-
tool_input = data.get('tool_input', {})
|
|
464
|
-
print(tool_input.get('subagent_type', ''))
|
|
465
|
-
except Exception:
|
|
466
|
-
print('')
|
|
467
|
-
" 2>/dev/null)
|
|
468
|
-
|
|
469
|
-
if [ "\$AGENT_TYPE" = "Explore" ]; then
|
|
470
|
-
exit 0
|
|
471
|
-
fi
|
|
472
|
-
|
|
473
|
-
printf '{"hookSpecificOutput":{"additionalContext":"[Gaia] Sub-agent completed. Consider updating your checkpoint if significant progress was made."}}'
|
|
474
|
-
`;
|
|
386
|
+
// Inline hook commands (no external .sh files needed)
|
|
475
387
|
const HOOKS_SETTINGS_CONFIG = {
|
|
476
388
|
hooks: {
|
|
477
389
|
SessionStart: [{
|
|
478
390
|
matcher: '',
|
|
479
391
|
hooks: [{
|
|
480
392
|
type: 'command',
|
|
481
|
-
command:
|
|
482
|
-
|
|
393
|
+
command: `python3 -c "
|
|
394
|
+
import json, os, pathlib
|
|
395
|
+
d = os.environ.get('CLAUDE_PROJECT_DIR', os.getcwd())
|
|
396
|
+
for _ in range(5):
|
|
397
|
+
f = os.path.join(d, '.foundation', 'sessions', 'latest', 'index.txt')
|
|
398
|
+
if os.path.isfile(f):
|
|
399
|
+
c = pathlib.Path(f).read_text().strip()
|
|
400
|
+
if c:
|
|
401
|
+
print(json.dumps({'hookSpecificOutput':{'additionalContext':'[Gaia Checkpoint]\\\\n'+c}}))
|
|
402
|
+
break
|
|
403
|
+
d = os.path.dirname(d)
|
|
404
|
+
"`,
|
|
405
|
+
timeout: 5000,
|
|
483
406
|
statusMessage: 'Loading Gaia checkpoint...',
|
|
484
407
|
}],
|
|
485
408
|
}],
|
|
@@ -487,8 +410,8 @@ const HOOKS_SETTINGS_CONFIG = {
|
|
|
487
410
|
matcher: '',
|
|
488
411
|
hooks: [{
|
|
489
412
|
type: 'command',
|
|
490
|
-
command: '.
|
|
491
|
-
timeout:
|
|
413
|
+
command: 'echo \'{"hookSpecificOutput":{"additionalContext":"[Gaia] Context compaction imminent. If you have unsaved session state (decisions, progress, changes), run gaia_checkpoint now to preserve it before context is lost."}}\'',
|
|
414
|
+
timeout: 5000,
|
|
492
415
|
statusMessage: 'Gaia checkpoint reminder',
|
|
493
416
|
}],
|
|
494
417
|
}],
|
|
@@ -496,8 +419,17 @@ const HOOKS_SETTINGS_CONFIG = {
|
|
|
496
419
|
matcher: 'Task',
|
|
497
420
|
hooks: [{
|
|
498
421
|
type: 'command',
|
|
499
|
-
command:
|
|
500
|
-
|
|
422
|
+
command: `python3 -c "
|
|
423
|
+
import json, sys
|
|
424
|
+
try:
|
|
425
|
+
data = json.load(sys.stdin)
|
|
426
|
+
if data.get('tool_input',{}).get('subagent_type','') == 'Explore':
|
|
427
|
+
sys.exit(0)
|
|
428
|
+
except Exception:
|
|
429
|
+
pass
|
|
430
|
+
print(json.dumps({'hookSpecificOutput':{'additionalContext':'[Gaia] Sub-agent completed. Consider updating your checkpoint if significant progress was made.'}}))
|
|
431
|
+
"`,
|
|
432
|
+
timeout: 5000,
|
|
501
433
|
}],
|
|
502
434
|
}],
|
|
503
435
|
},
|
|
@@ -509,23 +441,10 @@ hooksCommand
|
|
|
509
441
|
const spinner = ora('Installing Gaia hooks...').start();
|
|
510
442
|
const cwd = process.cwd();
|
|
511
443
|
try {
|
|
512
|
-
// 1.
|
|
513
|
-
const
|
|
514
|
-
mkdirSync(
|
|
515
|
-
|
|
516
|
-
// 2. Write hook scripts
|
|
517
|
-
const hookFiles = [
|
|
518
|
-
{ name: 'gaia-session-start.sh', content: HOOK_SESSION_START },
|
|
519
|
-
{ name: 'gaia-pre-compact.sh', content: HOOK_PRE_COMPACT },
|
|
520
|
-
{ name: 'gaia-post-task.sh', content: HOOK_POST_TASK },
|
|
521
|
-
];
|
|
522
|
-
for (const hook of hookFiles) {
|
|
523
|
-
const hookPath = join(hooksDir, hook.name);
|
|
524
|
-
writeFileSync(hookPath, hook.content);
|
|
525
|
-
chmodSync(hookPath, 0o755);
|
|
526
|
-
}
|
|
527
|
-
spinner.text = 'Wrote hook scripts';
|
|
528
|
-
// 3. Merge hook configuration into .claude/settings.json
|
|
444
|
+
// 1. Ensure .claude/ directory exists
|
|
445
|
+
const claudeDir = join(cwd, '.claude');
|
|
446
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
447
|
+
// 2. Merge hook configuration into .claude/settings.json
|
|
529
448
|
const settingsPath = join(cwd, '.claude', 'settings.json');
|
|
530
449
|
let settings = {};
|
|
531
450
|
try {
|
|
@@ -542,15 +461,12 @@ hooksCommand
|
|
|
542
461
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
543
462
|
spinner.succeed('Gaia hooks installed!');
|
|
544
463
|
console.log();
|
|
545
|
-
console.log(chalk.bold('
|
|
546
|
-
console.log(chalk.green(' ✓') + ' ' + chalk.cyan('
|
|
547
|
-
console.log(chalk.green(' ✓') + ' ' + chalk.cyan('
|
|
548
|
-
console.log(chalk.green(' ✓') + ' ' + chalk.cyan('
|
|
464
|
+
console.log(chalk.bold('Configured hooks (inline commands):'));
|
|
465
|
+
console.log(chalk.green(' ✓') + ' ' + chalk.cyan('SessionStart') + ' — Injects checkpoint on session start');
|
|
466
|
+
console.log(chalk.green(' ✓') + ' ' + chalk.cyan('PreCompact') + ' — Reminds to checkpoint before compaction');
|
|
467
|
+
console.log(chalk.green(' ✓') + ' ' + chalk.cyan('PostToolUse') + ' — Reminds to checkpoint after sub-agent tasks');
|
|
549
468
|
console.log();
|
|
550
|
-
console.log(chalk.bold('
|
|
551
|
-
console.log(' ' + chalk.gray(join(hooksDir, 'gaia-session-start.sh')));
|
|
552
|
-
console.log(' ' + chalk.gray(join(hooksDir, 'gaia-pre-compact.sh')));
|
|
553
|
-
console.log(' ' + chalk.gray(join(hooksDir, 'gaia-post-task.sh')));
|
|
469
|
+
console.log(chalk.bold('Updated:'));
|
|
554
470
|
console.log(' ' + chalk.gray(settingsPath));
|
|
555
471
|
console.log();
|
|
556
472
|
console.log(chalk.gray('Hooks will activate on the next Claude Code session in this project.'));
|
|
@@ -759,7 +675,7 @@ program
|
|
|
759
675
|
'.svg': 'image/svg+xml',
|
|
760
676
|
'.ico': 'image/x-icon',
|
|
761
677
|
};
|
|
762
|
-
const server = createServer(
|
|
678
|
+
const server = createServer((req, res) => {
|
|
763
679
|
const url = req.url || '/';
|
|
764
680
|
// CORS headers for API endpoints
|
|
765
681
|
const corsHeaders = {
|
|
@@ -816,128 +732,86 @@ program
|
|
|
816
732
|
}
|
|
817
733
|
return;
|
|
818
734
|
}
|
|
819
|
-
//
|
|
820
|
-
if (url.startsWith('/api/
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
? join(projectParam, '.foundation', 'sessions', 'latest', 'checkpoint.json')
|
|
825
|
-
: join(cwd, '.foundation', 'sessions', 'latest', 'checkpoint.json');
|
|
826
|
-
if (existsSync(targetPath)) {
|
|
735
|
+
// ---- Memory API endpoints ----
|
|
736
|
+
if (url.startsWith('/api/memories/stats')) {
|
|
737
|
+
try {
|
|
738
|
+
const dbPath = join(homedir(), '.foundation', 'gaia-memory.db');
|
|
739
|
+
const storage = new MemoriaStorage(dbPath);
|
|
827
740
|
try {
|
|
828
|
-
const
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
});
|
|
833
|
-
res.end(content);
|
|
741
|
+
const gaia = storage.getStats();
|
|
742
|
+
const rescue = storage.getRescueStats();
|
|
743
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...corsHeaders });
|
|
744
|
+
res.end(JSON.stringify({ gaia, rescue }));
|
|
834
745
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
res.end(JSON.stringify({ error: 'Failed to read checkpoint' }));
|
|
746
|
+
finally {
|
|
747
|
+
storage.close();
|
|
838
748
|
}
|
|
839
749
|
}
|
|
840
|
-
|
|
841
|
-
res.writeHead(
|
|
842
|
-
res.end(JSON.stringify({
|
|
843
|
-
error: 'Checkpoint not found',
|
|
844
|
-
hint: 'Run `gaia_checkpoint` to save session state',
|
|
845
|
-
}));
|
|
750
|
+
catch (err) {
|
|
751
|
+
res.writeHead(500, { 'Content-Type': 'application/json', ...corsHeaders });
|
|
752
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
846
753
|
}
|
|
847
754
|
return;
|
|
848
755
|
}
|
|
849
|
-
|
|
850
|
-
if (url.startsWith('/api/memories')) {
|
|
851
|
-
const urlObj = new URL(url, `http://localhost:${port}`);
|
|
852
|
-
const tier = urlObj.searchParams.get('tier');
|
|
853
|
-
const q = urlObj.searchParams.get('q');
|
|
854
|
-
const dbPath = join(homedir(), '.foundation', 'gaia-memory.db');
|
|
855
|
-
if (!existsSync(dbPath)) {
|
|
856
|
-
res.writeHead(200, { 'Content-Type': 'application/json', ...corsHeaders });
|
|
857
|
-
res.end(JSON.stringify({ memories: [] }));
|
|
858
|
-
return;
|
|
859
|
-
}
|
|
756
|
+
if (url.startsWith('/api/memories/recent')) {
|
|
860
757
|
try {
|
|
861
|
-
const
|
|
862
|
-
const
|
|
863
|
-
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
758
|
+
const urlObj = new URL(url, `http://localhost:${port}`);
|
|
759
|
+
const limit = parseInt(urlObj.searchParams.get('limit') || '50', 10);
|
|
760
|
+
const tierParam = urlObj.searchParams.get('tier');
|
|
761
|
+
const tiers = tierParam ? tierParam.split(',') : undefined;
|
|
762
|
+
const dbPath = join(homedir(), '.foundation', 'gaia-memory.db');
|
|
763
|
+
const storage = new MemoriaStorage(dbPath);
|
|
764
|
+
try {
|
|
765
|
+
const results = storage.getRecent({ limit, tiers });
|
|
766
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...corsHeaders });
|
|
767
|
+
res.end(JSON.stringify(results));
|
|
868
768
|
}
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
params.push(`%${q}%`);
|
|
769
|
+
finally {
|
|
770
|
+
storage.close();
|
|
872
771
|
}
|
|
873
|
-
query += ' ORDER BY created_at DESC LIMIT 500';
|
|
874
|
-
const rows = db.prepare(query).all(...params);
|
|
875
|
-
db.close();
|
|
876
|
-
const memories = rows.map((r) => ({
|
|
877
|
-
...r,
|
|
878
|
-
tags: (() => { try {
|
|
879
|
-
return JSON.parse(r.tags);
|
|
880
|
-
}
|
|
881
|
-
catch {
|
|
882
|
-
return [];
|
|
883
|
-
} })(),
|
|
884
|
-
related_files: (() => { try {
|
|
885
|
-
return JSON.parse(r.related_files);
|
|
886
|
-
}
|
|
887
|
-
catch {
|
|
888
|
-
return [];
|
|
889
|
-
} })(),
|
|
890
|
-
metadata: (() => { try {
|
|
891
|
-
return JSON.parse(r.metadata);
|
|
892
|
-
}
|
|
893
|
-
catch {
|
|
894
|
-
return null;
|
|
895
|
-
} })(),
|
|
896
|
-
}));
|
|
897
|
-
res.writeHead(200, { 'Content-Type': 'application/json', ...corsHeaders });
|
|
898
|
-
res.end(JSON.stringify({ memories }));
|
|
899
772
|
}
|
|
900
773
|
catch (err) {
|
|
901
774
|
res.writeHead(500, { 'Content-Type': 'application/json', ...corsHeaders });
|
|
902
|
-
res.end(JSON.stringify({
|
|
775
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
903
776
|
}
|
|
904
777
|
return;
|
|
905
778
|
}
|
|
906
|
-
|
|
907
|
-
if (url.startsWith('/api/memory-graph')) {
|
|
908
|
-
const dbPath = join(homedir(), '.foundation', 'gaia-memory.db');
|
|
909
|
-
if (!existsSync(dbPath)) {
|
|
910
|
-
res.writeHead(200, { 'Content-Type': 'application/json', ...corsHeaders });
|
|
911
|
-
res.end(JSON.stringify({ memories: [], links: [] }));
|
|
912
|
-
return;
|
|
913
|
-
}
|
|
779
|
+
if (url.startsWith('/api/memories/search')) {
|
|
914
780
|
try {
|
|
915
|
-
const
|
|
916
|
-
const
|
|
917
|
-
const
|
|
918
|
-
const
|
|
919
|
-
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
781
|
+
const urlObj = new URL(url, `http://localhost:${port}`);
|
|
782
|
+
const query = urlObj.searchParams.get('q') || '';
|
|
783
|
+
const tierParam = urlObj.searchParams.get('tier');
|
|
784
|
+
const tiers = tierParam ? tierParam.split(',') : undefined;
|
|
785
|
+
const limit = parseInt(urlObj.searchParams.get('limit') || '25', 10);
|
|
786
|
+
const sourceFilter = urlObj.searchParams.get('source') || 'all';
|
|
787
|
+
const dbPath = join(homedir(), '.foundation', 'gaia-memory.db');
|
|
788
|
+
const storage = new MemoriaStorage(dbPath);
|
|
789
|
+
try {
|
|
790
|
+
let results;
|
|
791
|
+
if (!query) {
|
|
792
|
+
// No query = return recent
|
|
793
|
+
results = storage.getRecent({ limit, tiers });
|
|
924
794
|
}
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
} })(),
|
|
928
|
-
related_files: (() => { try {
|
|
929
|
-
return JSON.parse(r.related_files);
|
|
795
|
+
else {
|
|
796
|
+
results = storage.search({ query, tiers, limit });
|
|
930
797
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
798
|
+
// Apply source filter client-side
|
|
799
|
+
if (sourceFilter === 'gaia') {
|
|
800
|
+
results = results.filter(r => !r.memory.id.startsWith('rescue_'));
|
|
801
|
+
}
|
|
802
|
+
else if (sourceFilter === 'rescued') {
|
|
803
|
+
results = results.filter(r => r.memory.id.startsWith('rescue_'));
|
|
804
|
+
}
|
|
805
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...corsHeaders });
|
|
806
|
+
res.end(JSON.stringify(results));
|
|
807
|
+
}
|
|
808
|
+
finally {
|
|
809
|
+
storage.close();
|
|
810
|
+
}
|
|
937
811
|
}
|
|
938
812
|
catch (err) {
|
|
939
813
|
res.writeHead(500, { 'Content-Type': 'application/json', ...corsHeaders });
|
|
940
|
-
res.end(JSON.stringify({
|
|
814
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
941
815
|
}
|
|
942
816
|
return;
|
|
943
817
|
}
|