import { Hono } from 'hono'; /** * Core types for open-agent-glossary * Schema-compatible with pi-glossary (ruliana/pi-glossary) */ interface GlossaryEntry { term: string; definition: string; aliases?: string[]; pattern?: string; flags?: string; enabled?: boolean; /** External reference / source-of-truth (e.g. wiki URL). Single string. */ source?: string; /** Semantic scope: projects/teams/bounded-contexts this term applies to. */ tags?: string[]; } type GlossaryMode = "merge" | "first" | "pin"; interface GlossaryConfig { /** Session dedup TTL in minutes. Default: 30. */ sessionTtlMinutes?: number; /** * How glossary files are resolved: * - "merge" (default): all tiers loaded and merged, later tiers win on collision * - "first": stop at the first file found across all tiers * - "pin": only load the file at `glossaryPin`, skip all discovery */ glossaryMode?: GlossaryMode; /** * Path to a single glossary file to use exclusively. * Absolute or relative to cwd. Only used when glossaryMode is "pin". */ glossaryPin?: string; /** * Additional glossary file paths appended after all built-in tiers. * These always win over the built-in tiers (highest priority in merge mode). * Absolute or relative to cwd. */ extraGlossaryPaths?: string[]; /** * Skip global user-level glossary tiers (~/.pi/agent, ~/.agents, ~/.open-agent-glossary). * Useful in CI environments where personal global terms should not bleed in. */ disableGlobalGlossary?: boolean; /** * Skip project-level glossary tiers (.pi/, .agents/, .open-agent-glossary/). * Unusual but valid when you want only global + extraGlossaryPaths. */ disableProjectGlossary?: boolean; /** Local UI / control server settings. */ ui?: UiConfig; /** Git auto-commit settings. */ git?: GitConfig; } type GitCommitMode = "manual" | "operation" | "batch"; interface GitConfig { /** Auto-commit mode. Default: "manual" (disabled). */ autoCommit?: GitCommitMode; /** * Commit message template for operation mode. * Supports: {operation} (add|edit|remove), {term}, {file} */ commitMessage?: string; /** * Commit message template for batch mode. * Supports: {count}, {terms} (comma-separated list), {file} */ batchCommitMessage?: string; /** * Seconds of inactivity before a batch commit fires. * Only used when autoCommit is "batch". Default: 300 (5 minutes). */ batchIdleSeconds?: number; } interface UiConfig { /** Start the control server + UI automatically when a session starts. */ autostart?: boolean; /** Control server port. Default: 7337. */ port?: number; /** Open the browser when the UI starts. Default: true. */ open?: boolean; } interface SessionState { sessionId: string; loadedTerms: string[]; lastUpdated: number; cwd: string; } interface LoadedGlossary { entries: GlossaryEntry[]; sources: string[]; } interface GlossaryFile { path: string; entries: GlossaryEntry[]; hash: string; } interface InjectionResult { text: string; matchedTerms: string[]; newTerms: string[]; } interface MatchResult { entry: GlossaryEntry; matchedOn: string; } declare const DEFAULT_CONFIG: Required; /** * Load and merge all glossary files according to config mode. * * Modes: * "merge" (default) — all tiers loaded, later tiers win on collision * "first" — stop at the first file found across all tiers * "pin" — only load config.glossaryPin, skip all discovery */ declare function loadGlossary(cwd?: string): LoadedGlossary; /** * Load glossary from a specific scope only (ignores mode/pin/extra). * Used by CLI --scope flag. */ declare function loadGlossaryByScope(scope: "global" | "project", cwd?: string): LoadedGlossary; /** * Build a regex for a glossary entry from its term, aliases, and optional pattern. */ declare function buildEntryRegex(entry: GlossaryEntry): RegExp; /** * Test a regex pattern (or term/alias-derived regex) against sample text using * the SAME logic as real injection matching. Reusable by the UI, MCP, and CLI * so any surface can verify a pattern actually matches. */ interface PatternTestResult { valid: boolean; error?: string; /** Effective regex source/flags actually used. */ source?: string; flags?: string; /** Match ranges in the sample (start inclusive, end exclusive). */ matches: Array<{ start: number; end: number; text: string; }>; } declare function testPattern(input: { pattern?: string; flags?: string; term?: string; aliases?: string[]; sample: string; }): PatternTestResult; /** * Match prompt text against all glossary entries. * Returns entries that match, with the specific trigger that matched. */ declare function matchEntries(prompt: string, entries: GlossaryEntry[]): MatchResult[]; /** * Build injection text from matched entries, filtering out already-loaded terms. */ declare function buildInjection(matches: MatchResult[], session: SessionState, cwd?: string): InjectionResult; interface GitCommitResult { committed: boolean; skipped: boolean; reason?: string; } interface PendingChange { operation: "add" | "edit" | "remove"; term: string; file: string; } interface GitBatcherOptions { cwd: string; idleSeconds: number; batchCommitMessage: string; /** Called after each batch commit attempt (for logging). */ onCommit?: (result: GitCommitResult, terms: string[]) => void; onError?: (error: unknown) => void; } /** * Accumulates glossary write operations and commits them as a single git * commit after a configurable idle window. * * Designed for long-running processes (i.e. the control server). * In one-shot CLI processes, fall back to operation mode instead. */ declare class GitBatcher { private readonly cwd; private readonly idleMs; private readonly messageTemplate; private readonly onCommit; private readonly onError; private pending; private timer; constructor(opts: GitBatcherOptions); /** * Record a write operation. Resets the idle timer. */ record(change: PendingChange): void; /** * Immediately flush pending changes as a single commit. * Clears pending list and cancels the timer. * Safe to call multiple times (idempotent if nothing is pending). */ flush(): Promise; /** * Cancel any pending timer and discard pending changes. * Call on server shutdown if flush() was already called. */ dispose(): void; /** Returns the number of currently pending (uncommitted) changes. */ get pendingCount(): number; private resetTimer; private cancelTimer; } interface StoreCommitOptions { mode: GitCommitMode; commitMessage: string; batchCommitMessage: string; cwd: string; batcher?: GitBatcher; } /** * Add a term to the glossary. */ declare function addTerm(scope: "global" | "project", entry: GlossaryEntry, cwd?: string, gitOptions?: StoreCommitOptions): Promise; /** * Edit an existing term's fields. */ declare function editTerm(scope: "global" | "project", term: string, updates: Partial>, cwd?: string, gitOptions?: StoreCommitOptions): Promise; /** * Remove a term from the glossary. */ declare function removeTerm(scope: "global" | "project", term: string, cwd?: string, gitOptions?: StoreCommitOptions): Promise; /** * Load current session state. Returns fresh state if expired or missing. */ declare function loadSession(cwd?: string, config?: GlossaryConfig): SessionState; /** * Save session state to disk. */ declare function saveSession(state: SessionState): void; /** * Mark terms as loaded in the session. */ declare function markTermsLoaded(session: SessionState, terms: string[]): SessionState; /** * Reset session state. */ declare function resetSession(cwd?: string): SessionState; /** * Load config from first found path, merged with defaults. */ declare function loadConfig(cwd?: string): Required; interface ConfigPathState { path: string; exists: boolean; used: boolean; shadowed: boolean; } interface ConfigProvenance { config: Required; /** Per-top-level-field origin: the winning file path, or "default". */ origins: Record; /** Full candidate stack with found/shadowed/used flags (priority order). */ stack: ConfigPathState[]; /** The file the active config came from, or null when all defaults. */ activeFile: string | null; } /** * Resolve config with provenance: which candidate files exist, which one wins, * and where each effective field value came from. Config resolution is * first-found-wins (whole file), so per-field origin is "winning file when the * key is present there, else default". */ declare function resolveConfigWithProvenance(cwd?: string): ConfigProvenance; /** * Write config to an EXISTING config file only (never creates new files). * Validates basic coherence before writing. Returns the written path. */ declare function writeConfigFile(filePath: string, next: GlossaryConfig): string; /** * Expand shell templates in a string. * Replaces `{{command}}` with the stdout of running that command. * Example: "Current branch: {{git branch --show-current}}" */ declare function expandTemplates(text: string, cwd?: string): string; /** * Expand cross-references in a definition. * Replaces `[[term-name]]` with a hint to look up that term. */ declare function expandCrossRefs(text: string): string; /** * Apply all expansions to a definition string. */ declare function expandDefinition(definition: string, cwd?: string): string; interface TermUsage { lookups: number; injections: number; lastUsed: number; } interface SessionUsage { sessionId: string; cwd: string; startedAt: number; lastUsed: number; lookups: number; injections: number; byTerm: Record; } interface UsageStore { version: 1; totals: { lookups: number; injections: number; byTerm: Record; }; sessions: Record; /** Capped ring-buffer of recent usage events for the timeline. */ events: UsageEvent[]; } interface UsageEvent { term: string; kind: UsageKind; ts: number; sessionId: string; } type UsageKind = "lookup" | "injection"; /** * Read the usage store from disk. Corrupt or missing files yield a fresh store. */ declare function readUsage(): UsageStore; /** * Record glossary usage for a session. Updates global totals, the per-session * record, and per-term counters. Term keys are lowercased. */ declare function recordUsage(kind: UsageKind, terms: string[], sessionId: string, cwd: string): void; /** Get a single session's usage record, or null if unknown. */ declare function getSessionUsage(sessionId: string): SessionUsage | null; /** * Top terms by usage, optionally filtered to a single kind for ordering. */ declare function getTopTerms(limit: number, kind?: UsageKind): Array<{ term: string; lookups: number; injections: number; }>; /** Recent usage events (most recent last), capped by `limit`. */ declare function getRecentEvents(limit?: number): UsageEvent[]; /** Reset all usage data (clears disk + in-memory queue). */ declare function resetUsage(): void; /** Force any queued usage writes to disk immediately. */ declare function flushUsage(): void; interface DiscoveredFile { path: string; format: "json" | "jsonl"; scope: "global" | "project"; tier: string; entryCount: number; exists: boolean; } interface ProjectGlossaries { root: string; lastSeen: number; files: DiscoveredFile[]; } interface DiscoveryResult { global: DiscoveredFile[]; projects: ProjectGlossaries[]; } interface ProjectRegistry { version: 1; projects: Array<{ root: string; lastSeen: number; }>; } /** * Discover all glossary files known to this machine: the global tiers plus * every project-tier file for roots in the registry. */ declare function discoverGlossaries(): DiscoveryResult; /** Read the project registry, tolerating missing/corrupt files. */ declare function readProjectRegistry(): ProjectRegistry; /** * Upsert a project root into the registry, refreshing its `lastSeen`. * Deduplicated by root. Most-recently-seen first. */ declare function registerProject(cwd: string): void; interface SuggestResult { scope: "project" | "global"; targetFile: string; duplicate: null | { term: string; scope: string; path: string; }; aliasCandidates: string[]; patternHint: string | null; formatHint: "json" | "jsonl"; } /** Derive simple alias candidates: dashed<->spaced and naive plural/singular. */ declare function deriveAliases(term: string): string[]; /** * Suggest scope, target file, duplicates, aliases, and format for a new term. */ declare function suggestForTerm(term: string, cwd?: string): SuggestResult; interface ControlServerOptions { port?: number; cwd?: string; serveUi?: boolean; open?: boolean; } interface ControlServerHandle { url: string; close: () => Promise; /** Flush any pending batch git commits immediately. No-op if not in batch mode. */ flushGit: () => Promise; } /** Resolve the installed UI package's static dist directory, if present. */ declare function resolveUiDist(): string | null; /** * Build the Hono app. Exposed separately so tests can use `app.request()` * without binding a socket. */ declare function createControlApp(opts?: ControlServerOptions): Hono; /** * Start the control server bound to 127.0.0.1 only. */ declare function startControlServer(opts?: ControlServerOptions): Promise; export { type ConfigPathState, type ConfigProvenance, type ControlServerHandle, type ControlServerOptions, DEFAULT_CONFIG, type DiscoveredFile, type DiscoveryResult, type GitCommitMode, type GitConfig, type GlossaryConfig, type GlossaryEntry, type GlossaryFile, type InjectionResult, type LoadedGlossary, type MatchResult, type PatternTestResult, type ProjectGlossaries, type ProjectRegistry, type SessionState, type SessionUsage, type SuggestResult, type TermUsage, type UsageEvent, type UsageKind, type UsageStore, addTerm, buildEntryRegex, buildInjection, createControlApp, deriveAliases, discoverGlossaries, editTerm, expandCrossRefs, expandDefinition, expandTemplates, flushUsage, getRecentEvents, getSessionUsage, getTopTerms, loadConfig, loadGlossary, loadGlossaryByScope, loadSession, markTermsLoaded, matchEntries, readProjectRegistry, readUsage, recordUsage, registerProject, removeTerm, resetSession, resetUsage, resolveConfigWithProvenance, resolveUiDist, saveSession, startControlServer, suggestForTerm, testPattern, writeConfigFile };