UNPKG

ai-dev-diary

Version:

Intelligent development diary system for AI-assisted projects

370 lines (342 loc) 13 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DiaryManager = void 0; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const dayjs_1 = __importDefault(require("dayjs")); const github_1 = require("../utils/github"); class DiaryManager { config; basePath; userPath; constructor(basePath = '.ai-diary', author) { this.basePath = basePath; this.config = this.loadConfig(); const effectiveAuthor = author || this.config.author || this.config.githubUsername || 'unknown'; this.userPath = (0, github_1.getAuthorPath)(this.basePath, effectiveAuthor, this.config.teamMode || false); } async initialize(config) { const detectedUsername = await (0, github_1.detectGitHubUsername)(); const defaultConfig = { author: config?.author || detectedUsername || 'Unknown', githubUsername: detectedUsername || undefined, projectName: path.basename(process.cwd()), teamMode: config?.teamMode || false, ai: { model: 'claude-3', autoContext: true, }, git: { autoEntries: true, linkCommits: true, }, sharing: { team: false, public: false, }, ...config, }; this.config = defaultConfig; this.userPath = (0, github_1.getAuthorPath)(this.basePath, defaultConfig.author, defaultConfig.teamMode || false); const dirs = [ this.basePath, path.join(this.basePath, 'context'), path.join(this.basePath, 'shared'), path.join(this.basePath, 'shared', 'knowledge'), path.join(this.basePath, 'shared', 'insights'), path.join(this.basePath, 'reports'), ]; if (defaultConfig.teamMode) { dirs.push(path.join(this.basePath, 'team'), path.join(this.userPath), path.join(this.userPath, 'entries'), path.join(this.userPath, 'insights'), path.join(this.userPath, 'context')); } else { dirs.push(path.join(this.basePath, 'entries'), path.join(this.basePath, 'knowledge'), path.join(this.basePath, 'insights')); } for (const dir of dirs) { await fs.ensureDir(dir); } await fs.writeJson(path.join(this.basePath, 'config.json'), defaultConfig, { spaces: 2 }); await this.createInitialContext(); await this.createKnowledgeBase(); } isInitialized() { return fs.existsSync(path.join(this.basePath, 'config.json')); } loadConfig() { const configPath = path.join(this.basePath, 'config.json'); if (fs.existsSync(configPath)) { return fs.readJsonSync(configPath); } return { author: 'Unknown', }; } async saveEntry(entry) { const date = (0, dayjs_1.default)(entry.timestamp); const entriesPath = this.config.teamMode ? path.join(this.userPath, 'entries') : path.join(this.basePath, 'entries'); const entryDir = path.join(entriesPath, date.format('YYYY'), date.format('MM'), date.format('DD')); await fs.ensureDir(entryDir); const entryCount = (await fs.readdir(entryDir)).length; const entryNumber = String(entryCount + 1).padStart(3, '0'); const filename = `${entryNumber}-${entry.type}-${this.sanitizeFilename(entry.title)}.md`; const content = this.formatEntry(entry); await fs.writeFile(path.join(entryDir, filename), content); } async getContext(author) { if (this.config.teamMode && author) { const personalPath = path.join((0, github_1.getAuthorPath)(this.basePath, author, true), 'context', 'AGENT_CONTEXT.md'); if (await fs.pathExists(personalPath)) { const content = await fs.readFile(personalPath, 'utf-8'); return this.parseContext(content); } } const contextPath = path.join(this.basePath, 'context', 'AGENT_CONTEXT.md'); if (await fs.pathExists(contextPath)) { const content = await fs.readFile(contextPath, 'utf-8'); return this.parseContext(content); } return { projectState: '', currentFocus: '', recentChanges: [], importantNotes: [], nextSteps: [], lastUpdated: new Date(), }; } async updateContext(updates, author) { const current = await this.getContext(author); const updated = { ...current, ...updates, lastUpdated: new Date() }; const content = this.formatContext(updated); if (this.config.teamMode && (author || this.config.author)) { const effectiveAuthor = author || this.config.author; const personalPath = path.join((0, github_1.getAuthorPath)(this.basePath, effectiveAuthor, true), 'context', 'AGENT_CONTEXT.md'); await fs.ensureDir(path.dirname(personalPath)); await fs.writeFile(personalPath, content); } else { await fs.writeFile(path.join(this.basePath, 'context', 'AGENT_CONTEXT.md'), content); } } async createInitialContext() { const contextFiles = { 'AGENT_CONTEXT.md': `# AI Agent Context ## Current Project State - **Phase**: Initial setup - **Status**: Diary initialized ## Important Context - Project uses ai-dev-diary for knowledge management - All significant events should be logged ## Recent Changes - Initialized development diary ## Next Steps - Begin logging development activities - Build knowledge base from experiences Last Updated: ${new Date().toISOString()} `, 'PROJECT_STATE.md': `# Project State ## Overview Development diary initialized for ${this.config.projectName || 'this project'}. ## Current Status - Diary system: Active - Knowledge capture: Enabled ## Key Metrics - Entries: 0 - Patterns identified: 0 - Mistakes logged: 0 `, 'MEMORY.md': `# Important Things to Remember ## Critical Information - Always log significant decisions - Document mistakes and their fixes - Capture breakthrough moments - Update context regularly ## Project-Specific Notes (Add project-specific notes here) `, }; for (const [filename, content] of Object.entries(contextFiles)) { await fs.writeFile(path.join(this.basePath, 'context', filename), content); } } async createKnowledgeBase() { const knowledgePath = this.config.teamMode ? path.join(this.basePath, 'shared', 'knowledge') : path.join(this.basePath, 'knowledge'); const knowledgeFiles = { 'patterns.md': `# Successful Patterns ## Overview Document patterns that work well in this project. ## Pattern Template ### Pattern Name - **Context**: When to use this pattern - **Solution**: How to implement it - **Benefits**: Why it works - **Examples**: Real usage examples --- `, 'antipatterns.md': `# Anti-patterns to Avoid ## Overview Document patterns that should be avoided. ## Anti-pattern Template ### Anti-pattern Name - **Context**: When this might be tempting - **Problem**: Why it's problematic - **Alternative**: Better approach - **Examples**: What to do instead --- `, 'mistakes.md': `# Mistakes Log ## Overview Learn from past mistakes to avoid repeating them. ## Entry Format Date | Mistake | Impact | Correction | Prevention --- `, 'breakthroughs.md': `# Breakthrough Moments ## Overview Capture major discoveries and solutions. ## Entry Format ### Date - Title - **Challenge**: What problem were we solving? - **Discovery**: What did we realize? - **Solution**: How did we solve it? - **Impact**: What changed as a result? --- `, }; for (const [filename, content] of Object.entries(knowledgeFiles)) { await fs.writeFile(path.join(knowledgePath, filename), content); } } formatEntry(entry) { const frontmatter = [ '---', `id: ${entry.id}`, `type: ${entry.type}`, `timestamp: ${entry.timestamp.toISOString()}`, `author: ${entry.metadata.author}`, ]; if (entry.metadata.aiAgent) { frontmatter.push(`ai_agent: ${entry.metadata.aiAgent}`); } if (entry.tags.length > 0) { frontmatter.push(`tags: [${entry.tags.join(', ')}]`); } frontmatter.push('---', ''); const content = [ `# ${entry.title}`, '', `Date: ${(0, dayjs_1.default)(entry.timestamp).format('YYYY-MM-DD HH:mm:ss')}`, `Type: ${entry.type}`, `Author: ${entry.metadata.author}`, '', '## Content', entry.content, ]; if (entry.metadata.correction) { content.push('', '## Correction', entry.metadata.correction); } if (entry.metadata.impact) { content.push('', '## Impact', entry.metadata.impact); } if (entry.metadata.relatedFiles && entry.metadata.relatedFiles.length > 0) { content.push('', '## Related Files', ...entry.metadata.relatedFiles.map(f => `- ${f}`)); } return [...frontmatter, ...content].join('\n'); } formatContext(context) { return `# AI Agent Context ## Current Project State ${context.projectState} ## Current Focus ${context.currentFocus} ## Recent Changes ${context.recentChanges.map(change => `- ${change}`).join('\n')} ## Important Notes ${context.importantNotes.map(note => `- ${note}`).join('\n')} ## Next Steps ${context.nextSteps.map(step => `- ${step}`).join('\n')} Last Updated: ${context.lastUpdated.toISOString()} `; } parseContext(content) { const sections = content.split('##').slice(1); const context = { projectState: '', currentFocus: '', recentChanges: [], importantNotes: [], nextSteps: [], lastUpdated: new Date(), }; sections.forEach(section => { const lines = section.trim().split('\n'); const title = lines[0].trim(); const content = lines.slice(1).join('\n').trim(); switch (title) { case 'Current Project State': context.projectState = content; break; case 'Current Focus': context.currentFocus = content; break; case 'Recent Changes': context.recentChanges = content.split('\n').map(l => l.replace(/^- /, '').trim()).filter(Boolean); break; case 'Important Notes': context.importantNotes = content.split('\n').map(l => l.replace(/^- /, '').trim()).filter(Boolean); break; case 'Next Steps': context.nextSteps = content.split('\n').map(l => l.replace(/^- /, '').trim()).filter(Boolean); break; } }); return context; } sanitizeFilename(name) { return name .toLowerCase() .replace(/[^a-z0-9-]/g, '-') .replace(/-+/g, '-') .slice(0, 50); } } exports.DiaryManager = DiaryManager; //# sourceMappingURL=diary-manager.js.map