@groeponline/pi-missions 0.2.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/database/index.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * Pi Missions Database Module\n * \n * SQLite database for persistent mission storage, pattern recognition,\n * and learning system.\n */\n\nimport type Database from 'better-sqlite3';\nimport { createRequire } from 'node:module';\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { existsSync, mkdirSync } from 'node:fs';\nimport { homedir } from 'node:os';\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Database singleton\n// ═══════════════════════════════════════════════════════════════════════════\n\nlet db: Database.Database | null = null;\n\nconst require = createRequire(import.meta.url);\nlet databaseDriver: 'better-sqlite3' | 'node:sqlite' | null = null;\nconst CURRENT_SCHEMA_VERSION = 1;\n\ntype DatabaseConstructor = new (filename: string) => Database.Database;\n\nfunction describeLoadError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction openSqliteDatabase(filename: string): Database.Database {\n try {\n const BetterSqlite3 = require('better-sqlite3') as DatabaseConstructor;\n databaseDriver = 'better-sqlite3';\n return new BetterSqlite3(filename);\n } catch (betterSqliteError) {\n try {\n const { DatabaseSync } = require('node:sqlite') as { DatabaseSync: DatabaseConstructor };\n databaseDriver = 'node:sqlite';\n return new DatabaseSync(filename);\n } catch (nodeSqliteError) {\n throw new Error(\n 'Unable to initialize SQLite. Run on Node.js >=22.5.0, or install better-sqlite3 manually. ' +\n `better-sqlite3: ${describeLoadError(betterSqliteError)}; ` +\n `node:sqlite: ${describeLoadError(nodeSqliteError)}`,\n );\n }\n }\n}\n\nfunction applyPragma(database: Database.Database, pragma: string): void {\n const maybePragma = (database as unknown as { pragma?: (statement: string) => unknown }).pragma;\n if (typeof maybePragma === 'function') {\n maybePragma.call(database, pragma);\n } else {\n database.exec(`PRAGMA ${pragma}`);\n }\n}\n\nexport function getDatabaseDriver(): string | null {\n return databaseDriver;\n}\n\nfunction resolveSchemaPath(): string {\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n join(moduleDir, 'schema.sql'),\n join(moduleDir, 'database', 'schema.sql'),\n join(moduleDir, '..', 'database', 'schema.sql'),\n join(process.cwd(), 'src', 'database', 'schema.sql'),\n join(process.cwd(), 'dist', 'database', 'schema.sql'),\n ];\n const schemaPath = candidates.find(existsSync);\n if (!schemaPath) {\n throw new Error(`Unable to locate database schema.sql. Searched: ${candidates.join(', ')}`);\n }\n return schemaPath;\n}\n\nexport function getDatabase(): Database.Database {\n if (!db) {\n const dbPath = getDatabasePath();\n \n // If dbPath is a directory, use it; otherwise use its parent\n let dbDir: string;\n let dbFile: string;\n \n if (dbPath === ':memory:') {\n dbDir = '';\n dbFile = dbPath;\n } else if (dbPath.endsWith('.db')) {\n dbDir = dirname(dbPath);\n dbFile = dbPath;\n } else {\n dbDir = dbPath;\n dbFile = join(dbPath, 'pi-missions.db');\n }\n \n if (dbDir && !existsSync(dbDir)) {\n mkdirSync(dbDir, { recursive: true });\n }\n \n const candidateDb = openSqliteDatabase(dbFile);\n try {\n applyPragma(candidateDb, 'journal_mode = WAL');\n applyPragma(candidateDb, 'foreign_keys = ON');\n\n // Initialize schema before publishing singleton\n initializeSchema(candidateDb);\n runMigrations(candidateDb);\n db = candidateDb;\n } catch (error) {\n candidateDb.close();\n throw error;\n }\n }\n \n return db;\n}\n\nexport function getDatabasePath(): string {\n return process.env.PI_MISSIONS_DB_PATH || join(homedir(), '.pi', 'missions', 'database');\n}\n\nfunction initializeSchema(db: Database.Database): void {\n const schemaPath = resolveSchemaPath();\n const schema = readFileSync(schemaPath, 'utf-8');\n\n try {\n db.exec(schema);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to initialize database schema from ${schemaPath}: ${message}`);\n }\n}\n\nfunction ensureSchemaVersionTable(database: Database.Database): void {\n database.exec(`\n CREATE TABLE IF NOT EXISTS schema_version (\n id INTEGER PRIMARY KEY CHECK(id = 1),\n version INTEGER NOT NULL,\n updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n );\n `);\n}\n\nfunction getSchemaVersion(database: Database.Database): number {\n ensureSchemaVersionTable(database);\n const row = database.prepare('SELECT version FROM schema_version WHERE id = 1').get() as { version?: number } | undefined;\n return row?.version ?? 0;\n}\n\nfunction setSchemaVersion(database: Database.Database, version: number): void {\n database.prepare(`\n INSERT INTO schema_version (id, version, updated_at)\n VALUES (1, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n version = excluded.version,\n updated_at = excluded.updated_at\n `).run(version, Date.now());\n}\n\nfunction baselineExistingSchema(database: Database.Database): void {\n const row = database.prepare(`\n SELECT 1 AS exists_flag\n FROM sqlite_master\n WHERE type = 'table' AND name = 'missions'\n LIMIT 1\n `).get() as { exists_flag?: number } | undefined;\n\n if (row?.exists_flag === 1) {\n setSchemaVersion(database, 1);\n }\n}\n\nfunction runMigrations(database: Database.Database): void {\n ensureSchemaVersionTable(database);\n\n const migrateBody = () => {\n let version = getSchemaVersion(database);\n\n if (version === 0) {\n baselineExistingSchema(database);\n version = getSchemaVersion(database);\n }\n\n if (version === 0) {\n setSchemaVersion(database, CURRENT_SCHEMA_VERSION);\n }\n };\n\n const maybeTransaction = (database as unknown as { transaction?: (fn: () => void) => () => void }).transaction;\n if (typeof maybeTransaction === 'function') {\n const migrate = maybeTransaction.call(database, migrateBody);\n migrate();\n return;\n }\n\n database.exec('BEGIN');\n try {\n migrateBody();\n database.exec('COMMIT');\n } catch (error) {\n database.exec('ROLLBACK');\n throw error;\n }\n}\n\nexport function closeDatabase(): void {\n if (db) {\n db.close();\n db = null;\n }\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Repository interfaces\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport interface MissionRow {\n id: string;\n title: string;\n goal: string | null;\n status: string;\n created_at: number;\n updated_at: number;\n completed_at: number | null;\n total_tokens: number;\n total_features: number;\n features_completed: number;\n features_failed: number;\n success_rate: number;\n tags: string; // JSON\n metadata: string; // JSON\n}\n\nexport interface MilestoneRow {\n id: string;\n mission_id: string;\n title: string;\n description: string | null;\n status: string;\n sort_order: number;\n created_at: number;\n completed_at: number | null;\n}\n\nexport interface FeatureRow {\n id: string;\n milestone_id: string;\n mission_id: string;\n title: string;\n description: string | null;\n priority: number;\n status: string;\n depends_on: string; // JSON\n acceptance_criteria: string; // JSON\n sessions: string; // JSON\n tool_call_count: number;\n tokens_used: number;\n error_count: number;\n blockers: string; // JSON\n notes: string | null;\n created_at: number;\n started_at: number | null;\n completed_at: number | null;\n evidence: string | null;\n}\n\nexport interface HistoryRow {\n id: number;\n mission_id: string;\n feature_id: string | null;\n event: string;\n note: string | null;\n details: string; // JSON\n timestamp: number;\n session_id: string | null;\n}\n\nexport interface LearningRow {\n id: number;\n mission_id: string | null;\n feature_id: string | null;\n type: string;\n category: string | null;\n insight: string;\n confidence: number;\n applicable_to: string; // JSON\n context: string; // JSON\n created_at: number;\n used_count: number;\n success_count: number;\n}\n\nexport interface PatternRow {\n id: number;\n pattern_type: string;\n name: string;\n description: string;\n pattern_data: string; // JSON\n success_count: number;\n failure_count: number;\n success_rate: number;\n avg_duration_ms: number | null;\n avg_tokens: number | null;\n example_missions: string; // JSON\n tags: string; // JSON\n created_at: number;\n updated_at: number;\n}\n\nexport interface TemplateRow {\n id: string;\n name: string;\n description: string | null;\n author: string | null;\n version: string;\n content: string; // JSON\n tags: string; // JSON\n difficulty: string;\n estimated_time_hours: number | null;\n estimated_tokens: number | null;\n usage_count: number;\n rating: number;\n rating_count: number;\n created_at: number;\n updated_at: number;\n is_builtin: number;\n}\n\nexport interface SessionRow {\n id: string;\n mission_id: string | null;\n feature_id: string | null;\n started_at: number;\n ended_at: number | null;\n tokens_used: number;\n tool_calls: number;\n errors: number;\n status: string;\n}\n\nexport interface MetricRow {\n id: number;\n metric_type: string;\n metric_name: string;\n value: number;\n unit: string | null;\n tags: string; // JSON\n recorded_at: number;\n period_start: number | null;\n period_end: number | null;\n}\n\nexport interface PredictionRow {\n id: number;\n mission_id: string | null;\n feature_id: string | null;\n prediction_type: string;\n predicted_value: number;\n actual_value: number | null;\n confidence: number;\n accuracy: number | null;\n model_version: string | null;\n created_at: number;\n validated_at: number | null;\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Repository classes\n// ═══════════════════════════════════════════════════════════════════════════\n\nconst MISSION_UPDATABLE_COLUMNS = new Set([\n 'title', 'goal', 'status', 'updated_at', 'completed_at',\n 'total_tokens', 'total_features', 'features_completed',\n 'features_failed', 'success_rate', 'tags', 'metadata'\n]);\n\nexport class MissionRepository {\n private db: Database.Database;\n \n constructor(db: Database.Database) {\n this.db = db;\n }\n \n create(mission: Omit<MissionRow, 'created_at' | 'updated_at'>): MissionRow {\n const now = Date.now();\n const stmt = this.db.prepare(`\n INSERT INTO missions (id, title, goal, status, created_at, updated_at, completed_at, \n total_tokens, total_features, features_completed, features_failed, \n success_rate, tags, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n stmt.run(\n mission.id, mission.title, mission.goal, mission.status,\n now, now, mission.completed_at,\n mission.total_tokens, mission.total_features, mission.features_completed,\n mission.features_failed, mission.success_rate, mission.tags, mission.metadata\n );\n \n return this.findById(mission.id)!;\n }\n \n findById(id: string): MissionRow | undefined {\n return this.db.prepare('SELECT * FROM missions WHERE id = ?').get(id) as MissionRow | undefined;\n }\n \n findAll(limit = 100, offset = 0): MissionRow[] {\n return this.db.prepare('SELECT * FROM missions ORDER BY updated_at DESC LIMIT ? OFFSET ?')\n .all(limit, offset) as MissionRow[];\n }\n \n findByStatus(status: string): MissionRow[] {\n return this.db.prepare('SELECT * FROM missions WHERE status = ? ORDER BY updated_at DESC')\n .all(status) as MissionRow[];\n }\n \n update(id: string, updates: Partial<MissionRow>): MissionRow | undefined {\n const fields = Object.keys(updates).filter(k => MISSION_UPDATABLE_COLUMNS.has(k));\n if (fields.length === 0) return this.findById(id);\n \n const setClause = fields.map(f => `${f} = ?`).join(', ');\n const values = fields.map(f => updates[f as keyof MissionRow]);\n \n this.db.prepare(`UPDATE missions SET ${setClause}, updated_at = ? WHERE id = ?`)\n .run(...values, Date.now(), id);\n \n return this.findById(id);\n }\n \n delete(id: string): boolean {\n const result = this.db.prepare('DELETE FROM missions WHERE id = ?').run(id);\n return result.changes > 0;\n }\n \n count(): number {\n const row = this.db.prepare('SELECT COUNT(*) as count FROM missions').get() as { count: number } | undefined;\n return row?.count ?? 0;\n }\n}\n\nconst FEATURE_UPDATABLE_COLUMNS = new Set([\n 'milestone_id', 'title', 'description', 'priority', 'status',\n 'depends_on', 'acceptance_criteria', 'sessions', 'tool_call_count',\n 'tokens_used', 'error_count', 'blockers', 'notes', 'started_at',\n 'completed_at', 'evidence'\n]);\n\nexport class FeatureRepository {\n private db: Database.Database;\n \n constructor(db: Database.Database) {\n this.db = db;\n }\n \n create(feature: Omit<FeatureRow, 'created_at'>): FeatureRow {\n const stmt = this.db.prepare(`\n INSERT INTO features (id, milestone_id, mission_id, title, description, priority, status,\n depends_on, acceptance_criteria, sessions, tool_call_count, tokens_used,\n error_count, blockers, notes, created_at, started_at, completed_at, evidence)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n stmt.run(\n feature.id, feature.milestone_id, feature.mission_id, feature.title, feature.description,\n feature.priority, feature.status, feature.depends_on, feature.acceptance_criteria,\n feature.sessions, feature.tool_call_count, feature.tokens_used, feature.error_count,\n feature.blockers, feature.notes, Date.now(), feature.started_at, feature.completed_at,\n feature.evidence\n );\n \n return this.findById(feature.id, feature.mission_id)!;\n }\n \n findById(id: string, missionId: string): FeatureRow | undefined {\n return this.db.prepare('SELECT * FROM features WHERE id = ? AND mission_id = ?')\n .get(id, missionId) as FeatureRow | undefined;\n }\n \n findByMission(missionId: string): FeatureRow[] {\n return this.db.prepare('SELECT * FROM features WHERE mission_id = ? ORDER BY created_at')\n .all(missionId) as FeatureRow[];\n }\n \n findByStatus(missionId: string, status: string): FeatureRow[] {\n return this.db.prepare('SELECT * FROM features WHERE mission_id = ? AND status = ?')\n .all(missionId, status) as FeatureRow[];\n }\n \n update(id: string, missionId: string, updates: Partial<FeatureRow>): FeatureRow | undefined {\n const fields = Object.keys(updates).filter(k => FEATURE_UPDATABLE_COLUMNS.has(k));\n if (fields.length === 0) return this.findById(id, missionId);\n \n const setClause = fields.map(f => `${f} = ?`).join(', ');\n const values = fields.map(f => updates[f as keyof FeatureRow]);\n \n this.db.prepare(`UPDATE features SET ${setClause} WHERE id = ? AND mission_id = ?`)\n .run(...values, id, missionId);\n \n return this.findById(id, missionId);\n }\n \n delete(id: string, missionId: string): boolean {\n const result = this.db.prepare('DELETE FROM features WHERE id = ? AND mission_id = ?')\n .run(id, missionId);\n return result.changes > 0;\n }\n}\n\nexport class HistoryRepository {\n private db: Database.Database;\n \n constructor(db: Database.Database) {\n this.db = db;\n }\n \n append(entry: Omit<HistoryRow, 'id' | 'timestamp'>): HistoryRow {\n const stmt = this.db.prepare(`\n INSERT INTO history (mission_id, feature_id, event, note, details, timestamp, session_id)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n \n const result = stmt.run(\n entry.mission_id, entry.feature_id, entry.event, entry.note,\n entry.details, Date.now(), entry.session_id\n );\n \n return this.db.prepare('SELECT * FROM history WHERE id = ?')\n .get(result.lastInsertRowid) as HistoryRow;\n }\n \n findByMission(missionId: string, limit = 100): HistoryRow[] {\n return this.db.prepare('SELECT * FROM history WHERE mission_id = ? ORDER BY timestamp DESC LIMIT ?')\n .all(missionId, limit) as HistoryRow[];\n }\n \n findByFeature(featureId: string, limit = 50): HistoryRow[] {\n return this.db.prepare('SELECT * FROM history WHERE feature_id = ? ORDER BY timestamp DESC LIMIT ?')\n .all(featureId, limit) as HistoryRow[];\n }\n \n findByEvent(event: string, limit = 50): HistoryRow[] {\n return this.db.prepare('SELECT * FROM history WHERE event = ? ORDER BY timestamp DESC LIMIT ?')\n .all(event, limit) as HistoryRow[];\n }\n \n search(query: string, limit = 50): HistoryRow[] {\n return this.db.prepare(`\n SELECT * FROM history \n WHERE note LIKE ? OR event LIKE ? \n ORDER BY timestamp DESC LIMIT ?\n `).all(`%${query}%`, `%${query}%`, limit) as HistoryRow[];\n }\n}\n\nexport class LearningRepository {\n private db: Database.Database;\n \n constructor(db: Database.Database) {\n this.db = db;\n }\n \n create(learning: Omit<LearningRow, 'id' | 'created_at' | 'used_count' | 'success_count'>): LearningRow {\n const stmt = this.db.prepare(`\n INSERT INTO learnings (mission_id, feature_id, type, category, insight, confidence, applicable_to, context)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n const result = stmt.run(\n learning.mission_id, learning.feature_id, learning.type, learning.category,\n learning.insight, learning.confidence, learning.applicable_to, learning.context\n );\n \n return this.db.prepare('SELECT * FROM learnings WHERE id = ?')\n .get(result.lastInsertRowid) as LearningRow;\n }\n \n findByType(type: string, limit = 50): LearningRow[] {\n return this.db.prepare('SELECT * FROM learnings WHERE type = ? ORDER BY confidence DESC LIMIT ?')\n .all(type, limit) as LearningRow[];\n }\n \n findByCategory(category: string, limit = 50): LearningRow[] {\n return this.db.prepare('SELECT * FROM learnings WHERE category = ? ORDER BY confidence DESC LIMIT ?')\n .all(category, limit) as LearningRow[];\n }\n \n findRelevant(tags: string[], limit = 10): LearningRow[] {\n if (tags.length === 0) return [];\n\n const placeholders = tags.map(() => '?').join(',');\n return this.db.prepare(`\n SELECT * FROM learnings \n WHERE json_valid(learnings.applicable_to)\n AND EXISTS (\n SELECT 1\n FROM json_each(learnings.applicable_to)\n WHERE json_each.value IN (${placeholders})\n )\n ORDER BY confidence DESC, used_count DESC\n LIMIT ?\n `).all(...tags, limit) as LearningRow[];\n }\n \n recordUsage(id: string, success: boolean): void {\n if (success) {\n this.db.prepare('UPDATE learnings SET used_count = used_count + 1, success_count = success_count + 1 WHERE id = ?')\n .run(id);\n } else {\n this.db.prepare('UPDATE learnings SET used_count = used_count + 1 WHERE id = ?')\n .run(id);\n }\n }\n}\n\nexport class PatternRepository {\n private db: Database.Database;\n \n constructor(db: Database.Database) {\n this.db = db;\n }\n \n create(pattern: Omit<PatternRow, 'id' | 'created_at' | 'updated_at'>): PatternRow {\n const stmt = this.db.prepare(`\n INSERT INTO patterns (pattern_type, name, description, pattern_data, success_count, failure_count,\n success_rate, avg_duration_ms, avg_tokens, example_missions, tags)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n const result = stmt.run(\n pattern.pattern_type, pattern.name, pattern.description, pattern.pattern_data,\n pattern.success_count, pattern.failure_count, pattern.success_rate,\n pattern.avg_duration_ms, pattern.avg_tokens, pattern.example_missions, pattern.tags\n );\n \n return this.db.prepare('SELECT * FROM patterns WHERE id = ?')\n .get(result.lastInsertRowid) as PatternRow;\n }\n \n findByType(type: string, limit = 20): PatternRow[] {\n return this.db.prepare('SELECT * FROM patterns WHERE pattern_type = ? ORDER BY success_rate DESC LIMIT ?')\n .all(type, limit) as PatternRow[];\n }\n \n findSuccessful(limit = 20): PatternRow[] {\n return this.db.prepare('SELECT * FROM patterns WHERE success_rate > 0.7 ORDER BY success_rate DESC LIMIT ?')\n .all(limit) as PatternRow[];\n }\n \n recordOutcome(id: string, success: boolean): void {\n if (success) {\n this.db.prepare(`\n UPDATE patterns \n SET success_count = success_count + 1,\n success_rate = CAST(success_count + 1 AS REAL) / (success_count + failure_count + 1),\n updated_at = ?\n WHERE id = ?\n `).run(Date.now(), id);\n } else {\n this.db.prepare(`\n UPDATE patterns \n SET failure_count = failure_count + 1,\n success_rate = CAST(success_count AS REAL) / (success_count + failure_count + 1),\n updated_at = ?\n WHERE id = ?\n `).run(Date.now(), id);\n }\n }\n}\n\nexport class TemplateRepository {\n private db: Database.Database;\n \n constructor(db: Database.Database) {\n this.db = db;\n }\n \n findById(id: string): TemplateRow | undefined {\n return this.db.prepare('SELECT * FROM templates WHERE id = ?').get(id) as TemplateRow | undefined;\n }\n \n findAll(limit = 50): TemplateRow[] {\n return this.db.prepare('SELECT * FROM templates ORDER BY rating DESC, usage_count DESC LIMIT ?')\n .all(limit) as TemplateRow[];\n }\n \n findByTags(tags: string[]): TemplateRow[] {\n if (tags.length === 0) return [];\n\n const placeholders = tags.map(() => '?').join(',');\n return this.db.prepare(`\n SELECT * FROM templates \n WHERE json_valid(templates.tags)\n AND EXISTS (\n SELECT 1\n FROM json_each(templates.tags)\n WHERE json_each.value IN (${placeholders})\n )\n ORDER BY rating DESC\n `).all(...tags) as TemplateRow[];\n }\n \n create(template: Omit<TemplateRow, 'created_at' | 'updated_at' | 'usage_count' | 'rating' | 'rating_count'>): TemplateRow {\n const now = Date.now();\n const stmt = this.db.prepare(`\n INSERT INTO templates (id, name, description, author, version, content, tags, difficulty,\n estimated_time_hours, estimated_tokens, usage_count, rating, rating_count,\n created_at, updated_at, is_builtin)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?, ?)\n `);\n \n stmt.run(\n template.id, template.name, template.description, template.author, template.version,\n template.content, template.tags, template.difficulty, template.estimated_time_hours,\n template.estimated_tokens, now, now, template.is_builtin\n );\n \n return this.findById(template.id)!;\n }\n \n incrementUsage(id: string): void {\n this.db.prepare('UPDATE templates SET usage_count = usage_count + 1, updated_at = ? WHERE id = ?')\n .run(Date.now(), id);\n }\n \n rate(id: string, rating: number): void {\n this.db.prepare(`\n UPDATE templates \n SET rating = (rating * rating_count + ?) / (rating_count + 1),\n rating_count = rating_count + 1,\n updated_at = ?\n WHERE id = ?\n `).run(rating, Date.now(), id);\n }\n}\n\nexport class MetricRepository {\n private db: Database.Database;\n \n constructor(db: Database.Database) {\n this.db = db;\n }\n \n record(metric: Omit<MetricRow, 'id' | 'recorded_at'>): MetricRow {\n const stmt = this.db.prepare(`\n INSERT INTO metrics (metric_type, metric_name, value, unit, tags, recorded_at, period_start, period_end)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n const result = stmt.run(\n metric.metric_type, metric.metric_name, metric.value, metric.unit,\n metric.tags, Date.now(), metric.period_start, metric.period_end\n );\n \n return this.db.prepare('SELECT * FROM metrics WHERE id = ?')\n .get(result.lastInsertRowid) as MetricRow;\n }\n \n findByType(type: string, name?: string, limit = 100): MetricRow[] {\n if (name) {\n return this.db.prepare('SELECT * FROM metrics WHERE metric_type = ? AND metric_name = ? ORDER BY recorded_at DESC LIMIT ?')\n .all(type, name, limit) as MetricRow[];\n }\n return this.db.prepare('SELECT * FROM metrics WHERE metric_type = ? ORDER BY recorded_at DESC LIMIT ?')\n .all(type, limit) as MetricRow[];\n }\n \n getAggregated(type: string, name: string, periodMs: number): { avg: number; min: number; max: number; count: number } {\n const since = Date.now() - periodMs;\n const row = this.db.prepare(`\n SELECT \n AVG(value) as avg,\n MIN(value) as min,\n MAX(value) as max,\n COUNT(*) as count\n FROM metrics \n WHERE metric_type = ? AND metric_name = ? AND recorded_at > ?\n `).get(type, name, since) as { avg?: number; min?: number; max?: number; count?: number } | undefined;\n \n return {\n avg: row?.avg ?? 0,\n min: row?.min ?? 0,\n max: row?.max ?? 0,\n count: row?.count ?? 0,\n };\n }\n}\n\nexport class PredictionRepository {\n private db: Database.Database;\n \n constructor(db: Database.Database) {\n this.db = db;\n }\n \n create(prediction: Omit<PredictionRow, 'id' | 'created_at' | 'validated_at' | 'accuracy' | 'actual_value'> & { actual_value?: number | null }): PredictionRow {\n const stmt = this.db.prepare(`\n INSERT INTO predictions (mission_id, feature_id, prediction_type, predicted_value, actual_value, confidence, model_version)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n \n const result = stmt.run(\n prediction.mission_id, prediction.feature_id, prediction.prediction_type,\n prediction.predicted_value, prediction.actual_value ?? null, prediction.confidence, prediction.model_version\n );\n \n return this.db.prepare('SELECT * FROM predictions WHERE id = ?')\n .get(result.lastInsertRowid) as PredictionRow;\n }\n \n validate(id: string, actualValue: number): void {\n const prediction = this.db.prepare('SELECT * FROM predictions WHERE id = ?').get(id) as PredictionRow;\n if (!prediction) return;\n \n const accuracy = 1 - Math.abs(prediction.predicted_value - actualValue) / Math.max(prediction.predicted_value, actualValue);\n \n this.db.prepare(`\n UPDATE predictions \n SET actual_value = ?, accuracy = ?, validated_at = ?\n WHERE id = ?\n `).run(actualValue, accuracy, Date.now(), id);\n }\n \n findByType(type: string, limit = 50): PredictionRow[] {\n return this.db.prepare('SELECT * FROM predictions WHERE prediction_type = ? ORDER BY created_at DESC LIMIT ?')\n .all(type, limit) as PredictionRow[];\n }\n \n getAccuracy(type: string): number {\n const row = this.db.prepare(`\n SELECT AVG(accuracy) as avg_accuracy \n FROM predictions \n WHERE prediction_type = ? AND accuracy IS NOT NULL\n `).get(type) as { avg_accuracy?: number } | undefined;\n \n return row?.avg_accuracy ?? 0;\n }\n}\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Convenience function to get all repositories\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport function getRepositories() {\n const db = getDatabase();\n return {\n missions: new MissionRepository(db),\n features: new FeatureRepository(db),\n history: new HistoryRepository(db),\n learnings: new LearningRepository(db),\n patterns: new PatternRepository(db),\n templates: new TemplateRepository(db),\n metrics: new MetricRepository(db),\n predictions: new PredictionRepository(db),\n };\n}\n","#!/usr/bin/env node\nimport { getRepositories } from '../database/index.js';\nimport { pathToFileURL } from 'node:url';\n\nexport interface CLICommand {\n name: string;\n description: string;\n execute(args: string[]): Promise<string>;\n}\n\nexport class CLI {\n private commands = new Map<string, CLICommand>();\n \n constructor() {\n this.registerDefaultCommands();\n }\n \n registerCommand(cmd: CLICommand) {\n this.commands.set(cmd.name, cmd);\n }\n \n async execute(args: string[]): Promise<string> {\n const [cmd, ...rest] = args;\n if (!cmd || cmd === '--help' || cmd === '-h') return this.showHelp();\n const command = this.commands.get(cmd);\n if (!command) return `Unknown command: ${cmd}. Use --help for available commands.`;\n return command.execute(rest);\n }\n \n showHelp(): string {\n const lines = ['Pi Missions CLI', 'Usage: pi-missions <command> [options]', '', 'Commands:'];\n for (const [name, cmd] of this.commands) lines.push(` ${name.padEnd(15)} ${cmd.description}`);\n return lines.join('\\n');\n }\n \n private registerDefaultCommands() {\n this.registerCommand({ name: 'list', description: 'List all missions', execute: async () => {\n const repos = getRepositories();\n const missions = repos.missions.findAll(50);\n if (!missions.length) return 'No missions found.';\n return missions.map(m => `${m.id} [${m.status}] ${m.title}`).join('\\n');\n }});\n \n this.registerCommand({ name: 'status', description: 'Show mission status', execute: async (args) => {\n const repos = getRepositories();\n const id = args[0];\n if (!id) return 'Usage: pi-missions status <mission-id>';\n const m = repos.missions.findById(id);\n if (!m) return `Mission not found: ${id}`;\n const features = repos.features.findByMission(id);\n const done = features.filter(f => f.status === 'done').length;\n return `${m.title}\\nStatus: ${m.status}\\nProgress: ${done}/${features.length}\\nTokens: ${m.total_tokens}`;\n }});\n \n this.registerCommand({ name: 'analytics', description: 'Show analytics', execute: async () => {\n const repos = getRepositories();\n const missions = repos.missions.findAll(100);\n const total = missions.length;\n const completed = missions.filter(m => m.status === 'complete').length;\n const active = missions.filter(m => m.status === 'active').length;\n return `📊 Analytics\\nTotal: ${total}\\nActive: ${active}\\nCompleted: ${completed}\\nSuccess Rate: ${total > 0 ? Math.round(completed/total*100) : 0}%`;\n }});\n \n this.registerCommand({ name: 'templates', description: 'List templates', execute: async () => {\n const repos = getRepositories();\n const templates = repos.templates.findAll();\n return templates.map(t => `${t.id.padEnd(15)} ${t.name} - ${t.description || ''}`).join('\\n');\n }});\n \n this.registerCommand({ name: 'history', description: 'Show mission history', execute: async (args) => {\n const repos = getRepositories();\n const id = args[0];\n if (!id) return 'Usage: pi-missions history <mission-id>';\n const history = repos.history.findByMission(id, 20);\n if (!history.length) return 'No history entries.';\n return history.map(h => `${new Date(h.timestamp).toISOString()} ${h.event} ${h.note || ''}`).join('\\n');\n }});\n \n this.registerCommand({ name: 'doctor', description: 'Run diagnostics', execute: async () => {\n const repos = getRepositories();\n const missions = repos.missions.count();\n const templates = repos.templates.findAll().length;\n return `✅ Database: OK\\n📊 Missions: ${missions}\\n📋 Templates: ${templates}`;\n }});\n }\n}\n\nexport function createCLI(): CLI {\n return new CLI();\n}\n\nfunction isDirectExecution(): boolean {\n const entry = process.argv[1];\n return Boolean(entry && import.meta.url === pathToFileURL(entry).href);\n}\n\nif (isDirectExecution()) {\n createCLI().execute(process.argv.slice(2))\n .then((output) => {\n if (output) console.log(output);\n })\n .catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n });\n}\n"],"mappings":"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","names":["require","db","db"]}
@@ -0,0 +1,322 @@
1
+ -- Pi Missions Database Schema
2
+ -- Version: 1.0.0
3
+
4
+ -- Schema version tracking (migration baseline)
5
+ CREATE TABLE IF NOT EXISTS schema_version (
6
+ id INTEGER PRIMARY KEY CHECK(id = 1),
7
+ version INTEGER NOT NULL,
8
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
9
+ );
10
+
11
+ -- Missions table
12
+ CREATE TABLE IF NOT EXISTS missions (
13
+ id TEXT PRIMARY KEY,
14
+ title TEXT NOT NULL,
15
+ goal TEXT,
16
+ status TEXT NOT NULL DEFAULT 'planning' CHECK(status IN ('planning', 'active', 'paused', 'complete', 'blocked', 'budget_limited', 'failed')),
17
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
18
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
19
+ completed_at INTEGER,
20
+ total_tokens INTEGER DEFAULT 0,
21
+ total_features INTEGER DEFAULT 0,
22
+ features_completed INTEGER DEFAULT 0,
23
+ features_failed INTEGER DEFAULT 0,
24
+ success_rate REAL DEFAULT 0.0,
25
+ tags TEXT DEFAULT '[]',
26
+ metadata TEXT DEFAULT '{}'
27
+ );
28
+
29
+ -- Milestones table
30
+ CREATE TABLE IF NOT EXISTS milestones (
31
+ id TEXT NOT NULL,
32
+ mission_id TEXT NOT NULL,
33
+ title TEXT NOT NULL,
34
+ description TEXT,
35
+ status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'active', 'complete')),
36
+ sort_order INTEGER DEFAULT 0,
37
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
38
+ completed_at INTEGER,
39
+ PRIMARY KEY (id, mission_id),
40
+ FOREIGN KEY (mission_id) REFERENCES missions(id) ON DELETE CASCADE
41
+ );
42
+
43
+ -- Features table
44
+ CREATE TABLE IF NOT EXISTS features (
45
+ id TEXT NOT NULL,
46
+ milestone_id TEXT NOT NULL,
47
+ mission_id TEXT NOT NULL,
48
+ title TEXT NOT NULL,
49
+ description TEXT,
50
+ priority INTEGER DEFAULT 3 CHECK(priority BETWEEN 1 AND 5),
51
+ status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'waiting', 'active', 'done', 'blocked', 'failed')),
52
+ depends_on TEXT DEFAULT '[]',
53
+ acceptance_criteria TEXT DEFAULT '[]',
54
+ sessions TEXT DEFAULT '[]',
55
+ tool_call_count INTEGER DEFAULT 0,
56
+ tokens_used INTEGER DEFAULT 0,
57
+ error_count INTEGER DEFAULT 0,
58
+ blockers TEXT DEFAULT '[]',
59
+ notes TEXT,
60
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
61
+ started_at INTEGER,
62
+ completed_at INTEGER,
63
+ evidence TEXT,
64
+ PRIMARY KEY (id, mission_id),
65
+ FOREIGN KEY (mission_id) REFERENCES missions(id) ON DELETE CASCADE,
66
+ FOREIGN KEY (milestone_id, mission_id) REFERENCES milestones(id, mission_id) ON DELETE CASCADE
67
+ );
68
+
69
+ -- Acceptance criteria table
70
+ CREATE TABLE IF NOT EXISTS acceptance_criteria (
71
+ id TEXT NOT NULL,
72
+ feature_id TEXT NOT NULL,
73
+ mission_id TEXT NOT NULL,
74
+ description TEXT NOT NULL,
75
+ check_type TEXT NOT NULL CHECK(check_type IN ('manual', 'bash', 'test_file')),
76
+ check_command TEXT,
77
+ verified INTEGER DEFAULT 0,
78
+ waived INTEGER DEFAULT 0,
79
+ evidence TEXT,
80
+ verified_at INTEGER,
81
+ PRIMARY KEY (id, feature_id, mission_id),
82
+ FOREIGN KEY (feature_id, mission_id) REFERENCES features(id, mission_id) ON DELETE CASCADE
83
+ );
84
+
85
+ -- History table
86
+ CREATE TABLE IF NOT EXISTS history (
87
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
88
+ mission_id TEXT NOT NULL,
89
+ feature_id TEXT,
90
+ event TEXT NOT NULL,
91
+ note TEXT,
92
+ details TEXT DEFAULT '{}',
93
+ timestamp INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
94
+ session_id TEXT,
95
+ FOREIGN KEY (mission_id) REFERENCES missions(id) ON DELETE CASCADE
96
+ );
97
+
98
+ CREATE INDEX IF NOT EXISTS idx_history_mission ON history(mission_id, timestamp);
99
+ CREATE INDEX IF NOT EXISTS idx_history_feature ON history(feature_id, timestamp);
100
+ CREATE INDEX IF NOT EXISTS idx_history_event ON history(event, timestamp);
101
+
102
+ -- Learnings table
103
+ CREATE TABLE IF NOT EXISTS learnings (
104
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
105
+ mission_id TEXT,
106
+ feature_id TEXT,
107
+ type TEXT NOT NULL CHECK(type IN ('success_pattern', 'failure_pattern', 'optimization', 'insight', 'warning')),
108
+ category TEXT,
109
+ insight TEXT NOT NULL,
110
+ confidence REAL DEFAULT 0.5 CHECK(confidence BETWEEN 0.0 AND 1.0),
111
+ applicable_to TEXT DEFAULT '[]',
112
+ context TEXT DEFAULT '{}',
113
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
114
+ used_count INTEGER DEFAULT 0,
115
+ success_count INTEGER DEFAULT 0,
116
+ FOREIGN KEY (mission_id) REFERENCES missions(id) ON DELETE SET NULL
117
+ );
118
+
119
+ CREATE INDEX IF NOT EXISTS idx_learnings_type ON learnings(type, confidence DESC);
120
+ CREATE INDEX IF NOT EXISTS idx_learnings_category ON learnings(category);
121
+
122
+ -- Patterns table
123
+ CREATE TABLE IF NOT EXISTS patterns (
124
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
125
+ pattern_type TEXT NOT NULL CHECK(pattern_type IN ('tool_sequence', 'error_solution', 'architecture', 'testing', 'performance', 'workflow')),
126
+ name TEXT NOT NULL,
127
+ description TEXT NOT NULL,
128
+ pattern_data TEXT NOT NULL,
129
+ success_count INTEGER DEFAULT 0,
130
+ failure_count INTEGER DEFAULT 0,
131
+ success_rate REAL DEFAULT 0.0,
132
+ avg_duration_ms INTEGER,
133
+ avg_tokens INTEGER,
134
+ example_missions TEXT DEFAULT '[]',
135
+ tags TEXT DEFAULT '[]',
136
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
137
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
138
+ );
139
+
140
+ CREATE INDEX IF NOT EXISTS idx_patterns_type ON patterns(pattern_type, success_rate DESC);
141
+ CREATE INDEX IF NOT EXISTS idx_patterns_tags ON patterns(tags);
142
+
143
+ -- Predictions table
144
+ CREATE TABLE IF NOT EXISTS predictions (
145
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
146
+ mission_id TEXT,
147
+ feature_id TEXT,
148
+ prediction_type TEXT NOT NULL CHECK(prediction_type IN ('success_probability', 'duration_estimate', 'risk_assessment', 'token_estimate')),
149
+ predicted_value REAL NOT NULL,
150
+ actual_value REAL,
151
+ confidence REAL DEFAULT 0.5,
152
+ accuracy REAL,
153
+ model_version TEXT,
154
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
155
+ validated_at INTEGER,
156
+ FOREIGN KEY (mission_id) REFERENCES missions(id) ON DELETE SET NULL
157
+ );
158
+
159
+ -- Templates table
160
+ CREATE TABLE IF NOT EXISTS templates (
161
+ id TEXT PRIMARY KEY,
162
+ name TEXT NOT NULL,
163
+ description TEXT,
164
+ author TEXT,
165
+ version TEXT DEFAULT '1.0.0',
166
+ content TEXT NOT NULL,
167
+ tags TEXT DEFAULT '[]',
168
+ difficulty TEXT DEFAULT 'intermediate' CHECK(difficulty IN ('beginner', 'intermediate', 'advanced')),
169
+ estimated_time_hours REAL,
170
+ estimated_tokens INTEGER,
171
+ usage_count INTEGER DEFAULT 0,
172
+ rating REAL DEFAULT 0.0,
173
+ rating_count INTEGER DEFAULT 0,
174
+ created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
175
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
176
+ is_builtin INTEGER DEFAULT 0
177
+ );
178
+
179
+ CREATE INDEX IF NOT EXISTS idx_templates_tags ON templates(tags);
180
+ CREATE INDEX IF NOT EXISTS idx_templates_rating ON templates(rating DESC, usage_count DESC);
181
+
182
+ -- Sessions table
183
+ CREATE TABLE IF NOT EXISTS sessions (
184
+ id TEXT PRIMARY KEY,
185
+ mission_id TEXT,
186
+ feature_id TEXT,
187
+ started_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
188
+ ended_at INTEGER,
189
+ tokens_used INTEGER DEFAULT 0,
190
+ tool_calls INTEGER DEFAULT 0,
191
+ errors INTEGER DEFAULT 0,
192
+ status TEXT DEFAULT 'active' CHECK(status IN ('active', 'completed', 'abandoned')),
193
+ FOREIGN KEY (mission_id) REFERENCES missions(id) ON DELETE SET NULL
194
+ );
195
+
196
+ CREATE INDEX IF NOT EXISTS idx_sessions_mission ON sessions(mission_id, started_at);
197
+
198
+ -- Metrics table
199
+ CREATE TABLE IF NOT EXISTS metrics (
200
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
201
+ metric_type TEXT NOT NULL,
202
+ metric_name TEXT NOT NULL,
203
+ value REAL NOT NULL,
204
+ unit TEXT,
205
+ tags TEXT DEFAULT '{}',
206
+ recorded_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
207
+ period_start INTEGER,
208
+ period_end INTEGER
209
+ );
210
+
211
+ CREATE INDEX IF NOT EXISTS idx_metrics_type ON metrics(metric_type, metric_name, recorded_at);
212
+ CREATE INDEX IF NOT EXISTS idx_metrics_period ON metrics(period_start, period_end);
213
+
214
+ -- Plugins table
215
+ CREATE TABLE IF NOT EXISTS plugins (
216
+ id TEXT PRIMARY KEY,
217
+ name TEXT NOT NULL,
218
+ version TEXT NOT NULL,
219
+ description TEXT,
220
+ author TEXT,
221
+ enabled INTEGER DEFAULT 1,
222
+ config TEXT DEFAULT '{}',
223
+ installed_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
224
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
225
+ );
226
+
227
+ -- Views
228
+ CREATE VIEW IF NOT EXISTS mission_summary AS
229
+ SELECT
230
+ m.id,
231
+ m.title,
232
+ m.status,
233
+ m.created_at,
234
+ m.completed_at,
235
+ m.total_tokens,
236
+ m.total_features,
237
+ m.features_completed,
238
+ m.features_failed,
239
+ CASE
240
+ WHEN m.total_features > 0
241
+ THEN ROUND(CAST(m.features_completed AS REAL) / m.total_features * 100, 1)
242
+ ELSE 0
243
+ END as progress_percent,
244
+ COUNT(DISTINCT s.id) as session_count,
245
+ COALESCE(SUM(s.tokens_used), 0) as session_tokens
246
+ FROM missions m
247
+ LEFT JOIN sessions s ON s.mission_id = m.id
248
+ GROUP BY m.id;
249
+
250
+ CREATE VIEW IF NOT EXISTS feature_details AS
251
+ SELECT
252
+ f.id,
253
+ f.mission_id,
254
+ f.milestone_id,
255
+ f.title,
256
+ f.status,
257
+ f.priority,
258
+ f.tool_call_count,
259
+ f.tokens_used,
260
+ f.error_count,
261
+ f.started_at,
262
+ f.completed_at,
263
+ CASE
264
+ WHEN f.started_at IS NOT NULL AND f.completed_at IS NOT NULL
265
+ THEN f.completed_at - f.started_at
266
+ WHEN f.started_at IS NOT NULL
267
+ THEN (unixepoch() * 1000) - f.started_at
268
+ ELSE NULL
269
+ END as duration_ms,
270
+ m.title as mission_title,
271
+ ms.title as milestone_title
272
+ FROM features f
273
+ JOIN missions m ON m.id = f.mission_id
274
+ JOIN milestones ms ON ms.id = f.milestone_id AND ms.mission_id = f.mission_id;
275
+
276
+ CREATE VIEW IF NOT EXISTS active_missions AS
277
+ SELECT * FROM mission_summary
278
+ WHERE status IN ('active', 'paused', 'budget_limited')
279
+ ORDER BY updated_at DESC;
280
+
281
+ -- Default templates
282
+ INSERT OR IGNORE INTO templates (id, name, description, author, version, content, tags, difficulty, estimated_time_hours, estimated_tokens, is_builtin)
283
+ VALUES
284
+ (
285
+ 'refactor',
286
+ 'Code Refactoring',
287
+ 'Systematic code refactoring with tests',
288
+ 'pi-missions',
289
+ '1.0.0',
290
+ '{"milestones":[{"id":"M01","title":"Analysis","features":[{"id":"F001","title":"Analyze current code","priority":1},{"id":"F002","title":"Identify refactoring targets","priority":1}]},{"id":"M02","title":"Refactoring","features":[{"id":"F003","title":"Refactor core logic","priority":2},{"id":"F004","title":"Update tests","priority":2}]},{"id":"M03","title":"Verification","features":[{"id":"F005","title":"Run full test suite","priority":1}]}]}',
291
+ '["refactoring","cleanup","quality"]',
292
+ 'intermediate',
293
+ 4.0,
294
+ 50000,
295
+ 1
296
+ ),
297
+ (
298
+ 'add-feature',
299
+ 'New Feature',
300
+ 'Add a new feature to existing codebase',
301
+ 'pi-missions',
302
+ '1.0.0',
303
+ '{"milestones":[{"id":"M01","title":"Planning","features":[{"id":"F001","title":"Design feature","priority":1},{"id":"F002","title":"Plan implementation","priority":1}]},{"id":"M02","title":"Implementation","features":[{"id":"F003","title":"Implement feature","priority":2},{"id":"F004","title":"Write tests","priority":2}]},{"id":"M03","title":"Integration","features":[{"id":"F005","title":"Integration testing","priority":1},{"id":"F006","title":"Documentation","priority":3}]}]}',
304
+ '["feature","development","new"]',
305
+ 'intermediate',
306
+ 6.0,
307
+ 75000,
308
+ 1
309
+ ),
310
+ (
311
+ 'fix-bug',
312
+ 'Bug Fix',
313
+ 'Systematic bug investigation and fix',
314
+ 'pi-missions',
315
+ '1.0.0',
316
+ '{"milestones":[{"id":"M01","title":"Investigation","features":[{"id":"F001","title":"Reproduce bug","priority":1},{"id":"F002","title":"Root cause analysis","priority":1}]},{"id":"M02","title":"Fix","features":[{"id":"F003","title":"Implement fix","priority":1},{"id":"F004","title":"Write regression test","priority":1}]},{"id":"M03","title":"Verification","features":[{"id":"F005","title":"Verify fix works","priority":1}]}]}',
317
+ '["bugfix","debugging","fix"]',
318
+ 'beginner',
319
+ 2.0,
320
+ 25000,
321
+ 1
322
+ );
@@ -0,0 +1,5 @@
1
+ import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
+
3
+ declare function piMissions(pi: ExtensionAPI): void;
4
+
5
+ export { piMissions as default };