/** * Commit miner (Phase 2 capture pipeline). * * Walks git history since the last mined commit and heuristically extracts * memory-worthy candidates from commit messages + touched files. Candidates * land in the proposal queue (human-in-the-loop) — never directly in memory. * * Each proposal is anchored with COMMIT_REF evidence so Phase 3 verification * can re-check it against history. */ import type { MemoryKind, Proposal } from "../types.js"; import type { MemoryStore } from "../db/store.js"; export interface MinedCommit { sha: string; subject: string; body: string; files: string[]; } export interface RevertInfo { originalSubject: string; /** SHA of the commit being reverted, when detectable. */ originalSha?: string; /** Free-text reason extracted from the revert body. */ reason?: string; } export interface MineResult { scanned: number; proposed: Proposal[]; skippedDuplicates: number; lastSha: string | null; /** True when the repo is a git repo but has no commits yet (HEAD does not exist). */ noCommits?: boolean; /** True when incremental mining found no commits newer than the stored cursor. */ noNewCommits?: boolean; } /** False when the repo is initialized but has no commits yet (no HEAD). */ export declare function hasGitCommits(repoRoot: string): boolean; export declare function readCommits(repoRoot: string, sinceSha: string | null, maxCommits?: number): MinedCommit[]; /** Detect a git revert commit and, when possible, link it back to the original commit. */ export declare function detectRevert(c: MinedCommit, repoRoot: string): RevertInfo | null; export declare function classifyCommit(c: MinedCommit): { kind: MemoryKind; matched: string; } | null; /** Reduce touched files to a few representative scope paths (common directories). */ export declare function scopeFromFiles(files: string[], maxPaths?: number): string[]; /** * Try to find files relevant to a claim by extracting keywords and running * `git grep`. Returns scoped paths (via scopeFromFiles) if matches are found, * otherwise returns [] (repo-wide). Never throws. */ export declare function scopeFromClaim(claim: string, repoRoot: string, maxPaths?: number): string[]; export declare function mineCommits(store: MemoryStore, repoRoot: string, opts?: { maxCommits?: number; full?: boolean; }): MineResult; export declare const COMMIT_EXTRACT_INSTRUCTIONS = "You are mining a git commit for durable, project-specific knowledge worth remembering across AI coding sessions: decisions (and rejected alternatives), conventions, gotchas, failed approaches, invariants, architecture facts.\n\nRules:\n1. Most commits contain NOTHING durable \u2014 routine features/fixes/refactors. Return zero claims for those. Do NOT invent.\n2. When there IS signal, SYNTHESIZE a falsifiable claim about the codebase \u2014 do not parrot the commit message. Bad: \"A decision was made: use Redis\". Good: \"Rate limiting uses Redis (src/limits); the in-memory limiter was abandoned because multi-instance deploys need shared counters\".\n3. BE SPECIFIC. Every claim must name concrete files, modules, functions, config keys, or commands. Bad: \"The project uses a specific tool for code completion\" (useless). Good: \"The UI is a single-page app generated from src/ui/page.ts which inlines all CSS and JS into one HTML file served by the Express server in src/ui/server.ts\".\n4. REJECT generic statements. If a claim could apply to any repo, discard it. \"Uses TypeScript\" is useless. \"All API handlers in src/api/ must extend BaseHandler and register via src/api/registry.ts\" is useful.\n5. Use the DIFF, not just the message \u2014 renamed modules, deleted approaches, and added config tell the real story.\n6. kinds: DECISION, CONVENTION, GOTCHA, FAILED_APPROACH, ARCHITECTURE, INVARIANT, GUARDRAIL (guardrail_level never|ask-first|always), SKILL, TODO_CONTEXT.\n7. Scope with the touched paths; add \"static_check\" (cheap shell command, exit 0 iff true) when an honest one exists.\n8. 0\u20132 claims per commit. Zero is the common case.\n\nRespond with ONLY: {\"claims\":[{\"kind\":\"DECISION\",\"claim\":\"Rate limiting uses Redis (src/limits); the in-memory limiter was abandoned because multi-instance deploys need shared counters\",\"paths\":[\"src/limits\"],\"symbols\":[],\"guardrail_level\":null,\"applies_when\":[],\"rationale\":\"commit switched from in-memory limiter to Redis\",\"static_check\":\"grep -r 'redis' src/limits/\"}]}"; /** * LLM-powered deep mining: reads message + diff, synthesizes claims with * suggested STATIC_CHECKs. Requires a text provider (OpenAI/Ollama); the * caller falls back to regex mining when none is available. Same cursor as * regex mining — the two modes are alternatives over the same history. */ export declare function mineCommitsLlm(store: MemoryStore, repoRoot: string, opts?: { maxCommits?: number; full?: boolean; }): Promise; /** Human-readable summary of a mine run (CLI + MCP). */ export declare function describeMineResult(r: MineResult, opts?: { llmProvider?: string | null; llmRequested?: boolean; }): string;