@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,55 @@
1
+ import { type SessionLogRecord } from "./sessionLog.js";
2
+ /** grep-baseline token-ratio multiplier used for the "estimated context
3
+ * tokens saved" stat. Sourced from `mcp/bench/README.md`'s Track 1 retrieval
4
+ * benchmark ("Mean token ratio on structural tasks: 8.4x (grep / RepoSkein,
5
+ * generous-to-grep)") — a measured, repo-local number, not invented here.
6
+ * Always LABEL output built from this as an estimate: it's one repo's
7
+ * 5-task mean, generalized to every call in a session. */
8
+ export declare const GREP_BASELINE_MULTIPLIER = 8.4;
9
+ export interface SessionSummary {
10
+ sessionId: string;
11
+ /** Absolute path to the session's .jsonl file. */
12
+ file: string;
13
+ recordCount: number;
14
+ callsByTool: Record<string, number>;
15
+ /** Top node ids / file paths touched, most-referenced first. */
16
+ topTouched: Array<{
17
+ id: string;
18
+ count: number;
19
+ }>;
20
+ /** Count of ADRs recorded this session (record_decision calls). */
21
+ decisionsWritten: number;
22
+ /** Count of semantic summaries written this session (write_semantic_summary calls). */
23
+ summariesWritten: number;
24
+ /** ms between the first and last record's `ts`. 0 for a single-record session. */
25
+ durationMs: number;
26
+ totalResultBytes: number;
27
+ /** GREP_BASELINE_MULTIPLIER * totalResultBytes / 4 (≈4 bytes/token), i.e.
28
+ * the estimated token cost an equivalent grep-based approach would have
29
+ * paid for what RepoSkein returned in `totalResultBytes`. Labeled an
30
+ * estimate everywhere it's rendered — see GREP_BASELINE_MULTIPLIER. */
31
+ estimatedGrepTokens: number;
32
+ estimatedRepoSkeinTokens: number;
33
+ estimatedTokensSaved: number;
34
+ failedCalls: number;
35
+ }
36
+ /** Reads and parses one session's JSONL file. Malformed lines are skipped
37
+ * (never thrown) — a session log is best-effort telemetry, not a source of
38
+ * truth that must be pristine. */
39
+ export declare function readSessionLog(file: string): SessionLogRecord[];
40
+ /** Every session file for a repo, sorted newest-first (by mtime). */
41
+ export declare function listSessionFiles(repoPath: string): Array<{
42
+ sessionId: string;
43
+ file: string;
44
+ mtimeMs: number;
45
+ }>;
46
+ /** Aggregates one session's records into the report shape rendered by
47
+ * `reposkein-mcp stats`. Pure function over already-parsed records so it's
48
+ * cheap to unit test independent of the filesystem. */
49
+ export declare function summarizeSession(sessionId: string, file: string, records: SessionLogRecord[]): SessionSummary;
50
+ /** Merges several sessions' records into one combined summary (`--all`) —
51
+ * same aggregation, over the union of records, with a synthetic id. */
52
+ export declare function summarizeSessions(sessions: Array<{
53
+ sessionId: string;
54
+ file: string;
55
+ }>): SessionSummary;
@@ -0,0 +1,140 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { sessionsDir } from "./sessionLog.js";
4
+ /** grep-baseline token-ratio multiplier used for the "estimated context
5
+ * tokens saved" stat. Sourced from `mcp/bench/README.md`'s Track 1 retrieval
6
+ * benchmark ("Mean token ratio on structural tasks: 8.4x (grep / RepoSkein,
7
+ * generous-to-grep)") — a measured, repo-local number, not invented here.
8
+ * Always LABEL output built from this as an estimate: it's one repo's
9
+ * 5-task mean, generalized to every call in a session. */
10
+ export const GREP_BASELINE_MULTIPLIER = 8.4;
11
+ /** Reads and parses one session's JSONL file. Malformed lines are skipped
12
+ * (never thrown) — a session log is best-effort telemetry, not a source of
13
+ * truth that must be pristine. */
14
+ export function readSessionLog(file) {
15
+ if (!existsSync(file))
16
+ return [];
17
+ let text;
18
+ try {
19
+ text = readFileSync(file, "utf8");
20
+ }
21
+ catch {
22
+ return [];
23
+ }
24
+ const out = [];
25
+ for (const line of text.split("\n")) {
26
+ if (line.trim() === "")
27
+ continue;
28
+ try {
29
+ const rec = JSON.parse(line);
30
+ if (typeof rec.tool === "string" && typeof rec.ts === "string") {
31
+ out.push({
32
+ ts: rec.ts,
33
+ tool: rec.tool,
34
+ argsShape: Array.isArray(rec.argsShape) ? rec.argsShape : [],
35
+ resultBytes: typeof rec.resultBytes === "number" ? rec.resultBytes : 0,
36
+ ...(Array.isArray(rec.nodeIds) && rec.nodeIds.length ? { nodeIds: rec.nodeIds } : {}),
37
+ ok: rec.ok !== false,
38
+ });
39
+ }
40
+ }
41
+ catch {
42
+ // skip malformed line
43
+ }
44
+ }
45
+ return out;
46
+ }
47
+ /** Every session file for a repo, sorted newest-first (by mtime). */
48
+ export function listSessionFiles(repoPath) {
49
+ const dir = sessionsDir(repoPath);
50
+ if (!existsSync(dir))
51
+ return [];
52
+ let names;
53
+ try {
54
+ names = readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
55
+ }
56
+ catch {
57
+ return [];
58
+ }
59
+ return names
60
+ .map((name) => {
61
+ const file = join(dir, name);
62
+ let mtimeMs = 0;
63
+ try {
64
+ mtimeMs = statSync(file).mtimeMs;
65
+ }
66
+ catch {
67
+ /* keep 0 — still listed, just sorts last */
68
+ }
69
+ return { sessionId: name.replace(/\.jsonl$/, ""), file, mtimeMs };
70
+ })
71
+ .sort((a, b) => b.mtimeMs - a.mtimeMs);
72
+ }
73
+ /** Tools whose calls count as "summaries/ADRs written this session". */
74
+ const DECISION_TOOLS = new Set(["record_decision"]);
75
+ const SUMMARY_TOOLS = new Set(["write_semantic_summary"]);
76
+ /** Aggregates one session's records into the report shape rendered by
77
+ * `reposkein-mcp stats`. Pure function over already-parsed records so it's
78
+ * cheap to unit test independent of the filesystem. */
79
+ export function summarizeSession(sessionId, file, records) {
80
+ const callsByTool = {};
81
+ const touchedCounts = new Map();
82
+ let decisionsWritten = 0;
83
+ let summariesWritten = 0;
84
+ let totalResultBytes = 0;
85
+ let failedCalls = 0;
86
+ let minTs = Infinity;
87
+ let maxTs = -Infinity;
88
+ for (const rec of records) {
89
+ callsByTool[rec.tool] = (callsByTool[rec.tool] ?? 0) + 1;
90
+ totalResultBytes += rec.resultBytes;
91
+ if (!rec.ok)
92
+ failedCalls++;
93
+ if (DECISION_TOOLS.has(rec.tool))
94
+ decisionsWritten++;
95
+ if (SUMMARY_TOOLS.has(rec.tool))
96
+ summariesWritten++;
97
+ for (const id of rec.nodeIds ?? [])
98
+ touchedCounts.set(id, (touchedCounts.get(id) ?? 0) + 1);
99
+ const t = Date.parse(rec.ts);
100
+ if (!Number.isNaN(t)) {
101
+ if (t < minTs)
102
+ minTs = t;
103
+ if (t > maxTs)
104
+ maxTs = t;
105
+ }
106
+ }
107
+ const topTouched = [...touchedCounts.entries()]
108
+ .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
109
+ .slice(0, 10)
110
+ .map(([id, count]) => ({ id, count }));
111
+ const durationMs = Number.isFinite(minTs) && Number.isFinite(maxTs) && maxTs > minTs ? maxTs - minTs : 0;
112
+ // ~4 bytes/token is the common rough estimator (also used by mcp/bench —
113
+ // see its token cost model) — deliberately simple and labeled an estimate.
114
+ const estimatedRepoSkeinTokens = Math.round(totalResultBytes / 4);
115
+ const estimatedGrepTokens = Math.round(estimatedRepoSkeinTokens * GREP_BASELINE_MULTIPLIER);
116
+ const estimatedTokensSaved = estimatedGrepTokens - estimatedRepoSkeinTokens;
117
+ return {
118
+ sessionId,
119
+ file,
120
+ recordCount: records.length,
121
+ callsByTool,
122
+ topTouched,
123
+ decisionsWritten,
124
+ summariesWritten,
125
+ durationMs,
126
+ totalResultBytes,
127
+ estimatedGrepTokens,
128
+ estimatedRepoSkeinTokens,
129
+ estimatedTokensSaved,
130
+ failedCalls,
131
+ };
132
+ }
133
+ /** Merges several sessions' records into one combined summary (`--all`) —
134
+ * same aggregation, over the union of records, with a synthetic id. */
135
+ export function summarizeSessions(sessions) {
136
+ const allRecords = sessions.flatMap((s) => readSessionLog(s.file));
137
+ const label = sessions.length === 1 ? sessions[0].sessionId : `${sessions.length} sessions`;
138
+ return summarizeSession(label, sessions.map((s) => s.file).join(","), allRecords);
139
+ }
140
+ //# sourceMappingURL=sessionStats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionStats.js","sourceRoot":"","sources":["../../src/store/sessionStats.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- /** A persisted summary record (git-ignored .reposkein/local/summaries.jsonl). */
1
+ import { type SummaryAccumulator } from "./summaryShards.js";
2
+ /** A persisted summary record (git-ignored .reposkein/local/summaries-<agent>.jsonl). */
2
3
  export interface SidecarSummary {
3
4
  id: string;
4
5
  semantic_summary: string;
@@ -7,10 +8,45 @@ export interface SidecarSummary {
7
8
  summary_at: string;
8
9
  summary_by: string;
9
10
  }
10
- export declare function sidecarPath(repoPath: string): string;
11
+ /** Directory holding every agent's sidecar. Git-ignored in full. */
12
+ export declare function sidecarDir(repoPath: string): string;
13
+ /** Filesystem-safe slug for the writing agent.
14
+ *
15
+ * Untrusted input: REPOSKEIN_AGENT comes from whatever launched the server, so
16
+ * it is reduced to `[a-z0-9._-]` and capped. A value that reduces to nothing
17
+ * (or is absent) falls back to "agent", which is also the pre-multi-agent
18
+ * name — so a single-agent setup keeps one stable file. */
19
+ export declare function agentSlug(agent?: string | undefined): string;
20
+ /** THIS process's sidecar: one file per agent.
21
+ *
22
+ * A single shared `local/summaries.jsonl` meant two agents running against the
23
+ * same checkout silently overwrote each other — `upsertSidecar` rewrites the
24
+ * whole file, so the slower writer's prose vanished with no error anywhere.
25
+ * One file per writer removes the shared mutable state; the indexer folds
26
+ * every `local/summaries*.jsonl` into the committed shards. */
27
+ export declare function sidecarPath(repoPath: string, agent?: string): string;
28
+ /** Every agent's sidecar in this repo, sorted, so reads are deterministic and
29
+ * independent of directory order. Includes the pre-split
30
+ * `local/summaries.jsonl` an older client may still be writing. */
31
+ export declare function sidecarPaths(repoPath: string): string[];
11
32
  /** Reads the sidecar into a map keyed by node id. Missing file → empty map.
12
33
  * Best-effort: malformed lines / read errors are skipped. */
13
34
  export declare function readSidecar(path: string): Map<string, SidecarSummary>;
35
+ /** Every agent's sidecar records, folded through the SAME rules the Rust
36
+ * indexer applies — `beats` within the sidecar set, losers preserved — so the
37
+ * server serves exactly the record the next `index` is going to write.
38
+ *
39
+ * This used to be a plain last-file-wins merge, which disagreed with the
40
+ * indexer whenever two agents summarised the same node, and dropped the loser
41
+ * with no trace. Callers should hand `conflicts` to `recordSummaryConflicts`. */
42
+ export declare function loadAllSidecars(repoPath: string): SummaryAccumulator;
43
+ /** Convenience view of `loadAllSidecars` for callers that only want the
44
+ * winning record per node id. Divergence losers are dropped on the floor
45
+ * here, so prefer `loadAllSidecars` anywhere they can be recorded. */
46
+ export declare function readAllSidecars(repoPath: string): Map<string, SidecarSummary>;
14
47
  /** Upserts one record and rewrites the sidecar sorted by id (deterministic, no
15
- * duplicate lines). Creates the local/ dir if needed. Best-effort. */
48
+ * duplicate lines). Creates the local/ dir if needed. Best-effort.
49
+ *
50
+ * Rewriting the whole file is safe only because `path` belongs to one agent:
51
+ * see `sidecarPath`. */
16
52
  export declare function upsertSidecar(path: string, rec: SidecarSummary): void;
