@theihtisham/agent-shadow-brain 1.0.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/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/adapters/base-adapter.d.ts +18 -0
- package/dist/adapters/base-adapter.d.ts.map +1 -0
- package/dist/adapters/base-adapter.js +110 -0
- package/dist/adapters/base-adapter.js.map +1 -0
- package/dist/adapters/claude-code.d.ts +14 -0
- package/dist/adapters/claude-code.d.ts.map +1 -0
- package/dist/adapters/claude-code.js +125 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/cline.d.ts +13 -0
- package/dist/adapters/cline.d.ts.map +1 -0
- package/dist/adapters/cline.js +119 -0
- package/dist/adapters/cline.js.map +1 -0
- package/dist/adapters/codex.d.ts +12 -0
- package/dist/adapters/codex.d.ts.map +1 -0
- package/dist/adapters/codex.js +65 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +38 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/kilo-code.d.ts +12 -0
- package/dist/adapters/kilo-code.d.ts.map +1 -0
- package/dist/adapters/kilo-code.js +94 -0
- package/dist/adapters/kilo-code.js.map +1 -0
- package/dist/adapters/opencode.d.ts +12 -0
- package/dist/adapters/opencode.d.ts.map +1 -0
- package/dist/adapters/opencode.js +100 -0
- package/dist/adapters/opencode.js.map +1 -0
- package/dist/brain/analyzer.d.ts +23 -0
- package/dist/brain/analyzer.d.ts.map +1 -0
- package/dist/brain/analyzer.js +218 -0
- package/dist/brain/analyzer.js.map +1 -0
- package/dist/brain/llm-client.d.ts +30 -0
- package/dist/brain/llm-client.d.ts.map +1 -0
- package/dist/brain/llm-client.js +240 -0
- package/dist/brain/llm-client.js.map +1 -0
- package/dist/brain/orchestrator.d.ts +41 -0
- package/dist/brain/orchestrator.d.ts.map +1 -0
- package/dist/brain/orchestrator.js +289 -0
- package/dist/brain/orchestrator.js.map +1 -0
- package/dist/brain/project-context.d.ts +15 -0
- package/dist/brain/project-context.d.ts.map +1 -0
- package/dist/brain/project-context.js +225 -0
- package/dist/brain/project-context.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +254 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/dashboard.d.ts +3 -0
- package/dist/ui/dashboard.d.ts.map +1 -0
- package/dist/ui/dashboard.js +110 -0
- package/dist/ui/dashboard.js.map +1 -0
- package/dist/watchers/file-watcher.d.ts +18 -0
- package/dist/watchers/file-watcher.d.ts.map +1 -0
- package/dist/watchers/file-watcher.js +132 -0
- package/dist/watchers/file-watcher.js.map +1 -0
- package/dist/watchers/git-watcher.d.ts +31 -0
- package/dist/watchers/git-watcher.d.ts.map +1 -0
- package/dist/watchers/git-watcher.js +93 -0
- package/dist/watchers/git-watcher.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// src/adapters/codex.ts — Adapter for OpenAI Codex CLI
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import { BaseAdapter } from './base-adapter.js';
|
|
7
|
+
export class CodexAdapter extends BaseAdapter {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
this.name = 'codex';
|
|
11
|
+
this.displayName = 'Codex CLI';
|
|
12
|
+
}
|
|
13
|
+
getGlobalDir() {
|
|
14
|
+
return path.join(os.homedir(), '.codex');
|
|
15
|
+
}
|
|
16
|
+
async detect() {
|
|
17
|
+
if (fs.existsSync(this.getGlobalDir()))
|
|
18
|
+
return true;
|
|
19
|
+
if (fs.existsSync(path.join(this.projectDir, 'AGENTS.md')))
|
|
20
|
+
return true;
|
|
21
|
+
try {
|
|
22
|
+
execSync('codex --version', { timeout: 3000, encoding: 'utf-8' });
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
getConfigPaths() {
|
|
30
|
+
return {
|
|
31
|
+
memoryDir: path.join(this.projectDir, '.codex', 'memory'),
|
|
32
|
+
rulesDir: this.projectDir, // Codex reads AGENTS.md from project root
|
|
33
|
+
conversationDir: path.join(this.getGlobalDir(), 'sessions'),
|
|
34
|
+
configFile: path.join(this.getGlobalDir(), 'config.json'),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async readActivity() {
|
|
38
|
+
// Codex CLI is newer, may not have persistent session logs yet
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
async injectContext(insight) {
|
|
42
|
+
// Codex reads AGENTS.md from project root
|
|
43
|
+
const agentsFile = path.join(this.projectDir, 'AGENTS.md');
|
|
44
|
+
const brainSection = `
|
|
45
|
+
## 🧠 Shadow Brain — ${insight.type.toUpperCase()}
|
|
46
|
+
|
|
47
|
+
**Priority:** ${insight.priority}
|
|
48
|
+
**Time:** ${insight.timestamp.toISOString()}
|
|
49
|
+
|
|
50
|
+
${insight.content}
|
|
51
|
+
${insight.files?.length ? `\nRelevant files: ${insight.files.map(f => `\`${f}\``).join(', ')}` : ''}
|
|
52
|
+
`;
|
|
53
|
+
const existing = this.readFile(agentsFile) || '# Agent Instructions\n';
|
|
54
|
+
// Replace or append brain section
|
|
55
|
+
if (existing.includes('## 🧠 Shadow Brain')) {
|
|
56
|
+
const before = existing.split('## 🧠 Shadow Brain')[0];
|
|
57
|
+
fs.writeFileSync(agentsFile, before + brainSection, 'utf-8');
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
fs.writeFileSync(agentsFile, existing + '\n' + brainSection, 'utf-8');
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/adapters/codex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AgentAdapter, AgentTool } from '../types.js';
|
|
2
|
+
import { ClaudeCodeAdapter } from './claude-code.js';
|
|
3
|
+
import { KiloCodeAdapter } from './kilo-code.js';
|
|
4
|
+
import { ClineAdapter } from './cline.js';
|
|
5
|
+
import { OpenCodeAdapter } from './opencode.js';
|
|
6
|
+
import { CodexAdapter } from './codex.js';
|
|
7
|
+
export declare const ALL_ADAPTERS: Record<AgentTool, () => AgentAdapter>;
|
|
8
|
+
export declare function createAdapter(tool: AgentTool): AgentAdapter;
|
|
9
|
+
export declare function detectRunningAgents(projectDir: string): Promise<AgentAdapter[]>;
|
|
10
|
+
export { ClaudeCodeAdapter, KiloCodeAdapter, ClineAdapter, OpenCodeAdapter, CodexAdapter };
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,YAAY,CAS9D,CAAC;AAEF,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,YAAY,CAI3D;AAED,wBAAsB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAcrF;AAED,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/adapters/index.ts — Export all adapters + factory
|
|
2
|
+
import { ClaudeCodeAdapter } from './claude-code.js';
|
|
3
|
+
import { KiloCodeAdapter } from './kilo-code.js';
|
|
4
|
+
import { ClineAdapter } from './cline.js';
|
|
5
|
+
import { OpenCodeAdapter } from './opencode.js';
|
|
6
|
+
import { CodexAdapter } from './codex.js';
|
|
7
|
+
export const ALL_ADAPTERS = {
|
|
8
|
+
'claude-code': () => new ClaudeCodeAdapter(),
|
|
9
|
+
'kilo-code': () => new KiloCodeAdapter(),
|
|
10
|
+
'cline': () => new ClineAdapter(),
|
|
11
|
+
'opencode': () => new OpenCodeAdapter(),
|
|
12
|
+
'codex': () => new CodexAdapter(),
|
|
13
|
+
'roo-code': () => new KiloCodeAdapter(), // Roo Code is Kilo Code fork
|
|
14
|
+
'aider': () => new OpenCodeAdapter(), // Similar CLI pattern
|
|
15
|
+
'cursor': () => new ClineAdapter(), // Similar VS Code pattern
|
|
16
|
+
};
|
|
17
|
+
export function createAdapter(tool) {
|
|
18
|
+
const factory = ALL_ADAPTERS[tool];
|
|
19
|
+
if (!factory)
|
|
20
|
+
throw new Error(`Unknown agent tool: ${tool}`);
|
|
21
|
+
return factory();
|
|
22
|
+
}
|
|
23
|
+
export async function detectRunningAgents(projectDir) {
|
|
24
|
+
const detected = [];
|
|
25
|
+
for (const [tool, factory] of Object.entries(ALL_ADAPTERS)) {
|
|
26
|
+
const adapter = factory();
|
|
27
|
+
adapter.projectDir = projectDir;
|
|
28
|
+
try {
|
|
29
|
+
if (await adapter.detect()) {
|
|
30
|
+
detected.push(adapter);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch { /* skip failures */ }
|
|
34
|
+
}
|
|
35
|
+
return detected;
|
|
36
|
+
}
|
|
37
|
+
export { ClaudeCodeAdapter, KiloCodeAdapter, ClineAdapter, OpenCodeAdapter, CodexAdapter };
|
|
38
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAGxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,CAAC,MAAM,YAAY,GAA0C;IACjE,aAAa,EAAE,GAAG,EAAE,CAAC,IAAI,iBAAiB,EAAE;IAC5C,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,eAAe,EAAE;IACxC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,YAAY,EAAE;IACjC,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,eAAe,EAAE;IACvC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,YAAY,EAAE;IACjC,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,eAAe,EAAE,EAAE,6BAA6B;IACtE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,eAAe,EAAE,EAAK,sBAAsB;IAC/D,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,YAAY,EAAE,EAAO,0BAA0B;CACpE,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,IAAe;IAC3C,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;IAC7D,OAAO,OAAO,EAAE,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,UAAkB;IAC1D,MAAM,QAAQ,GAAmB,EAAE,CAAC;IAEpC,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QAC3D,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC;QACzB,OAAe,CAAC,UAAU,GAAG,UAAU,CAAC;QACzC,IAAI,CAAC;YACH,IAAI,MAAM,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC3B,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseAdapter } from './base-adapter.js';
|
|
2
|
+
import { AgentTool, AgentPaths, AgentActivity, BrainInsight } from '../types.js';
|
|
3
|
+
export declare class KiloCodeAdapter extends BaseAdapter {
|
|
4
|
+
name: AgentTool;
|
|
5
|
+
displayName: string;
|
|
6
|
+
private getGlobalDir;
|
|
7
|
+
detect(): Promise<boolean>;
|
|
8
|
+
getConfigPaths(): AgentPaths;
|
|
9
|
+
readActivity(): Promise<AgentActivity[]>;
|
|
10
|
+
injectContext(insight: BrainInsight): Promise<boolean>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=kilo-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kilo-code.d.ts","sourceRoot":"","sources":["../../src/adapters/kilo-code.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEjF,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,IAAI,EAAE,SAAS,CAAe;IAC9B,WAAW,SAAe;IAE1B,OAAO,CAAC,YAAY;IAWd,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAShC,cAAc,IAAI,UAAU;IAStB,YAAY,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IA2CxC,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;CAe7D"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// src/adapters/kilo-code.ts — Adapter for Kilo Code (VS Code extension)
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
import { BaseAdapter } from './base-adapter.js';
|
|
6
|
+
export class KiloCodeAdapter extends BaseAdapter {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
this.name = 'kilo-code';
|
|
10
|
+
this.displayName = 'Kilo Code';
|
|
11
|
+
}
|
|
12
|
+
getGlobalDir() {
|
|
13
|
+
// Kilo Code stores data in VS Code's extension data
|
|
14
|
+
const platform = os.platform();
|
|
15
|
+
if (platform === 'win32') {
|
|
16
|
+
return path.join(os.homedir(), 'AppData', 'Roaming', 'Code', 'User', 'globalStorage', 'kilocode.kilo-code');
|
|
17
|
+
}
|
|
18
|
+
else if (platform === 'darwin') {
|
|
19
|
+
return path.join(os.homedir(), 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'kilocode.kilo-code');
|
|
20
|
+
}
|
|
21
|
+
return path.join(os.homedir(), '.config', 'Code', 'User', 'globalStorage', 'kilocode.kilo-code');
|
|
22
|
+
}
|
|
23
|
+
async detect() {
|
|
24
|
+
// Check for Kilo Code extension data
|
|
25
|
+
const dir = this.getGlobalDir();
|
|
26
|
+
if (fs.existsSync(dir))
|
|
27
|
+
return true;
|
|
28
|
+
// Check for .kilocode in project
|
|
29
|
+
return fs.existsSync(path.join(this.projectDir, '.kilocode'));
|
|
30
|
+
}
|
|
31
|
+
getConfigPaths() {
|
|
32
|
+
return {
|
|
33
|
+
memoryDir: path.join(this.projectDir, '.kilocode', 'memory'),
|
|
34
|
+
rulesDir: path.join(this.projectDir, '.kilocode', 'rules'),
|
|
35
|
+
conversationDir: path.join(this.getGlobalDir(), 'tasks'),
|
|
36
|
+
configFile: path.join(this.projectDir, '.kilocode', 'settings.json'),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async readActivity() {
|
|
40
|
+
const activities = [];
|
|
41
|
+
const tasksDir = path.join(this.getGlobalDir(), 'tasks');
|
|
42
|
+
if (!fs.existsSync(tasksDir))
|
|
43
|
+
return activities;
|
|
44
|
+
try {
|
|
45
|
+
// Kilo Code stores tasks as directories with JSON files
|
|
46
|
+
const taskDirs = fs.readdirSync(tasksDir)
|
|
47
|
+
.map(d => path.join(tasksDir, d))
|
|
48
|
+
.filter(d => fs.statSync(d).isDirectory())
|
|
49
|
+
.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs)
|
|
50
|
+
.slice(0, 5);
|
|
51
|
+
for (const taskDir of taskDirs) {
|
|
52
|
+
const apiFile = path.join(taskDir, 'api_conversation_history.json');
|
|
53
|
+
if (fs.existsSync(apiFile)) {
|
|
54
|
+
try {
|
|
55
|
+
const data = JSON.parse(fs.readFileSync(apiFile, 'utf-8'));
|
|
56
|
+
if (Array.isArray(data)) {
|
|
57
|
+
for (const msg of data.slice(-20)) {
|
|
58
|
+
if (msg.role === 'assistant' && Array.isArray(msg.content)) {
|
|
59
|
+
for (const block of msg.content) {
|
|
60
|
+
if (block.type === 'tool_use') {
|
|
61
|
+
activities.push({
|
|
62
|
+
timestamp: new Date(),
|
|
63
|
+
type: block.name?.includes('write') ? 'file_edit' : 'file_read',
|
|
64
|
+
detail: `${block.name}: ${JSON.stringify(block.input || {}).slice(0, 200)}`,
|
|
65
|
+
file: block.input?.path,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch { /* malformed */ }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch { /* permission issues */ }
|
|
78
|
+
return activities;
|
|
79
|
+
}
|
|
80
|
+
async injectContext(insight) {
|
|
81
|
+
// Inject into .kilocode/rules/ as custom instructions
|
|
82
|
+
const rulesDir = path.join(this.projectDir, '.kilocode', 'rules');
|
|
83
|
+
fs.mkdirSync(rulesDir, { recursive: true });
|
|
84
|
+
const brainFile = path.join(rulesDir, 'shadow-brain.md');
|
|
85
|
+
const existing = this.readFile(brainFile) || '';
|
|
86
|
+
const updated = this.appendToBrainFile(existing, insight);
|
|
87
|
+
fs.writeFileSync(brainFile, updated, 'utf-8');
|
|
88
|
+
// Also write to the memory dir
|
|
89
|
+
const memoryDir = path.join(this.projectDir, '.kilocode', 'memory');
|
|
90
|
+
fs.mkdirSync(memoryDir, { recursive: true });
|
|
91
|
+
return await super.injectContext(insight);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=kilo-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kilo-code.js","sourceRoot":"","sources":["../../src/adapters/kilo-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseAdapter } from './base-adapter.js';
|
|
2
|
+
import { AgentTool, AgentPaths, AgentActivity, BrainInsight } from '../types.js';
|
|
3
|
+
export declare class OpenCodeAdapter extends BaseAdapter {
|
|
4
|
+
name: AgentTool;
|
|
5
|
+
displayName: string;
|
|
6
|
+
private getGlobalDir;
|
|
7
|
+
detect(): Promise<boolean>;
|
|
8
|
+
getConfigPaths(): AgentPaths;
|
|
9
|
+
readActivity(): Promise<AgentActivity[]>;
|
|
10
|
+
injectContext(insight: BrainInsight): Promise<boolean>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=opencode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../src/adapters/opencode.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEjF,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,IAAI,EAAE,SAAS,CAAc;IAC7B,WAAW,SAAc;IAEzB,OAAO,CAAC,YAAY;IAId,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAkBhC,cAAc,IAAI,UAAU;IAStB,YAAY,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAuCxC,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;CAkB7D"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// src/adapters/opencode.ts — Adapter for OpenCode CLI
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import { BaseAdapter } from './base-adapter.js';
|
|
7
|
+
export class OpenCodeAdapter extends BaseAdapter {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
this.name = 'opencode';
|
|
11
|
+
this.displayName = 'OpenCode';
|
|
12
|
+
}
|
|
13
|
+
getGlobalDir() {
|
|
14
|
+
return path.join(os.homedir(), '.opencode');
|
|
15
|
+
}
|
|
16
|
+
async detect() {
|
|
17
|
+
// Check for opencode config
|
|
18
|
+
if (fs.existsSync(this.getGlobalDir()))
|
|
19
|
+
return true;
|
|
20
|
+
if (fs.existsSync(path.join(this.projectDir, '.opencode')))
|
|
21
|
+
return true;
|
|
22
|
+
if (fs.existsSync(path.join(this.projectDir, 'opencode.json')))
|
|
23
|
+
return true;
|
|
24
|
+
// Check for running process
|
|
25
|
+
try {
|
|
26
|
+
const result = execSync('tasklist /FI "IMAGENAME eq opencode*" /NH 2>NUL || pgrep -f opencode 2>/dev/null', {
|
|
27
|
+
encoding: 'utf-8',
|
|
28
|
+
timeout: 3000,
|
|
29
|
+
});
|
|
30
|
+
return result.trim().length > 0;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
getConfigPaths() {
|
|
37
|
+
return {
|
|
38
|
+
memoryDir: path.join(this.projectDir, '.opencode', 'memory'),
|
|
39
|
+
rulesDir: path.join(this.projectDir, '.opencode', 'rules'),
|
|
40
|
+
conversationDir: path.join(this.getGlobalDir(), 'sessions'),
|
|
41
|
+
configFile: path.join(this.projectDir, 'opencode.json'),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async readActivity() {
|
|
45
|
+
const activities = [];
|
|
46
|
+
const sessionsDir = path.join(this.getGlobalDir(), 'sessions');
|
|
47
|
+
if (!fs.existsSync(sessionsDir))
|
|
48
|
+
return activities;
|
|
49
|
+
try {
|
|
50
|
+
const files = fs.readdirSync(sessionsDir)
|
|
51
|
+
.filter(f => f.endsWith('.json'))
|
|
52
|
+
.sort((a, b) => {
|
|
53
|
+
try {
|
|
54
|
+
return fs.statSync(path.join(sessionsDir, b)).mtimeMs - fs.statSync(path.join(sessionsDir, a)).mtimeMs;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
.slice(0, 3);
|
|
61
|
+
for (const file of files) {
|
|
62
|
+
try {
|
|
63
|
+
const data = JSON.parse(fs.readFileSync(path.join(sessionsDir, file), 'utf-8'));
|
|
64
|
+
if (data.messages) {
|
|
65
|
+
for (const msg of data.messages.slice(-20)) {
|
|
66
|
+
if (msg.tool_calls) {
|
|
67
|
+
for (const call of msg.tool_calls) {
|
|
68
|
+
activities.push({
|
|
69
|
+
timestamp: new Date(msg.timestamp || Date.now()),
|
|
70
|
+
type: call.function?.name?.includes('write') ? 'file_edit' : 'file_read',
|
|
71
|
+
detail: `${call.function?.name}: ${JSON.stringify(call.function?.arguments || {}).slice(0, 200)}`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch { /* skip */ }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch { /* permission */ }
|
|
82
|
+
return activities;
|
|
83
|
+
}
|
|
84
|
+
async injectContext(insight) {
|
|
85
|
+
// OpenCode: inject into project .opencode/rules/
|
|
86
|
+
const rulesDir = path.join(this.projectDir, '.opencode', 'rules');
|
|
87
|
+
fs.mkdirSync(rulesDir, { recursive: true });
|
|
88
|
+
const brainFile = path.join(rulesDir, 'shadow-brain.md');
|
|
89
|
+
const existing = this.readFile(brainFile) || '';
|
|
90
|
+
const updated = this.appendToBrainFile(existing, insight);
|
|
91
|
+
fs.writeFileSync(brainFile, updated, 'utf-8');
|
|
92
|
+
// Also create AGENTS.md or .opencode.md for native pickup
|
|
93
|
+
const agentsMd = path.join(this.projectDir, 'AGENTS.md');
|
|
94
|
+
if (!fs.existsSync(agentsMd)) {
|
|
95
|
+
fs.writeFileSync(agentsMd, `# Agent Instructions\n\nSee .opencode/rules/shadow-brain.md for AI brain insights.\n`, 'utf-8');
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=opencode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../src/adapters/opencode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { LLMClient } from './llm-client.js';
|
|
2
|
+
import { BrainPersonality, BrainInsight, FileChange, ProjectContext, AgentActivity, AgentMemory } from '../types.js';
|
|
3
|
+
type ReviewDepth = 'quick' | 'standard' | 'deep';
|
|
4
|
+
interface AnalysisParams {
|
|
5
|
+
changes: FileChange[];
|
|
6
|
+
context: ProjectContext;
|
|
7
|
+
activity: AgentActivity[];
|
|
8
|
+
agentMemory?: AgentMemory;
|
|
9
|
+
}
|
|
10
|
+
export declare class Analyzer {
|
|
11
|
+
private llmClient;
|
|
12
|
+
private personality;
|
|
13
|
+
private reviewDepth;
|
|
14
|
+
constructor(llmClient: LLMClient, personality: BrainPersonality, reviewDepth: ReviewDepth);
|
|
15
|
+
analyze(params: AnalysisParams): Promise<BrainInsight[]>;
|
|
16
|
+
private ruleBasedAnalysis;
|
|
17
|
+
}
|
|
18
|
+
export declare class PromptBuilder {
|
|
19
|
+
static buildSystemPrompt(personality: BrainPersonality): string;
|
|
20
|
+
static buildUserPrompt(params: AnalysisParams, depth: ReviewDepth): string;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../src/brain/analyzer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAWrH,KAAK,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAEjD,UAAU,cAAc;IACtB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,WAAW,CAAc;gBAErB,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW;IAMnF,OAAO,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAqB9D,OAAO,CAAC,iBAAiB;CA8G1B;AAED,qBAAa,aAAa;IACxB,MAAM,CAAC,iBAAiB,CAAC,WAAW,EAAE,gBAAgB,GAAG,MAAM;IA4C/D,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM;CA6D3E"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// src/brain/analyzer.ts — Core analysis engine with LLM + rule-based fallback
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
const BrainInsightSchema = z.object({
|
|
4
|
+
type: z.enum(['review', 'suggestion', 'warning', 'context', 'pattern', 'instruction']),
|
|
5
|
+
priority: z.enum(['critical', 'high', 'medium', 'low']),
|
|
6
|
+
title: z.string().max(100),
|
|
7
|
+
content: z.string().max(2000),
|
|
8
|
+
files: z.array(z.string()).optional(),
|
|
9
|
+
});
|
|
10
|
+
const BrainInsightsSchema = z.array(BrainInsightSchema).min(1).max(5);
|
|
11
|
+
export class Analyzer {
|
|
12
|
+
constructor(llmClient, personality, reviewDepth) {
|
|
13
|
+
this.llmClient = llmClient;
|
|
14
|
+
this.personality = personality;
|
|
15
|
+
this.reviewDepth = reviewDepth;
|
|
16
|
+
}
|
|
17
|
+
async analyze(params) {
|
|
18
|
+
const systemPrompt = PromptBuilder.buildSystemPrompt(this.personality);
|
|
19
|
+
const userPrompt = PromptBuilder.buildUserPrompt(params, this.reviewDepth);
|
|
20
|
+
try {
|
|
21
|
+
const raw = await this.llmClient.completeWithSchema(userPrompt, BrainInsightsSchema, systemPrompt);
|
|
22
|
+
return raw.map(insight => ({
|
|
23
|
+
...insight,
|
|
24
|
+
timestamp: new Date(),
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Fallback to rule-based analysis when LLM is unavailable
|
|
29
|
+
return this.ruleBasedAnalysis(params.changes, params.context);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
ruleBasedAnalysis(changes, context) {
|
|
33
|
+
const insights = [];
|
|
34
|
+
const now = new Date();
|
|
35
|
+
// Security: .env file changes
|
|
36
|
+
const envChanges = changes.filter(c => c.path.includes('.env') && !c.path.includes('.example'));
|
|
37
|
+
if (envChanges.length > 0) {
|
|
38
|
+
insights.push({
|
|
39
|
+
type: 'warning',
|
|
40
|
+
priority: 'critical',
|
|
41
|
+
title: 'Potential secrets in .env file',
|
|
42
|
+
content: 'Environment file was modified. Ensure no secrets, API keys, or credentials are committed to version control. Verify .gitignore includes .env files.',
|
|
43
|
+
files: envChanges.map(c => c.path),
|
|
44
|
+
timestamp: now,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
// Dependency changes
|
|
48
|
+
const pkgChanges = changes.filter(c => c.path.endsWith('package.json') || c.path.endsWith('requirements.txt') || c.path.endsWith('Cargo.toml'));
|
|
49
|
+
if (pkgChanges.length > 0) {
|
|
50
|
+
insights.push({
|
|
51
|
+
type: 'suggestion',
|
|
52
|
+
priority: 'medium',
|
|
53
|
+
title: 'Dependencies updated — consider audit',
|
|
54
|
+
content: 'A dependency manifest was modified. Review the changes for: version pinning, known vulnerabilities, license compatibility, and unnecessary additions.',
|
|
55
|
+
files: pkgChanges.map(c => c.path),
|
|
56
|
+
timestamp: now,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// Large batch of changes
|
|
60
|
+
if (changes.length > 10) {
|
|
61
|
+
insights.push({
|
|
62
|
+
type: 'review',
|
|
63
|
+
priority: 'high',
|
|
64
|
+
title: 'Large batch of changes detected',
|
|
65
|
+
content: `${changes.length} files changed at once. Consider splitting into smaller, focused commits for easier review and rollback. Large batches increase the risk of introducing bugs.`,
|
|
66
|
+
files: changes.slice(0, 5).map(c => c.path),
|
|
67
|
+
timestamp: now,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// Deleted files
|
|
71
|
+
const deletedFiles = changes.filter(c => c.type === 'delete');
|
|
72
|
+
if (deletedFiles.length > 0) {
|
|
73
|
+
insights.push({
|
|
74
|
+
type: 'warning',
|
|
75
|
+
priority: 'high',
|
|
76
|
+
title: 'Files deleted',
|
|
77
|
+
content: `Files were deleted: ${deletedFiles.map(f => f.path).join(', ')}. Verify these deletions are intentional and no other code depends on them.`,
|
|
78
|
+
files: deletedFiles.map(c => c.path),
|
|
79
|
+
timestamp: now,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// New test files — positive note
|
|
83
|
+
const testFiles = changes.filter(c => c.type === 'add' && (c.path.includes('.test.') || c.path.includes('.spec.') || c.path.includes('_test.')));
|
|
84
|
+
if (testFiles.length > 0) {
|
|
85
|
+
insights.push({
|
|
86
|
+
type: 'context',
|
|
87
|
+
priority: 'low',
|
|
88
|
+
title: 'New test files added',
|
|
89
|
+
content: `Test files created: ${testFiles.map(f => f.path).join(', ')}. Good practice — ensure tests cover edge cases and failure modes.`,
|
|
90
|
+
files: testFiles.map(c => c.path),
|
|
91
|
+
timestamp: now,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// Missing .gitignore
|
|
95
|
+
if (!context.structure.some(f => f.includes('.gitignore'))) {
|
|
96
|
+
insights.push({
|
|
97
|
+
type: 'suggestion',
|
|
98
|
+
priority: 'medium',
|
|
99
|
+
title: 'No .gitignore found',
|
|
100
|
+
content: 'Project does not have a .gitignore file. Consider adding one to prevent committing build artifacts, dependencies, and secrets.',
|
|
101
|
+
files: [],
|
|
102
|
+
timestamp: now,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// Language/framework mismatch
|
|
106
|
+
if (context.framework === 'Express' && changes.some(c => c.path.includes('pages/'))) {
|
|
107
|
+
insights.push({
|
|
108
|
+
type: 'suggestion',
|
|
109
|
+
priority: 'medium',
|
|
110
|
+
title: 'Possible framework mismatch',
|
|
111
|
+
content: 'Pages directory detected in an Express project. If migrating to Next.js, ensure proper configuration. Otherwise, follow Express conventions.',
|
|
112
|
+
files: changes.filter(c => c.path.includes('pages/')).map(c => c.path),
|
|
113
|
+
timestamp: now,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
// Ensure at least one insight
|
|
117
|
+
if (insights.length === 0) {
|
|
118
|
+
insights.push({
|
|
119
|
+
type: 'context',
|
|
120
|
+
priority: 'low',
|
|
121
|
+
title: 'Changes monitored',
|
|
122
|
+
content: `${changes.length} file change(s) detected in ${context.name}. No immediate concerns found. Project uses ${context.language.join(', ')}${context.framework ? ` with ${context.framework}` : ''}.`,
|
|
123
|
+
files: changes.map(c => c.path),
|
|
124
|
+
timestamp: now,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return insights;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export class PromptBuilder {
|
|
131
|
+
static buildSystemPrompt(personality) {
|
|
132
|
+
const base = `You are the Shadow Brain, an AI that watches agentic coding tools and provides real-time insights.
|
|
133
|
+
You review file changes, git activity, and agent behavior, then generate actionable insights.
|
|
134
|
+
You respond in JSON only — an array of 1-5 insight objects.
|
|
135
|
+
Each insight has: type (review|suggestion|warning|context|pattern|instruction), priority (critical|high|medium|low), title (max 100 chars), content (max 2000 chars), files (array of relevant file paths).`;
|
|
136
|
+
const personalities = {
|
|
137
|
+
mentor: `\nYour personality is MENTOR — you teach and guide.
|
|
138
|
+
- Explain WHY something might be a problem, not just that it is.
|
|
139
|
+
- Suggest better approaches with brief explanations.
|
|
140
|
+
- Acknowledge good patterns when you see them.
|
|
141
|
+
- Be encouraging but honest.
|
|
142
|
+
- Focus on: code quality, best practices, learning opportunities.`,
|
|
143
|
+
critic: `\nYour personality is CRITIC — you give thorough, no-nonsense code reviews.
|
|
144
|
+
- Be direct and specific about issues.
|
|
145
|
+
- Cite specific lines/patterns that concern you.
|
|
146
|
+
- Rate the overall quality of changes.
|
|
147
|
+
- Focus on: bugs, logic errors, missing edge cases, poor naming.`,
|
|
148
|
+
architect: `\nYour personality is ARCHITECT — you think about the big picture.
|
|
149
|
+
- Consider how changes affect the overall system design.
|
|
150
|
+
- Look for coupling, cohesion, and separation of concerns.
|
|
151
|
+
- Suggest structural improvements and patterns.
|
|
152
|
+
- Focus on: modularity, scalability, maintainability, design patterns.`,
|
|
153
|
+
security: `\nYour personality is SECURITY — you are paranoid about vulnerabilities.
|
|
154
|
+
- Flag anything that could be a security risk.
|
|
155
|
+
- Check for: SQL injection, XSS, CSRF, insecure deserialization, hardcoded secrets, weak crypto, missing input validation, exposed endpoints.
|
|
156
|
+
- Be explicit about attack vectors and mitigations.`,
|
|
157
|
+
performance: `\nYour personality is PERFORMANCE — you optimize everything.
|
|
158
|
+
- Look for: N+1 queries, unnecessary re-renders, memory leaks, blocking I/O, large bundle sizes, unoptimized loops, missing caching.
|
|
159
|
+
- Suggest specific optimizations with expected impact.`,
|
|
160
|
+
balanced: `\nYour personality is BALANCED — you combine all perspectives.
|
|
161
|
+
- Mix mentorship, critique, architecture, security, and performance insights.
|
|
162
|
+
- Prioritize by severity: security > bugs > architecture > performance > style.
|
|
163
|
+
- Provide 2-3 insights per analysis covering different aspects.`,
|
|
164
|
+
};
|
|
165
|
+
return base + personalities[personality];
|
|
166
|
+
}
|
|
167
|
+
static buildUserPrompt(params, depth) {
|
|
168
|
+
const { changes, context, activity, agentMemory } = params;
|
|
169
|
+
const sections = [];
|
|
170
|
+
// Project context
|
|
171
|
+
sections.push(`## Project Context
|
|
172
|
+
- Name: ${context.name}
|
|
173
|
+
- Languages: ${context.language.join(', ') || 'Unknown'}
|
|
174
|
+
- Framework: ${context.framework || 'Unknown'}
|
|
175
|
+
- Package Manager: ${context.packageManager || 'Unknown'}
|
|
176
|
+
- Branch: ${context.gitBranch || 'N/A'}
|
|
177
|
+
- Git Status: ${context.gitStatus || 'N/A'}`);
|
|
178
|
+
// File changes
|
|
179
|
+
if (changes.length > 0) {
|
|
180
|
+
sections.push(`## File Changes (${changes.length} files)`);
|
|
181
|
+
for (const change of changes) {
|
|
182
|
+
let entry = `### ${change.type.toUpperCase()}: ${change.path}`;
|
|
183
|
+
if (depth === 'standard' && change.diff) {
|
|
184
|
+
const lines = change.diff.split('\n');
|
|
185
|
+
entry += '\n' + lines.slice(0, 100).join('\n');
|
|
186
|
+
if (lines.length > 100)
|
|
187
|
+
entry += `\n... (${lines.length - 100} more lines)`;
|
|
188
|
+
}
|
|
189
|
+
else if (depth === 'deep' && change.diff) {
|
|
190
|
+
entry += '\n' + change.diff;
|
|
191
|
+
}
|
|
192
|
+
sections.push(entry);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Agent activity
|
|
196
|
+
if (activity.length > 0) {
|
|
197
|
+
const activityLines = activity.slice(-20).map(a => `- [${a.type}] ${a.detail}${a.file ? ` (${a.file})` : ''}`);
|
|
198
|
+
sections.push(`## Agent Activity (${activity.length} events)\n${activityLines.join('\n')}`);
|
|
199
|
+
}
|
|
200
|
+
// Agent memory
|
|
201
|
+
if (agentMemory && agentMemory.rules.length > 0) {
|
|
202
|
+
const rulesText = agentMemory.rules.slice(0, 3).join('\n').slice(0, 2000);
|
|
203
|
+
sections.push(`## Current Agent Rules\n${rulesText}`);
|
|
204
|
+
}
|
|
205
|
+
// Token budget guidance
|
|
206
|
+
const tokenLimits = { quick: 4000, standard: 8000, deep: 16000 };
|
|
207
|
+
const budgetNote = `\n---\nToken budget: ~${tokenLimits[depth]} tokens. Be concise but insightful.`;
|
|
208
|
+
sections.push(budgetNote);
|
|
209
|
+
// Truncate if too long
|
|
210
|
+
let result = sections.join('\n\n');
|
|
211
|
+
const maxChars = tokenLimits[depth] * 4; // rough token → char conversion
|
|
212
|
+
if (result.length > maxChars) {
|
|
213
|
+
result = result.slice(0, maxChars) + '\n\n[Content truncated to fit token budget]';
|
|
214
|
+
}
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=analyzer.js.map
|