@reposkein/mcp 0.2.7 → 0.4.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.
Files changed (140) hide show
  1. package/README.md +73 -1
  2. package/binary-digests.json +4 -4
  3. package/dist/SKILL.md +63 -1
  4. package/dist/cli/adr.d.ts +37 -0
  5. package/dist/cli/adr.js +231 -0
  6. package/dist/cli/adr.js.map +1 -0
  7. package/dist/cli/agentAdapters.d.ts +68 -0
  8. package/dist/cli/agentAdapters.js +223 -0
  9. package/dist/cli/agentAdapters.js.map +1 -0
  10. package/dist/cli/ansi.d.ts +24 -0
  11. package/dist/cli/ansi.js +40 -0
  12. package/dist/cli/ansi.js.map +1 -0
  13. package/dist/cli/doctor.d.ts +34 -2
  14. package/dist/cli/doctor.js +53 -5
  15. package/dist/cli/doctor.js.map +1 -1
  16. package/dist/cli/doctorDecisions.d.ts +5 -0
  17. package/dist/cli/doctorDecisions.js +83 -0
  18. package/dist/cli/doctorDecisions.js.map +1 -0
  19. package/dist/cli/doctorFreshness.d.ts +43 -0
  20. package/dist/cli/doctorFreshness.js +111 -0
  21. package/dist/cli/doctorFreshness.js.map +1 -0
  22. package/dist/cli/doctorSummaries.d.ts +2 -0
  23. package/dist/cli/doctorSummaries.js +95 -0
  24. package/dist/cli/doctorSummaries.js.map +1 -0
  25. package/dist/cli/federatedDiscovery.d.ts +16 -0
  26. package/dist/cli/federatedDiscovery.js +75 -0
  27. package/dist/cli/federatedDiscovery.js.map +1 -0
  28. package/dist/cli/init.d.ts +69 -6
  29. package/dist/cli/init.js +194 -28
  30. package/dist/cli/init.js.map +1 -1
  31. package/dist/cli/sourceSlices.d.ts +17 -0
  32. package/dist/cli/sourceSlices.js +59 -0
  33. package/dist/cli/sourceSlices.js.map +1 -0
  34. package/dist/cli/stats.d.ts +29 -0
  35. package/dist/cli/stats.js +180 -0
  36. package/dist/cli/stats.js.map +1 -0
  37. package/dist/cli/view.d.ts +71 -3
  38. package/dist/cli/view.js +147 -12
  39. package/dist/cli/view.js.map +1 -1
  40. package/dist/guard/decisionValidation.d.ts +29 -0
  41. package/dist/guard/decisionValidation.js +51 -0
  42. package/dist/guard/decisionValidation.js.map +1 -0
  43. package/dist/guard/summaryValidation.d.ts +3 -0
  44. package/dist/guard/summaryValidation.js +5 -3
  45. package/dist/guard/summaryValidation.js.map +1 -1
  46. package/dist/index.d.ts +48 -0
  47. package/dist/index.js +355 -78
  48. package/dist/index.js.map +1 -1
  49. package/dist/indexer/decisionsAffected.d.ts +42 -0
  50. package/dist/indexer/decisionsAffected.js +149 -0
  51. package/dist/indexer/decisionsAffected.js.map +1 -0
  52. package/dist/indexer/fetchBinary.d.ts +14 -0
  53. package/dist/indexer/fetchBinary.js +81 -40
  54. package/dist/indexer/fetchBinary.js.map +1 -1
  55. package/dist/indexer/runIndexer.d.ts +3 -2
  56. package/dist/indexer/runIndexer.js +8 -4
  57. package/dist/indexer/runIndexer.js.map +1 -1
  58. package/dist/profile/decisions.d.ts +37 -0
  59. package/dist/profile/decisions.js +112 -0
  60. package/dist/profile/decisions.js.map +1 -0
  61. package/dist/profile/types.d.ts +8 -0
  62. package/dist/store/GraphStore.d.ts +5 -0
  63. package/dist/store/JsonlGraphStore.d.ts +14 -3
  64. package/dist/store/JsonlGraphStore.js +66 -19
  65. package/dist/store/JsonlGraphStore.js.map +1 -1
  66. package/dist/store/decisions.d.ts +131 -0
  67. package/dist/store/decisions.js +321 -0
  68. package/dist/store/decisions.js.map +1 -0
  69. package/dist/store/indexedAt.d.ts +18 -0
  70. package/dist/store/indexedAt.js +56 -0
  71. package/dist/store/indexedAt.js.map +1 -0
  72. package/dist/store/instrumentTool.d.ts +40 -0
  73. package/dist/store/instrumentTool.js +82 -0
  74. package/dist/store/instrumentTool.js.map +1 -0
  75. package/dist/store/repoContextCache.d.ts +15 -0
  76. package/dist/store/repoContextCache.js +26 -0
  77. package/dist/store/repoContextCache.js.map +1 -0
  78. package/dist/store/repoSession.d.ts +63 -0
  79. package/dist/store/repoSession.js +123 -0
  80. package/dist/store/repoSession.js.map +1 -0
  81. package/dist/store/resolveRepoPath.d.ts +31 -0
  82. package/dist/store/resolveRepoPath.js +85 -0
  83. package/dist/store/resolveRepoPath.js.map +1 -0
  84. package/dist/store/sessionLog.d.ts +98 -0
  85. package/dist/store/sessionLog.js +216 -0
  86. package/dist/store/sessionLog.js.map +1 -0
  87. package/dist/store/sessionStats.d.ts +55 -0
  88. package/dist/store/sessionStats.js +140 -0
  89. package/dist/store/sessionStats.js.map +1 -0
  90. package/dist/store/sidecar.d.ts +39 -3
  91. package/dist/store/sidecar.js +89 -5
  92. package/dist/store/sidecar.js.map +1 -1
  93. package/dist/store/summaryShards.d.ts +117 -0
  94. package/dist/store/summaryShards.js +291 -0
  95. package/dist/store/summaryShards.js.map +1 -0
  96. package/dist/store/teamConfig.d.ts +13 -0
  97. package/dist/store/teamConfig.js +49 -0
  98. package/dist/store/teamConfig.js.map +1 -0
  99. package/dist/tools/getContextProfile.d.ts +1 -1
  100. package/dist/tools/getContextProfile.js +9 -1
  101. package/dist/tools/getContextProfile.js.map +1 -1
  102. package/dist/tools/getDecision.d.ts +6 -0
  103. package/dist/tools/getDecision.js +59 -0
  104. package/dist/tools/getDecision.js.map +1 -0
  105. package/dist/tools/impact.d.ts +1 -1
  106. package/dist/tools/impact.js +13 -1
  107. package/dist/tools/impact.js.map +1 -1
  108. package/dist/tools/indexerTools.d.ts +16 -2
  109. package/dist/tools/indexerTools.js +44 -5
  110. package/dist/tools/indexerTools.js.map +1 -1
  111. package/dist/tools/listDecisions.d.ts +19 -0
  112. package/dist/tools/listDecisions.js +93 -0
  113. package/dist/tools/listDecisions.js.map +1 -0
  114. package/dist/tools/reaffirmDecision.d.ts +11 -0
  115. package/dist/tools/reaffirmDecision.js +55 -0
  116. package/dist/tools/reaffirmDecision.js.map +1 -0
  117. package/dist/tools/recordDecision.d.ts +24 -0
  118. package/dist/tools/recordDecision.js +135 -0
  119. package/dist/tools/recordDecision.js.map +1 -0
  120. package/dist/tools/semanticFind.d.ts +9 -2
  121. package/dist/tools/semanticFind.js +44 -2
  122. package/dist/tools/semanticFind.js.map +1 -1
  123. package/dist/tools/setDecisionStatus.d.ts +8 -0
  124. package/dist/tools/setDecisionStatus.js +34 -0
  125. package/dist/tools/setDecisionStatus.js.map +1 -0
  126. package/dist/viz/assets/{graph.worker-CC7BeE-8.js → graph.worker-DkH6-lMU.js} +1 -1
  127. package/dist/viz/assets/index-CxqeGuRS.css +1 -0
  128. package/dist/viz/assets/index-DiNZyrXx.js +9 -0
  129. package/dist/viz/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  130. package/dist/viz/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  131. package/dist/viz/assets/jetbrains-mono-latin-700-normal-BYuf6tUa.woff2 +0 -0
  132. package/dist/viz/assets/r3f-BZY3QR6_.js +336 -0
  133. package/dist/viz/assets/tanstack-CjXr-L-s.js +62 -0
  134. package/dist/viz/assets/three-Cpu5RUEM.js +4116 -0
  135. package/dist/viz/index.html +5 -4
  136. package/package.json +5 -4
  137. package/dist/viz/assets/index-C_8UkiJ8.js +0 -9
  138. package/dist/viz/assets/r3f-Cku1OhB-.js +0 -277
  139. package/dist/viz/assets/tanstack-DyXcNRWk.js +0 -77
  140. package/dist/viz/assets/three-Z8XaLGmf.js +0 -3839
