@phantomind/core 0.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/dist/adapters/aider.d.ts +12 -0
- package/dist/adapters/aider.d.ts.map +1 -0
- package/dist/adapters/aider.js +24 -0
- package/dist/adapters/aider.js.map +1 -0
- package/dist/adapters/base.d.ts +21 -0
- package/dist/adapters/base.d.ts.map +1 -0
- package/dist/adapters/base.js +83 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/claude-code.d.ts +12 -0
- package/dist/adapters/claude-code.d.ts.map +1 -0
- package/dist/adapters/claude-code.js +31 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/cline.d.ts +12 -0
- package/dist/adapters/cline.d.ts.map +1 -0
- package/dist/adapters/cline.js +32 -0
- package/dist/adapters/cline.js.map +1 -0
- package/dist/adapters/codex.d.ts +18 -0
- package/dist/adapters/codex.d.ts.map +1 -0
- package/dist/adapters/codex.js +38 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/continue.d.ts +12 -0
- package/dist/adapters/continue.d.ts.map +1 -0
- package/dist/adapters/continue.js +29 -0
- package/dist/adapters/continue.js.map +1 -0
- package/dist/adapters/copilot.d.ts +12 -0
- package/dist/adapters/copilot.d.ts.map +1 -0
- package/dist/adapters/copilot.js +31 -0
- package/dist/adapters/copilot.js.map +1 -0
- package/dist/adapters/cursor.d.ts +12 -0
- package/dist/adapters/cursor.d.ts.map +1 -0
- package/dist/adapters/cursor.js +34 -0
- package/dist/adapters/cursor.js.map +1 -0
- package/dist/adapters/index.d.ts +32 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +64 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/windsurf.d.ts +12 -0
- package/dist/adapters/windsurf.d.ts.map +1 -0
- package/dist/adapters/windsurf.js +32 -0
- package/dist/adapters/windsurf.js.map +1 -0
- package/dist/adapters/zed.d.ts +12 -0
- package/dist/adapters/zed.d.ts.map +1 -0
- package/dist/adapters/zed.js +27 -0
- package/dist/adapters/zed.js.map +1 -0
- package/dist/agent/decomposer.d.ts +55 -0
- package/dist/agent/decomposer.d.ts.map +1 -0
- package/dist/agent/decomposer.js +172 -0
- package/dist/agent/decomposer.js.map +1 -0
- package/dist/agent/executor.d.ts +33 -0
- package/dist/agent/executor.d.ts.map +1 -0
- package/dist/agent/executor.js +260 -0
- package/dist/agent/executor.js.map +1 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +8 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/memory.d.ts +95 -0
- package/dist/agent/memory.d.ts.map +1 -0
- package/dist/agent/memory.js +211 -0
- package/dist/agent/memory.js.map +1 -0
- package/dist/agent/orchestrator.d.ts +54 -0
- package/dist/agent/orchestrator.d.ts.map +1 -0
- package/dist/agent/orchestrator.js +190 -0
- package/dist/agent/orchestrator.js.map +1 -0
- package/dist/agent/queue.d.ts +95 -0
- package/dist/agent/queue.d.ts.map +1 -0
- package/dist/agent/queue.js +231 -0
- package/dist/agent/queue.js.map +1 -0
- package/dist/agent/retry.d.ts +61 -0
- package/dist/agent/retry.d.ts.map +1 -0
- package/dist/agent/retry.js +162 -0
- package/dist/agent/retry.js.map +1 -0
- package/dist/agent/roles.d.ts +35 -0
- package/dist/agent/roles.d.ts.map +1 -0
- package/dist/agent/roles.js +269 -0
- package/dist/agent/roles.js.map +1 -0
- package/dist/cli/agent.d.ts +12 -0
- package/dist/cli/agent.d.ts.map +1 -0
- package/dist/cli/agent.js +85 -0
- package/dist/cli/agent.js.map +1 -0
- package/dist/cli/audit.d.ts +11 -0
- package/dist/cli/audit.d.ts.map +1 -0
- package/dist/cli/audit.js +63 -0
- package/dist/cli/audit.js.map +1 -0
- package/dist/cli/eval.d.ts +11 -0
- package/dist/cli/eval.d.ts.map +1 -0
- package/dist/cli/eval.js +79 -0
- package/dist/cli/eval.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +9 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +13 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +157 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/main.d.ts +7 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +153 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/serve.d.ts +11 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +23 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/stats.d.ts +10 -0
- package/dist/cli/stats.d.ts.map +1 -0
- package/dist/cli/stats.js +68 -0
- package/dist/cli/stats.js.map +1 -0
- package/dist/cli/sync.d.ts +11 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +49 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/cli/validate.d.ts +13 -0
- package/dist/cli/validate.d.ts.map +1 -0
- package/dist/cli/validate.js +125 -0
- package/dist/cli/validate.js.map +1 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +25 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +190 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/context/embedder.d.ts +53 -0
- package/dist/context/embedder.d.ts.map +1 -0
- package/dist/context/embedder.js +235 -0
- package/dist/context/embedder.js.map +1 -0
- package/dist/context/engine.d.ts +82 -0
- package/dist/context/engine.d.ts.map +1 -0
- package/dist/context/engine.js +343 -0
- package/dist/context/engine.js.map +1 -0
- package/dist/context/index.d.ts +5 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +5 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/learner.d.ts +44 -0
- package/dist/context/learner.d.ts.map +1 -0
- package/dist/context/learner.js +246 -0
- package/dist/context/learner.js.map +1 -0
- package/dist/context/versioning.d.ts +29 -0
- package/dist/context/versioning.d.ts.map +1 -0
- package/dist/context/versioning.js +81 -0
- package/dist/context/versioning.js.map +1 -0
- package/dist/index.d.ts +169 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +285 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +2 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +31 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +334 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/observability/audit.d.ts +61 -0
- package/dist/observability/audit.d.ts.map +1 -0
- package/dist/observability/audit.js +168 -0
- package/dist/observability/audit.js.map +1 -0
- package/dist/observability/cost-tracker.d.ts +71 -0
- package/dist/observability/cost-tracker.d.ts.map +1 -0
- package/dist/observability/cost-tracker.js +206 -0
- package/dist/observability/cost-tracker.js.map +1 -0
- package/dist/observability/dashboard.d.ts +52 -0
- package/dist/observability/dashboard.d.ts.map +1 -0
- package/dist/observability/dashboard.js +134 -0
- package/dist/observability/dashboard.js.map +1 -0
- package/dist/observability/index.d.ts +4 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +4 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/providers/anthropic.d.ts +14 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +99 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/base.d.ts +52 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +68 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/deepseek.d.ts +15 -0
- package/dist/providers/deepseek.d.ts.map +1 -0
- package/dist/providers/deepseek.js +99 -0
- package/dist/providers/deepseek.js.map +1 -0
- package/dist/providers/gemini.d.ts +15 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +118 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/groq.d.ts +15 -0
- package/dist/providers/groq.d.ts.map +1 -0
- package/dist/providers/groq.js +101 -0
- package/dist/providers/groq.js.map +1 -0
- package/dist/providers/index.d.ts +11 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +11 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/mistral.d.ts +15 -0
- package/dist/providers/mistral.d.ts.map +1 -0
- package/dist/providers/mistral.js +99 -0
- package/dist/providers/mistral.js.map +1 -0
- package/dist/providers/ollama.d.ts +14 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +122 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openai.d.ts +14 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +80 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openrouter.d.ts +15 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.js +101 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/router.d.ts +51 -0
- package/dist/providers/router.d.ts.map +1 -0
- package/dist/providers/router.js +198 -0
- package/dist/providers/router.js.map +1 -0
- package/dist/quality/anomaly.d.ts +32 -0
- package/dist/quality/anomaly.d.ts.map +1 -0
- package/dist/quality/anomaly.js +126 -0
- package/dist/quality/anomaly.js.map +1 -0
- package/dist/quality/consistency.d.ts +26 -0
- package/dist/quality/consistency.d.ts.map +1 -0
- package/dist/quality/consistency.js +156 -0
- package/dist/quality/consistency.js.map +1 -0
- package/dist/quality/dual-verifier.d.ts +22 -0
- package/dist/quality/dual-verifier.d.ts.map +1 -0
- package/dist/quality/dual-verifier.js +137 -0
- package/dist/quality/dual-verifier.js.map +1 -0
- package/dist/quality/hallucination-guard.d.ts +57 -0
- package/dist/quality/hallucination-guard.d.ts.map +1 -0
- package/dist/quality/hallucination-guard.js +245 -0
- package/dist/quality/hallucination-guard.js.map +1 -0
- package/dist/quality/index.d.ts +7 -0
- package/dist/quality/index.d.ts.map +1 -0
- package/dist/quality/index.js +7 -0
- package/dist/quality/index.js.map +1 -0
- package/dist/quality/regression.d.ts +44 -0
- package/dist/quality/regression.d.ts.map +1 -0
- package/dist/quality/regression.js +181 -0
- package/dist/quality/regression.js.map +1 -0
- package/dist/quality/secret-scanner.d.ts +36 -0
- package/dist/quality/secret-scanner.d.ts.map +1 -0
- package/dist/quality/secret-scanner.js +187 -0
- package/dist/quality/secret-scanner.js.map +1 -0
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/registry.d.ts +72 -0
- package/dist/schemas/registry.d.ts.map +1 -0
- package/dist/schemas/registry.js +483 -0
- package/dist/schemas/registry.js.map +1 -0
- package/dist/templates/engine.d.ts +70 -0
- package/dist/templates/engine.d.ts.map +1 -0
- package/dist/templates/engine.js +71 -0
- package/dist/templates/engine.js.map +1 -0
- package/dist/templates/index.d.ts +2 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +2 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/types.d.ts +912 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +75 -0
- package/dist/types.js.map +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhantomMindAI — CLI Validate Command
|
|
3
|
+
* Validate code output for quality issues.
|
|
4
|
+
*/
|
|
5
|
+
import { readFile } from 'node:fs/promises';
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
import { SecretScanner } from '../quality/secret-scanner.js';
|
|
8
|
+
import { HallucinationGuard } from '../quality/hallucination-guard.js';
|
|
9
|
+
import { ConsistencyEnforcer } from '../quality/consistency.js';
|
|
10
|
+
export async function validateCommand(projectRoot, options) {
|
|
11
|
+
const chalk = (await import('chalk')).default;
|
|
12
|
+
const ora = (await import('ora')).default;
|
|
13
|
+
const fastGlob = (await import('fast-glob')).default;
|
|
14
|
+
console.log(chalk.bold.cyan('\n🔍 PhantomMindAI — Code Validation\n'));
|
|
15
|
+
const spinner = ora('Scanning files...').start();
|
|
16
|
+
let totalIssues = 0;
|
|
17
|
+
try {
|
|
18
|
+
// Determine files to scan
|
|
19
|
+
let files;
|
|
20
|
+
if (options.files && options.files.length > 0) {
|
|
21
|
+
files = options.files.filter(f => existsSync(f));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
files = await fastGlob(['**/*.{ts,tsx,js,jsx,py,go,rs,java,kt,swift}'], {
|
|
25
|
+
cwd: projectRoot,
|
|
26
|
+
ignore: ['**/node_modules/**', '**/dist/**', '**/.git/**', '**/build/**'],
|
|
27
|
+
absolute: true,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
spinner.text = `Found ${files.length} files to validate`;
|
|
31
|
+
// Secret scanning
|
|
32
|
+
if (options.secrets !== false) {
|
|
33
|
+
spinner.text = 'Scanning for secrets...';
|
|
34
|
+
const scanner = new SecretScanner();
|
|
35
|
+
let secretCount = 0;
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
const content = await readFile(file, 'utf-8');
|
|
38
|
+
const secrets = scanner.scan(content, file);
|
|
39
|
+
if (secrets.length > 0) {
|
|
40
|
+
secretCount += secrets.length;
|
|
41
|
+
for (const secret of secrets) {
|
|
42
|
+
const relPath = file.replace(projectRoot + '/', '');
|
|
43
|
+
console.log(` ${chalk.red('SECRET')} ${chalk.dim(relPath)}:${secret.line} ${chalk.yellow(secret.pattern)} ${chalk.dim(`[${secret.severity}]`)}`);
|
|
44
|
+
if (options.fix) {
|
|
45
|
+
const { cleaned } = scanner.scanAndReplace(content, file);
|
|
46
|
+
await import('node:fs/promises').then(fs => fs.writeFile(file, cleaned));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
totalIssues += secretCount;
|
|
52
|
+
if (secretCount > 0) {
|
|
53
|
+
console.log(chalk.red(`\n Found ${secretCount} secret(s)${options.fix ? ' (auto-fixed)' : ''}`));
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.log(chalk.green(' ✓ No secrets detected'));
|
|
57
|
+
}
|
|
58
|
+
console.log('');
|
|
59
|
+
}
|
|
60
|
+
// Hallucination checking
|
|
61
|
+
if (options.hallucinations !== false) {
|
|
62
|
+
spinner.text = 'Checking for hallucinations...';
|
|
63
|
+
const guard = new HallucinationGuard(projectRoot);
|
|
64
|
+
let hallucinationCount = 0;
|
|
65
|
+
for (const file of files.filter(f => f.endsWith('.ts') || f.endsWith('.tsx') || f.endsWith('.js') || f.endsWith('.jsx'))) {
|
|
66
|
+
const content = await readFile(file, 'utf-8');
|
|
67
|
+
const issues = await guard.check(content, file);
|
|
68
|
+
if (issues.length > 0) {
|
|
69
|
+
hallucinationCount += issues.length;
|
|
70
|
+
for (const issue of issues) {
|
|
71
|
+
const relPath = file.replace(projectRoot + '/', '');
|
|
72
|
+
console.log(` ${chalk.yellow('HALLUCINATION')} ${chalk.dim(relPath)}:${issue.line} [${issue.type}] ${issue.reference} ${issue.exists ? chalk.green('exists') : chalk.red('not found')}`);
|
|
73
|
+
if (issue.suggestions && issue.suggestions.length > 0) {
|
|
74
|
+
console.log(chalk.dim(` Suggestions: ${issue.suggestions.join(', ')}`));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
totalIssues += hallucinationCount;
|
|
80
|
+
if (hallucinationCount > 0) {
|
|
81
|
+
console.log(chalk.yellow(`\n Found ${hallucinationCount} potential hallucination(s)`));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.log(chalk.green(' ✓ No hallucinations detected'));
|
|
85
|
+
}
|
|
86
|
+
console.log('');
|
|
87
|
+
}
|
|
88
|
+
// Consistency checking
|
|
89
|
+
if (options.consistency !== false) {
|
|
90
|
+
spinner.text = 'Checking consistency...';
|
|
91
|
+
const enforcer = new ConsistencyEnforcer(projectRoot);
|
|
92
|
+
const report = await enforcer.scan();
|
|
93
|
+
if (report.issues.length > 0) {
|
|
94
|
+
totalIssues += report.issues.length;
|
|
95
|
+
for (const issue of report.issues) {
|
|
96
|
+
console.log(` ${chalk.magenta('CONSISTENCY')} ${chalk.dim(issue.files.join(', '))} ${issue.description} ${chalk.dim(`[${issue.severity}]`)}`);
|
|
97
|
+
}
|
|
98
|
+
console.log(chalk.magenta(`\n Found ${report.issues.length} consistency issue(s)`));
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
console.log(chalk.green(' ✓ No consistency issues'));
|
|
102
|
+
}
|
|
103
|
+
console.log('');
|
|
104
|
+
}
|
|
105
|
+
spinner.stop();
|
|
106
|
+
// Summary
|
|
107
|
+
console.log(chalk.dim('─'.repeat(50)));
|
|
108
|
+
if (totalIssues > 0) {
|
|
109
|
+
console.log(chalk.yellow(`\n⚠️ Total: ${totalIssues} issue(s) found`));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log(chalk.green('\n✅ All checks passed!'));
|
|
113
|
+
}
|
|
114
|
+
console.log('');
|
|
115
|
+
if (totalIssues > 0) {
|
|
116
|
+
process.exitCode = 1;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
spinner.fail('Validation failed');
|
|
121
|
+
console.error(chalk.red(error.message));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhantomMindAI — Configuration Loader
|
|
3
|
+
*/
|
|
4
|
+
import { PhantomConfig } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Find project root by looking for .phantomind/ directory
|
|
7
|
+
*/
|
|
8
|
+
export declare function findProjectRoot(startDir?: string): Promise<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Load PhantomMindAI configuration from .phantomind/config.yaml
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadConfig(projectRoot?: string): Promise<PhantomConfig>;
|
|
13
|
+
/**
|
|
14
|
+
* Load .phantomind/.env file into process.env
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadEnvFile(projectRoot?: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Get the default configuration
|
|
19
|
+
*/
|
|
20
|
+
export declare function getDefaultConfig(): PhantomConfig;
|
|
21
|
+
/**
|
|
22
|
+
* Deep merge two objects
|
|
23
|
+
*/
|
|
24
|
+
export declare function deepMerge(target: unknown, source: unknown): unknown;
|
|
25
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAAE,aAAa,EAAuB,MAAM,aAAa,CAAC;AAgEjE;;GAEG;AACH,wBAAsB,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAexE;AAwBD;;GAEG;AACH,wBAAsB,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA0B7E;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBrE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAenE"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhantomMindAI — Configuration Loader
|
|
3
|
+
*/
|
|
4
|
+
import { readFile, access } from 'node:fs/promises';
|
|
5
|
+
import { join, resolve } from 'node:path';
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
import { PhantomConfigSchema } from '../types.js';
|
|
8
|
+
const DEFAULT_CONFIG = {
|
|
9
|
+
version: '1',
|
|
10
|
+
providers: {
|
|
11
|
+
primary: {
|
|
12
|
+
name: 'anthropic',
|
|
13
|
+
model: 'claude-sonnet-4-20250514',
|
|
14
|
+
maxTokens: 8096,
|
|
15
|
+
temperature: 0.2,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
context: {
|
|
19
|
+
skills: '.phantomind/SKILLS.md',
|
|
20
|
+
rules: '.phantomind/RULES.md',
|
|
21
|
+
schema: '.phantomind/schema.json',
|
|
22
|
+
prds: '.phantomind/prds/',
|
|
23
|
+
decisions: '.phantomind/decisions/',
|
|
24
|
+
},
|
|
25
|
+
adapters: ['copilot', 'cursor', 'cline', 'continue', 'windsurf'],
|
|
26
|
+
mcp: {
|
|
27
|
+
enabled: true,
|
|
28
|
+
port: 3741,
|
|
29
|
+
autoStart: true,
|
|
30
|
+
},
|
|
31
|
+
quality: {
|
|
32
|
+
secretScanner: true,
|
|
33
|
+
hallucinationGuard: true,
|
|
34
|
+
dualVerification: false,
|
|
35
|
+
},
|
|
36
|
+
agent: {
|
|
37
|
+
maxSteps: 30,
|
|
38
|
+
humanCheckpoint: {
|
|
39
|
+
before: ['write_file', 'delete_file', 'run_command'],
|
|
40
|
+
after: ['run_tests'],
|
|
41
|
+
onError: true,
|
|
42
|
+
onAnomalyDetected: true,
|
|
43
|
+
},
|
|
44
|
+
sandbox: {
|
|
45
|
+
allowedCommands: ['npm test', 'npm run build', 'npx tsc --noEmit'],
|
|
46
|
+
networkAccess: false,
|
|
47
|
+
},
|
|
48
|
+
memory: {
|
|
49
|
+
type: 'persistent',
|
|
50
|
+
path: '.phantomind/memory/',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
budget: {
|
|
54
|
+
maxTokensPerTask: 100000,
|
|
55
|
+
maxCostPerDay: 10.0,
|
|
56
|
+
warningAt: 80,
|
|
57
|
+
fallbackOnBudget: 'budget',
|
|
58
|
+
},
|
|
59
|
+
git: {
|
|
60
|
+
autoCommit: false,
|
|
61
|
+
commitMessageFormat: 'conventional',
|
|
62
|
+
requireApproval: true,
|
|
63
|
+
},
|
|
64
|
+
team: {
|
|
65
|
+
contextSync: false,
|
|
66
|
+
remoteContext: '',
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Find project root by looking for .phantomind/ directory
|
|
71
|
+
*/
|
|
72
|
+
export async function findProjectRoot(startDir) {
|
|
73
|
+
let dir = startDir ? resolve(startDir) : process.cwd();
|
|
74
|
+
const root = resolve('/');
|
|
75
|
+
while (dir !== root) {
|
|
76
|
+
try {
|
|
77
|
+
await access(join(dir, '.phantomind'));
|
|
78
|
+
return dir;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
dir = resolve(dir, '..');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// If no .phantomind/ found, use cwd
|
|
85
|
+
return startDir ? resolve(startDir) : process.cwd();
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Resolve environment variable references in config values
|
|
89
|
+
*/
|
|
90
|
+
function resolveEnvVars(value) {
|
|
91
|
+
if (typeof value === 'string') {
|
|
92
|
+
return value.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
|
|
93
|
+
return process.env[envVar] ?? '';
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
return value.map(resolveEnvVars);
|
|
98
|
+
}
|
|
99
|
+
if (value !== null && typeof value === 'object') {
|
|
100
|
+
const resolved = {};
|
|
101
|
+
for (const [key, val] of Object.entries(value)) {
|
|
102
|
+
resolved[key] = resolveEnvVars(val);
|
|
103
|
+
}
|
|
104
|
+
return resolved;
|
|
105
|
+
}
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Load PhantomMindAI configuration from .phantomind/config.yaml
|
|
110
|
+
*/
|
|
111
|
+
export async function loadConfig(projectRoot) {
|
|
112
|
+
const root = projectRoot ?? await findProjectRoot();
|
|
113
|
+
const configPath = join(root, '.phantomind', 'config.yaml');
|
|
114
|
+
try {
|
|
115
|
+
const raw = await readFile(configPath, 'utf-8');
|
|
116
|
+
const parsed = yaml.load(raw);
|
|
117
|
+
const resolved = resolveEnvVars(parsed);
|
|
118
|
+
// Deep merge with defaults
|
|
119
|
+
const merged = deepMerge(DEFAULT_CONFIG, resolved);
|
|
120
|
+
// Validate
|
|
121
|
+
const result = PhantomConfigSchema.safeParse(merged);
|
|
122
|
+
if (!result.success) {
|
|
123
|
+
const errors = result.error.issues.map(i => ` - ${i.path.join('.')}: ${i.message}`).join('\n');
|
|
124
|
+
throw new Error(`Invalid PhantomMindAI config:\n${errors}`);
|
|
125
|
+
}
|
|
126
|
+
return result.data;
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (err.code === 'ENOENT') {
|
|
130
|
+
return DEFAULT_CONFIG;
|
|
131
|
+
}
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Load .phantomind/.env file into process.env
|
|
137
|
+
*/
|
|
138
|
+
export async function loadEnvFile(projectRoot) {
|
|
139
|
+
const root = projectRoot ?? await findProjectRoot();
|
|
140
|
+
const envPath = join(root, '.phantomind', '.env');
|
|
141
|
+
try {
|
|
142
|
+
const content = await readFile(envPath, 'utf-8');
|
|
143
|
+
for (const line of content.split('\n')) {
|
|
144
|
+
const trimmed = line.trim();
|
|
145
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
146
|
+
continue;
|
|
147
|
+
const eqIdx = trimmed.indexOf('=');
|
|
148
|
+
if (eqIdx === -1)
|
|
149
|
+
continue;
|
|
150
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
151
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
152
|
+
// Remove surrounding quotes
|
|
153
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
154
|
+
value = value.slice(1, -1);
|
|
155
|
+
}
|
|
156
|
+
if (!process.env[key]) {
|
|
157
|
+
process.env[key] = value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// .env file not required
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get the default configuration
|
|
167
|
+
*/
|
|
168
|
+
export function getDefaultConfig() {
|
|
169
|
+
return structuredClone(DEFAULT_CONFIG);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Deep merge two objects
|
|
173
|
+
*/
|
|
174
|
+
export function deepMerge(target, source) {
|
|
175
|
+
if (source === undefined || source === null)
|
|
176
|
+
return target;
|
|
177
|
+
if (target === undefined || target === null)
|
|
178
|
+
return source;
|
|
179
|
+
if (typeof target !== 'object' || typeof source !== 'object') {
|
|
180
|
+
return source;
|
|
181
|
+
}
|
|
182
|
+
if (Array.isArray(source))
|
|
183
|
+
return source;
|
|
184
|
+
const result = { ...target };
|
|
185
|
+
for (const [key, value] of Object.entries(source)) {
|
|
186
|
+
result[key] = deepMerge(result[key], value);
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhantomMindAI — Live Codebase Embedder
|
|
3
|
+
* Creates and maintains embeddings of project source files for semantic search.
|
|
4
|
+
*/
|
|
5
|
+
export declare class CodebaseEmbedder {
|
|
6
|
+
private projectRoot;
|
|
7
|
+
private embeddings;
|
|
8
|
+
private documentFrequency;
|
|
9
|
+
private totalDocuments;
|
|
10
|
+
private cachePath;
|
|
11
|
+
private initialized;
|
|
12
|
+
constructor(projectRoot: string);
|
|
13
|
+
/**
|
|
14
|
+
* Build or update embeddings for the codebase
|
|
15
|
+
*/
|
|
16
|
+
build(): Promise<{
|
|
17
|
+
indexed: number;
|
|
18
|
+
skipped: number;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* Semantic search across the codebase
|
|
22
|
+
*/
|
|
23
|
+
search(query: string, limit?: number): Promise<Array<{
|
|
24
|
+
path: string;
|
|
25
|
+
score: number;
|
|
26
|
+
snippet: string;
|
|
27
|
+
}>>;
|
|
28
|
+
/**
|
|
29
|
+
* Get relevant code snippet from a file
|
|
30
|
+
*/
|
|
31
|
+
private getSnippet;
|
|
32
|
+
/**
|
|
33
|
+
* Tokenize text into meaningful words
|
|
34
|
+
*/
|
|
35
|
+
private tokenize;
|
|
36
|
+
/**
|
|
37
|
+
* Compute term frequency
|
|
38
|
+
*/
|
|
39
|
+
private computeTF;
|
|
40
|
+
/**
|
|
41
|
+
* Cosine similarity between two TF-IDF vectors
|
|
42
|
+
*/
|
|
43
|
+
private cosineSimilarity;
|
|
44
|
+
/**
|
|
45
|
+
* Save embeddings to cache
|
|
46
|
+
*/
|
|
47
|
+
private saveCache;
|
|
48
|
+
/**
|
|
49
|
+
* Load embeddings from cache
|
|
50
|
+
*/
|
|
51
|
+
private loadCache;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=embedder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedder.d.ts","sourceRoot":"","sources":["../../src/context/embedder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA0BH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAyC;IAC3D,OAAO,CAAC,iBAAiB,CAAkC;IAC3D,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAS;gBAEhB,WAAW,EAAE,MAAM;IAK/B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAmE5D;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAkCxG;;OAEG;YACW,UAAU;IA4BxB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAShB;;OAEG;IACH,OAAO,CAAC,SAAS;IAYjB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;OAEG;YACW,SAAS;IAmBvB;;OAEG;YACW,SAAS;CAqBxB"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhantomMindAI — Live Codebase Embedder
|
|
3
|
+
* Creates and maintains embeddings of project source files for semantic search.
|
|
4
|
+
*/
|
|
5
|
+
import { readFile, stat, writeFile, mkdir } from 'node:fs/promises';
|
|
6
|
+
import { join, extname } from 'node:path';
|
|
7
|
+
import fastGlob from 'fast-glob';
|
|
8
|
+
const CODE_EXTENSIONS = new Set([
|
|
9
|
+
'.ts', '.tsx', '.js', '.jsx', '.swift', '.go', '.py', '.rs', '.java',
|
|
10
|
+
'.kt', '.rb', '.php', '.c', '.cpp', '.h', '.cs', '.vue', '.svelte',
|
|
11
|
+
'.md', '.json', '.yaml', '.yml', '.toml',
|
|
12
|
+
]);
|
|
13
|
+
const IGNORE_DIRS = new Set([
|
|
14
|
+
'node_modules', '.git', 'dist', 'build', '.next', '.nuxt',
|
|
15
|
+
'coverage', '.phantomind/cache', '.phantomind/memory', '.phantomind/audit',
|
|
16
|
+
'vendor', '__pycache__', '.tox', 'venv', '.venv',
|
|
17
|
+
]);
|
|
18
|
+
export class CodebaseEmbedder {
|
|
19
|
+
projectRoot;
|
|
20
|
+
embeddings = new Map();
|
|
21
|
+
documentFrequency = new Map();
|
|
22
|
+
totalDocuments = 0;
|
|
23
|
+
cachePath;
|
|
24
|
+
initialized = false;
|
|
25
|
+
constructor(projectRoot) {
|
|
26
|
+
this.projectRoot = projectRoot;
|
|
27
|
+
this.cachePath = join(projectRoot, '.phantomind', 'cache', 'embeddings.json');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build or update embeddings for the codebase
|
|
31
|
+
*/
|
|
32
|
+
async build() {
|
|
33
|
+
let indexed = 0;
|
|
34
|
+
let skipped = 0;
|
|
35
|
+
const files = await fastGlob('**/*', {
|
|
36
|
+
cwd: this.projectRoot,
|
|
37
|
+
ignore: [...IGNORE_DIRS].map(d => `${d}/**`),
|
|
38
|
+
onlyFiles: true,
|
|
39
|
+
absolute: false,
|
|
40
|
+
});
|
|
41
|
+
const sourceFiles = files.filter(f => CODE_EXTENSIONS.has(extname(f)));
|
|
42
|
+
// Build TF for each document
|
|
43
|
+
for (const file of sourceFiles) {
|
|
44
|
+
const fullPath = join(this.projectRoot, file);
|
|
45
|
+
try {
|
|
46
|
+
const stats = await stat(fullPath);
|
|
47
|
+
const existing = this.embeddings.get(file);
|
|
48
|
+
// Skip if not modified
|
|
49
|
+
if (existing && existing.lastModified >= stats.mtimeMs) {
|
|
50
|
+
skipped++;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
54
|
+
const tokens = this.tokenize(content);
|
|
55
|
+
const tf = this.computeTF(tokens);
|
|
56
|
+
this.embeddings.set(file, {
|
|
57
|
+
path: file,
|
|
58
|
+
tokens,
|
|
59
|
+
tfidf: tf,
|
|
60
|
+
lastModified: stats.mtimeMs,
|
|
61
|
+
});
|
|
62
|
+
indexed++;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
skipped++;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Compute IDF
|
|
69
|
+
this.totalDocuments = this.embeddings.size;
|
|
70
|
+
this.documentFrequency.clear();
|
|
71
|
+
for (const embedding of this.embeddings.values()) {
|
|
72
|
+
const uniqueTokens = new Set(embedding.tokens);
|
|
73
|
+
for (const token of uniqueTokens) {
|
|
74
|
+
this.documentFrequency.set(token, (this.documentFrequency.get(token) ?? 0) + 1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Update TF-IDF scores
|
|
78
|
+
for (const embedding of this.embeddings.values()) {
|
|
79
|
+
for (const [token, tf] of embedding.tfidf.entries()) {
|
|
80
|
+
const df = this.documentFrequency.get(token) ?? 1;
|
|
81
|
+
const idf = Math.log(this.totalDocuments / df) + 1;
|
|
82
|
+
embedding.tfidf.set(token, tf * idf);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
this.initialized = true;
|
|
86
|
+
await this.saveCache();
|
|
87
|
+
return { indexed, skipped };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Semantic search across the codebase
|
|
91
|
+
*/
|
|
92
|
+
async search(query, limit = 5) {
|
|
93
|
+
if (!this.initialized) {
|
|
94
|
+
await this.loadCache();
|
|
95
|
+
if (!this.initialized) {
|
|
96
|
+
await this.build();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const queryTokens = this.tokenize(query);
|
|
100
|
+
const queryTF = this.computeTF(queryTokens);
|
|
101
|
+
const results = [];
|
|
102
|
+
for (const [path, embedding] of this.embeddings) {
|
|
103
|
+
const score = this.cosineSimilarity(queryTF, embedding.tfidf);
|
|
104
|
+
if (score > 0) {
|
|
105
|
+
results.push({ path, score });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
results.sort((a, b) => b.score - a.score);
|
|
109
|
+
const topResults = results.slice(0, limit);
|
|
110
|
+
// Load snippets for top results
|
|
111
|
+
const withSnippets = await Promise.all(topResults.map(async (r) => {
|
|
112
|
+
const content = await this.getSnippet(r.path, queryTokens);
|
|
113
|
+
return { ...r, snippet: content };
|
|
114
|
+
}));
|
|
115
|
+
return withSnippets;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get relevant code snippet from a file
|
|
119
|
+
*/
|
|
120
|
+
async getSnippet(filePath, queryTokens) {
|
|
121
|
+
try {
|
|
122
|
+
const content = await readFile(join(this.projectRoot, filePath), 'utf-8');
|
|
123
|
+
const lines = content.split('\n');
|
|
124
|
+
// Find the most relevant line range
|
|
125
|
+
let bestStart = 0;
|
|
126
|
+
let bestScore = 0;
|
|
127
|
+
const windowSize = 15;
|
|
128
|
+
for (let i = 0; i < lines.length - windowSize; i++) {
|
|
129
|
+
const window = lines.slice(i, i + windowSize).join('\n').toLowerCase();
|
|
130
|
+
let score = 0;
|
|
131
|
+
for (const token of queryTokens) {
|
|
132
|
+
if (window.includes(token))
|
|
133
|
+
score++;
|
|
134
|
+
}
|
|
135
|
+
if (score > bestScore) {
|
|
136
|
+
bestScore = score;
|
|
137
|
+
bestStart = i;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return lines.slice(bestStart, bestStart + windowSize).join('\n');
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return '';
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Tokenize text into meaningful words
|
|
148
|
+
*/
|
|
149
|
+
tokenize(text) {
|
|
150
|
+
return (text
|
|
151
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase split
|
|
152
|
+
.replace(/[_\-./\\]/g, ' ') // separator split
|
|
153
|
+
.match(/[a-zA-Z][a-zA-Z0-9]{1,}/g) ?? [])
|
|
154
|
+
.map(w => w.toLowerCase())
|
|
155
|
+
.filter(w => w.length > 2);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Compute term frequency
|
|
159
|
+
*/
|
|
160
|
+
computeTF(tokens) {
|
|
161
|
+
const tf = new Map();
|
|
162
|
+
const total = tokens.length || 1;
|
|
163
|
+
for (const token of tokens) {
|
|
164
|
+
tf.set(token, (tf.get(token) ?? 0) + 1);
|
|
165
|
+
}
|
|
166
|
+
for (const [token, count] of tf.entries()) {
|
|
167
|
+
tf.set(token, count / total);
|
|
168
|
+
}
|
|
169
|
+
return tf;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Cosine similarity between two TF-IDF vectors
|
|
173
|
+
*/
|
|
174
|
+
cosineSimilarity(a, b) {
|
|
175
|
+
let dotProduct = 0;
|
|
176
|
+
let normA = 0;
|
|
177
|
+
let normB = 0;
|
|
178
|
+
for (const [token, va] of a) {
|
|
179
|
+
const vb = b.get(token) ?? 0;
|
|
180
|
+
dotProduct += va * vb;
|
|
181
|
+
normA += va * va;
|
|
182
|
+
}
|
|
183
|
+
for (const vb of b.values()) {
|
|
184
|
+
normB += vb * vb;
|
|
185
|
+
}
|
|
186
|
+
const denominator = Math.sqrt(normA) * Math.sqrt(normB);
|
|
187
|
+
return denominator === 0 ? 0 : dotProduct / denominator;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Save embeddings to cache
|
|
191
|
+
*/
|
|
192
|
+
async saveCache() {
|
|
193
|
+
try {
|
|
194
|
+
await mkdir(join(this.projectRoot, '.phantomind', 'cache'), { recursive: true });
|
|
195
|
+
const data = {
|
|
196
|
+
totalDocuments: this.totalDocuments,
|
|
197
|
+
embeddings: Array.from(this.embeddings.entries()).map(([path, emb]) => ({
|
|
198
|
+
path,
|
|
199
|
+
tokens: emb.tokens.slice(0, 200), // limit for cache size
|
|
200
|
+
tfidf: Object.fromEntries(emb.tfidf),
|
|
201
|
+
lastModified: emb.lastModified,
|
|
202
|
+
})),
|
|
203
|
+
documentFrequency: Object.fromEntries(this.documentFrequency),
|
|
204
|
+
};
|
|
205
|
+
await writeFile(this.cachePath, JSON.stringify(data), 'utf-8');
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// Cache save failure is non-critical
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Load embeddings from cache
|
|
213
|
+
*/
|
|
214
|
+
async loadCache() {
|
|
215
|
+
try {
|
|
216
|
+
const raw = await readFile(this.cachePath, 'utf-8');
|
|
217
|
+
const data = JSON.parse(raw);
|
|
218
|
+
this.totalDocuments = data.totalDocuments;
|
|
219
|
+
this.documentFrequency = new Map(Object.entries(data.documentFrequency));
|
|
220
|
+
for (const emb of data.embeddings) {
|
|
221
|
+
this.embeddings.set(emb.path, {
|
|
222
|
+
path: emb.path,
|
|
223
|
+
tokens: emb.tokens,
|
|
224
|
+
tfidf: new Map(Object.entries(emb.tfidf)),
|
|
225
|
+
lastModified: emb.lastModified,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
this.initialized = true;
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// No cache or invalid cache
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=embedder.js.map
|