@soleri/forge 0.0.1 → 4.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/CHANGELOG.md +98 -0
- package/README.md +199 -0
- package/dist/facades/forge.facade.d.ts +9 -0
- package/dist/facades/forge.facade.js +134 -0
- package/dist/facades/forge.facade.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -0
- package/dist/knowledge-installer.d.ts +31 -0
- package/dist/knowledge-installer.js +437 -0
- package/dist/knowledge-installer.js.map +1 -0
- package/dist/scaffolder.d.ts +13 -0
- package/dist/scaffolder.js +330 -0
- package/dist/scaffolder.js.map +1 -0
- package/dist/templates/activate.d.ts +9 -0
- package/dist/templates/activate.js +139 -0
- package/dist/templates/activate.js.map +1 -0
- package/dist/templates/brain.d.ts +6 -0
- package/dist/templates/brain.js +478 -0
- package/dist/templates/brain.js.map +1 -0
- package/dist/templates/claude-md-template.d.ts +11 -0
- package/dist/templates/claude-md-template.js +73 -0
- package/dist/templates/claude-md-template.js.map +1 -0
- package/dist/templates/core-facade.d.ts +6 -0
- package/dist/templates/core-facade.js +456 -0
- package/dist/templates/core-facade.js.map +1 -0
- package/dist/templates/domain-facade.d.ts +7 -0
- package/dist/templates/domain-facade.js +119 -0
- package/dist/templates/domain-facade.js.map +1 -0
- package/dist/templates/entry-point.d.ts +5 -0
- package/dist/templates/entry-point.js +116 -0
- package/dist/templates/entry-point.js.map +1 -0
- package/dist/templates/facade-factory.d.ts +1 -0
- package/dist/templates/facade-factory.js +63 -0
- package/dist/templates/facade-factory.js.map +1 -0
- package/dist/templates/facade-types.d.ts +1 -0
- package/dist/templates/facade-types.js +46 -0
- package/dist/templates/facade-types.js.map +1 -0
- package/dist/templates/inject-claude-md.d.ts +11 -0
- package/dist/templates/inject-claude-md.js +92 -0
- package/dist/templates/inject-claude-md.js.map +1 -0
- package/dist/templates/intelligence-loader.d.ts +1 -0
- package/dist/templates/intelligence-loader.js +43 -0
- package/dist/templates/intelligence-loader.js.map +1 -0
- package/dist/templates/intelligence-types.d.ts +1 -0
- package/dist/templates/intelligence-types.js +24 -0
- package/dist/templates/intelligence-types.js.map +1 -0
- package/dist/templates/llm-client.d.ts +7 -0
- package/dist/templates/llm-client.js +300 -0
- package/dist/templates/llm-client.js.map +1 -0
- package/dist/templates/llm-key-pool.d.ts +7 -0
- package/dist/templates/llm-key-pool.js +211 -0
- package/dist/templates/llm-key-pool.js.map +1 -0
- package/dist/templates/llm-types.d.ts +5 -0
- package/dist/templates/llm-types.js +161 -0
- package/dist/templates/llm-types.js.map +1 -0
- package/dist/templates/llm-utils.d.ts +5 -0
- package/dist/templates/llm-utils.js +260 -0
- package/dist/templates/llm-utils.js.map +1 -0
- package/dist/templates/package-json.d.ts +2 -0
- package/dist/templates/package-json.js +37 -0
- package/dist/templates/package-json.js.map +1 -0
- package/dist/templates/persona.d.ts +2 -0
- package/dist/templates/persona.js +42 -0
- package/dist/templates/persona.js.map +1 -0
- package/dist/templates/planner.d.ts +5 -0
- package/dist/templates/planner.js +150 -0
- package/dist/templates/planner.js.map +1 -0
- package/dist/templates/readme.d.ts +5 -0
- package/dist/templates/readme.js +316 -0
- package/dist/templates/readme.js.map +1 -0
- package/dist/templates/setup-script.d.ts +6 -0
- package/dist/templates/setup-script.js +112 -0
- package/dist/templates/setup-script.js.map +1 -0
- package/dist/templates/test-brain.d.ts +6 -0
- package/dist/templates/test-brain.js +474 -0
- package/dist/templates/test-brain.js.map +1 -0
- package/dist/templates/test-facades.d.ts +6 -0
- package/dist/templates/test-facades.js +649 -0
- package/dist/templates/test-facades.js.map +1 -0
- package/dist/templates/test-llm.d.ts +7 -0
- package/dist/templates/test-llm.js +574 -0
- package/dist/templates/test-llm.js.map +1 -0
- package/dist/templates/test-loader.d.ts +5 -0
- package/dist/templates/test-loader.js +146 -0
- package/dist/templates/test-loader.js.map +1 -0
- package/dist/templates/test-planner.d.ts +5 -0
- package/dist/templates/test-planner.js +271 -0
- package/dist/templates/test-planner.js.map +1 -0
- package/dist/templates/test-vault.d.ts +5 -0
- package/dist/templates/test-vault.js +380 -0
- package/dist/templates/test-vault.js.map +1 -0
- package/dist/templates/tsconfig.d.ts +1 -0
- package/dist/templates/tsconfig.js +25 -0
- package/dist/templates/tsconfig.js.map +1 -0
- package/dist/templates/vault.d.ts +5 -0
- package/dist/templates/vault.js +263 -0
- package/dist/templates/vault.js.map +1 -0
- package/dist/templates/vitest-config.d.ts +1 -0
- package/dist/templates/vitest-config.js +27 -0
- package/dist/templates/vitest-config.js.map +1 -0
- package/dist/types.d.ts +89 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/package.json +42 -4
- package/src/__tests__/knowledge-installer.test.ts +805 -0
- package/src/__tests__/scaffolder.test.ts +323 -0
- package/src/facades/forge.facade.ts +150 -0
- package/src/index.ts +101 -0
- package/src/knowledge-installer.ts +532 -0
- package/src/scaffolder.ts +386 -0
- package/src/templates/activate.ts +145 -0
- package/src/templates/claude-md-template.ts +137 -0
- package/src/templates/core-facade.ts +457 -0
- package/src/templates/domain-facade.ts +121 -0
- package/src/templates/entry-point.ts +120 -0
- package/src/templates/inject-claude-md.ts +94 -0
- package/src/templates/llm-client.ts +301 -0
- package/src/templates/package-json.ts +39 -0
- package/src/templates/persona.ts +45 -0
- package/src/templates/readme.ts +319 -0
- package/src/templates/setup-script.ts +113 -0
- package/src/templates/test-facades.ts +656 -0
- package/src/templates/tsconfig.ts +25 -0
- package/src/templates/vitest-config.ts +26 -0
- package/src/types.ts +68 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates the brain.ts source file for a new agent.
|
|
3
|
+
* The Brain provides a local, zero-dependency intelligence layer:
|
|
4
|
+
* TF-IDF scoring, auto-tagging, duplicate detection, and search feedback.
|
|
5
|
+
*/
|
|
6
|
+
export function generateBrain() {
|
|
7
|
+
return BRAIN_TEMPLATE;
|
|
8
|
+
}
|
|
9
|
+
const BRAIN_TEMPLATE = [
|
|
10
|
+
"import type { Vault } from '../vault/vault.js';",
|
|
11
|
+
"import type { SearchResult } from '../vault/vault.js';",
|
|
12
|
+
"import type { IntelligenceEntry } from '../intelligence/types.js';",
|
|
13
|
+
'',
|
|
14
|
+
'// ─── Types ───────────────────────────────────────────────────────────',
|
|
15
|
+
'',
|
|
16
|
+
'export interface ScoringWeights {',
|
|
17
|
+
' semantic: number;',
|
|
18
|
+
' severity: number;',
|
|
19
|
+
' recency: number;',
|
|
20
|
+
' tagOverlap: number;',
|
|
21
|
+
' domainMatch: number;',
|
|
22
|
+
'}',
|
|
23
|
+
'',
|
|
24
|
+
'export interface ScoreBreakdown {',
|
|
25
|
+
' semantic: number;',
|
|
26
|
+
' severity: number;',
|
|
27
|
+
' recency: number;',
|
|
28
|
+
' tagOverlap: number;',
|
|
29
|
+
' domainMatch: number;',
|
|
30
|
+
' total: number;',
|
|
31
|
+
'}',
|
|
32
|
+
'',
|
|
33
|
+
'export interface RankedResult {',
|
|
34
|
+
' entry: IntelligenceEntry;',
|
|
35
|
+
' score: number;',
|
|
36
|
+
' breakdown: ScoreBreakdown;',
|
|
37
|
+
'}',
|
|
38
|
+
'',
|
|
39
|
+
'export interface SearchOptions {',
|
|
40
|
+
' domain?: string;',
|
|
41
|
+
' type?: string;',
|
|
42
|
+
' severity?: string;',
|
|
43
|
+
' limit?: number;',
|
|
44
|
+
' tags?: string[];',
|
|
45
|
+
'}',
|
|
46
|
+
'',
|
|
47
|
+
'export interface CaptureResult {',
|
|
48
|
+
' captured: boolean;',
|
|
49
|
+
' id: string;',
|
|
50
|
+
' autoTags: string[];',
|
|
51
|
+
' duplicate?: { id: string; similarity: number };',
|
|
52
|
+
' blocked?: boolean;',
|
|
53
|
+
'}',
|
|
54
|
+
'',
|
|
55
|
+
'export interface BrainStats {',
|
|
56
|
+
' vocabularySize: number;',
|
|
57
|
+
' feedbackCount: number;',
|
|
58
|
+
' weights: ScoringWeights;',
|
|
59
|
+
'}',
|
|
60
|
+
'',
|
|
61
|
+
'export interface QueryContext {',
|
|
62
|
+
' query: string;',
|
|
63
|
+
' domain?: string;',
|
|
64
|
+
' tags?: string[];',
|
|
65
|
+
'}',
|
|
66
|
+
'',
|
|
67
|
+
'type SparseVector = Map<string, number>;',
|
|
68
|
+
'',
|
|
69
|
+
'// ─── Stopwords ─────────────────────────────────────────────────────',
|
|
70
|
+
'',
|
|
71
|
+
'const STOPWORDS = new Set([',
|
|
72
|
+
" 'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',",
|
|
73
|
+
" 'of', 'with', 'by', 'from', 'as', 'is', 'was', 'are', 'were', 'been',",
|
|
74
|
+
" 'be', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',",
|
|
75
|
+
" 'should', 'may', 'might', 'shall', 'can', 'need', 'must', 'it', 'its',",
|
|
76
|
+
" 'this', 'that', 'these', 'those', 'i', 'you', 'he', 'she', 'we', 'they',",
|
|
77
|
+
" 'me', 'him', 'her', 'us', 'them', 'my', 'your', 'his', 'our', 'their',",
|
|
78
|
+
" 'what', 'which', 'who', 'whom', 'when', 'where', 'why', 'how', 'all',",
|
|
79
|
+
" 'each', 'every', 'both', 'few', 'more', 'most', 'other', 'some', 'such',",
|
|
80
|
+
" 'no', 'not', 'only', 'same', 'so', 'than', 'too', 'very', 'just',",
|
|
81
|
+
" 'because', 'if', 'then', 'else', 'about', 'up', 'out', 'into',",
|
|
82
|
+
']);',
|
|
83
|
+
'',
|
|
84
|
+
'// ─── Text Processing (pure functions) ────────────────────────────',
|
|
85
|
+
'',
|
|
86
|
+
'function tokenize(text: string): string[] {',
|
|
87
|
+
' return text',
|
|
88
|
+
' .toLowerCase()',
|
|
89
|
+
" .replace(/[^a-z0-9\\s-]/g, ' ')",
|
|
90
|
+
' .split(/\\s+/)',
|
|
91
|
+
' .filter((t) => t.length > 2 && !STOPWORDS.has(t));',
|
|
92
|
+
'}',
|
|
93
|
+
'',
|
|
94
|
+
'function calculateTf(tokens: string[]): SparseVector {',
|
|
95
|
+
' const tf: SparseVector = new Map();',
|
|
96
|
+
' for (const token of tokens) {',
|
|
97
|
+
' tf.set(token, (tf.get(token) ?? 0) + 1);',
|
|
98
|
+
' }',
|
|
99
|
+
' // Normalize by document length',
|
|
100
|
+
' const len = tokens.length || 1;',
|
|
101
|
+
' for (const [term, count] of tf) {',
|
|
102
|
+
' tf.set(term, count / len);',
|
|
103
|
+
' }',
|
|
104
|
+
' return tf;',
|
|
105
|
+
'}',
|
|
106
|
+
'',
|
|
107
|
+
'function calculateTfIdf(tokens: string[], vocabulary: Map<string, number>): SparseVector {',
|
|
108
|
+
' const tf = calculateTf(tokens);',
|
|
109
|
+
' const tfidf: SparseVector = new Map();',
|
|
110
|
+
' for (const [term, tfValue] of tf) {',
|
|
111
|
+
' const idf = vocabulary.get(term) ?? 0;',
|
|
112
|
+
' if (idf > 0) {',
|
|
113
|
+
' tfidf.set(term, tfValue * idf);',
|
|
114
|
+
' }',
|
|
115
|
+
' }',
|
|
116
|
+
' return tfidf;',
|
|
117
|
+
'}',
|
|
118
|
+
'',
|
|
119
|
+
'function cosineSimilarity(a: SparseVector, b: SparseVector): number {',
|
|
120
|
+
' let dot = 0;',
|
|
121
|
+
' let normA = 0;',
|
|
122
|
+
' let normB = 0;',
|
|
123
|
+
' for (const [term, valA] of a) {',
|
|
124
|
+
' normA += valA * valA;',
|
|
125
|
+
' const valB = b.get(term);',
|
|
126
|
+
' if (valB !== undefined) {',
|
|
127
|
+
' dot += valA * valB;',
|
|
128
|
+
' }',
|
|
129
|
+
' }',
|
|
130
|
+
' for (const [, valB] of b) {',
|
|
131
|
+
' normB += valB * valB;',
|
|
132
|
+
' }',
|
|
133
|
+
' const denom = Math.sqrt(normA) * Math.sqrt(normB);',
|
|
134
|
+
' return denom === 0 ? 0 : dot / denom;',
|
|
135
|
+
'}',
|
|
136
|
+
'',
|
|
137
|
+
'function jaccardSimilarity(a: string[], b: string[]): number {',
|
|
138
|
+
' if (a.length === 0 && b.length === 0) return 0;',
|
|
139
|
+
' const setA = new Set(a);',
|
|
140
|
+
' const setB = new Set(b);',
|
|
141
|
+
' let intersection = 0;',
|
|
142
|
+
' for (const item of setA) {',
|
|
143
|
+
' if (setB.has(item)) intersection++;',
|
|
144
|
+
' }',
|
|
145
|
+
' const union = new Set([...a, ...b]).size;',
|
|
146
|
+
' return union === 0 ? 0 : intersection / union;',
|
|
147
|
+
'}',
|
|
148
|
+
'',
|
|
149
|
+
'// ─── Severity scoring ──────────────────────────────────────────────',
|
|
150
|
+
'',
|
|
151
|
+
'const SEVERITY_SCORES: Record<string, number> = {',
|
|
152
|
+
' critical: 1.0,',
|
|
153
|
+
' warning: 0.7,',
|
|
154
|
+
' suggestion: 0.4,',
|
|
155
|
+
'};',
|
|
156
|
+
'',
|
|
157
|
+
'// ─── Brain Class ─────────────────────────────────────────────────',
|
|
158
|
+
'',
|
|
159
|
+
'const DEFAULT_WEIGHTS: ScoringWeights = {',
|
|
160
|
+
' semantic: 0.40,',
|
|
161
|
+
' severity: 0.15,',
|
|
162
|
+
' recency: 0.15,',
|
|
163
|
+
' tagOverlap: 0.15,',
|
|
164
|
+
' domainMatch: 0.15,',
|
|
165
|
+
'};',
|
|
166
|
+
'',
|
|
167
|
+
'const WEIGHT_BOUND = 0.15;',
|
|
168
|
+
'const FEEDBACK_THRESHOLD = 30;',
|
|
169
|
+
'const DUPLICATE_BLOCK_THRESHOLD = 0.8;',
|
|
170
|
+
'const DUPLICATE_WARN_THRESHOLD = 0.6;',
|
|
171
|
+
'const RECENCY_HALF_LIFE_DAYS = 365;',
|
|
172
|
+
'',
|
|
173
|
+
'export class Brain {',
|
|
174
|
+
' private vault: Vault;',
|
|
175
|
+
' private vocabulary: Map<string, number> = new Map();',
|
|
176
|
+
' private weights: ScoringWeights = { ...DEFAULT_WEIGHTS };',
|
|
177
|
+
'',
|
|
178
|
+
' constructor(vault: Vault) {',
|
|
179
|
+
' this.vault = vault;',
|
|
180
|
+
' this.rebuildVocabulary();',
|
|
181
|
+
' this.recomputeWeights();',
|
|
182
|
+
' }',
|
|
183
|
+
'',
|
|
184
|
+
' intelligentSearch(query: string, options?: SearchOptions): RankedResult[] {',
|
|
185
|
+
' const limit = options?.limit ?? 10;',
|
|
186
|
+
' // Get raw FTS5 results with higher limit for re-ranking',
|
|
187
|
+
' const rawResults = this.vault.search(query, {',
|
|
188
|
+
' domain: options?.domain,',
|
|
189
|
+
' type: options?.type,',
|
|
190
|
+
' severity: options?.severity,',
|
|
191
|
+
' limit: Math.max(limit * 3, 30),',
|
|
192
|
+
' });',
|
|
193
|
+
'',
|
|
194
|
+
' if (rawResults.length === 0) return [];',
|
|
195
|
+
'',
|
|
196
|
+
' const queryTokens = tokenize(query);',
|
|
197
|
+
' const queryTags = options?.tags ?? [];',
|
|
198
|
+
' const queryDomain = options?.domain;',
|
|
199
|
+
' const now = Math.floor(Date.now() / 1000);',
|
|
200
|
+
'',
|
|
201
|
+
' const ranked = rawResults.map((result) => {',
|
|
202
|
+
' const entry = result.entry;',
|
|
203
|
+
' const breakdown = this.scoreEntry(entry, queryTokens, queryTags, queryDomain, now);',
|
|
204
|
+
' return { entry, score: breakdown.total, breakdown };',
|
|
205
|
+
' });',
|
|
206
|
+
'',
|
|
207
|
+
' ranked.sort((a, b) => b.score - a.score);',
|
|
208
|
+
' return ranked.slice(0, limit);',
|
|
209
|
+
' }',
|
|
210
|
+
'',
|
|
211
|
+
" enrichAndCapture(entry: Partial<IntelligenceEntry> & { id: string; type: IntelligenceEntry['type']; domain: string; title: string; severity: IntelligenceEntry['severity']; description: string }): CaptureResult {",
|
|
212
|
+
' // Auto-tag',
|
|
213
|
+
' const autoTags = this.generateTags(entry.title, entry.description, entry.context);',
|
|
214
|
+
' const mergedTags = Array.from(new Set([...(entry.tags ?? []), ...autoTags]));',
|
|
215
|
+
'',
|
|
216
|
+
' // Duplicate detection: compare against top 50 FTS5 matches for the title',
|
|
217
|
+
' const duplicate = this.detectDuplicate(entry.title, entry.domain);',
|
|
218
|
+
'',
|
|
219
|
+
' if (duplicate && duplicate.similarity >= DUPLICATE_BLOCK_THRESHOLD) {',
|
|
220
|
+
' return {',
|
|
221
|
+
' captured: false,',
|
|
222
|
+
' id: entry.id,',
|
|
223
|
+
' autoTags,',
|
|
224
|
+
' duplicate,',
|
|
225
|
+
' blocked: true,',
|
|
226
|
+
' };',
|
|
227
|
+
' }',
|
|
228
|
+
'',
|
|
229
|
+
' // Proceed with capture',
|
|
230
|
+
' const fullEntry: IntelligenceEntry = {',
|
|
231
|
+
' id: entry.id,',
|
|
232
|
+
' type: entry.type,',
|
|
233
|
+
' domain: entry.domain,',
|
|
234
|
+
' title: entry.title,',
|
|
235
|
+
' severity: entry.severity,',
|
|
236
|
+
' description: entry.description,',
|
|
237
|
+
' context: entry.context,',
|
|
238
|
+
' example: entry.example,',
|
|
239
|
+
' counterExample: entry.counterExample,',
|
|
240
|
+
' why: entry.why,',
|
|
241
|
+
' tags: mergedTags,',
|
|
242
|
+
' appliesTo: entry.appliesTo,',
|
|
243
|
+
' };',
|
|
244
|
+
'',
|
|
245
|
+
' this.vault.add(fullEntry);',
|
|
246
|
+
' this.updateVocabularyIncremental(fullEntry);',
|
|
247
|
+
'',
|
|
248
|
+
' const result: CaptureResult = {',
|
|
249
|
+
' captured: true,',
|
|
250
|
+
' id: entry.id,',
|
|
251
|
+
' autoTags,',
|
|
252
|
+
' };',
|
|
253
|
+
'',
|
|
254
|
+
' if (duplicate && duplicate.similarity >= DUPLICATE_WARN_THRESHOLD) {',
|
|
255
|
+
' result.duplicate = duplicate;',
|
|
256
|
+
' }',
|
|
257
|
+
'',
|
|
258
|
+
' return result;',
|
|
259
|
+
' }',
|
|
260
|
+
'',
|
|
261
|
+
" recordFeedback(query: string, entryId: string, action: 'accepted' | 'dismissed'): void {",
|
|
262
|
+
' const db = this.vault.getDb();',
|
|
263
|
+
" db.prepare('INSERT INTO brain_feedback (query, entry_id, action) VALUES (?, ?, ?)').run(query, entryId, action);",
|
|
264
|
+
' this.recomputeWeights();',
|
|
265
|
+
' }',
|
|
266
|
+
'',
|
|
267
|
+
' getRelevantPatterns(context: QueryContext): RankedResult[] {',
|
|
268
|
+
' return this.intelligentSearch(context.query, {',
|
|
269
|
+
' domain: context.domain,',
|
|
270
|
+
' tags: context.tags,',
|
|
271
|
+
' });',
|
|
272
|
+
' }',
|
|
273
|
+
'',
|
|
274
|
+
' rebuildVocabulary(): void {',
|
|
275
|
+
' const entries = this.vault.list({ limit: 100000 });',
|
|
276
|
+
' const docCount = entries.length;',
|
|
277
|
+
' if (docCount === 0) {',
|
|
278
|
+
' this.vocabulary.clear();',
|
|
279
|
+
' this.persistVocabulary();',
|
|
280
|
+
' return;',
|
|
281
|
+
' }',
|
|
282
|
+
'',
|
|
283
|
+
' const termDocFreq = new Map<string, number>();',
|
|
284
|
+
' for (const entry of entries) {',
|
|
285
|
+
" const text = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(' ');",
|
|
286
|
+
' const tokens = new Set(tokenize(text));',
|
|
287
|
+
' for (const token of tokens) {',
|
|
288
|
+
' termDocFreq.set(token, (termDocFreq.get(token) ?? 0) + 1);',
|
|
289
|
+
' }',
|
|
290
|
+
' }',
|
|
291
|
+
'',
|
|
292
|
+
' this.vocabulary.clear();',
|
|
293
|
+
' for (const [term, df] of termDocFreq) {',
|
|
294
|
+
' const idf = Math.log((docCount + 1) / (df + 1)) + 1;',
|
|
295
|
+
' this.vocabulary.set(term, idf);',
|
|
296
|
+
' }',
|
|
297
|
+
'',
|
|
298
|
+
' this.persistVocabulary();',
|
|
299
|
+
' }',
|
|
300
|
+
'',
|
|
301
|
+
' getStats(): BrainStats {',
|
|
302
|
+
' const db = this.vault.getDb();',
|
|
303
|
+
" const feedbackCount = (db.prepare('SELECT COUNT(*) as count FROM brain_feedback').get() as { count: number }).count;",
|
|
304
|
+
' return {',
|
|
305
|
+
' vocabularySize: this.vocabulary.size,',
|
|
306
|
+
' feedbackCount,',
|
|
307
|
+
' weights: { ...this.weights },',
|
|
308
|
+
' };',
|
|
309
|
+
' }',
|
|
310
|
+
'',
|
|
311
|
+
' getVocabularySize(): number {',
|
|
312
|
+
' return this.vocabulary.size;',
|
|
313
|
+
' }',
|
|
314
|
+
'',
|
|
315
|
+
' // ─── Private methods ─────────────────────────────────────────────',
|
|
316
|
+
'',
|
|
317
|
+
' private scoreEntry(',
|
|
318
|
+
' entry: IntelligenceEntry,',
|
|
319
|
+
' queryTokens: string[],',
|
|
320
|
+
' queryTags: string[],',
|
|
321
|
+
' queryDomain: string | undefined,',
|
|
322
|
+
' now: number',
|
|
323
|
+
' ): ScoreBreakdown {',
|
|
324
|
+
' // 1. Semantic (TF-IDF cosine similarity)',
|
|
325
|
+
' let semantic = 0;',
|
|
326
|
+
' if (this.vocabulary.size > 0 && queryTokens.length > 0) {',
|
|
327
|
+
" const entryText = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(' ');",
|
|
328
|
+
' const entryTokens = tokenize(entryText);',
|
|
329
|
+
' const queryVec = calculateTfIdf(queryTokens, this.vocabulary);',
|
|
330
|
+
' const entryVec = calculateTfIdf(entryTokens, this.vocabulary);',
|
|
331
|
+
' semantic = cosineSimilarity(queryVec, entryVec);',
|
|
332
|
+
' }',
|
|
333
|
+
'',
|
|
334
|
+
' // 2. Severity',
|
|
335
|
+
' const severity = SEVERITY_SCORES[entry.severity] ?? 0.4;',
|
|
336
|
+
'',
|
|
337
|
+
' // 3. Recency (exponential decay over 1 year)',
|
|
338
|
+
' const entryAge = now - (entry as unknown as { created_at?: number }).created_at!;',
|
|
339
|
+
' const halfLifeSeconds = RECENCY_HALF_LIFE_DAYS * 86400;',
|
|
340
|
+
' const recency = entryAge > 0 ? Math.exp(-0.693 * entryAge / halfLifeSeconds) : 1;',
|
|
341
|
+
'',
|
|
342
|
+
' // 4. Tag overlap (Jaccard)',
|
|
343
|
+
' const tagOverlap = queryTags.length > 0 ? jaccardSimilarity(queryTags, entry.tags) : 0;',
|
|
344
|
+
'',
|
|
345
|
+
' // 5. Domain match',
|
|
346
|
+
' const domainMatch = queryDomain && entry.domain === queryDomain ? 1.0 : 0;',
|
|
347
|
+
'',
|
|
348
|
+
' const total =',
|
|
349
|
+
' this.weights.semantic * semantic +',
|
|
350
|
+
' this.weights.severity * severity +',
|
|
351
|
+
' this.weights.recency * recency +',
|
|
352
|
+
' this.weights.tagOverlap * tagOverlap +',
|
|
353
|
+
' this.weights.domainMatch * domainMatch;',
|
|
354
|
+
'',
|
|
355
|
+
' return { semantic, severity, recency, tagOverlap, domainMatch, total };',
|
|
356
|
+
' }',
|
|
357
|
+
'',
|
|
358
|
+
' private generateTags(title: string, description: string, context?: string): string[] {',
|
|
359
|
+
" const text = [title, description, context ?? ''].join(' ');",
|
|
360
|
+
' const tokens = tokenize(text);',
|
|
361
|
+
' if (tokens.length === 0) return [];',
|
|
362
|
+
'',
|
|
363
|
+
' // Score tokens by TF-IDF',
|
|
364
|
+
' const tf = calculateTf(tokens);',
|
|
365
|
+
' const scored: Array<[string, number]> = [];',
|
|
366
|
+
' for (const [term, tfValue] of tf) {',
|
|
367
|
+
' const idf = this.vocabulary.get(term) ?? 1;',
|
|
368
|
+
' scored.push([term, tfValue * idf]);',
|
|
369
|
+
' }',
|
|
370
|
+
'',
|
|
371
|
+
' scored.sort((a, b) => b[1] - a[1]);',
|
|
372
|
+
' return scored.slice(0, 5).map(([term]) => term);',
|
|
373
|
+
' }',
|
|
374
|
+
'',
|
|
375
|
+
' private detectDuplicate(title: string, domain: string): { id: string; similarity: number } | null {',
|
|
376
|
+
' let candidates: SearchResult[];',
|
|
377
|
+
' try {',
|
|
378
|
+
' candidates = this.vault.search(title, { domain, limit: 50 });',
|
|
379
|
+
' } catch {',
|
|
380
|
+
' return null;',
|
|
381
|
+
' }',
|
|
382
|
+
' if (candidates.length === 0) return null;',
|
|
383
|
+
'',
|
|
384
|
+
' const titleTokens = tokenize(title);',
|
|
385
|
+
' if (titleTokens.length === 0) return null;',
|
|
386
|
+
' const titleVec = calculateTfIdf(titleTokens, this.vocabulary);',
|
|
387
|
+
' if (titleVec.size === 0) {',
|
|
388
|
+
' // Fallback: use raw TF for comparison when vocabulary is empty',
|
|
389
|
+
' const titleTf = calculateTf(titleTokens);',
|
|
390
|
+
' let bestMatch: { id: string; similarity: number } | null = null;',
|
|
391
|
+
' for (const candidate of candidates) {',
|
|
392
|
+
' const candidateTokens = tokenize(candidate.entry.title);',
|
|
393
|
+
' const candidateTf = calculateTf(candidateTokens);',
|
|
394
|
+
' const sim = cosineSimilarity(titleTf, candidateTf);',
|
|
395
|
+
' if (!bestMatch || sim > bestMatch.similarity) {',
|
|
396
|
+
' bestMatch = { id: candidate.entry.id, similarity: sim };',
|
|
397
|
+
' }',
|
|
398
|
+
' }',
|
|
399
|
+
' return bestMatch;',
|
|
400
|
+
' }',
|
|
401
|
+
'',
|
|
402
|
+
' let bestMatch: { id: string; similarity: number } | null = null;',
|
|
403
|
+
' for (const candidate of candidates) {',
|
|
404
|
+
" const candidateText = [candidate.entry.title, candidate.entry.description].join(' ');",
|
|
405
|
+
' const candidateTokens = tokenize(candidateText);',
|
|
406
|
+
' const candidateVec = calculateTfIdf(candidateTokens, this.vocabulary);',
|
|
407
|
+
' const sim = cosineSimilarity(titleVec, candidateVec);',
|
|
408
|
+
' if (!bestMatch || sim > bestMatch.similarity) {',
|
|
409
|
+
' bestMatch = { id: candidate.entry.id, similarity: sim };',
|
|
410
|
+
' }',
|
|
411
|
+
' }',
|
|
412
|
+
' return bestMatch;',
|
|
413
|
+
' }',
|
|
414
|
+
'',
|
|
415
|
+
' private updateVocabularyIncremental(entry: IntelligenceEntry): void {',
|
|
416
|
+
" const text = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(' ');",
|
|
417
|
+
' const tokens = new Set(tokenize(text));',
|
|
418
|
+
' const totalDocs = this.vault.stats().totalEntries;',
|
|
419
|
+
'',
|
|
420
|
+
' for (const token of tokens) {',
|
|
421
|
+
' const currentDocCount = (this.vocabulary.has(token)) ? Math.round((totalDocs) / Math.exp(this.vocabulary.get(token)! - 1)) + 1 : 1;',
|
|
422
|
+
' const newIdf = Math.log((totalDocs + 1) / (currentDocCount + 1)) + 1;',
|
|
423
|
+
' this.vocabulary.set(token, newIdf);',
|
|
424
|
+
' }',
|
|
425
|
+
'',
|
|
426
|
+
' this.persistVocabulary();',
|
|
427
|
+
' }',
|
|
428
|
+
'',
|
|
429
|
+
' private persistVocabulary(): void {',
|
|
430
|
+
' const db = this.vault.getDb();',
|
|
431
|
+
" db.exec('DELETE FROM brain_vocabulary');",
|
|
432
|
+
' if (this.vocabulary.size === 0) return;',
|
|
433
|
+
" const insert = db.prepare('INSERT INTO brain_vocabulary (term, idf, doc_count) VALUES (?, ?, ?)');",
|
|
434
|
+
' const tx = db.transaction(() => {',
|
|
435
|
+
' for (const [term, idf] of this.vocabulary) {',
|
|
436
|
+
' insert.run(term, idf, 1);',
|
|
437
|
+
' }',
|
|
438
|
+
' });',
|
|
439
|
+
' tx();',
|
|
440
|
+
' }',
|
|
441
|
+
'',
|
|
442
|
+
' private recomputeWeights(): void {',
|
|
443
|
+
' const db = this.vault.getDb();',
|
|
444
|
+
" const feedbackCount = (db.prepare('SELECT COUNT(*) as count FROM brain_feedback').get() as { count: number }).count;",
|
|
445
|
+
' if (feedbackCount < FEEDBACK_THRESHOLD) {',
|
|
446
|
+
' this.weights = { ...DEFAULT_WEIGHTS };',
|
|
447
|
+
' return;',
|
|
448
|
+
' }',
|
|
449
|
+
'',
|
|
450
|
+
' // Count accepted vs dismissed feedback',
|
|
451
|
+
' const accepted = (db.prepare("SELECT COUNT(*) as count FROM brain_feedback WHERE action = \'accepted\'").get() as { count: number }).count;',
|
|
452
|
+
' const acceptRate = feedbackCount > 0 ? accepted / feedbackCount : 0.5;',
|
|
453
|
+
'',
|
|
454
|
+
' // High accept rate -> increase semantic weight, decrease others',
|
|
455
|
+
' // Low accept rate -> decrease semantic weight, increase others',
|
|
456
|
+
' const semanticDelta = (acceptRate - 0.5) * WEIGHT_BOUND * 2;',
|
|
457
|
+
'',
|
|
458
|
+
' const newWeights = { ...DEFAULT_WEIGHTS };',
|
|
459
|
+
' newWeights.semantic = clamp(DEFAULT_WEIGHTS.semantic + semanticDelta, DEFAULT_WEIGHTS.semantic - WEIGHT_BOUND, DEFAULT_WEIGHTS.semantic + WEIGHT_BOUND);',
|
|
460
|
+
'',
|
|
461
|
+
' // Redistribute remaining weight evenly across other dimensions',
|
|
462
|
+
' const remaining = 1.0 - newWeights.semantic;',
|
|
463
|
+
' const otherSum = DEFAULT_WEIGHTS.severity + DEFAULT_WEIGHTS.recency + DEFAULT_WEIGHTS.tagOverlap + DEFAULT_WEIGHTS.domainMatch;',
|
|
464
|
+
' const scale = remaining / otherSum;',
|
|
465
|
+
' newWeights.severity = DEFAULT_WEIGHTS.severity * scale;',
|
|
466
|
+
' newWeights.recency = DEFAULT_WEIGHTS.recency * scale;',
|
|
467
|
+
' newWeights.tagOverlap = DEFAULT_WEIGHTS.tagOverlap * scale;',
|
|
468
|
+
' newWeights.domainMatch = DEFAULT_WEIGHTS.domainMatch * scale;',
|
|
469
|
+
'',
|
|
470
|
+
' this.weights = newWeights;',
|
|
471
|
+
' }',
|
|
472
|
+
'}',
|
|
473
|
+
'',
|
|
474
|
+
'function clamp(value: number, min: number, max: number): number {',
|
|
475
|
+
' return Math.max(min, Math.min(max, value));',
|
|
476
|
+
'}',
|
|
477
|
+
].join('\n');
|
|
478
|
+
//# sourceMappingURL=brain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brain.js","sourceRoot":"","sources":["../../src/templates/brain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AgentConfig } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generates src/activation/claude-md-content.ts for a new agent.
|
|
4
|
+
* Returns the CLAUDE.md section content with activation triggers,
|
|
5
|
+
* facade tables, intent detection, and knowledge protocol.
|
|
6
|
+
*
|
|
7
|
+
* Strategy: build the markdown as plain strings, then escape into
|
|
8
|
+
* single-quoted TypeScript string literals. This avoids nested
|
|
9
|
+
* backtick/template-literal escaping nightmares entirely.
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateClaudeMdTemplate(config: AgentConfig): string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates src/activation/claude-md-content.ts for a new agent.
|
|
3
|
+
* Returns the CLAUDE.md section content with activation triggers,
|
|
4
|
+
* facade tables, intent detection, and knowledge protocol.
|
|
5
|
+
*
|
|
6
|
+
* Strategy: build the markdown as plain strings, then escape into
|
|
7
|
+
* single-quoted TypeScript string literals. This avoids nested
|
|
8
|
+
* backtick/template-literal escaping nightmares entirely.
|
|
9
|
+
*/
|
|
10
|
+
export function generateClaudeMdTemplate(config) {
|
|
11
|
+
const facadeId = config.id.replace(/-/g, '_');
|
|
12
|
+
const marker = `${config.id}:mode`;
|
|
13
|
+
const bt = '`'; // backtick — keeps template clean
|
|
14
|
+
// Build the raw markdown lines (plain text, no escaping needed)
|
|
15
|
+
const mdLines = [
|
|
16
|
+
`<!-- ${marker} -->`,
|
|
17
|
+
'',
|
|
18
|
+
`# ${config.name} Mode`,
|
|
19
|
+
'',
|
|
20
|
+
`## ${config.name} Integration`,
|
|
21
|
+
'',
|
|
22
|
+
`**Activate:** "Hello, ${config.name}!" \u2192 ${bt}${facadeId}_core op:activate params:{ projectPath: "." }${bt}`,
|
|
23
|
+
`**Deactivate:** "Goodbye, ${config.name}!" \u2192 ${bt}${facadeId}_core op:activate params:{ deactivate: true }${bt}`,
|
|
24
|
+
'',
|
|
25
|
+
'On activation, adopt the returned persona. Stay in character until deactivated.',
|
|
26
|
+
'',
|
|
27
|
+
'## Session Start',
|
|
28
|
+
'',
|
|
29
|
+
`On every new session: ${bt}${facadeId}_core op:register params:{ projectPath: "." }${bt}`,
|
|
30
|
+
'',
|
|
31
|
+
'## Facades',
|
|
32
|
+
'',
|
|
33
|
+
'| Need | Facade | Op |',
|
|
34
|
+
'|------|--------|----|',
|
|
35
|
+
`| Health check | ${bt}${facadeId}_core${bt} | ${bt}health${bt} |`,
|
|
36
|
+
`| Search all | ${bt}${facadeId}_core${bt} | ${bt}search${bt} |`,
|
|
37
|
+
`| Vault stats | ${bt}${facadeId}_core${bt} | ${bt}vault_stats${bt} |`,
|
|
38
|
+
`| Identity | ${bt}${facadeId}_core${bt} | ${bt}identity${bt} |`,
|
|
39
|
+
];
|
|
40
|
+
// Domain-specific facade rows
|
|
41
|
+
for (const d of config.domains) {
|
|
42
|
+
const toolName = `${facadeId}_${d.replace(/-/g, '_')}`;
|
|
43
|
+
mdLines.push(`| ${d} patterns | ${bt}${toolName}${bt} | ${bt}get_patterns${bt} |`);
|
|
44
|
+
mdLines.push(`| Search ${d} | ${bt}${toolName}${bt} | ${bt}search${bt} |`);
|
|
45
|
+
mdLines.push(`| Capture ${d} | ${bt}${toolName}${bt} | ${bt}capture${bt} |`);
|
|
46
|
+
}
|
|
47
|
+
// Memory + Session + Export + Brain + Planning rows
|
|
48
|
+
mdLines.push(`| Memory search | ${bt}${facadeId}_core${bt} | ${bt}memory_search${bt} |`, `| Memory capture | ${bt}${facadeId}_core${bt} | ${bt}memory_capture${bt} |`, `| Memory list | ${bt}${facadeId}_core${bt} | ${bt}memory_list${bt} |`, `| Session capture | ${bt}${facadeId}_core${bt} | ${bt}session_capture${bt} |`, `| Export knowledge | ${bt}${facadeId}_core${bt} | ${bt}export${bt} |`, `| Record feedback | ${bt}${facadeId}_core${bt} | ${bt}record_feedback${bt} |`, `| Rebuild vocabulary | ${bt}${facadeId}_core${bt} | ${bt}rebuild_vocabulary${bt} |`, `| Brain stats | ${bt}${facadeId}_core${bt} | ${bt}brain_stats${bt} |`, `| LLM status | ${bt}${facadeId}_core${bt} | ${bt}llm_status${bt} |`, `| Create plan | ${bt}${facadeId}_core${bt} | ${bt}create_plan${bt} |`, `| Get plan | ${bt}${facadeId}_core${bt} | ${bt}get_plan${bt} |`, `| Approve plan | ${bt}${facadeId}_core${bt} | ${bt}approve_plan${bt} |`, `| Update task | ${bt}${facadeId}_core${bt} | ${bt}update_task${bt} |`, `| Complete plan | ${bt}${facadeId}_core${bt} | ${bt}complete_plan${bt} |`);
|
|
49
|
+
mdLines.push('', '## Intent Detection', '', '| Signal | Intent |', '|--------|--------|', '| Problem described ("broken", "janky", "weird") | FIX |', '| Need expressed ("I need", "we should have") | BUILD |', '| Quality questioned ("is this right?") | REVIEW |', '| Advice sought ("how should I", "best way") | PLAN |', '| Improvement requested ("make it faster") | IMPROVE |', '', '## Knowledge Protocol', '', 'When seeking guidance: vault before codebase before web.', '', `1. Search vault \u2014 ${bt}${facadeId}_core op:search${bt}`, '2. Codebase \u2014 only if vault has nothing', '3. Web \u2014 last resort', '', '## Knowledge Capture', '', 'When learning something that should persist, use the domain capture ops.', '', '## Session Capture', '', 'A PreCompact hook is configured to call `session_capture` before context compaction.', 'This automatically preserves session summaries as memories for future sessions.', `To manually capture: ${bt}${facadeId}_core op:session_capture params:{ summary: "..." }${bt}`, '', '## Planning', '', 'For multi-step tasks, use the planning system:', `1. Create: ${bt}${facadeId}_core op:create_plan params:{ objective: "...", scope: "...", tasks: [...] }${bt}`, `2. Approve: ${bt}${facadeId}_core op:approve_plan params:{ planId: "...", startExecution: true }${bt}`, `3. Track: ${bt}${facadeId}_core op:update_task params:{ planId: "...", taskId: "...", status: "completed" }${bt}`, `4. Complete: ${bt}${facadeId}_core op:complete_plan params:{ planId: "..." }${bt}`, '', 'Check activation response for recovered plans in `executing` state — remind the user.', '', `<!-- /${marker} -->`);
|
|
50
|
+
// Escape each markdown line for use in a single-quoted TS string literal
|
|
51
|
+
const quotedLines = mdLines.map((line) => {
|
|
52
|
+
const escaped = line.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
53
|
+
return ` '${escaped}',`;
|
|
54
|
+
});
|
|
55
|
+
// Build the generated TypeScript file
|
|
56
|
+
const tsLines = [
|
|
57
|
+
'/**',
|
|
58
|
+
` * CLAUDE.md content for ${config.name}.`,
|
|
59
|
+
' * Generated by Soleri — do not edit manually.',
|
|
60
|
+
' */',
|
|
61
|
+
'export function getClaudeMdContent(): string {',
|
|
62
|
+
' return [',
|
|
63
|
+
...quotedLines,
|
|
64
|
+
" ].join('\\n');",
|
|
65
|
+
'}',
|
|
66
|
+
'',
|
|
67
|
+
'export function getClaudeMdMarker(): string {',
|
|
68
|
+
` return '${marker}';`,
|
|
69
|
+
'}',
|
|
70
|
+
];
|
|
71
|
+
return tsLines.join('\n');
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=claude-md-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-md-template.js","sourceRoot":"","sources":["../../src/templates/claude-md-template.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AgentConfig } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generate the core facade that every agent gets — vault stats, search all,
|
|
4
|
+
* health, identity, plus activation system (activate, deactivate, inject_claude_md, setup).
|
|
5
|
+
*/
|
|
6
|
+
export declare function generateCoreFacade(config: AgentConfig): string;
|