universal-agent-memory 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.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +321 -0
  3. package/dist/analyzers/index.d.ts +3 -0
  4. package/dist/analyzers/index.d.ts.map +1 -0
  5. package/dist/analyzers/index.js +373 -0
  6. package/dist/analyzers/index.js.map +1 -0
  7. package/dist/bin/cli.d.ts +3 -0
  8. package/dist/bin/cli.d.ts.map +1 -0
  9. package/dist/bin/cli.js +119 -0
  10. package/dist/bin/cli.js.map +1 -0
  11. package/dist/cli/analyze.d.ts +7 -0
  12. package/dist/cli/analyze.d.ts.map +1 -0
  13. package/dist/cli/analyze.js +103 -0
  14. package/dist/cli/analyze.js.map +1 -0
  15. package/dist/cli/droids.d.ts +9 -0
  16. package/dist/cli/droids.d.ts.map +1 -0
  17. package/dist/cli/droids.js +227 -0
  18. package/dist/cli/droids.js.map +1 -0
  19. package/dist/cli/generate.d.ts +9 -0
  20. package/dist/cli/generate.d.ts.map +1 -0
  21. package/dist/cli/generate.js +203 -0
  22. package/dist/cli/generate.js.map +1 -0
  23. package/dist/cli/init.d.ts +12 -0
  24. package/dist/cli/init.d.ts.map +1 -0
  25. package/dist/cli/init.js +260 -0
  26. package/dist/cli/init.js.map +1 -0
  27. package/dist/cli/memory.d.ts +15 -0
  28. package/dist/cli/memory.d.ts.map +1 -0
  29. package/dist/cli/memory.js +274 -0
  30. package/dist/cli/memory.js.map +1 -0
  31. package/dist/cli/sync.d.ts +7 -0
  32. package/dist/cli/sync.d.ts.map +1 -0
  33. package/dist/cli/sync.js +26 -0
  34. package/dist/cli/sync.js.map +1 -0
  35. package/dist/cli/worktree.d.ts +9 -0
  36. package/dist/cli/worktree.d.ts.map +1 -0
  37. package/dist/cli/worktree.js +175 -0
  38. package/dist/cli/worktree.js.map +1 -0
  39. package/dist/generators/claude-md.d.ts +3 -0
  40. package/dist/generators/claude-md.d.ts.map +1 -0
  41. package/dist/generators/claude-md.js +643 -0
  42. package/dist/generators/claude-md.js.map +1 -0
  43. package/dist/index.d.ts +4 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +4 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/memory/backends/base.d.ts +18 -0
  48. package/dist/memory/backends/base.d.ts.map +1 -0
  49. package/dist/memory/backends/base.js +2 -0
  50. package/dist/memory/backends/base.js.map +1 -0
  51. package/dist/memory/backends/factory.d.ts +4 -0
  52. package/dist/memory/backends/factory.d.ts.map +1 -0
  53. package/dist/memory/backends/factory.js +52 -0
  54. package/dist/memory/backends/factory.js.map +1 -0
  55. package/dist/memory/backends/github.d.ts +22 -0
  56. package/dist/memory/backends/github.d.ts.map +1 -0
  57. package/dist/memory/backends/github.js +118 -0
  58. package/dist/memory/backends/github.js.map +1 -0
  59. package/dist/memory/backends/qdrant-cloud.d.ts +19 -0
  60. package/dist/memory/backends/qdrant-cloud.d.ts.map +1 -0
  61. package/dist/memory/backends/qdrant-cloud.js +111 -0
  62. package/dist/memory/backends/qdrant-cloud.js.map +1 -0
  63. package/dist/memory/prepopulate.d.ts +76 -0
  64. package/dist/memory/prepopulate.d.ts.map +1 -0
  65. package/dist/memory/prepopulate.js +815 -0
  66. package/dist/memory/prepopulate.js.map +1 -0
  67. package/dist/memory/short-term/factory.d.ts +23 -0
  68. package/dist/memory/short-term/factory.d.ts.map +1 -0
  69. package/dist/memory/short-term/factory.js +28 -0
  70. package/dist/memory/short-term/factory.js.map +1 -0
  71. package/dist/memory/short-term/indexeddb.d.ts +25 -0
  72. package/dist/memory/short-term/indexeddb.d.ts.map +1 -0
  73. package/dist/memory/short-term/indexeddb.js +64 -0
  74. package/dist/memory/short-term/indexeddb.js.map +1 -0
  75. package/dist/memory/short-term/sqlite.d.ts +40 -0
  76. package/dist/memory/short-term/sqlite.d.ts.map +1 -0
  77. package/dist/memory/short-term/sqlite.js +136 -0
  78. package/dist/memory/short-term/sqlite.js.map +1 -0
  79. package/dist/types/analysis.d.ts +82 -0
  80. package/dist/types/analysis.d.ts.map +1 -0
  81. package/dist/types/analysis.js +2 -0
  82. package/dist/types/analysis.js.map +1 -0
  83. package/dist/types/config.d.ts +923 -0
  84. package/dist/types/config.d.ts.map +1 -0
  85. package/dist/types/config.js +97 -0
  86. package/dist/types/config.js.map +1 -0
  87. package/dist/types/index.d.ts +3 -0
  88. package/dist/types/index.d.ts.map +1 -0
  89. package/dist/types/index.js +3 -0
  90. package/dist/types/index.js.map +1 -0
  91. package/dist/utils/merge-claude-md.d.ts +15 -0
  92. package/dist/utils/merge-claude-md.d.ts.map +1 -0
  93. package/dist/utils/merge-claude-md.js +149 -0
  94. package/dist/utils/merge-claude-md.js.map +1 -0
  95. package/package.json +90 -0
  96. package/templates/CLAUDE.template.md +632 -0