@@ -1,7 +1,49 @@
1
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
2
- import { dirname, join } from "node:path";
3
- export function sidecarPath(repoPath) {
4
- return join(repoPath, ".reposkein", "local", "summaries.jsonl");
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from "node:fs";
2
+ import { basename, dirname, join } from "node:path";
3
+ import { absorbSummaryLines, emptyAccumulator, } from "./summaryShards.js";
4
+ /** Directory holding every agent's sidecar. Git-ignored in full. */
5
+ export function sidecarDir(repoPath) {
6
+ return join(repoPath, ".reposkein", "local");
7
+ }
8
+ /** Filesystem-safe slug for the writing agent.
9
+ *
10
+ * Untrusted input: REPOSKEIN_AGENT comes from whatever launched the server, so
11
+ * it is reduced to `[a-z0-9._-]` and capped. A value that reduces to nothing
12
+ * (or is absent) falls back to "agent", which is also the pre-multi-agent
13
+ * name — so a single-agent setup keeps one stable file. */
14
+ export function agentSlug(agent = process.env.REPOSKEIN_AGENT) {
15
+ const slug = (agent ?? "")
16
+ .toLowerCase()
17
+ .replace(/[^a-z0-9._-]+/g, "-")
18
+ .replace(/^[-.]+|[-.]+$/g, "")
19
+ .slice(0, 40)
20
+ .replace(/[-.]+$/, "");
21
+ return slug === "" ? "agent" : slug;
22
+ }
23
+ /** THIS process's sidecar: one file per agent.
24
+ *
25
+ * A single shared `local/summaries.jsonl` meant two agents running against the
26
+ * same checkout silently overwrote each other — `upsertSidecar` rewrites the
27
+ * whole file, so the slower writer's prose vanished with no error anywhere.
28
+ * One file per writer removes the shared mutable state; the indexer folds
29
+ * every `local/summaries*.jsonl` into the committed shards. */
30
+ export function sidecarPath(repoPath, agent) {
31
+ return join(sidecarDir(repoPath), `summaries-${agentSlug(agent)}.jsonl`);
32
+ }
33
+ /** Every agent's sidecar in this repo, sorted, so reads are deterministic and
34
+ * independent of directory order. Includes the pre-split
35
+ * `local/summaries.jsonl` an older client may still be writing. */
36
+ export function sidecarPaths(repoPath) {
37
+ const dir = sidecarDir(repoPath);
38
+ try {
39
+ return readdirSync(dir)
40
+ .filter((f) => f.startsWith("summaries") && f.endsWith(".jsonl"))
41
+ .sort()
42
+ .map((f) => join(dir, f));
43
+ }
44
+ catch {
45
+ return [];
46
+ }
5
47
  }
6
48
  /** Reads the sidecar into a map keyed by node id. Missing file → empty map.
7
49
  * Best-effort: malformed lines / read errors are skipped. */
@@ -38,8 +80,50 @@ export function readSidecar(path) {
38
80
  }
39
81
  return map;
40
82
  }
