/** * Statusline script section: knowledge, trigger, daemon, hook-latency, graph, * loop, HIL and monograph stats collectors. * Spliced verbatim into generateStatuslineScript() output. */ export declare const STATUSLINE_STATS_SECTION = "// \u2500\u2500 Knowledge & trigger stats (Tasks 28 + 32) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction getKnowledgeStats() {\n const chunksPath = path.join(CWD, '.monomind', 'knowledge', 'chunks.jsonl');\n const skillsPath = path.join(CWD, '.monomind', 'skills.jsonl');\n let chunks = 0, skills = 0;\n try {\n const chunksStat = safeStat(chunksPath);\n if (chunksStat && chunksStat.size <= MAX_JSON_READ_BYTES) {\n chunks = fs.readFileSync(chunksPath, 'utf-8').split('\\n').filter(Boolean).length;\n }\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); /* ignore */ }\n try {\n const skillsStat = safeStat(skillsPath);\n if (skillsStat && skillsStat.size <= MAX_JSON_READ_BYTES) {\n skills = fs.readFileSync(skillsPath, 'utf-8').split('\\n').filter(Boolean).length;\n }\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); /* ignore */ }\n return { chunks, skills };\n}\n\n// Document / knowledge stats \u2014 docs indexed + chunks + memory entries\nfunction getDocStats() {\n const docPath = path.join(CWD, '.monomind', 'knowledge', 'doc-metadata.jsonl');\n const chunksPath = path.join(CWD, '.monomind', 'knowledge', 'chunks.jsonl');\n const memDbPath = path.join(CWD, '.monomind', 'memory', 'memory.db');\n let docs = 0, chunks = 0, memories = 0, exists = false;\n try {\n const ds = safeStat(docPath);\n if (ds && ds.size > 0 && ds.size <= MAX_JSON_READ_BYTES) {\n docs = fs.readFileSync(docPath, 'utf-8').split('\\n').filter(Boolean).length;\n exists = true;\n }\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); /* ignore */ }\n try {\n const cs = safeStat(chunksPath);\n if (cs && cs.size > 0 && cs.size <= MAX_JSON_READ_BYTES) {\n chunks = fs.readFileSync(chunksPath, 'utf-8').split('\\n').filter(Boolean).length;\n exists = true;\n }\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); /* ignore */ }\n try {\n if (fs.existsSync(memDbPath)) {\n const n = cached('memoryCount', TTL.counts, () => {\n const result = spawnSync('sqlite3', [memDbPath, 'SELECT COUNT(*) FROM memory_entries;'], { encoding: 'utf-8', timeout: 1000 });\n return parseInt((result.stdout || '').trim(), 10) || 0;\n });\n if (n > 0) { memories = n; exists = true; }\n }\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); /* ignore */ }\n return { docs, chunks, memories, exists };\n}\n\nfunction getTriggerStats() {\n const indexPath = path.join(CWD, '.monomind', 'trigger-index.json');\n try {\n const idxStat = safeStat(indexPath);\n if (!idxStat || idxStat.size > MAX_JSON_READ_BYTES) return { triggers: 0, agents: 0 };\n const raw = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));\n const idx = raw.index || raw;\n const triggers = Object.keys(idx).length;\n const agents = Object.values(idx).flat().length;\n return { triggers, agents };\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); return { triggers: 0, agents: 0 }; }\n}\n\n// Daemon worker alerts \u2014 security-audit, testgaps, benchmark. Fast: single\n// stat + JSON.parse per file (files are small, KB-scale), skip entirely when\n// the worker hasn't run yet (no metrics dir / file) so cold projects pay\n// zero cost beyond the existsSync check.\nfunction getDaemonAlerts() {\n const metricsDir = path.join(CWD, '.monomind', 'metrics');\n const out = { security: null, testgaps: null, benchmark: null };\n if (!fs.existsSync(metricsDir)) return out;\n\n const audit = readJSON(path.join(metricsDir, 'security-audit.json'));\n if (audit) {\n const riskLevel = (audit.riskLevel || 'low').toLowerCase();\n const findings = (audit.recommendations || []).length + (audit.priorityScanTargets || []).length;\n out.security = { riskLevel, findings };\n }\n\n const gaps = readJSON(path.join(metricsDir, 'testgaps.json'));\n if (gaps) {\n const gapList = Array.isArray(gaps.gaps) ? gaps.gaps : [];\n const uncovered = gaps.uncoveredCount ?? gapList.length;\n if (uncovered > 0 || gapList.length > 0) out.testgaps = { uncovered };\n }\n\n const bench = readJSON(path.join(metricsDir, 'benchmark.json'));\n if (bench?.benchmarks) {\n const heapMB = bench.benchmarks.memoryUsage ? Math.floor(bench.benchmarks.memoryUsage.heapUsed / 1024 / 1024) : null;\n const uptimeMin = bench.benchmarks.uptime ? Math.floor(bench.benchmarks.uptime / 60) : null;\n out.benchmark = { heapMB, uptimeMin, timestamp: bench.timestamp };\n }\n\n return out;\n}\n\n// Hook latency \u2014 surface slow per-prompt hooks in the statusline.\nfunction getHookLatency() {\n const p = path.join(CWD, '.monomind', 'metrics', 'hook-latency.json');\n try {\n const latStat = safeStat(p);\n if (!latStat || latStat.size > MAX_JSON_READ_BYTES) return null;\n const d = JSON.parse(fs.readFileSync(p, 'utf-8'));\n const perPrompt = ['route'];\n let totalMs = 0; let count = 0;\n for (const h of perPrompt) {\n if (d[h] && d[h].mean) { totalMs += d[h].mean; count++; }\n }\n if (count === 0) return null;\n let slowest = null;\n for (const k of Object.keys(d)) {\n if (k === 'lastUpdated' || !d[k] || typeof d[k] !== 'object') continue;\n if (!slowest || d[k].mean > slowest.mean) slowest = { name: k, mean: d[k].mean };\n }\n return { perPromptMs: totalMs, slowest: slowest };\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); return null; }\n}\n\n// Graph usage telemetry \u2014 counts ALL graph wins (MCP calls + silent assists)\n// vs greps that got no graph help.\nfunction getGraphUsage() {\n const usagePath = path.join(CWD, '.monomind', 'metrics', 'graph-usage.json');\n try {\n const usageStat = safeStat(usagePath);\n if (!usageStat || usageStat.size > MAX_JSON_READ_BYTES) return null;\n const d = JSON.parse(fs.readFileSync(usagePath, 'utf-8'));\n const graphWins = (d.monograph_call || 0) + (d.preresolve_hit || 0)\n + (d.graph_assist_search || 0) + (d.graph_assist_neighbors || 0);\n const searches = (d.grep_call || 0) + (d.glob_call || 0)\n + (d.bash_grep_call || 0) + (d.bash_find_call || 0);\n const total = graphWins + searches + (d.preresolve_miss || 0);\n if (total === 0) return null;\n return { graphWins: graphWins, searches: searches, pct: Math.round((graphWins / total) * 100), hints: d.graph_assist_search || 0 };\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); return null; }\n}\n\n// Graph freshness \u2014 compare last build time vs commits since\nfunction getGraphFreshness() {\n const lockPath = path.join(CWD, '.monomind', 'graph', '.rebuild-lock');\n const dbPath = path.join(CWD, '.monomind', 'monograph.db');\n let buildMs = 0;\n try {\n const lockStat = safeStat(lockPath);\n const dbStat = safeStat(dbPath);\n buildMs = Math.max(lockStat?.mtimeMs || 0, dbStat?.mtimeMs || 0);\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); /* ignore */ }\n if (!buildMs) return { commitsBehind: -1, stale: true, fresh: false };\n const buildIso = new Date(buildMs).toISOString();\n const out = cached('graphCommitsSince', TTL.counts, () => safeExec(`git rev-list --count --since='${buildIso}' HEAD 2>/dev/null`, 1500));\n const commitsBehind = parseInt(out, 10) || 0;\n return { commitsBehind, stale: commitsBehind > 5, fresh: commitsBehind === 0 };\n}\n\n// Active loops \u2014 scan .monomind/loops/*.json, skip stale (>6h)\nfunction getLoopStatus() {\n const loopsDir = path.join(CWD, '.monomind', 'loops');\n if (!fs.existsSync(loopsDir)) return { count: 0, loops: [] };\n const STALE_MS = 6 * 60 * 60 * 1000;\n const now = Date.now();\n const loops = [];\n try {\n const files = fs.readdirSync(loopsDir).filter(f =>\n f.endsWith('.json') && !f.includes('-hil') && !f.endsWith('.stop'));\n for (const f of files) {\n const d = readJSON(path.join(loopsDir, f));\n if (!d || !d.command) continue;\n const last = d.lastRunAt || d.nextRunAt || d.startedAt || 0;\n if (last && (now - last) > STALE_MS) continue;\n loops.push({\n cmd: String(d.command).replace(/^\\//,''),\n type: d.type || 'repeat',\n rep: d.currentRep || 0,\n max: d.maxReps || 0,\n status: d.status || 'running',\n });\n }\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); /* ignore */ }\n return { count: loops.length, loops };\n}\n\n// HIL pending \u2014 count -hil.md files with no human response yet\nfunction getHILPending() {\n const loopsDir = path.join(CWD, '.monomind', 'loops');\n if (!fs.existsSync(loopsDir)) return { pending: 0 };\n let pending = 0;\n try {\n const files = fs.readdirSync(loopsDir).filter(f => f.endsWith('-hil.md'));\n for (const f of files) {\n try {\n const hilPath = path.join(loopsDir, f);\n const hilStat = safeStat(hilPath);\n if (!hilStat || hilStat.size > 512 * 1024) continue; // skip > 512 KB\n const txt = fs.readFileSync(hilPath, 'utf-8');\n const answered = /^[ \\t]*>[ \\t]+\\S/m.test(txt);\n if (!answered) pending++;\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); /* ignore */ }\n }\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); /* ignore */ }\n return { pending };\n}\n\n// Monograph knowledge graph stats\n// Sources, in priority order:\n// 1. .monomind/graph/stats.json \u2014 explicit cached stats\n// 2. .monomind/monograph.db \u2014 live SQLite (read counts via sqlite3)\n// 3. .monomind/graph/graph.json \u2014 legacy JSON dump\nfunction getMonographStats() {\n const statsPath = path.join(CWD, '.monomind', 'graph', 'stats.json');\n const dbPath = path.join(CWD, '.monomind', 'monograph.db');\n const graphPath = path.join(CWD, '.monomind', 'graph', 'graph.json');\n\n try {\n const s = readJSON(statsPath);\n if (s && s.nodes !== undefined) return { nodes: s.nodes, edges: s.edges || 0, exists: true };\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); /* ignore */ }\n\n try {\n if (fs.existsSync(dbPath)) {\n const out = cached('monographCounts', TTL.counts, () => safeExec(`sqlite3 \"${dbPath}\" \"SELECT (SELECT COUNT(*) FROM nodes), (SELECT COUNT(*) FROM edges);\"`, 1000));\n if (out) {\n const [n, e] = out.split('|').map(v => parseInt(v, 10) || 0);\n if (n > 0) return { nodes: n, edges: e, exists: true };\n }\n }\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); /* ignore */ }\n\n try {\n const stat = safeStat(graphPath);\n if (stat && stat.size < 10 * 1024 * 1024) {\n const g = JSON.parse(fs.readFileSync(graphPath, 'utf-8'));\n const nodes = Array.isArray(g.nodes) ? g.nodes.length : 0;\n const edges = (Array.isArray(g.edges) ? g.edges : (Array.isArray(g.links) ? g.links : [])).length;\n return { nodes, edges, exists: true };\n }\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); /* ignore */ }\n return { nodes: 0, edges: 0, exists: false };\n}\n\n// V4: how many commits is the monograph behind HEAD?\n// Reads last_commit_hash from index_meta in the SQLite DB and runs\n// git rev-list --count ..HEAD. Returns null when the DB is\n// missing, the meta row is absent, or git is unavailable.\nfunction getGraphStaleness() {\n const dbPath = path.join(CWD, '.monomind', 'monograph.db');\n if (!fs.existsSync(dbPath)) return null;\n return cached('graphStaleness', TTL.counts, readGraphStaleness);\n}\n\nfunction readGraphStaleness() {\n const dbPath = path.join(CWD, '.monomind', 'monograph.db');\n try {\n // Pull the last indexed commit out of index_meta via sqlite3 (same\n // shell-out convention getMonographStats uses for node/edge counts).\n const lastCommit = safeExec(`sqlite3 \"${dbPath}\" \"SELECT value FROM index_meta WHERE key = 'last_commit_hash' OR key = 'lastCommit' LIMIT 1;\"`, 1000);\n if (!lastCommit || !/^[0-9a-f]{7,40}$/i.test(lastCommit.trim())) return null;\n const out = safeExec(`git rev-list --count ${lastCommit.trim()}..HEAD`, 1500);\n if (!out) return null;\n const n = parseInt(out.trim(), 10);\n return Number.isFinite(n) ? { commitsBehind: n, lastCommit: lastCommit.trim() } : null;\n } catch (err) {\n if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err);\n return null;\n }\n}\n\nfunction getSIBudget() {\n const SI_LIMIT = 1500;\n const siPath = path.join(CWD, '.agents', 'shared_instructions.md');\n try {\n const siStat = safeStat(siPath);\n if (!siStat) return null;\n if (siStat.size > 2 * 1024 * 1024) return null; // skip > 2 MB\n const len = fs.readFileSync(siPath, 'utf-8').length;\n return { len, pct: Math.round((len / SI_LIMIT) * 100), limit: SI_LIMIT };\n } catch (err) { if (process.env.MONOMIND_DEBUG) console.error('[statusline]', err); return null; }\n}\n\n"; //# sourceMappingURL=statusline-script-stats.d.ts.map