@@ -0,0 +1,223 @@
1
+ /** Per-agent MCP config adapters for `reposkein-mcp init`'s join mode
2
+ * (REP-16 T5). A "join" is: someone cloned a repo that already has RepoSkein
3
+ * set up (committed `.reposkein/meta.json`) — the one thing left to do is
4
+ * wire the local agent(s) up, which today means hand-copying a JSON snippet
5
+ * from docs/INSTALL.md §6. These adapters do that automatically.
6
+ *
7
+ * Schemas match docs/INSTALL.md §6 exactly — that doc is the spec:
8
+ * - Claude Code: `<repo>/.mcp.json`, `mcpServers` key. Prefers `claude mcp
9
+ * add` (scope "project", which itself targets .mcp.json) when the CLI is
10
+ * on PATH, so Claude's own approval/health-check flow sees the entry;
11
+ * falls back to writing the file directly.
12
+ * - OpenCode (+omo): `<repo>/opencode.json`, `mcp` key (NOT `mcpServers`).
13
+ * - Cursor: `<repo>/.cursor/mcp.json`, `mcpServers` key.
14
+ * - Unknown/no agent detected: falls back to `.mcp.json` (§6.6 "generic").
15
+ *
16
+ * Every write is idempotent (re-run = no-op, byte-identical file), dry-run
17
+ * aware (never touches disk when `dryRun: true`), and backs up any file it
18
+ * actually modifies to a timestamped `.bak` sibling first. */
19
+ import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync } from "node:fs";
20
+ import { execFileSync } from "node:child_process";
21
+ import { dirname, join, resolve } from "node:path";
22
+ export const ALL_AGENT_IDS = ["claude", "opencode", "cursor"];
23
+ export const defaultExec = (cmd, args, opts) => {
24
+ try {
25
+ const stdout = execFileSync(cmd, args, { cwd: opts.cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
26
+ return { status: 0, stdout, stderr: "" };
27
+ }
28
+ catch (err) {
29
+ const e = err;
30
+ return {
31
+ status: typeof e.status === "number" ? e.status : 1,
32
+ stdout: e.stdout ? e.stdout.toString() : "",
33
+ stderr: e.stderr ? e.stderr.toString() : String(e.message ?? err),
34
+ };
35
+ }
36
+ };
37
+ function commandAvailable(exec, cmd, cwd) {
38
+ return exec(cmd, ["--version"], { cwd }).status === 0;
39
+ }
40
+ /** Detects which agents are "present": their CLI is on PATH, or their config
41
+ * file already exists (someone opted in previously — keep writing there).
42
+ * Falls back to `["claude"]` (→ `.mcp.json`, the generic/broadest-compat
43
+ * target) when nothing is detected at all, matching docs/INSTALL.md §6.6. */
44
+ export function detectAgents(repoPath, exec = defaultExec) {
45
+ const present = [];
46
+ if (existsSync(join(repoPath, ".mcp.json")) || commandAvailable(exec, "claude", repoPath))
47
+ present.push("claude");
48
+ if (existsSync(join(repoPath, "opencode.json")) || commandAvailable(exec, "opencode", repoPath))
49
+ present.push("opencode");
50
+ if (existsSync(join(repoPath, ".cursor")))
51
+ present.push("cursor");
52
+ if (present.length === 0)
53
+ present.push("claude");
54
+ return present;
55
+ }
56
+ /** Parses a comma/space-separated `--agents` value into AgentId[], ignoring
57
+ * unknown tokens (never throws — an unrecognized agent name just writes
58
+ * nothing rather than crashing `init`). */
59
+ export function parseAgentsFlag(value) {
60
+ const known = new Set(ALL_AGENT_IDS);
61
+ return value
62
+ .split(/[,\s]+/)
63
+ .map((s) => s.trim().toLowerCase())
64
+ .filter((s) => known.has(s));
65
+ }
66
+ function timestampSuffix(now) {
67
+ return now.toISOString().replace(/[:.]/g, "-");
68
+ }
69
+ /** Copies `path` to `<path>.<timestamp>.bak` before it's modified. */
70
+ function backupFile(path, now) {
71
+ const backupPath = `${path}.${timestampSuffix(now)}.bak`;
72
+ copyFileSync(path, backupPath);
73
+ return backupPath;
74
+ }
75
+ const MCP_JSON_SPEC = {
76
+ relPath: ".mcp.json",
77
+ topKey: "mcpServers",
78
+ buildEntry: (absRepoPath) => ({ command: "reposkein-mcp", env: { REPOSKEIN_REPO_PATH: absRepoPath } }),
79
+ };
80
+ const OPENCODE_JSON_SPEC = {
81
+ relPath: "opencode.json",
82
+ topKey: "mcp",
83
+ buildEntry: (absRepoPath) => ({
84
+ type: "local",
85
+ command: ["reposkein-mcp"],
86
+ environment: { REPOSKEIN_REPO_PATH: absRepoPath },
87
+ enabled: true,
88
+ }),
89
+ };
90
+ const CURSOR_JSON_SPEC = {
91
+ relPath: join(".cursor", "mcp.json"),
92
+ topKey: "mcpServers",
93
+ buildEntry: (absRepoPath) => ({ command: "reposkein-mcp", env: { REPOSKEIN_REPO_PATH: absRepoPath } }),
94
+ };
95
+ /** Idempotently upserts `doc[topKey].reposkein = entry` in a JSON config
96
+ * file, preserving every other key untouched (other MCP servers, `$schema`,
97
+ * etc.). Never guesses at a corrupt/hand-edited existing file — see the
98
+ * early return below: it backs the file up (real run only) and reports an
99
+ * "error" result instead of overwriting it with a reposkein-only doc,
100
+ * which would silently destroy whatever else was in there. */
101
+ function upsertJsonAdapter(repoPath, spec, dryRun, now) {
102
+ const path = join(repoPath, spec.relPath);
103
+ const exists = existsSync(path);
104
+ let doc = {};
105
+ if (exists) {
106
+ let raw;
107
+ try {
108
+ raw = readFileSync(path, "utf8");
109
+ }
110
+ catch (err) {
111
+ return {
112
+ path,
113
+ action: "error",
114
+ message: `could not read ${spec.relPath}: ${err instanceof Error ? err.message : String(err)}`,
115
+ };
116
+ }
117
+ try {
118
+ doc = JSON.parse(raw);
119
+ }
120
+ catch (err) {
121
+ const parseMessage = err instanceof Error ? err.message : String(err);
122
+ if (dryRun) {
123
+ return {
124
+ path,
125
+ action: "error",
126
+ message: `${spec.relPath} is not valid JSON (${parseMessage}) — left untouched. ` +
127
+ "Fix the JSON and re-run, or delete the file.",
128
+ };
129
+ }
130
+ const backupPath = backupFile(path, now);
131
+ return {
132
+ path,
133
+ action: "error",
134
+ backupPath,
135
+ message: `${spec.relPath} is not valid JSON (${parseMessage}) — left untouched, backed up to ${backupPath}. ` +
136
+ "Fix the JSON and re-run, or delete the file.",
137
+ };
138
+ }
139
+ }
140
+ const section = doc[spec.topKey] ?? {};
141
+ const entry = spec.buildEntry(resolve(repoPath));
142
+ if (JSON.stringify(section.reposkein ?? null) === JSON.stringify(entry)) {
143
+ return { path, action: "unchanged", message: `${spec.relPath} already has an up-to-date reposkein entry` };
144
+ }
145
+ if (dryRun) {
146
+ return {
147
+ path,
148
+ action: "dry-run",
149
+ message: `would ${exists ? "update" : "create"} ${spec.relPath} (${spec.topKey}.reposkein)`,
150
+ };
151
+ }
152
+ let backupPath;
153
+ if (exists)
154
+ backupPath = backupFile(path, now);
155
+ doc[spec.topKey] = { ...section, reposkein: entry };
156
+ mkdirSync(dirname(path), { recursive: true });
157
+ writeFileSync(path, JSON.stringify(doc, null, 2) + "\n", "utf8");
158
+ return { path, action: exists ? "updated" : "created", backupPath, message: `${exists ? "updated" : "created"} ${spec.relPath}` };
159
+ }
160
+ function claudeCliEntryExists(exec, repoPath) {
161
+ return exec("claude", ["mcp", "get", "reposkein"], { cwd: repoPath }).status === 0;
162
+ }
163
+ /** Writes the Claude Code entry via `claude mcp add -s project` (which
164
+ * itself targets `.mcp.json`), so Claude's own health-check/approval UI
165
+ * picks it up immediately. Idempotent via `claude mcp get` — a re-run
166
+ * no-ops instead of erroring on a duplicate name. */
167
+ function writeViaClaudeCli(repoPath, dryRun, exec) {
168
+ const path = join(repoPath, ".mcp.json");
169
+ if (claudeCliEntryExists(exec, repoPath)) {
170
+ return { agent: "claude", path, action: "unchanged", message: "`claude mcp get reposkein` already resolves — nothing to add" };
171
+ }
172
+ const args = ["mcp", "add", "reposkein", "-s", "project", "-e", `REPOSKEIN_REPO_PATH=${resolve(repoPath)}`, "--", "reposkein-mcp"];
173
+ if (dryRun) {
174
+ return { agent: "claude", path, action: "dry-run", message: `would run: claude ${args.join(" ")}` };
175
+ }
176
+ const r = exec("claude", args, { cwd: repoPath });
177
+ if (r.status !== 0) {
178
+ return { agent: "claude", path, action: "error", message: `claude mcp add failed: ${(r.stderr || r.stdout).trim()}` };
179
+ }
180
+ return { agent: "claude", path, action: "created", message: "added via `claude mcp add` (project scope → .mcp.json)" };
181
+ }
182
+ /** Writes (or plans, under `dryRun`) the local agent MCP config for every
183
+ * detected/requested agent. Pure w.r.t. its inputs beyond `exec`/`now`/fs —
184
+ * same repo state + same options always produces the same plan, which is
185
+ * what makes idempotence and dry-run trustworthy. */
186
+ export function writeAgentConfigs(repoPath, opts = {}) {
187
+ const exec = opts.exec ?? defaultExec;
188
+ const now = (opts.now ?? (() => new Date()))();
189
+ const dryRun = !!opts.dryRun;
190
+ const agents = opts.agents ?? detectAgents(repoPath, exec);
191
+ const results = [];
192
+ for (const agent of agents) {
193
+ if (agent === "claude") {
194
+ if (commandAvailable(exec, "claude", repoPath)) {
195
+ results.push(writeViaClaudeCli(repoPath, dryRun, exec));
196
+ }
197
+ else {
198
+ const r = upsertJsonAdapter(repoPath, MCP_JSON_SPEC, dryRun, now);
199
+ results.push({ agent: "claude", ...r });
200
+ }
201
+ }
202
+ else if (agent === "opencode") {
203
+ const r = upsertJsonAdapter(repoPath, OPENCODE_JSON_SPEC, dryRun, now);
204
+ results.push({ agent: "opencode", ...r });
205
+ }
206
+ else if (agent === "cursor") {
207
+ const r = upsertJsonAdapter(repoPath, CURSOR_JSON_SPEC, dryRun, now);
208
+ results.push({ agent: "cursor", ...r });
209
+ }
210
+ }
211
+ return results;
212
+ }
213
+ /** One line per adapter result, for `init`'s stderr summary. */
214
+ export function formatAdapterResult(r) {
215
+ const verb = r.action === "created" ? "wrote" :
216
+ r.action === "updated" ? "updated" :
217
+ r.action === "unchanged" ? "unchanged" :
218
+ r.action === "dry-run" ? "[dry-run]" :
219
+ "ERROR";
220
+ const backup = r.backupPath ? ` (backup: ${r.backupPath})` : "";
221
+ return `reposkein: [${r.agent}] ${verb} — ${r.message}${backup}`;
222
+ }
223
+ //# sourceMappingURL=agentAdapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentAdapters.js","sourceRoot":"","sources":["../../src/cli/agentAdapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;+DAiB+D;AAE/D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGnD,MAAM,CAAC,MAAM,aAAa,GAAuB,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;AA2BlF,MAAM,CAAC,MAAM,WAAW,GAAS,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IACnD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/G,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,GAAgG,CAAC;QAC3G,OAAO;YACL,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACnD,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE;YAC3C,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,GAAG,CAAC;SAClE,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF,SAAS,gBAAgB,CAAC,IAAU,EAAE,GAAW,EAAE,GAAW;IAC5D,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AACxD,CAAC;AAED;;;8EAG8E;AAC9E,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,OAAa,WAAW;IACrE,MAAM,OAAO,GAAc,EAAE,CAAC;IAC9B,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,IAAI,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClH,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,IAAI,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC1H,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;4CAE4C;AAC5C,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,KAAK,GAAG,IAAI,GAAG,CAAS,aAAa,CAAC,CAAC;IAC7C,OAAO,KAAK;SACT,KAAK,CAAC,QAAQ,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;SAClC,MAAM,CAAC,CAAC,CAAC,EAAgB,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,eAAe,CAAC,GAAS;IAChC,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AACjD,CAAC;AAED,sEAAsE;AACtE,SAAS,UAAU,CAAC,IAAY,EAAE,GAAS;IACzC,MAAM,UAAU,GAAG,GAAG,IAAI,IAAI,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC;IACzD,YAAY,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC/B,OAAO,UAAU,CAAC;AACpB,CAAC;AASD,MAAM,aAAa,GAAoB;IACrC,OAAO,EAAE,WAAW;IACpB,MAAM,EAAE,YAAY;IACpB,UAAU,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,EAAE,mBAAmB,EAAE,WAAW,EAAE,EAAE,CAAC;CACvG,CAAC;AAEF,MAAM,kBAAkB,GAAoB;IAC1C,OAAO,EAAE,eAAe;IACxB,MAAM,EAAE,KAAK;IACb,UAAU,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC5B,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,CAAC,eAAe,CAAC;QAC1B,WAAW,EAAE,EAAE,mBAAmB,EAAE,WAAW,EAAE;QACjD,OAAO,EAAE,IAAI;KACd,CAAC;CACH,CAAC;AAEF,MAAM,gBAAgB,GAAoB;IACxC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IACpC,MAAM,EAAE,YAAY;IACpB,UAAU,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,EAAE,mBAAmB,EAAE,WAAW,EAAE,EAAE,CAAC;CACvG,CAAC;AAEF;;;;;+DAK+D;AAC/D,SAAS,iBAAiB,CACxB,QAAgB,EAChB,IAAqB,EACrB,MAAe,EACf,GAAS;IAET,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,GAAG,GAA4B,EAAE,CAAC;IACtC,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,IAAI;gBACJ,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,kBAAkB,IAAI,CAAC,OAAO,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;aAC/F,CAAC;QACJ,CAAC;QACD,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QACnD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,YAAY,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACtE,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO;oBACL,IAAI;oBACJ,MAAM,EAAE,OAAO;oBACf,OAAO,EACL,GAAG,IAAI,CAAC,OAAO,uBAAuB,YAAY,sBAAsB;wBACxE,8CAA8C;iBACjD,CAAC;YACJ,CAAC;YACD,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACzC,OAAO;gBACL,IAAI;gBACJ,MAAM,EAAE,OAAO;gBACf,UAAU;gBACV,OAAO,EACL,GAAG,IAAI,CAAC,OAAO,uBAAuB,YAAY,oCAAoC,UAAU,IAAI;oBACpG,8CAA8C;aACjD,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAyC,IAAI,EAAE,CAAC;IAChF,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjD,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,4CAA4C,EAAE,CAAC;IAC7G,CAAC;IACD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO;YACL,IAAI;YACJ,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,SAAS,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,MAAM,aAAa;SAC5F,CAAC;IACJ,CAAC;IACD,IAAI,UAA8B,CAAC;IACnC,IAAI,MAAM;QAAE,UAAU,GAAG,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC/C,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACpD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IACjE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;AACpI,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAU,EAAE,QAAgB;IACxD,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AACrF,CAAC;AAED;;;sDAGsD;AACtD,SAAS,iBAAiB,CAAC,QAAgB,EAAE,MAAe,EAAE,IAAU;IACtE,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IACzC,IAAI,oBAAoB,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC;QACzC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,8DAA8D,EAAE,CAAC;IACjI,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,uBAAuB,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;IACnI,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,qBAAqB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;IACtG,CAAC;IACD,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;IAClD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnB,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,0BAA0B,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC;IACxH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,wDAAwD,EAAE,CAAC;AACzH,CAAC;AAWD;;;sDAGsD;AACtD,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,OAAiC,EAAE;IACrF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,WAAW,CAAC;IACtC,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;IAC/C,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;IAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAE3D,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvB,IAAI,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;gBAC/C,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;YAC1D,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,iBAAiB,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;gBAClE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;aAAM,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;YAChC,MAAM,CAAC,GAAG,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;YACvE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5C,CAAC;aAAM,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,CAAC,GAAG,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;YACrE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,mBAAmB,CAAC,CAAgB;IAClD,MAAM,IAAI,GACR,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YACpC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;gBACxC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;oBACtC,OAAO,CAAC;IACV,MAAM,MAAM,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,OAAO,eAAe,CAAC,CAAC,KAAK,KAAK,IAAI,MAAM,CAAC,CAAC,OAAO,GAAG,MAAM,EAAE,CAAC;AACnE,CAAC"}
@@ -0,0 +1,24 @@
1
+ /** Dependency-free ANSI styling for `reposkein-mcp stats` — raw escape codes,
2
+ * no chalk/picocolors. Auto-disables when stdout isn't a TTY or `NO_COLOR`
3
+ * is set (https://no-color.org), per the zero-infra/deterministic-tooling
4
+ * convention: never assume a terminal, never require a dependency for a
5
+ * cosmetic feature.
6
+ *
7
+ * Colors: teal `#2DD4BF` ≈ ANSI 256 color 44 (`38;5;44`), amber ≈ 256 color
8
+ * 214 (`38;5;214`) — same palette as the project's README banners. Falls
9
+ * back to the portable 16-color codes (36 cyan, 33 yellow) is intentionally
10
+ * NOT done: 256-color support is effectively universal on anything that
11
+ * reports `isTTY`, and matching the brand hex matters more here than
12
+ * supporting truly ancient terminals for a purely cosmetic CLI report. */
13
+ export declare function colorEnabled(stream?: {
14
+ isTTY?: boolean;
15
+ }, env?: NodeJS.ProcessEnv): boolean;
16
+ export interface Styler {
17
+ teal(s: string): string;
18
+ amber(s: string): string;
19
+ bold(s: string): string;
20
+ dim(s: string): string;
21
+ }
22
+ /** Returns the styling functions to use, given whether color is enabled —
23
+ * callers pass `colorEnabled()`'s result so this stays pure/testable. */
24
+ export declare function styler(enabled: boolean): Styler;
@@ -0,0 +1,40 @@
1
+ /** Dependency-free ANSI styling for `reposkein-mcp stats` — raw escape codes,
2
+ * no chalk/picocolors. Auto-disables when stdout isn't a TTY or `NO_COLOR`
3
+ * is set (https://no-color.org), per the zero-infra/deterministic-tooling
4
+ * convention: never assume a terminal, never require a dependency for a
5
+ * cosmetic feature.
6
+ *
7
+ * Colors: teal `#2DD4BF` ≈ ANSI 256 color 44 (`38;5;44`), amber ≈ 256 color
8
+ * 214 (`38;5;214`) — same palette as the project's README banners. Falls
9
+ * back to the portable 16-color codes (36 cyan, 33 yellow) is intentionally
10
+ * NOT done: 256-color support is effectively universal on anything that
11
+ * reports `isTTY`, and matching the brand hex matters more here than
12
+ * supporting truly ancient terminals for a purely cosmetic CLI report. */
13
+ const RESET = "\x1b[0m";
14
+ const BOLD = "\x1b[1m";
15
+ const DIM = "\x1b[2m";
16
+ const TEAL = "\x1b[38;5;44m";
17
+ const AMBER = "\x1b[38;5;214m";
18
+ export function colorEnabled(stream = process.stdout, env = process.env) {
19
+ if (env.NO_COLOR !== undefined)
20
+ return false;
21
+ return !!stream.isTTY;
22
+ }
23
+ const identity = {
24
+ teal: (s) => s,
25
+ amber: (s) => s,
26
+ bold: (s) => s,
27
+ dim: (s) => s,
28
+ };
29
+ const styled = {
30
+ teal: (s) => `${TEAL}${s}${RESET}`,
31
+ amber: (s) => `${AMBER}${s}${RESET}`,
32
+ bold: (s) => `${BOLD}${s}${RESET}`,
33
+ dim: (s) => `${DIM}${s}${RESET}`,
34
+ };
35
+ /** Returns the styling functions to use, given whether color is enabled —
36
+ * callers pass `colorEnabled()`'s result so this stays pure/testable. */
37
+ export function styler(enabled) {
38
+ return enabled ? styled : identity;
39
+ }
40
+ //# sourceMappingURL=ansi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansi.js","sourceRoot":"","sources":["../../src/cli/ansi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;2EAW2E;AAE3E,MAAM,KAAK,GAAG,SAAS,CAAC;AACxB,MAAM,IAAI,GAAG,SAAS,CAAC;AACvB,MAAM,GAAG,GAAG,SAAS,CAAC;AACtB,MAAM,IAAI,GAAG,eAAe,CAAC;AAC7B,MAAM,KAAK,GAAG,gBAAgB,CAAC;AAE/B,MAAM,UAAU,YAAY,CAAC,SAA8B,OAAO,CAAC,MAAM,EAAE,MAAyB,OAAO,CAAC,GAAG;IAC7G,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7C,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;AACxB,CAAC;AASD,MAAM,QAAQ,GAAW;IACvB,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACd,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACf,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACd,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,GAAW;IACrB,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,KAAK,EAAE;IAClC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,KAAK,EAAE;IACpC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,KAAK,EAAE;IAClC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,KAAK,EAAE;CACjC,CAAC;AAEF;0EAC0E;AAC1E,MAAM,UAAU,MAAM,CAAC,OAAgB;IACrC,OAAO,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;AACrC,CAAC"}
@@ -1,3 +1,15 @@
1
+ export interface DoctorPathResolution {
2
+ path: string;
3
+ /** Set when walk-down found 2+ candidate repos and neither an explicit
4
+ * path nor REPOSKEIN_REPO_PATH picked one — `path` falls back to `cwd`
5
+ * in this case so the caller can still print a real (failing) report. */
6
+ error?: string;
7
+ }
8
+ /** Resolves the repo path for `reposkein-mcp doctor [path]`: explicit arg >
9
+ * REPOSKEIN_REPO_PATH > walk-up (nearest ancestor with .reposkein/) >
10
+ * walk-down (workspace mode). Verifies doctor works from a subdirectory
11
+ * cwd — the common case of running it somewhere other than the repo root. */
12
+ export declare function resolveDoctorRepoPath(explicitPath: string | undefined, cwd: string, envRepoPath: string | undefined): DoctorPathResolution;
1
13
  export interface Check {
2
14
  id: string;
3
15
  label: string;
@@ -15,5 +27,25 @@ export interface DoctorReport {
15
27
  * Does NOT check MCP host registration — a CLI can't see that; the
16
28
  * reposkein-setup skill verifies reachability via a probe tool call. */
17
29
  export declare function runChecks(repoPath: string): Promise<DoctorReport>;
18
- /** Entry point for `reposkein-mcp doctor [path] [--json]`. Returns process exit code. */
19
- export declare function runDoctor(repoPath?: string, json?: boolean): Promise<number>;
30
+ /** Check ids that `doctor --ci` treats as build-breaking even though they're
31
+ * non-critical for interactive use (degrade gracefully for a human at the
32
+ * keyboard, but should fail a CI job so drift gets caught before it's
33
+ * merged): missing/foreign git hooks, a stale committed graph, and an
34
+ * unsplit legacy `summaries.jsonl` (see docs/INSTALL.md §4.2 — every branch
35
+ * that writes a summary would conflict on it). */
36
+ export declare const CI_FAIL_IDS: Set<string>;
37
+ /** Checks (by id) that `doctor --ci` additionally fails on, beyond the
38
+ * normal critical-check gate. */
39
+ export declare function ciFailingChecks(report: DoctorReport): Check[];
40
+ export declare function renderDoctorReport(report: DoctorReport): string;
41
+ export interface RunDoctorOptions {
42
+ json?: boolean;
43
+ /** `doctor --ci`: additionally fail (non-zero exit) when any CI_FAIL_IDS
44
+ * check is non-ok, even though those checks are non-critical for
45
+ * interactive use. Doesn't change what's printed — only the exit code. */
46
+ ci?: boolean;
47
+ }
48
+ /** Entry point for `reposkein-mcp doctor [path] [--json] [--ci]`. Returns the
49
+ * process exit code. Accepts a bare boolean for `json` for backwards
50
+ * compatibility with the pre-`--ci` call signature. */
51
+ export declare function runDoctor(repoPath?: string, opts?: RunDoctorOptions | boolean): Promise<number>;
@@ -3,6 +3,27 @@ import { join } from "node:path";
3
3
  import { ensureIndexerBinary } from "../indexer/fetchBinary.js";
4
4
  import { spawnIndexer } from "../indexer/runIndexer.js";
5
5
  import { resolveRepoId } from "../store/repoId.js";
6
+ import { resolveRepoPath } from "../store/resolveRepoPath.js";
7
+ import { decisionChecks } from "./doctorDecisions.js";
8
+ import { summaryChecks } from "./doctorSummaries.js";
9
+ import { hooksCheck, graphStaleCheck } from "./doctorFreshness.js";
10
+ /** Resolves the repo path for `reposkein-mcp doctor [path]`: explicit arg >
11
+ * REPOSKEIN_REPO_PATH > walk-up (nearest ancestor with .reposkein/) >
12
+ * walk-down (workspace mode). Verifies doctor works from a subdirectory
13
+ * cwd — the common case of running it somewhere other than the repo root. */
14
+ export function resolveDoctorRepoPath(explicitPath, cwd, envRepoPath) {
15
+ const resolution = resolveRepoPath({ cwd, envRepoPath, explicit: explicitPath });
16
+ if (resolution.repoPath)
17
+ return { path: resolution.repoPath };
18
+ if (resolution.candidates && resolution.candidates.length > 0) {
19
+ return {
20
+ path: cwd,
21
+ error: `multiple RepoSkein repos found under ${cwd}: ${resolution.candidates.join(", ")}. ` +
22
+ "Pass one explicitly: `reposkein-mcp doctor <path>` (or set REPOSKEIN_REPO_PATH).",
23
+ };
24
+ }
25
+ return { path: cwd };
26
+ }
6
27
  /** Counts non-empty lines (≈ node count) without parsing every row. */
7
28
  function countLines(text) {
8
29
  let n = 0;
@@ -65,10 +86,30 @@ export async function runChecks(repoPath) {
65
86
  detail: repoId ?? "could not resolve a repo id",
66
87
  fix: repoId ? undefined : "set REPOSKEIN_REPO_PATH (or REPOSKEIN_REPO_ID) for the MCP server",
67
88
  });
89
+ // 4) Committed summary shards (all non-critical: degrade, don't block).
90
+ checks.push(...summaryChecks(repoPath));
91
+ // 5) Decision log validation (all non-critical: degrade, don't block).
92
+ checks.push(...decisionChecks(repoPath));
93
+ // 6) Git hooks installed + graph freshness (non-critical here; `doctor
94
+ // --ci` promotes both to a failing exit code — see CI_FAIL_IDS below).
95
+ checks.push(hooksCheck(repoPath));
96
+ checks.push(graphStaleCheck(repoPath));
68
97
  const ok = checks.filter((c) => c.critical).every((c) => c.ok);
69
98
  return { repoPath, ok, checks };
70
99
  }
71
- function render(report) {
100
+ /** Check ids that `doctor --ci` treats as build-breaking even though they're
101
+ * non-critical for interactive use (degrade gracefully for a human at the
102
+ * keyboard, but should fail a CI job so drift gets caught before it's
103
+ * merged): missing/foreign git hooks, a stale committed graph, and an
104
+ * unsplit legacy `summaries.jsonl` (see docs/INSTALL.md §4.2 — every branch
105
+ * that writes a summary would conflict on it). */
106
+ export const CI_FAIL_IDS = new Set(["hooks_installed", "graph_stale", "summaries_unsplit"]);
107
+ /** Checks (by id) that `doctor --ci` additionally fails on, beyond the
108
+ * normal critical-check gate. */
109
+ export function ciFailingChecks(report) {
110
+ return report.checks.filter((c) => CI_FAIL_IDS.has(c.id) && !c.ok);
111
+ }
112
+ export function renderDoctorReport(report) {
72
113
  const lines = [`reposkein doctor — ${report.repoPath}`, ""];
73
114
  for (const c of report.checks) {
74
115
  lines.push(`${c.ok ? "✓" : "✗"} ${c.label}: ${c.detail}`);
@@ -80,13 +121,20 @@ function render(report) {
80
121
  lines.push("(Note: this checks prerequisites only. To confirm the MCP server is wired into your agent, ask it to call get_context_profile — see the reposkein-setup skill.)");
81
122
  return lines.join("\n");
82
123
  }
83
- /** Entry point for `reposkein-mcp doctor [path] [--json]`. Returns process exit code. */
84
- export async function runDoctor(repoPath = ".", json = false) {
124
+ /** Entry point for `reposkein-mcp doctor [path] [--json] [--ci]`. Returns the
125
+ * process exit code. Accepts a bare boolean for `json` for backwards
126
+ * compatibility with the pre-`--ci` call signature. */
127
+ export async function runDoctor(repoPath = ".", opts = {}) {
128
+ const { json = false, ci = false } = typeof opts === "boolean" ? { json: opts } : opts;
85
129
  const report = await runChecks(repoPath);
86
130
  if (json)
87
131
  console.log(JSON.stringify(report, null, 2));
88
132
  else
89
- console.error(render(report));
90
- return report.ok ? 0 : 1;
133
+ console.error(renderDoctorReport(report));
134
+ const ciFailures = ci ? ciFailingChecks(report) : [];
135
+ if (ci && ciFailures.length > 0 && !json) {
136
+ console.error(`\n--ci: failing on ${ciFailures.length} additional check(s): ${ciFailures.map((c) => c.id).join(", ")}`);
137
+ }
138
+ return report.ok && ciFailures.length === 0 ? 0 : 1;
91
139
  }
92
140
  //# sourceMappingURL=doctor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAUnE;;;8EAG8E;AAC9E,MAAM,UAAU,qBAAqB,CACnC,YAAgC,EAChC,GAAW,EACX,WAA+B;IAE/B,MAAM,UAAU,GAAG,eAAe,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC;IACjF,IAAI,UAAU,CAAC,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC;IAC9D,IAAI,UAAU,CAAC,UAAU,IAAI,UAAU,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9D,OAAO;YACL,IAAI,EAAE,GAAG;YACT,KAAK,EACH,wCAAwC,GAAG,KAAK,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;gBACpF,kFAAkF;SACrF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;AACvB,CAAC;AAiBD,uEAAuE;AACvE,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,IAAI,IAAI,CAAC,IAAI,EAAE;YAAE,CAAC,EAAE,CAAC;IAC1D,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;yEAEyE;AACzE,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB;IAC9C,MAAM,MAAM,GAAY,EAAE,CAAC;IAE3B,mDAAmD;IACnD,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,mBAAmB,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;QACjD,KAAK,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC;QACrB,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACvF,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,SAAS,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,gBAAgB;QACvB,EAAE,EAAE,KAAK;QACT,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,SAAS;QACjB,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,oFAAoF;KAC9G,CAAC,CAAC;IAEH,yEAAyE;IACzE,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;IAC9D,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACvC,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC;YAAC,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,gBAAgB,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,SAAS,GAAG,QAAQ,IAAI,KAAK,GAAG,CAAC,CAAC;IACxC,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,uCAAuC;QAC9C,EAAE,EAAE,SAAS;QACb,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,2BAA2B;QACtG,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,iCAAiC,QAAQ,iEAAiE;KACxI,CAAC,CAAC;IAEH,2EAA2E;IAC3E,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACtE,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,SAAS;QAChB,EAAE,EAAE,CAAC,CAAC,MAAM;QACZ,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,MAAM,IAAI,6BAA6B;QAC/C,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,mEAAmE;KAC9F,CAAC,CAAC;IAEH,wEAAwE;IACxE,MAAM,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;IAExC,uEAAuE;IACvE,MAAM,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEzC,uEAAuE;IACvE,0EAA0E;IAC1E,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEvC,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/D,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;AAClC,CAAC;AAED;;;;;mDAKmD;AACnD,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,iBAAiB,EAAE,aAAa,EAAE,mBAAmB,CAAC,CAAC,CAAC;AAE5F;kCACkC;AAClC,MAAM,UAAU,eAAe,CAAC,MAAoB;IAClD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAoB;IACrD,MAAM,KAAK,GAAG,CAAC,sBAAsB,MAAM,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5D,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,mEAAmE,CAAC,CAAC;IAC1H,KAAK,CAAC,IAAI,CAAC,iKAAiK,CAAC,CAAC;IAC9K,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAUD;;wDAEwD;AACxD,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAAQ,GAAG,GAAG,EACd,OAAmC,EAAE;IAErC,MAAM,EAAE,IAAI,GAAG,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,GAAG,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACvF,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,IAAI;QAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;;QAClD,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACrD,IAAI,EAAE,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACzC,OAAO,CAAC,KAAK,CACX,sBAAsB,UAAU,CAAC,MAAM,yBAAyB,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzG,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,EAAE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Check } from "./doctor.js";
2
+ /** Decision-log validation for `reposkein-mcp doctor`. Every check is
3
+ * non-critical (exit 0 with warnings): a damaged decision log must degrade,
4
+ * not block the graph. Returns [] only when there is no decisions dir. */
5
+ export declare function decisionChecks(repoPath: string): Check[];
@@ -0,0 +1,83 @@
1
+ import { computeBodyHash, loadDecisionFiles, } from "../store/decisions.js";
2
+ /** ~Zimmermann ceiling: past this many active records, recall quality and the
3
+ * profile budget both degrade — time to deprecate/archive. */
4
+ const ACTIVE_BUDGET = 100;
5
+ const warn = (id, label, ok, detail, fix) => ({
6
+ id,
7
+ label,
8
+ ok,
9
+ critical: false, // decision-log problems degrade; they never block startup
10
+ detail,
11
+ fix: ok ? undefined : fix,
12
+ });
13
+ function findCycle(records) {
14
+ // DFS along supersedes edges, tracking the actual path so the report names
15
+ // exactly the cycle's members — not everything reachable along the way.
16
+ for (const start of records.keys()) {
17
+ const stack = [{ id: start, path: [start] }];
18
+ const visited = new Set();
19
+ while (stack.length > 0) {
20
+ const { id, path } = stack.pop();
21
+ for (const target of records.get(id)?.supersedes ?? []) {
22
+ if (target === start)
23
+ return [...path, target];
24
+ if (!visited.has(target)) {
25
+ visited.add(target);
26
+ stack.push({ id: target, path: [...path, target] });
27
+ }
28
+ }
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+ /** Decision-log validation for `reposkein-mcp doctor`. Every check is
34
+ * non-critical (exit 0 with warnings): a damaged decision log must degrade,
35
+ * not block the graph. Returns [] only when there is no decisions dir. */
36
+ export function decisionChecks(repoPath) {
37
+ const entries = loadDecisionFiles(repoPath);
38
+ if (entries.length === 0)
39
+ return [];
40
+ const checks = [];
41
+ // 1) Every file parses.
42
+ const malformed = entries.filter((e) => e.record === null).map((e) => e.file);
43
+ checks.push(warn("decisions_parse", "decision files parse", malformed.length === 0, malformed.length === 0
44
+ ? `${entries.length} files`
45
+ : `malformed: ${malformed.join(", ")}`, "fix or remove the malformed files (git conflict markers are the usual cause)"));
46
+ const parsed = entries.filter((e) => e.record !== null);
47
+ // 2) No duplicate ids across files (cross-branch same-day same-slug merges).
48
+ const byId = new Map();
49
+ const dupes = new Set();
50
+ for (const { record } of parsed) {
51
+ if (byId.has(record.id))
52
+ dupes.add(record.id);
53
+ else
54
+ byId.set(record.id, record);
55
+ }
56
+ checks.push(warn("decisions_duplicates", "decision ids unique", dupes.size === 0, dupes.size === 0 ? `${byId.size} decisions` : `duplicate ids: ${[...dupes].join(", ")}`, "two branches minted the same id — keep one file per id (merge the bodies by hand if they differ)"));
57
+ // 3) Bodies unmodified since signing (immutability as invariant, not
58
+ // convention — reaffirm/supersede re-sign, hand edits don't).
59
+ const tampered = parsed
60
+ .filter(({ record }) => record.body_hash !== computeBodyHash(record))
61
+ .map(({ record }) => record.id);
62
+ checks.push(warn("decisions_integrity", "decision bodies unmodified (body_hash)", tampered.length === 0, tampered.length === 0 ? "all bodies verify" : `hand-edited: ${tampered.join(", ")}`, "decision bodies are immutable — record a superseding decision instead of editing; revert the edit or re-record"));
63
+ // 4) Supersession references resolve, in both directions.
64
+ const dangling = [];
65
+ for (const rec of byId.values()) {
66
+ for (const sid of rec.supersedes) {
67
+ if (!byId.has(sid))
68
+ dangling.push(`${rec.id} → ${sid}`);
69
+ }
70
+ if (rec.superseded_by && !byId.has(rec.superseded_by)) {
71
+ dangling.push(`${rec.id} ← ${rec.superseded_by}`);
72
+ }
73
+ }
74
+ checks.push(warn("decisions_refs", "supersession references resolve", dangling.length === 0, dangling.length === 0 ? "no dangling references" : `dangling: ${dangling.join(", ")}`, "a referenced decision file is missing — restore it or correct the reference"));
75
+ // 5) No supersession cycles.
76
+ const cycle = findCycle(byId);
77
+ checks.push(warn("decisions_cycles", "supersession chain acyclic", cycle === null, cycle === null ? "no cycles" : `cycle: ${cycle.join(" → ")}`, "break the cycle — a decision cannot (transitively) supersede itself"));
78
+ // 6) Active-record budget.
79
+ const active = [...byId.values()].filter((r) => r.status === "accepted" || r.status === "proposed").length;
80
+ checks.push(warn("decisions_budget", `active decisions within budget (≤${ACTIVE_BUDGET})`, active <= ACTIVE_BUDGET, `${active} active records`, "the log is past the point where recall stays sharp — deprecate or supersede aged-out decisions"));
81
+ return checks;
82
+ }
83
+ //# sourceMappingURL=doctorDecisions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctorDecisions.js","sourceRoot":"","sources":["../../src/cli/doctorDecisions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { Check } from "./doctor.js";
2
+ /** Non-critical by default: a repo can be perfectly usable without hooks
3
+ * (an agent can always run `reposkein-mcp index` by hand). `doctor --ci`
4
+ * promotes this id to a failing exit code — see CI_FAIL_IDS in doctor.ts. */
5
+ export declare function hooksCheck(repoPath: string): Check;
6
+ /** Content-based staleness: the committed graph is fresh iff the commit SHA
7
+ * recorded the last time it was (re)built (`.reposkein/local/indexed-at` —
8
+ * see mcp/src/store/indexedAt.ts) equals the current git HEAD. The marker
9
+ * is kept current two ways: the installed git hooks maintain it on their
10
+ * own (`post-commit` after every commit, `post-merge`/`post-checkout` —
11
+ * which also re-index — after a merge/pull/branch switch), and
12
+ * `reposkein-mcp index`/`init` write it directly. A mismatch therefore
13
+ * means one of: the hooks were never installed (or were removed), or
14
+ * nobody's indexed this repo at all yet.
15
+ *
16
+ * Replaces an earlier mtime-based heuristic (mtime(nodes.jsonl) vs. the last
17
+ * commit's timestamp) that turned out to be structurally blind after any
18
+ * fresh checkout: `git clone`/`checkout` — or a CI cache restoring
19
+ * `.reposkein/` from a prior run — stamps every file it writes with
20
+ * "now", which is always ≥ any past commit's timestamp, so
21
+ * `mtime < lastCommitMs` could never be true right when it mattered most.
22
+ * A recorded SHA has no such blind spot: it's compared by value, not by
23
+ * filesystem timestamp.
24
+ *
25
+ * Deliberately a **plain SHA mismatch**, not a diff filtered to
26
+ * indexed-language extensions: filtering would need to stay in sync with
27
+ * `config.toml`'s `[languages] enabled` list and the indexer's own
28
+ * extension→language table, which is one more thing to drift. The cost of
29
+ * getting it wrong is asymmetric — a false "stale" just costs a redundant
30
+ * `reposkein-mcp index` (cheap), while a false "fresh" is exactly the bug
31
+ * being fixed — so the simpler, conservative rule wins here.
32
+ *
33
+ * Two distinct failure shapes, both reported under the same check id:
34
+ * - no recorded SHA at all → never indexed via a hook or the mcp-side path
35
+ * (a fresh clone's local/ never carries over, since it's gitignored) —
36
+ * FAIL.
37
+ * - a recorded SHA that doesn't match HEAD → the hooks are missing (or
38
+ * were bypassed, e.g. `git commit --no-verify`) and nobody's re-indexed
39
+ * by hand either — FAIL.
40
+ *
41
+ * Non-critical by default; `doctor --ci` promotes it (CI_FAIL_IDS in
42
+ * doctor.ts). */
43
+ export declare function graphStaleCheck(repoPath: string): Check;