@@ -0,0 +1,815 @@
1
+ import { execSync } from 'child_process';
2
+ import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
3
+ import { join, relative, extname, basename } from 'path';
4
+ /**
5
+ * Prepopulate memory from project documentation, git history, and skills/artifacts
6
+ */
7
+ export async function prepopulateMemory(cwd, options = {}) {
8
+ const shortTermMemories = [];
9
+ const longTermMemories = [];
10
+ const discoveredSkills = [];
11
+ const doAll = !options.docs && !options.git && !options.skills;
12
+ // Parse documentation
13
+ if (doAll || options.docs) {
14
+ const docs = await parseDocumentation(cwd, options.verbose);
15
+ const docMemories = extractDocumentationMemories(docs);
16
+ // Short-term: recent/important observations
17
+ shortTermMemories.push(...docMemories.filter(m => m.importance && m.importance >= 7).slice(0, 20));
18
+ // Long-term: all documentation memories
19
+ longTermMemories.push(...docMemories);
20
+ }
21
+ // Extract git learnings
22
+ if (doAll || options.git) {
23
+ const commits = await extractGitHistory(cwd, options.limit || 500, options.since);
24
+ const gitMemories = extractGitLearnings(commits);
25
+ // Short-term: recent significant commits
26
+ shortTermMemories.push(...gitMemories.filter(m => m.importance && m.importance >= 6).slice(0, 30));
27
+ // Long-term: all git learnings
28
+ longTermMemories.push(...gitMemories);
29
+ }
30
+ // Discover skills, droids, commands, and artifacts
31
+ if (doAll || options.skills) {
32
+ const skills = await discoverSkillsAndArtifacts(cwd, options.verbose);
33
+ discoveredSkills.push(...skills);
34
+ // Create memories for discovered skills
35
+ const skillMemories = extractSkillMemories(skills);
36
+ longTermMemories.push(...skillMemories);
37
+ // Add important skills to short-term
38
+ shortTermMemories.push(...skillMemories.filter(m => m.importance && m.importance >= 7).slice(0, 10));
39
+ }
40
+ return { shortTerm: shortTermMemories, longTerm: longTermMemories, skills: discoveredSkills };
41
+ }
42
+ /**
43
+ * Parse all documentation files in the project
44
+ */
45
+ export async function parseDocumentation(cwd, verbose = false) {
46
+ const docs = [];
47
+ // Documentation file patterns to search for
48
+ const docPatterns = [
49
+ 'README.md',
50
+ 'README.txt',
51
+ 'README',
52
+ 'CONTRIBUTING.md',
53
+ 'CHANGELOG.md',
54
+ 'ARCHITECTURE.md',
55
+ 'DEVELOPMENT.md',
56
+ 'SECURITY.md',
57
+ '.github/PULL_REQUEST_TEMPLATE.md',
58
+ '.github/ISSUE_TEMPLATE.md',
59
+ ];
60
+ // Parse root-level docs
61
+ for (const pattern of docPatterns) {
62
+ const docPath = join(cwd, pattern);
63
+ if (existsSync(docPath)) {
64
+ const doc = parseMarkdownFile(docPath, cwd);
65
+ if (doc) {
66
+ docs.push(doc);
67
+ if (verbose)
68
+ console.log(` Parsed: ${pattern}`);
69
+ }
70
+ }
71
+ }
72
+ // Parse docs/ directory
73
+ const docsDir = join(cwd, 'docs');
74
+ if (existsSync(docsDir) && statSync(docsDir).isDirectory()) {
75
+ const docFiles = findMarkdownFiles(docsDir);
76
+ for (const docFile of docFiles) {
77
+ const doc = parseMarkdownFile(docFile, cwd);
78
+ if (doc) {
79
+ docs.push(doc);
80
+ if (verbose)
81
+ console.log(` Parsed: ${relative(cwd, docFile)}`);
82
+ }
83
+ }
84
+ }
85
+ // Parse ADRs (Architecture Decision Records)
86
+ const adrDirs = ['docs/adr', 'docs/decisions', 'adr', 'decisions'];
87
+ for (const adrDir of adrDirs) {
88
+ const adrPath = join(cwd, adrDir);
89
+ if (existsSync(adrPath) && statSync(adrPath).isDirectory()) {
90
+ const adrFiles = findMarkdownFiles(adrPath);
91
+ for (const adrFile of adrFiles) {
92
+ const doc = parseMarkdownFile(adrFile, cwd, 'adr');
93
+ if (doc) {
94
+ docs.push(doc);
95
+ if (verbose)
96
+ console.log(` Parsed ADR: ${relative(cwd, adrFile)}`);
97
+ }
98
+ }
99
+ }
100
+ }
101
+ return docs;
102
+ }
103
+ /**
104
+ * Extract git commit history with details
105
+ */
106
+ export async function extractGitHistory(cwd, limit = 500, since) {
107
+ const commits = [];
108
+ try {
109
+ // Check if it's a git repo
110
+ execSync('git rev-parse --git-dir', { cwd, encoding: 'utf-8', stdio: 'pipe' });
111
+ }
112
+ catch {
113
+ return commits; // Not a git repo
114
+ }
115
+ // Build git log command
116
+ let gitCmd = `git log --pretty=format:"%H|%aI|%an|%s|%b<<<END>>>" -n ${limit}`;
117
+ if (since) {
118
+ gitCmd += ` --since="${since}"`;
119
+ }
120
+ try {
121
+ const output = execSync(gitCmd, { cwd, encoding: 'utf-8', maxBuffer: 50 * 1024 * 1024 });
122
+ const commitStrings = output.split('<<<END>>>').filter(s => s.trim());
123
+ for (const commitStr of commitStrings) {
124
+ const parts = commitStr.trim().split('|');
125
+ if (parts.length < 4)
126
+ continue;
127
+ const [hash, date, author, ...rest] = parts;
128
+ const messageAndBody = rest.join('|');
129
+ const [message, ...bodyParts] = messageAndBody.split('\n');
130
+ const body = bodyParts.join('\n').trim();
131
+ // Get files changed in this commit
132
+ let files = [];
133
+ try {
134
+ const filesOutput = execSync(`git diff-tree --no-commit-id --name-only -r ${hash}`, {
135
+ cwd,
136
+ encoding: 'utf-8',
137
+ stdio: 'pipe',
138
+ });
139
+ files = filesOutput.trim().split('\n').filter(f => f);
140
+ }
141
+ catch {
142
+ // Ignore errors getting file list
143
+ }
144
+ // Determine commit type from conventional commit format
145
+ const type = categorizeCommit(message);
146
+ commits.push({
147
+ hash: hash.trim(),
148
+ date: date.trim(),
149
+ author: author.trim(),
150
+ message: message.trim(),
151
+ body,
152
+ files,
153
+ type,
154
+ });
155
+ }
156
+ }
157
+ catch (error) {
158
+ // Git command failed, return empty
159
+ console.warn('Failed to extract git history:', error);
160
+ }
161
+ return commits;
162
+ }
163
+ /**
164
+ * Convert documentation into memory entries
165
+ */
166
+ export function extractDocumentationMemories(docs) {
167
+ const memories = [];
168
+ const now = new Date().toISOString();
169
+ for (const doc of docs) {
170
+ for (const section of doc.sections) {
171
+ // Skip empty or very short sections
172
+ if (!section.content || section.content.length < 50)
173
+ continue;
174
+ // Chunk large sections
175
+ const chunks = chunkContent(section.content, 500);
176
+ for (let i = 0; i < chunks.length; i++) {
177
+ const memoryType = mapSectionToMemoryType(section.type);
178
+ const importance = calculateDocImportance(section, doc.path);
179
+ memories.push({
180
+ id: `doc-${basename(doc.path, extname(doc.path))}-${section.heading.toLowerCase().replace(/\s+/g, '-')}-${i}`,
181
+ timestamp: now,
182
+ type: memoryType,
183
+ content: chunks[i],
184
+ tags: extractTags(section, doc.path),
185
+ importance,
186
+ metadata: {
187
+ source: 'documentation',
188
+ file: doc.path,
189
+ section: section.heading,
190
+ docTitle: doc.title,
191
+ },
192
+ });
193
+ }
194
+ }
195
+ }
196
+ return memories;
197
+ }
198
+ /**
199
+ * Convert git history into memory entries with learnings
200
+ */
201
+ export function extractGitLearnings(commits) {
202
+ const memories = [];
203
+ // Group commits by type for analysis
204
+ const bugFixes = commits.filter(c => c.type === 'fix');
205
+ const features = commits.filter(c => c.type === 'feat');
206
+ const refactors = commits.filter(c => c.type === 'refactor');
207
+ const reverts = commits.filter(c => c.type === 'revert');
208
+ // Extract learnings from bug fixes
209
+ for (const commit of bugFixes.slice(0, 50)) {
210
+ const learning = extractBugFixLearning(commit);
211
+ if (learning) {
212
+ memories.push({
213
+ id: `git-fix-${commit.hash.substring(0, 8)}`,
214
+ timestamp: commit.date,
215
+ type: 'observation',
216
+ content: learning,
217
+ tags: ['bug-fix', ...extractFileTags(commit.files)],
218
+ importance: calculateCommitImportance(commit),
219
+ metadata: {
220
+ source: 'git',
221
+ commit: commit.hash,
222
+ author: commit.author,
223
+ files: commit.files.slice(0, 10),
224
+ },
225
+ });
226
+ }
227
+ }
228
+ // Extract learnings from features
229
+ for (const commit of features.slice(0, 30)) {
230
+ memories.push({
231
+ id: `git-feat-${commit.hash.substring(0, 8)}`,
232
+ timestamp: commit.date,
233
+ type: 'observation',
234
+ content: `Feature added: ${commit.message}${commit.body ? `. ${summarizeBody(commit.body)}` : ''}`,
235
+ tags: ['feature', ...extractFileTags(commit.files)],
236
+ importance: calculateCommitImportance(commit),
237
+ metadata: {
238
+ source: 'git',
239
+ commit: commit.hash,
240
+ author: commit.author,
241
+ files: commit.files.slice(0, 10),
242
+ },
243
+ });
244
+ }
245
+ // Extract learnings from refactors (architectural decisions)
246
+ for (const commit of refactors.slice(0, 20)) {
247
+ memories.push({
248
+ id: `git-refactor-${commit.hash.substring(0, 8)}`,
249
+ timestamp: commit.date,
250
+ type: 'thought',
251
+ content: `Architectural change: ${commit.message}${commit.body ? `. Reasoning: ${summarizeBody(commit.body)}` : ''}`,
252
+ tags: ['refactor', 'architecture', ...extractFileTags(commit.files)],
253
+ importance: calculateCommitImportance(commit) + 1, // Refactors are usually important decisions
254
+ metadata: {
255
+ source: 'git',
256
+ commit: commit.hash,
257
+ author: commit.author,
258
+ files: commit.files.slice(0, 10),
259
+ },
260
+ });
261
+ }
262
+ // Extract learnings from reverts (failed approaches)
263
+ for (const commit of reverts) {
264
+ memories.push({
265
+ id: `git-revert-${commit.hash.substring(0, 8)}`,
266
+ timestamp: commit.date,
267
+ type: 'thought',
268
+ content: `Failed approach reverted: ${commit.message}. This approach did not work - avoid similar changes.`,
269
+ tags: ['revert', 'failed-approach', ...extractFileTags(commit.files)],
270
+ importance: 8, // Reverts are always important learnings
271
+ metadata: {
272
+ source: 'git',
273
+ commit: commit.hash,
274
+ author: commit.author,
275
+ files: commit.files.slice(0, 10),
276
+ },
277
+ });
278
+ }
279
+ // Identify hot spots (frequently modified files)
280
+ const fileChangeCounts = new Map();
281
+ for (const commit of commits) {
282
+ for (const file of commit.files) {
283
+ fileChangeCounts.set(file, (fileChangeCounts.get(file) || 0) + 1);
284
+ }
285
+ }
286
+ const hotSpots = [...fileChangeCounts.entries()]
287
+ .sort((a, b) => b[1] - a[1])
288
+ .slice(0, 10)
289
+ .filter(([_, count]) => count >= 5);
290
+ if (hotSpots.length > 0) {
291
+ memories.push({
292
+ id: 'git-hotspots',
293
+ timestamp: new Date().toISOString(),
294
+ type: 'observation',
295
+ content: `Frequently modified files (hot spots): ${hotSpots.map(([f, c]) => `${f} (${c} changes)`).join(', ')}. These files may need extra attention during changes.`,
296
+ tags: ['hot-spots', 'code-quality'],
297
+ importance: 7,
298
+ metadata: {
299
+ source: 'git-analysis',
300
+ hotSpots: Object.fromEntries(hotSpots),
301
+ },
302
+ });
303
+ }
304
+ return memories;
305
+ }
306
+ // Helper functions
307
+ function findMarkdownFiles(dir) {
308
+ const files = [];
309
+ const entries = readdirSync(dir, { withFileTypes: true });
310
+ for (const entry of entries) {
311
+ const fullPath = join(dir, entry.name);
312
+ if (entry.isDirectory() && !entry.name.startsWith('.')) {
313
+ files.push(...findMarkdownFiles(fullPath));
314
+ }
315
+ else if (entry.isFile() && (entry.name.endsWith('.md') || entry.name.endsWith('.mdx'))) {
316
+ files.push(fullPath);
317
+ }
318
+ }
319
+ return files;
320
+ }
321
+ function parseMarkdownFile(filePath, cwd, docType) {
322
+ try {
323
+ const content = readFileSync(filePath, 'utf-8');
324
+ const relativePath = relative(cwd, filePath);
325
+ // Extract title from first heading or filename
326
+ const titleMatch = content.match(/^#\s+(.+)$/m);
327
+ const title = titleMatch ? titleMatch[1] : basename(filePath, extname(filePath));
328
+ // Split content by headings
329
+ const sections = [];
330
+ const headingRegex = /^(#{1,3})\s+(.+)$/gm;
331
+ let lastIndex = 0;
332
+ let lastHeading = 'Introduction';
333
+ let match;
334
+ while ((match = headingRegex.exec(content)) !== null) {
335
+ // Save previous section
336
+ if (lastIndex > 0 || match.index > 0) {
337
+ const sectionContent = content.slice(lastIndex, match.index).trim();
338
+ if (sectionContent) {
339
+ sections.push({
340
+ heading: lastHeading,
341
+ content: cleanMarkdown(sectionContent),
342
+ type: categorizeSectionType(lastHeading, sectionContent, docType),
343
+ });
344
+ }
345
+ }
346
+ lastHeading = match[2];
347
+ lastIndex = match.index + match[0].length;
348
+ }
349
+ // Add final section
350
+ const finalContent = content.slice(lastIndex).trim();
351
+ if (finalContent) {
352
+ sections.push({
353
+ heading: lastHeading,
354
+ content: cleanMarkdown(finalContent),
355
+ type: categorizeSectionType(lastHeading, finalContent, docType),
356
+ });
357
+ }
358
+ return {
359
+ path: relativePath,
360
+ title,
361
+ sections,
362
+ };
363
+ }
364
+ catch {
365
+ return null;
366
+ }
367
+ }
368
+ function cleanMarkdown(content) {
369
+ return content
370
+ .replace(/```[\s\S]*?```/g, '[code block]') // Replace code blocks
371
+ .replace(/`[^`]+`/g, (m) => m) // Keep inline code
372
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Convert links to text
373
+ .replace(/!\[([^\]]*)\]\([^)]+\)/g, '[image: $1]') // Simplify images
374
+ .replace(/\n{3,}/g, '\n\n') // Normalize newlines
375
+ .trim();
376
+ }
377
+ function categorizeSectionType(heading, content, docType) {
378
+ const headingLower = heading.toLowerCase();
379
+ const contentLower = content.toLowerCase();
380
+ if (docType === 'adr')
381
+ return 'architecture';
382
+ if (headingLower.includes('install') ||
383
+ headingLower.includes('setup') ||
384
+ headingLower.includes('getting started') ||
385
+ headingLower.includes('quickstart') ||
386
+ headingLower.includes('prerequisites')) {
387
+ return 'setup';
388
+ }
389
+ if (headingLower.includes('architecture') ||
390
+ headingLower.includes('design') ||
391
+ headingLower.includes('structure') ||
392
+ headingLower.includes('overview')) {
393
+ return 'architecture';
394
+ }
395
+ if (headingLower.includes('api') ||
396
+ headingLower.includes('endpoint') ||
397
+ headingLower.includes('reference')) {
398
+ return 'api';
399
+ }
400
+ if (headingLower.includes('troubleshoot') ||
401
+ headingLower.includes('faq') ||
402
+ headingLower.includes('common issues') ||
403
+ headingLower.includes('known issues') ||
404
+ contentLower.includes('if you encounter')) {
405
+ return 'troubleshooting';
406
+ }
407
+ return 'general';
408
+ }
409
+ function mapSectionToMemoryType(sectionType) {
410
+ switch (sectionType) {
411
+ case 'architecture':
412
+ return 'thought';
413
+ case 'troubleshooting':
414
+ return 'observation';
415
+ default:
416
+ return 'observation';
417
+ }
418
+ }
419
+ function calculateDocImportance(section, filePath) {
420
+ let importance = 5;
421
+ // Boost README
422
+ if (filePath.toLowerCase().includes('readme'))
423
+ importance += 2;
424
+ // Boost architecture docs
425
+ if (section.type === 'architecture')
426
+ importance += 1;
427
+ // Boost setup/troubleshooting
428
+ if (section.type === 'setup' || section.type === 'troubleshooting')
429
+ importance += 1;
430
+ // ADRs are important
431
+ if (filePath.includes('adr') || filePath.includes('decision'))
432
+ importance += 2;
433
+ return Math.min(importance, 10);
434
+ }
435
+ function categorizeCommit(message) {
436
+ const msgLower = message.toLowerCase();
437
+ if (msgLower.startsWith('revert'))
438
+ return 'revert';
439
+ if (msgLower.startsWith('fix:') || msgLower.startsWith('fix(') || msgLower.includes('bugfix'))
440
+ return 'fix';
441
+ if (msgLower.startsWith('feat:') || msgLower.startsWith('feat(') || msgLower.startsWith('feature'))
442
+ return 'feat';
443
+ if (msgLower.startsWith('refactor:') || msgLower.startsWith('refactor('))
444
+ return 'refactor';
445
+ if (msgLower.startsWith('docs:') || msgLower.startsWith('docs('))
446
+ return 'docs';
447
+ if (msgLower.startsWith('test:') || msgLower.startsWith('test('))
448
+ return 'test';
449
+ if (msgLower.startsWith('chore:') || msgLower.startsWith('chore('))
450
+ return 'chore';
451
+ return 'other';
452
+ }
453
+ function extractBugFixLearning(commit) {
454
+ const message = commit.message;
455
+ const body = commit.body;
456
+ // Try to extract what was fixed
457
+ let learning = `Bug fixed: ${message}`;
458
+ if (body) {
459
+ // Look for "Fixes #123" or similar
460
+ const issueMatch = body.match(/(?:fixes|closes|resolves)\s+#(\d+)/i);
461
+ if (issueMatch) {
462
+ learning += ` (Issue #${issueMatch[1]})`;
463
+ }
464
+ // Add summary of body if present
465
+ const summary = summarizeBody(body);
466
+ if (summary) {
467
+ learning += `. ${summary}`;
468
+ }
469
+ }
470
+ // Add affected files context
471
+ if (commit.files.length > 0 && commit.files.length <= 5) {
472
+ learning += ` Affected files: ${commit.files.join(', ')}.`;
473
+ }
474
+ return learning;
475
+ }
476
+ function summarizeBody(body) {
477
+ // Take first meaningful line of body
478
+ const lines = body.split('\n').filter(l => l.trim() && !l.startsWith('Co-authored') && !l.startsWith('Signed-off'));
479
+ if (lines.length === 0)
480
+ return '';
481
+ const firstLine = lines[0].trim();
482
+ if (firstLine.length > 200) {
483
+ return firstLine.substring(0, 200) + '...';
484
+ }
485
+ return firstLine;
486
+ }
487
+ function calculateCommitImportance(commit) {
488
+ let importance = 5;
489
+ // Reverts are always important
490
+ if (commit.type === 'revert')
491
+ importance = 8;
492
+ // Large changes are more significant
493
+ if (commit.files.length > 10)
494
+ importance += 1;
495
+ // Changes to core files
496
+ const coreFiles = commit.files.filter(f => f.includes('config') ||
497
+ f.includes('package.json') ||
498
+ f.includes('index') ||
499
+ f.includes('main') ||
500
+ f.includes('app'));
501
+ if (coreFiles.length > 0)
502
+ importance += 1;
503
+ // Has detailed body
504
+ if (commit.body && commit.body.length > 50)
505
+ importance += 1;
506
+ return Math.min(importance, 10);
507
+ }
508
+ function extractFileTags(files) {
509
+ const tags = new Set();
510
+ for (const file of files.slice(0, 10)) {
511
+ // Extract directory as tag
512
+ const parts = file.split('/');
513
+ if (parts.length > 1) {
514
+ tags.add(parts[0]);
515
+ }
516
+ // Extract file type
517
+ const ext = extname(file).toLowerCase();
518
+ if (ext === '.ts' || ext === '.tsx')
519
+ tags.add('typescript');
520
+ if (ext === '.js' || ext === '.jsx')
521
+ tags.add('javascript');
522
+ if (ext === '.py')
523
+ tags.add('python');
524
+ if (ext === '.go')
525
+ tags.add('go');
526
+ if (ext === '.rs')
527
+ tags.add('rust');
528
+ if (ext === '.yml' || ext === '.yaml')
529
+ tags.add('config');
530
+ if (ext === '.json')
531
+ tags.add('config');
532
+ if (ext === '.md')
533
+ tags.add('docs');
534
+ // Special files
535
+ if (file.includes('test') || file.includes('spec'))
536
+ tags.add('tests');
537
+ if (file.includes('docker') || file.includes('Dockerfile'))
538
+ tags.add('docker');
539
+ if (file.includes('terraform') || file.endsWith('.tf'))
540
+ tags.add('terraform');
541
+ }
542
+ return [...tags].slice(0, 5);
543
+ }
544
+ function extractTags(section, filePath) {
545
+ const tags = [section.type];
546
+ // Add source file as tag
547
+ const fileName = basename(filePath, extname(filePath)).toLowerCase();
548
+ if (fileName !== 'readme') {
549
+ tags.push(fileName);
550
+ }
551
+ // Extract keywords from heading
552
+ const keywords = section.heading.toLowerCase()
553
+ .split(/[\s-_]+/)
554
+ .filter(w => w.length > 3 && !['the', 'and', 'for', 'with'].includes(w));
555
+ tags.push(...keywords.slice(0, 3));
556
+ return [...new Set(tags)].slice(0, 5);
557
+ }
558
+ function chunkContent(content, maxLength) {
559
+ if (content.length <= maxLength)
560
+ return [content];
561
+ const chunks = [];
562
+ const sentences = content.split(/(?<=[.!?])\s+/);
563
+ let currentChunk = '';
564
+ for (const sentence of sentences) {
565
+ if (currentChunk.length + sentence.length > maxLength) {
566
+ if (currentChunk)
567
+ chunks.push(currentChunk.trim());
568
+ currentChunk = sentence;
569
+ }
570
+ else {
571
+ currentChunk += (currentChunk ? ' ' : '') + sentence;
572
+ }
573
+ }
574
+ if (currentChunk)
575
+ chunks.push(currentChunk.trim());
576
+ return chunks;
577
+ }
578
+ /**
579
+ * Discover skills, droids, commands, and artifacts from various platforms
580
+ */
581
+ export async function discoverSkillsAndArtifacts(cwd, verbose = false) {
582
+ const skills = [];
583
+ // Factory.ai droids, skills, and commands
584
+ const factoryPaths = [
585
+ { dir: '.factory/droids', type: 'droid' },
586
+ { dir: '.factory/skills', type: 'skill' },
587
+ { dir: '.factory/commands', type: 'command' },
588
+ ];
589
+ for (const { dir, type } of factoryPaths) {
590
+ const fullPath = join(cwd, dir);
591
+ if (existsSync(fullPath) && statSync(fullPath).isDirectory()) {
592
+ const files = readdirSync(fullPath);
593
+ for (const file of files) {
594
+ if (file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml')) {
595
+ const filePath = join(fullPath, file);
596
+ const skill = parseSkillFile(filePath, cwd, type, 'factory');
597
+ if (skill) {
598
+ skills.push(skill);
599
+ if (verbose)
600
+ console.log(` Found Factory ${type}: ${skill.name}`);
601
+ }
602
+ }
603
+ }
604
+ }
605
+ }
606
+ // Claude Code agents and commands
607
+ const claudePaths = [
608
+ { dir: '.claude/agents', type: 'droid' },
609
+ { dir: '.claude/commands', type: 'command' },
610
+ ];
611
+ for (const { dir, type } of claudePaths) {
612
+ const fullPath = join(cwd, dir);
613
+ if (existsSync(fullPath) && statSync(fullPath).isDirectory()) {
614
+ const files = readdirSync(fullPath);
615
+ for (const file of files) {
616
+ if (file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml')) {
617
+ const filePath = join(fullPath, file);
618
+ const skill = parseSkillFile(filePath, cwd, type, 'claude');
619
+ if (skill) {
620
+ skills.push(skill);
621
+ if (verbose)
622
+ console.log(` Found Claude ${type}: ${skill.name}`);
623
+ }
624
+ }
625
+ }
626
+ }
627
+ }
628
+ // OpenCode agents and commands
629
+ const opencodePaths = [
630
+ { dir: '.opencode/agent', type: 'droid' },
631
+ { dir: '.opencode/command', type: 'command' },
632
+ ];
633
+ for (const { dir, type } of opencodePaths) {
634
+ const fullPath = join(cwd, dir);
635
+ if (existsSync(fullPath) && statSync(fullPath).isDirectory()) {
636
+ const files = readdirSync(fullPath);
637
+ for (const file of files) {
638
+ if (file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml') || file.endsWith('.json')) {
639
+ const filePath = join(fullPath, file);
640
+ const skill = parseSkillFile(filePath, cwd, type, 'opencode');
641
+ if (skill) {
642
+ skills.push(skill);
643
+ if (verbose)
644
+ console.log(` Found OpenCode ${type}: ${skill.name}`);
645
+ }
646
+ }
647
+ }
648
+ }
649
+ }
650
+ // Discover artifacts (reusable code patterns, templates)
651
+ const artifactDirs = [
652
+ 'templates',
653
+ 'snippets',
654
+ '.github/workflows',
655
+ 'scripts',
656
+ 'tools',
657
+ ];
658
+ for (const dir of artifactDirs) {
659
+ const fullPath = join(cwd, dir);
660
+ if (existsSync(fullPath) && statSync(fullPath).isDirectory()) {
661
+ const files = readdirSync(fullPath).slice(0, 20); // Limit to avoid too many
662
+ for (const file of files) {
663
+ const filePath = join(fullPath, file);
664
+ if (statSync(filePath).isFile()) {
665
+ skills.push({
666
+ name: basename(file, extname(file)),
667
+ path: relative(cwd, filePath),
668
+ type: 'artifact',
669
+ description: `Reusable ${dir} artifact`,
670
+ platform: 'generic',
671
+ });
672
+ if (verbose)
673
+ console.log(` Found artifact: ${file}`);
674
+ }
675
+ }
676
+ }
677
+ }
678
+ return skills;
679
+ }
680
+ /**
681
+ * Parse a skill/droid/command file to extract metadata
682
+ */
683
+ function parseSkillFile(filePath, cwd, type, platform) {
684
+ try {
685
+ const content = readFileSync(filePath, 'utf-8');
686
+ const fileName = basename(filePath, extname(filePath));
687
+ let description = '';
688
+ // Try to extract description from different formats
689
+ if (filePath.endsWith('.md')) {
690
+ // Extract first paragraph or heading
691
+ const match = content.match(/^#\s+.+\n+(.+)/m);
692
+ if (match)
693
+ description = match[1].trim();
694
+ }
695
+ else if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) {
696
+ // Look for description field
697
+ const match = content.match(/description:\s*['"]?([^'"\n]+)/i);
698
+ if (match)
699
+ description = match[1].trim();
700
+ }
701
+ else if (filePath.endsWith('.json')) {
702
+ try {
703
+ const json = JSON.parse(content);
704
+ description = json.description || '';
705
+ }
706
+ catch {
707
+ // Ignore JSON parse errors
708
+ }
709
+ }
710
+ return {
711
+ name: fileName,
712
+ path: relative(cwd, filePath),
713
+ type,
714
+ description: description || `${platform} ${type}`,
715
+ platform,
716
+ };
717
+ }
718
+ catch {
719
+ return null;
720
+ }
721
+ }
722
+ /**
723
+ * Convert discovered skills into memory entries
724
+ */
725
+ export function extractSkillMemories(skills) {
726
+ const memories = [];
727
+ const now = new Date().toISOString();
728
+ // Group by type
729
+ const droids = skills.filter(s => s.type === 'droid');
730
+ const skillItems = skills.filter(s => s.type === 'skill');
731
+ const commands = skills.filter(s => s.type === 'command');
732
+ const artifacts = skills.filter(s => s.type === 'artifact');
733
+ // Create memory for available droids/agents
734
+ if (droids.length > 0) {
735
+ memories.push({
736
+ id: 'skills-droids-available',
737
+ timestamp: now,
738
+ type: 'observation',
739
+ content: `Available AI agents/droids: ${droids.map(d => `${d.name} (${d.platform})`).join(', ')}. These can be invoked for specialized tasks.`,
740
+ tags: ['droids', 'agents', 'capabilities'],
741
+ importance: 8,
742
+ metadata: {
743
+ source: 'skills-discovery',
744
+ droids: droids.map(d => ({ name: d.name, platform: d.platform, path: d.path })),
745
+ },
746
+ });
747
+ }
748
+ // Create memory for available skills
749
+ if (skillItems.length > 0) {
750
+ memories.push({
751
+ id: 'skills-skills-available',
752
+ timestamp: now,
753
+ type: 'observation',
754
+ content: `Available skills: ${skillItems.map(s => s.name).join(', ')}. These provide specialized capabilities.`,
755
+ tags: ['skills', 'capabilities'],
756
+ importance: 7,
757
+ metadata: {
758
+ source: 'skills-discovery',
759
+ skills: skillItems.map(s => ({ name: s.name, platform: s.platform, path: s.path })),
760
+ },
761
+ });
762
+ }
763
+ // Create memory for available commands
764
+ if (commands.length > 0) {
765
+ memories.push({
766
+ id: 'skills-commands-available',
767
+ timestamp: now,
768
+ type: 'observation',
769
+ content: `Available slash commands: ${commands.map(c => `/${c.name}`).join(', ')}. Use these for quick actions.`,
770
+ tags: ['commands', 'shortcuts'],
771
+ importance: 7,
772
+ metadata: {
773
+ source: 'skills-discovery',
774
+ commands: commands.map(c => ({ name: c.name, platform: c.platform, path: c.path })),
775
+ },
776
+ });
777
+ }
778
+ // Create memories for individual droids with descriptions
779
+ for (const droid of droids) {
780
+ if (droid.description && droid.description.length > 20) {
781
+ memories.push({
782
+ id: `skill-droid-${droid.name}`,
783
+ timestamp: now,
784
+ type: 'thought',
785
+ content: `Droid "${droid.name}" (${droid.platform}): ${droid.description}. Located at ${droid.path}.`,
786
+ tags: ['droid', droid.platform, droid.name],
787
+ importance: 6,
788
+ metadata: {
789
+ source: 'skills-discovery',
790
+ ...droid,
791
+ },
792
+ });
793
+ }
794
+ }
795
+ // Create memory for useful artifacts/templates
796
+ const usefulArtifacts = artifacts.filter(a => a.path.includes('workflow') ||
797
+ a.path.includes('template') ||
798
+ a.path.includes('script'));
799
+ if (usefulArtifacts.length > 0) {
800
+ memories.push({
801
+ id: 'skills-artifacts-available',
802
+ timestamp: now,
803
+ type: 'observation',
804
+ content: `Useful project artifacts: ${usefulArtifacts.slice(0, 10).map(a => a.path).join(', ')}. These can be referenced or reused.`,
805
+ tags: ['artifacts', 'templates', 'reusable'],
806
+ importance: 5,
807
+ metadata: {
808
+ source: 'skills-discovery',
809
+ artifacts: usefulArtifacts.map(a => a.path),
810
+ },
811
+ });
812
+ }
813
+ return memories;
814
+ }
815
+ //# sourceMappingURL=prepopulate.js.map