83
+ /** Every agent's sidecar records, folded through the SAME rules the Rust
84
+ * indexer applies — `beats` within the sidecar set, losers preserved — so the
85
+ * server serves exactly the record the next `index` is going to write.
86
+ *
87
+ * This used to be a plain last-file-wins merge, which disagreed with the
88
+ * indexer whenever two agents summarised the same node, and dropped the loser
89
+ * with no trace. Callers should hand `conflicts` to `recordSummaryConflicts`. */
90
+ export function loadAllSidecars(repoPath) {
91
+ const acc = emptyAccumulator();
92
+ for (const p of sidecarPaths(repoPath)) {
93
+ let text;
94
+ try {
95
+ text = readFileSync(p, "utf8");
96
+ }
97
+ catch {
98
+ continue;
99
+ }
100
+ absorbSummaryLines(acc, `local/${basename(p)}`, text);
101
+ }
102
+ return acc;
103
+ }
104
+ /** Convenience view of `loadAllSidecars` for callers that only want the
105
+ * winning record per node id. Divergence losers are dropped on the floor
106
+ * here, so prefer `loadAllSidecars` anywhere they can be recorded. */
107
+ export function readAllSidecars(repoPath) {
108
+ const merged = new Map();
109
+ for (const [id, rec] of loadAllSidecars(repoPath).summaries) {
110
+ const s = (k) => typeof rec.props[k] === "string" ? rec.props[k] : "";
111
+ merged.set(id, {
112
+ id,
113
+ semantic_summary: s("semantic_summary"),
114
+ summary_of_hash: s("summary_of_hash"),
115
+ summary_model: s("summary_model"),
116
+ summary_at: s("summary_at"),
117
+ summary_by: s("summary_by"),
118
+ });
119
+ }
120
+ return merged;
121
+ }
41
122
  /** Upserts one record and rewrites the sidecar sorted by id (deterministic, no
42
- * duplicate lines). Creates the local/ dir if needed. Best-effort. */
123
+ * duplicate lines). Creates the local/ dir if needed. Best-effort.
124
+ *
125
+ * Rewriting the whole file is safe only because `path` belongs to one agent:
126
+ * see `sidecarPath`. */
43
127
  export function upsertSidecar(path, rec) {
44
128
  const map = readSidecar(path);
45
129
  map.set(rec.id, rec);
@@ -1 +1 @@
1
- {"version":3,"file":"sidecar.js","sourceRoot":"","sources":["../../src/store/sidecar.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sidecar.js","sourceRoot":"","sources":["../../src/store/sidecar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /** Reader for the committed authored summaries, `.reposkein/summaries/<xx>.jsonl`.
2
+ *
3
+ * Sharding replaced a single `.reposkein/summaries.jsonl` for the reason
4
+ * `docs/migrations/2026-08-06-stop-committing-derived-graph.md` records: one
5
+ * committed file that every branch rewrites conflicts on every merge, and no
6
+ * `.gitattributes` rule can fix it, because a forge computes mergeability in a
7
+ * bare repo where tree-level attributes are never consulted. File granularity
8
+ * is the only thing that works there — the same reason decisions are one file
9
+ * each.
10
+ *
11
+ * This module ONLY READS. The Rust indexer is the sole writer of committed
12
+ * shards, so byte-identical output has exactly one serializer to hold to it
13
+ * (serde_json and JSON.stringify differ on escapes and number formatting).
14
+ * Writes from this process go to the git-ignored per-agent sidecar, which the
15
+ * next `index` folds into the shards.
16
+ *
17
+ * Reading never needs a shard key — `readdir` enumerates the files — which is
18
+ * why no BLAKE3 implementation is required on this side.
19
+ *
20
+ * Tolerance is the other half. A shard is a MERGED file: it can carry git
21
+ * conflict markers, duplicate lines from a `merge=union` resolution, or two
22
+ * genuinely different summaries for the same node. None of that may take
23
+ * recall down, and none of it may silently destroy authored prose — the same
24
+ * contract `loadDecisions` keeps for a damaged decision file. */
25
+ export interface SummaryShardRecord {
26
+ id: string;
27
+ /** Every field except `id`, exactly as parsed. */
28
+ props: Record<string, unknown>;
29
+ /** The raw source line, which is also the divergence tiebreak. */
30
+ line: string;
31
+ }
32
+ export interface LoadedSummaryShards {
33
+ /** Winner per node id, insertion-ordered by id. */
34
+ summaries: Map<string, SummaryShardRecord>;
35
+ /** Divergence losers, preserved rather than dropped. */
36
+ conflicts: SummaryShardRecord[];
37
+ warnings: string[];
38
+ /** Set when the pre-sharding `.reposkein/summaries.jsonl` is still present —
39
+ * doctor turns this into "run `reposkein-indexer index` to migrate". */
40
+ legacyFilePresent: boolean;
41
+ }
42
+ export declare function summariesDir(repoPath: string): string;
43
+ /** The pre-sharding committed file. Still read (dual-read) for one release. */
44
+ export declare function legacySummariesPath(repoPath: string): string;
45
+ export declare function conflictsPath(repoPath: string): string;
46
+ /** Mirrors the Rust `is_shard_file_name`: exactly two lowercase hex chars. */
47
+ export declare function isShardFileName(name: string): boolean;
48
+ /** A line git left behind from an unresolved textual merge. A canonical
49
+ * summary line always starts with `{`, so a marker run is damage, not data. */
50
+ export declare function isConflictMarker(line: string): boolean;
51
+ /** Whether these props carry an authored summary worth keeping.
52
+ *
53
+ * Deliberately identical to the Rust `has_summary`. The two languages read the
54
+ * same committed shards, so a record one accepts and the other rejects is a
55
+ * divergence between what the server serves and what the indexer writes — and
56
+ * a record with nothing in it must never displace one that has prose, in
57
+ * either language. Pinned by the `null-valued`/`non-string` fixture vectors. */
58
+ export declare function hasSummary(props: Record<string, unknown>): boolean;
59
+ /** Deterministic winner between two records claiming the same node id, when
60
+ * nothing is known about where either came from:
61
+ * newer `summary_at`, then smaller raw line by UTF-8 byte order. Both are
62
+ * pure functions of bytes on disk, so every machine — and the Rust indexer —
63
+ * picks the same winner without coordinating. Kept in lockstep with the Rust
64
+ * `beats` by the shared vectors in `fixtures/summary-shard-vectors.json`.
65
+ *
66
+ * This is the rule for records found in the SAME source (two lines of one
67
+ * merged shard, two agents' sidecars). Across sources of known provenance,
68
+ * use `supersedes`. */
69
+ export declare function beats(candidate: SummaryShardRecord, incumbent: SummaryShardRecord): boolean;
70
+ /** Whether a record from a strictly NEWER-provenance source displaces one
71
+ * already held from an older source.
72
+ *
73
+ * `summary_at` is day-precision (PRD §6.2.5 keeps wall-clock time out of
74
+ * committed bytes), so re-summarising a node already summarised today TIES.
75
+ * Resolving that tie by byte order would silently discard the rewrite. A
76
+ * newer source therefore wins unless it is *strictly older* — which is the
77
+ * case that matters, because that is a stale local record about to clobber a
78
+ * teammate's newer one. Mirrors the Rust `supersedes`. */
79
+ export declare function supersedes(candidate: SummaryShardRecord, incumbent: SummaryShardRecord): boolean;
80
+ export interface SummaryAccumulator {
81
+ summaries: Map<string, SummaryShardRecord>;
82
+ conflicts: SummaryShardRecord[];
83
+ warnings: string[];
84
+ }
85
+ export declare function emptyAccumulator(): SummaryAccumulator;
86
+ /** Folds a whole source of strictly NEWER provenance into `acc`. Mirrors the
87
+ * Rust `absorb_source`: displacement uses `supersedes`, and every displaced
88
+ * record is preserved. */
89
+ export declare function absorbSummarySource(acc: SummaryAccumulator, other: SummaryAccumulator): void;
90
+ /** Folds one file's text in, resolving same-source records with `beats`.
91
+ * Exported for the shared vectors. */
92
+ export declare function absorbSummaryLines(acc: SummaryAccumulator, source: string, text: string): void;
93
+ /** Reads every committed shard plus the pre-sharding file, tolerantly.
94
+ * A missing directory is the normal state for a repo with no authored prose,
95
+ * and returns an empty result rather than an error. */
96
+ export declare function loadSummaryShards(repoPath: string): LoadedSummaryShards;
97
+ /** A cheap fingerprint of everything the summary overlay reads on disk.
98
+ *
99
+ * The store keys its reload on this, so a `git pull` that brings in a
100
+ * teammate's shards — or another agent's sidecar write on this machine — is
101
+ * visible without restarting the process. Stat calls only, O(shards +
102
+ * sidecars), no content read, because this runs on every read and not just on
103
+ * change. mtime alone is not enough on filesystems with coarse timestamps, so
104
+ * size rides along.
105
+ *
106
+ * `sidecarFiles` is injected rather than imported to keep this module free of
107
+ * a cycle with sidecar.ts, which imports the fold from here. */
108
+ export declare function summaryShardsStamp(repoPath: string, sidecarFiles?: (repoPath: string) => string[]): string;
109
+ /** Merges divergence losers into the git-ignored `local/conflicts.jsonl`.
110
+ *
111
+ * Deduped and sorted, so repeated reads converge instead of growing the file,
112
+ * and never truncating: a loser recorded by an earlier run (or by the indexer)
113
+ * stays. Best-effort — failing to record a conflict must not break a tool
114
+ * call. Mirrors the Rust `conflicts_to_jsonl`. */
115
+ export declare function recordSummaryConflicts(repoPath: string, losers: readonly SummaryShardRecord[]): void;
116
+ /** How many divergence losers are on record. Doctor surfaces this. */
117
+ export declare function countRecordedConflicts(repoPath: string): number;