devsmind-mcp 2.4.0 → 4.0.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 (135) hide show
  1. package/README.md +317 -234
  2. package/dist/cli/activity.d.ts +9 -0
  3. package/dist/cli/activity.js +69 -0
  4. package/dist/cli/activity.js.map +1 -0
  5. package/dist/cli/describe.d.ts +31 -0
  6. package/dist/cli/describe.js +201 -0
  7. package/dist/cli/describe.js.map +1 -0
  8. package/dist/cli/diff.d.ts +14 -0
  9. package/dist/cli/diff.js +161 -0
  10. package/dist/cli/diff.js.map +1 -0
  11. package/dist/cli/embed.d.ts +6 -0
  12. package/dist/cli/embed.js +109 -0
  13. package/dist/cli/embed.js.map +1 -0
  14. package/dist/cli/extract-agent.d.ts +66 -0
  15. package/dist/cli/extract-agent.js +192 -0
  16. package/dist/cli/extract-agent.js.map +1 -0
  17. package/dist/cli/feedback.d.ts +11 -0
  18. package/dist/cli/feedback.js +90 -0
  19. package/dist/cli/feedback.js.map +1 -0
  20. package/dist/cli/index.js +139 -9
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/cli/init.d.ts +30 -0
  23. package/dist/cli/init.js +89 -18
  24. package/dist/cli/init.js.map +1 -1
  25. package/dist/cli/integrations/memory-topics.d.ts +55 -0
  26. package/dist/cli/integrations/memory-topics.js +318 -0
  27. package/dist/cli/integrations/memory-topics.js.map +1 -0
  28. package/dist/cli/integrations/memory.d.ts +17 -6
  29. package/dist/cli/integrations/memory.js +52 -139
  30. package/dist/cli/integrations/memory.js.map +1 -1
  31. package/dist/cli/integrations/prompt.d.ts +8 -0
  32. package/dist/cli/integrations/prompt.js +24 -3
  33. package/dist/cli/integrations/prompt.js.map +1 -1
  34. package/dist/cli/integrations/registry.d.ts +26 -9
  35. package/dist/cli/integrations/registry.js +61 -30
  36. package/dist/cli/integrations/registry.js.map +1 -1
  37. package/dist/cli/llm-client.d.ts +116 -0
  38. package/dist/cli/llm-client.js +322 -0
  39. package/dist/cli/llm-client.js.map +1 -0
  40. package/dist/cli/rule.d.ts +22 -2
  41. package/dist/cli/rule.js +110 -34
  42. package/dist/cli/rule.js.map +1 -1
  43. package/dist/cli/runner.d.ts +21 -0
  44. package/dist/cli/runner.js +94 -276
  45. package/dist/cli/runner.js.map +1 -1
  46. package/dist/cli/sync-progress.d.ts +9 -0
  47. package/dist/cli/sync-progress.js +41 -0
  48. package/dist/cli/sync-progress.js.map +1 -0
  49. package/dist/cli/sync.js +5 -2
  50. package/dist/cli/sync.js.map +1 -1
  51. package/dist/cli/view.js +3 -2
  52. package/dist/cli/view.js.map +1 -1
  53. package/dist/cli/workflow.js +26 -20
  54. package/dist/cli/workflow.js.map +1 -1
  55. package/dist/db/activity-graph.d.ts +55 -0
  56. package/dist/db/activity-graph.js +314 -0
  57. package/dist/db/activity-graph.js.map +1 -0
  58. package/dist/db/activity.d.ts +221 -0
  59. package/dist/db/activity.js +348 -0
  60. package/dist/db/activity.js.map +1 -0
  61. package/dist/db/analyze.js +18 -6
  62. package/dist/db/analyze.js.map +1 -1
  63. package/dist/db/database.d.ts +654 -102
  64. package/dist/db/database.js +1911 -558
  65. package/dist/db/database.js.map +1 -1
  66. package/dist/db/edges.d.ts +24 -0
  67. package/dist/db/edges.js +84 -0
  68. package/dist/db/edges.js.map +1 -1
  69. package/dist/db/embedder.d.ts +39 -0
  70. package/dist/db/embedder.js +0 -0
  71. package/dist/db/embedder.js.map +1 -0
  72. package/dist/db/feedback.d.ts +128 -0
  73. package/dist/db/feedback.js +182 -0
  74. package/dist/db/feedback.js.map +1 -0
  75. package/dist/db/file-diff.d.ts +32 -0
  76. package/dist/db/file-diff.js +110 -0
  77. package/dist/db/file-diff.js.map +1 -0
  78. package/dist/db/grep.d.ts +78 -0
  79. package/dist/db/grep.js +475 -0
  80. package/dist/db/grep.js.map +1 -0
  81. package/dist/db/index-build.d.ts +75 -0
  82. package/dist/db/index-build.js +177 -0
  83. package/dist/db/index-build.js.map +1 -0
  84. package/dist/db/message-revert.d.ts +63 -0
  85. package/dist/db/message-revert.js +258 -0
  86. package/dist/db/message-revert.js.map +1 -0
  87. package/dist/db/revert.d.ts +31 -0
  88. package/dist/db/revert.js +108 -0
  89. package/dist/db/revert.js.map +1 -0
  90. package/dist/db/schema.d.ts +49 -4
  91. package/dist/db/schema.js +125 -73
  92. package/dist/db/schema.js.map +1 -1
  93. package/dist/db/search-index.d.ts +65 -0
  94. package/dist/db/search-index.js +74 -0
  95. package/dist/db/search-index.js.map +1 -0
  96. package/dist/db/staging.d.ts +91 -5
  97. package/dist/db/staging.js +148 -22
  98. package/dist/db/staging.js.map +1 -1
  99. package/dist/mcp/server.d.ts +29 -7
  100. package/dist/mcp/server.js +2637 -878
  101. package/dist/mcp/server.js.map +1 -1
  102. package/dist/mcp/vendor/3d-force-graph.min.js +5 -0
  103. package/dist/mcp/vendor/force-graph.min.js +5 -0
  104. package/dist/mcp/vendor/model/model_int8.onnx +0 -0
  105. package/dist/mcp/vendor/model/vocab.txt +30522 -0
  106. package/dist/mcp/vendor/three.min.js +7 -0
  107. package/dist/mcp/view.css +419 -0
  108. package/dist/mcp/view.html +161 -0
  109. package/dist/mcp/view.js +245 -0
  110. package/dist/mcp/view_chat.js +382 -0
  111. package/dist/mcp/view_graph.js +576 -0
  112. package/dist/mcp/visualizer.d.ts +25 -2
  113. package/dist/mcp/visualizer.js +31 -4
  114. package/dist/mcp/visualizer.js.map +1 -1
  115. package/dist/utils/ast.d.ts +89 -0
  116. package/dist/utils/ast.js +378 -10
  117. package/dist/utils/ast.js.map +1 -1
  118. package/dist/utils/diff.d.ts +44 -0
  119. package/dist/utils/diff.js +78 -0
  120. package/dist/utils/diff.js.map +1 -0
  121. package/dist/utils/edit.d.ts +6 -0
  122. package/dist/utils/edit.js +47 -14
  123. package/dist/utils/edit.js.map +1 -1
  124. package/dist/utils/scanner.d.ts +6 -4
  125. package/dist/utils/scanner.js +18 -8
  126. package/dist/utils/scanner.js.map +1 -1
  127. package/dist/utils/tokenize.d.ts +45 -0
  128. package/dist/utils/tokenize.js +129 -0
  129. package/dist/utils/tokenize.js.map +1 -0
  130. package/dist/utils/version.d.ts +14 -0
  131. package/dist/utils/version.js +61 -0
  132. package/dist/utils/version.js.map +1 -0
  133. package/package.json +18 -5
  134. package/dist/mcp/visualizer_2d.html +0 -635
  135. package/dist/mcp/visualizer_3d.html +0 -613
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.GRAPH_SOURCE_CAVEATS = void 0;
37
+ exports.queryGraphActivityLog = queryGraphActivityLog;
38
+ exports.resolveActivityLog = resolveActivityLog;
39
+ const crypto = __importStar(require("crypto"));
40
+ const database_1 = require("./database");
41
+ const activity_1 = require("./activity");
42
+ /**
43
+ * The SHARED half of `get_activity_log` — "what changed" reconstructed from committed graph
44
+ * history instead of this machine's local message store.
45
+ *
46
+ * The local log (activity.ts) is gitignored by design: it holds verbatim request text and full
47
+ * before/after backups, neither of which belongs in a shared repo. The cost of that decision is
48
+ * that it is empty for everyone except the person who wrote it — a teammate who clones the repo,
49
+ * or the same developer on a second machine, asks "what changed here lately?" and gets nothing
50
+ * back, even though `.devmind/history/*.json` has been recording exactly that all along, committed
51
+ * and pulled.
52
+ *
53
+ * This module reconstructs per-commit entries from that shared record so the answer degrades
54
+ * instead of disappearing. It is a genuinely lossier view, and says so rather than papering over
55
+ * the difference — see {@link GRAPH_SOURCE_CAVEATS}.
56
+ *
57
+ * ── How a commit is recovered ────────────────────────────────────────────────
58
+ * History is stored per NODE, but `commitStagedChanges` hands one `reasoning` object to
59
+ * `updateHistory` for every node in the batch, so all of them come back carrying a byte-identical
60
+ * formatted block. Block text is therefore the primary commit identity.
61
+ *
62
+ * `session_id` is deliberately NOT part of that key, despite looking like the obvious
63
+ * discriminator. The 1-hour merge writes a later block into an EXISTING row and keeps that row's
64
+ * original session id (`writeHistoryToDisk(latest.id, …, latest.session_id, …)`), so within a
65
+ * single commit, a node whose row already existed reports the session that created it while a node
66
+ * getting a fresh row reports the current one. Keying on session would split one real commit in
67
+ * two — a strictly worse error than the one it would prevent.
68
+ *
69
+ * What separates two commits that happen to share reasoning text is TIME. Blocks are stamped
70
+ * inside `commitStagedChanges`'s synchronous per-node loop, so one commit's blocks land
71
+ * milliseconds apart, while a repeat of the same text is a separate editing act minutes or hours
72
+ * later. Grouping therefore cuts on the gap between CONSECUTIVE blocks ({@link COMMIT_CLUSTER_MS}),
73
+ * never on total span, so a slow many-node commit stays intact however long it ran end to end.
74
+ *
75
+ * The irreducible ambiguity: two sessions committing byte-identical reasoning within the same
76
+ * minute read as one commit. The shared record genuinely does not distinguish them.
77
+ */
78
+ /**
79
+ * Longest gap between consecutive same-reasoning blocks that still counts as one commit.
80
+ *
81
+ * Sized to sit far above a per-node write (milliseconds) and far below any plausible re-run of the
82
+ * same reasoning text by a human or an agent. It bounds the gap between neighbours, never the
83
+ * group's total duration.
84
+ */
85
+ const COMMIT_CLUSTER_MS = 60_000;
86
+ /** Everything the shared record cannot answer, stated once and attached to any graph-backed
87
+ * response. Callers act on this data (reverting, attributing, scoping a test pass), so the gaps
88
+ * have to travel with it — a silently thinner entry reads as an authoritative one. */
89
+ exports.GRAPH_SOURCE_CAVEATS = [
90
+ 'status is always "applied": revert state lives in the local log only, so a reverted change still appears here as applied.',
91
+ 'request is the reasoning block\'s Requirement field, not the verbatim user request — that text is never committed.',
92
+ 'files/node_ids cover only nodes tracked in the graph; whole-file edits that traced to no node are absent.',
93
+ 'developer is null for commits made before a DEVELOPER_NAME was configured.',
94
+ 'session_id names the session that created the history row, which the 1-hour merge can attribute to an earlier session than the one that wrote the change.'
95
+ ];
96
+ /** Stable, content-derived id — the same commit must get the same id on every machine that pulls
97
+ * it, which rules out a random uuid. Prefixed so a graph id is never mistaken for a local message
98
+ * id by a caller that then tries to `readMessage` it. */
99
+ function graphEntryId(startedAt, blockText) {
100
+ const hash = crypto.createHash('sha1').update(`${startedAt}\n${blockText}`).digest('hex');
101
+ return `graph:${hash.slice(0, 32)}`;
102
+ }
103
+ /**
104
+ * Rebuilds per-commit activity entries from committed history.
105
+ *
106
+ * The date bounds are applied twice on purpose: {@link DevMindDatabase.queryHistoryForActivity}
107
+ * over-selects rows whose accumulation span merely OVERLAPS the window (it cannot see inside the
108
+ * blob), then each block is tested against its own timestamp here. Filtering only in SQL would
109
+ * either drop in-window blocks appended to an older row, or admit out-of-window ones from a row
110
+ * that stayed active — both wrong in a way the caller could not detect.
111
+ */
112
+ function queryGraphActivityLog(db, opts = {}) {
113
+ const sinceIso = opts.since ?? (opts.sinceHours !== undefined
114
+ ? new Date(Date.now() - opts.sinceHours * 3600_000).toISOString()
115
+ : undefined);
116
+ const scanLimit = opts.historyScanLimit ?? 5000;
117
+ const rows = db.queryHistoryForActivity({
118
+ sessionId: opts.sessionId,
119
+ since: sinceIso,
120
+ until: opts.until,
121
+ limit: scanLimit
122
+ });
123
+ const byText = new Map();
124
+ for (const row of rows) {
125
+ for (const block of (0, database_1.parseReasoningBlocksTimed)(row.reasoning, row.created_at)) {
126
+ if (sinceIso && block.at < sinceIso)
127
+ continue;
128
+ if (opts.until && block.at > opts.until)
129
+ continue;
130
+ const item = { row, at: block.at, parsed: block.parsed };
131
+ const bucket = byText.get(block.text);
132
+ if (bucket)
133
+ bucket.push(item);
134
+ else
135
+ byText.set(block.text, [item]);
136
+ }
137
+ }
138
+ // Pass 2 — cut each bucket into commits wherever consecutive blocks fall more than
139
+ // COMMIT_CLUSTER_MS apart. The ascending sort makes `last_at` a running maximum, so the gap test
140
+ // only ever measures against the nearest earlier block, never the group's total span.
141
+ const groups = [];
142
+ for (const [text, items] of byText) {
143
+ items.sort((a, b) => a.at.localeCompare(b.at));
144
+ let group = null;
145
+ for (const item of items) {
146
+ if (!group || Date.parse(item.at) - Date.parse(group.last_at) > COMMIT_CLUSTER_MS) {
147
+ const p = item.parsed;
148
+ group = {
149
+ session_ids: [], seenSessions: new Set(),
150
+ block_text: text,
151
+ developer: p.developer || null,
152
+ // The closest the shared record gets to "what was asked for". The verbatim request is
153
+ // local-only, so this is a substitute, not the same field — flagged in the caveats.
154
+ request: p.requirement || null,
155
+ summary: p.what_changed || p.why || p.goal || '(no summary recorded)',
156
+ first_at: item.at,
157
+ last_at: item.at,
158
+ files: [], node_ids: [], seenFiles: new Set(), seenNodes: new Set(),
159
+ edit_count: 0
160
+ };
161
+ groups.push(group);
162
+ }
163
+ group.last_at = item.at;
164
+ if (!group.seenSessions.has(item.row.session_id)) {
165
+ group.seenSessions.add(item.row.session_id);
166
+ group.session_ids.push(item.row.session_id);
167
+ }
168
+ if (!group.seenNodes.has(item.row.node_id)) {
169
+ group.seenNodes.add(item.row.node_id);
170
+ group.node_ids.push(item.row.node_id);
171
+ }
172
+ // Null when the node was hard-deleted out from under its history. Skipped rather than
173
+ // recorded as an empty path, but the row still counts toward edit_count: something was
174
+ // edited, we just can no longer say in which file.
175
+ if (item.row.file_path && !group.seenFiles.has(item.row.file_path)) {
176
+ group.seenFiles.add(item.row.file_path);
177
+ group.files.push(item.row.file_path);
178
+ }
179
+ group.edit_count++;
180
+ }
181
+ }
182
+ let entries = groups.map(g => ({
183
+ // Keyed on the group's start time as well as its text: two separate commits that share
184
+ // reasoning must not collide on one id, and `first_at` is exactly what told them apart.
185
+ id: graphEntryId(g.first_at, g.block_text),
186
+ source: 'graph',
187
+ session_id: g.session_ids[0],
188
+ session_ids: g.session_ids,
189
+ developer: g.developer,
190
+ created_at: g.first_at,
191
+ updated_at: g.last_at,
192
+ request: g.request,
193
+ summary: g.summary,
194
+ // Committed history records no revert state — that is a local-log concept. Claiming anything
195
+ // other than "applied" here would be invention; the caveats say so out loud.
196
+ status: 'applied',
197
+ files: g.files,
198
+ node_ids: g.node_ids,
199
+ edit_count: g.edit_count
200
+ }));
201
+ // The same predicates the local path applies, over the same fields — so a filter means the same
202
+ // thing regardless of which store answered.
203
+ if (opts.developer) {
204
+ const needle = opts.developer.toLowerCase();
205
+ entries = entries.filter(e => (e.developer ?? '').toLowerCase().includes(needle));
206
+ }
207
+ if (opts.requirementContains) {
208
+ const needle = opts.requirementContains.toLowerCase();
209
+ entries = entries.filter(e => (e.request ?? '').toLowerCase().includes(needle) || e.summary.toLowerCase().includes(needle));
210
+ }
211
+ entries.sort((a, b) => b.created_at.localeCompare(a.created_at));
212
+ const matched = entries.length;
213
+ const limited = entries.slice(0, opts.limit ?? 100);
214
+ const allFiles = new Set();
215
+ for (const e of limited)
216
+ for (const f of e.files)
217
+ allFiles.add(f);
218
+ return {
219
+ result: {
220
+ total_messages: limited.length,
221
+ total_matched: matched,
222
+ all_files: Array.from(allFiles),
223
+ entries: limited
224
+ },
225
+ scanTruncated: rows.length >= scanLimit
226
+ };
227
+ }
228
+ /**
229
+ * The single read path behind `get_activity_log` — picks a store, or merges both.
230
+ *
231
+ * `auto` (the default) answers from the local log and falls through to shared history only when it
232
+ * produced nothing. That covers the case the local-only design silently failed: a teammate on a
233
+ * fresh clone, or this developer on another machine.
234
+ *
235
+ * It does NOT cover the inverse, which is why `both` exists — once you have any local activity of
236
+ * your own, `auto` stops short and you never see a teammate's work at all. `both` is the honest
237
+ * team-wide view: local entries in full fidelity, plus shared history for every commit that did not
238
+ * happen here.
239
+ */
240
+ function resolveActivityLog(db, devmindPath, mode, opts = {}) {
241
+ const withCaveats = (r) => r.entries.some(e => e.source === 'graph') ? { ...r, caveats: exports.GRAPH_SOURCE_CAVEATS } : r;
242
+ if (mode === 'local') {
243
+ return { ...(0, activity_1.queryActivityLog)(devmindPath, opts), source: 'local', fell_back: false };
244
+ }
245
+ if (mode === 'graph') {
246
+ const { result, scanTruncated } = queryGraphActivityLog(db, opts);
247
+ return withCaveats({ ...result, source: 'graph', fell_back: false, history_scan_truncated: scanTruncated });
248
+ }
249
+ if (mode === 'auto') {
250
+ const local = (0, activity_1.queryActivityLog)(devmindPath, opts);
251
+ if (local.entries.length > 0) {
252
+ return { ...local, source: 'local', fell_back: false };
253
+ }
254
+ const { result, scanTruncated } = queryGraphActivityLog(db, opts);
255
+ return withCaveats({ ...result, source: 'graph', fell_back: true, history_scan_truncated: scanTruncated });
256
+ }
257
+ // mode === 'both'
258
+ const local = (0, activity_1.queryActivityLog)(devmindPath, opts);
259
+ const { result: graph, scanTruncated } = queryGraphActivityLog(db, opts);
260
+ // Read once per call, not per entry: sessions.json can gain a row while this server is running,
261
+ // so it must not be cached across calls, but it also cannot change midway through one.
262
+ const localSessionIds = new Set((0, activity_1.readSessions)(devmindPath).map(s => s.id));
263
+ const me = db.getDeveloperName();
264
+ const foreign = graph.entries.filter(e => !isOwnWork(e, localSessionIds, me));
265
+ const merged = [...local.entries, ...foreign]
266
+ .sort((a, b) => b.created_at.localeCompare(a.created_at))
267
+ .slice(0, opts.limit ?? 100);
268
+ const allFiles = new Set();
269
+ for (const e of merged)
270
+ for (const f of e.files)
271
+ allFiles.add(f);
272
+ return withCaveats({
273
+ total_messages: merged.length,
274
+ total_matched: local.total_matched + foreign.length,
275
+ all_files: Array.from(allFiles),
276
+ entries: merged,
277
+ source: 'both',
278
+ fell_back: false,
279
+ history_scan_truncated: scanTruncated
280
+ });
281
+ }
282
+ /**
283
+ * True when a graph entry describes work that already appears in this machine's local log, and
284
+ * would therefore be a duplicate in a merged view.
285
+ *
286
+ * Both conditions are required, and each covers a hole the other leaves:
287
+ *
288
+ * - The session must be one of THIS machine's. Sessions are minted per machine, so a teammate's id
289
+ * never collides. Checked against `sessions.json` rather than against the local entries actually
290
+ * returned, because those are already narrowed by the caller's filters — a message excluded by a
291
+ * date range would stop shadowing its own graph twin and the duplicate would slip through.
292
+ *
293
+ * - The developer must not be someone else. This is what the session test alone gets wrong: the
294
+ * 1-hour merge stamps a block with the row's ORIGINAL session, so a teammate who pulls your
295
+ * history and edits the same node within the window gets their work filed under your session id.
296
+ * Unlike session_id, the Developer field is written per block, so it survives the merge intact.
297
+ *
298
+ * The asymmetry is deliberate. Failing to drop a duplicate shows your own work twice, which is
299
+ * visible and harmless; dropping too much silently hides a teammate's commit, which is the exact
300
+ * failure `both` exists to fix. When the two signals disagree, keep the entry.
301
+ */
302
+ function isOwnWork(entry, localSessionIds, me) {
303
+ const ids = entry.session_ids || [entry.session_id];
304
+ if (!ids.some(id => localSessionIds.has(id)))
305
+ return false;
306
+ // An unattributed entry on a local session is mine — nothing contradicts the session match.
307
+ if (!entry.developer)
308
+ return true;
309
+ // A NAMED developer must actually be me. Notably not "me is unknown, so assume mine": with no
310
+ // DEVELOPER_NAME configured there is no identity to match against, and treating every named
311
+ // commit on a shared session as local is how a teammate's work would disappear.
312
+ return !!me && entry.developer.toLowerCase() === me.toLowerCase();
313
+ }
314
+ //# sourceMappingURL=activity-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity-graph.js","sourceRoot":"","sources":["../../src/db/activity-graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,221 @@
1
+ /**
2
+ * The local, gitignored activity log — sessions and messages, grouped for the Activity page.
3
+ *
4
+ * Everything here lives under `.devmind/local/`, which `devsmind init` adds to `.devmind/.gitignore`
5
+ * (see [[cli/init.ts]]). It never gets pushed: message text is only ever meaningful on the machine
6
+ * that wrote it, and the point of this store is to be a working-tree time machine for one
7
+ * developer, not a second copy of the shared graph. Committed history in `.devmind/history/*.json`
8
+ * is untouched by anything in this file — a message's `edits[]` is a self-contained backup, not a
9
+ * reference into committed data, so revert never depends on (or corrupts) what the team shares.
10
+ *
11
+ * JSON on disk, one file per message plus one sessions index — same shape of storage as staging.ts,
12
+ * same atomic temp+rename write, deliberately no new SQLite tables: this data has no reason to
13
+ * survive a `brain.db` rebuild via anything other than being read back from these files.
14
+ */
15
+ /**
16
+ * `'partial'` means some but not all of a message's edits are reverted — the result of reverting
17
+ * one file, or one single edit, within a message rather than the whole thing. Whole-message
18
+ * revert/un-revert (message-revert.ts) still exists and still flips every edit at once; this
19
+ * status is what lets the UI (and the un-revert ordering rule) tell "fully reverted" apart from
20
+ * "partially reverted via a more surgical action."
21
+ */
22
+ export type MessageStatus = 'applied' | 'partial' | 'reverted';
23
+ export interface MessageEdit {
24
+ /** Stable identity for this one edit — what a single-edit revert/un-revert targets. */
25
+ id: string;
26
+ node_id: string;
27
+ file_path: string;
28
+ at: string;
29
+ before: string;
30
+ after: string;
31
+ /** True once this specific edit has been reverted (via any granularity). Absent/false = applied. */
32
+ reverted?: boolean;
33
+ }
34
+ /** A message's status is derived from its edits, not tracked independently — this is the one
35
+ * place that mapping happens, so whole-message and granular (file/edit) revert paths can't drift
36
+ * out of sync with each other. */
37
+ export declare function deriveStatus(edits: MessageEdit[]): MessageStatus;
38
+ export interface ActivityMessage {
39
+ id: string;
40
+ session_id: string;
41
+ developer: string | null;
42
+ created_at: string;
43
+ updated_at: string;
44
+ /** The user's original request, when the caller supplied one. Null degrades to `summary` only. */
45
+ request: string | null;
46
+ /** Short machine-generated label (from the commit's reasoning), always present. */
47
+ summary: string;
48
+ status: MessageStatus;
49
+ edits: MessageEdit[];
50
+ /**
51
+ * Which of this message's edits have already been turned into workflow steps.
52
+ *
53
+ * Tracked as EDIT IDS rather than a "synced: true" flag because a message keeps growing after it
54
+ * is tagged — `recordMessage` appends edits to an existing message when consecutive commits share
55
+ * a request, and the revert engine rewrites `edits` in place. A boolean would permanently strand
56
+ * everything added after the moment it was set; a list of consumed ids means a re-sync is a true
57
+ * no-op and a message that grew produces a delta step.
58
+ */
59
+ workflow_sync?: {
60
+ workflow_id: string;
61
+ step_ids: string[];
62
+ synced_edit_ids: string[];
63
+ }[];
64
+ }
65
+ export interface ActivitySession {
66
+ id: string;
67
+ developer: string | null;
68
+ started_at: string;
69
+ last_active: string;
70
+ message_ids: string[];
71
+ /** Human-readable name for the session, set at `start_session` time. Shown on the Activity page. */
72
+ label?: string;
73
+ /**
74
+ * The workflow this session is currently working on, or absent/null when unbound.
75
+ *
76
+ * Deliberately LOCAL. A workflow is a shared record, but "which one am I on right now" is a
77
+ * property of the reader, not the thing being read — the previous design kept one global pointer
78
+ * that synced through git, so two sessions (or two teammates) silently took it from each other
79
+ * mid-work and logged their commits onto the wrong timeline.
80
+ */
81
+ workflow_id?: string | null;
82
+ }
83
+ /** Exported so other `.devmind/local/`-based stores (e.g. `feedback.ts`) share the same directory
84
+ * and gitignore convention instead of re-deriving it. */
85
+ export declare function localDir(devmindPath: string): string;
86
+ /**
87
+ * Collapse a `.gitignore` line to what git actually reads it as, so `local`, `local/`, `/local`
88
+ * and `/local/` all compare equal — they name the same directory. Exported because `devsmind
89
+ * init` tops the same file up from its own entry list, and the two must agree on "is this
90
+ * already ignored?": comparing raw strings meant a hand-written `local` went unrecognized and a
91
+ * second `local/` got appended underneath it on every run.
92
+ */
93
+ export declare function normalizeIgnoreEntry(line: string): string;
94
+ /** Exported for reuse by other `.devmind/local/` stores — see {@link localDir}. */
95
+ export declare function ensureGitignored(devmindPath: string): void;
96
+ export declare function readSessions(devmindPath: string): ActivitySession[];
97
+ /**
98
+ * The only way a session comes into being — called by the `start_session` tool. Idempotent: calling
99
+ * it again with an id that already exists (a client retrying a dropped response) just no-ops rather
100
+ * than clobbering `started_at` or the messages already linked to it.
101
+ */
102
+ export declare function createSession(devmindPath: string, sessionId: string, developer: string | null, label?: string): ActivitySession;
103
+ /**
104
+ * Binds this session to a workflow, or unbinds it when `workflowId` is null.
105
+ *
106
+ * Creates the session row if it does not exist yet, mirroring `createSession`'s upsert shape — an
107
+ * agent can legitimately bind before its first commit has created anything.
108
+ */
109
+ export declare function bindSessionWorkflow(devmindPath: string, sessionId: string, workflowId: string | null, developer?: string | null): ActivitySession;
110
+ /** The workflow this session is bound to right now, or null. */
111
+ export declare function readSessionWorkflow(devmindPath: string, sessionId: string): string | null;
112
+ /**
113
+ * The workflow most recently worked on from this machine — what a fresh session offers to continue.
114
+ *
115
+ * Derived, not stored: the newest session that carries a binding. A separate "last workflow"
116
+ * field would be a second copy of a fact already recorded here, and second copies drift — which is
117
+ * precisely the failure the global active pointer this replaces kept producing.
118
+ *
119
+ * `excludeSessionId` skips the session asking the question, so a session that already bound itself
120
+ * does not get offered its own binding back as a suggestion.
121
+ */
122
+ export declare function lastBoundWorkflowId(devmindPath: string, excludeSessionId?: string): string | null;
123
+ export declare function readMessage(devmindPath: string, messageId: string): ActivityMessage | null;
124
+ /** Every message, newest-created first — the read path for the Activity page and the CLI. */
125
+ export declare function listMessages(devmindPath: string): ActivityMessage[];
126
+ /**
127
+ * Where one activity entry was reconstructed from.
128
+ *
129
+ * `'local'` is this machine's message store — verbatim request text, per-edit before/after, and a
130
+ * live revert status. `'graph'` is the committed history every teammate shares, which carries less
131
+ * (see db/activity-graph.ts) but is the ONLY thing a fresh clone has. Reported per entry so a
132
+ * merged listing never leaves the caller guessing which fields are trustworthy.
133
+ */
134
+ export type ActivitySource = 'local' | 'graph';
135
+ /** One message's changes, shaped for `get_activity_log` — the per-commit "what changed" view. */
136
+ export interface ActivityLogEntry {
137
+ id: string;
138
+ /** Always `'local'` from this file; graph-derived entries are built in db/activity-graph.ts. */
139
+ source: ActivitySource;
140
+ session_id: string;
141
+ /** Graph-derived entries only. Committed history stores the session that CREATED each row, not
142
+ * the one that wrote each block (the 1-hour merge reuses the original), so one reconstructed
143
+ * commit can legitimately cite more than one — `session_id` is just the first of these. Absent
144
+ * on local entries, where a message belongs to exactly one session. */
145
+ session_ids?: string[];
146
+ developer: string | null;
147
+ created_at: string;
148
+ updated_at: string;
149
+ request: string | null;
150
+ summary: string;
151
+ status: MessageStatus;
152
+ /** Deduped, first-touched order — every file this message touched. This is the field the old
153
+ * `get_developer_activity`/`get_changes_by_requirement` never carried (only `get_recent_changes`
154
+ * did, and only without a developer filter) — the actual gap this tool closes. */
155
+ files: string[];
156
+ /** Deduped graph node ids this message touched. */
157
+ node_ids: string[];
158
+ edit_count: number;
159
+ }
160
+ export interface ActivityLogResult {
161
+ /** How many entries this response actually carries — i.e. after `limit` was applied. */
162
+ total_messages: number;
163
+ /** How many entries MATCHED the filters, before `limit` truncated them. Without this,
164
+ * `total_messages` alone makes a capped result indistinguishable from a complete one — the same
165
+ * honesty contract `nodes_total`/`getHistoryPage().total` keep elsewhere. */
166
+ total_matched: number;
167
+ /** Every distinct file touched across ALL returned entries, flattened — "give me every file
168
+ * that changed" in one list, e.g. to scope a test-writing pass, without walking `entries`
169
+ * yourself. */
170
+ all_files: string[];
171
+ entries: ActivityLogEntry[];
172
+ }
173
+ /**
174
+ * Queries the local activity log — the one place `developer`, a time window, a specific session,
175
+ * AND the actual files touched all already live together (`ActivityMessage`/`MessageEdit`, see
176
+ * this file's module doc). This is what `get_activity_log` (MCP) wraps; it replaces three
177
+ * narrower, non-composable tools that each only covered one dimension: `get_recent_changes` had
178
+ * dates + files but no developer filter, `get_developer_activity` had a developer filter but no
179
+ * date range and no files, `get_changes_by_requirement` had neither dates nor files. All three
180
+ * filters compose here (AND'd together) since they're all just predicates over the same message
181
+ * list.
182
+ */
183
+ export declare function queryActivityLog(devmindPath: string, opts?: {
184
+ /** Case-insensitive substring match against `developer`. */
185
+ developer?: string;
186
+ sessionId?: string;
187
+ /** Lookback window in hours — convenience for "the last N hours/days". Ignored when `since`
188
+ * is also given. */
189
+ sinceHours?: number;
190
+ /** ISO timestamp lower bound (inclusive) — takes priority over `sinceHours` when both given. */
191
+ since?: string;
192
+ /** ISO timestamp upper bound (inclusive). */
193
+ until?: string;
194
+ /** Case-insensitive substring match against `request` OR `summary` — the requirement/ticket
195
+ * lookup `get_changes_by_requirement` used to do, now against the actual verbatim user
196
+ * request text rather than a reasoning-block LIKE search. */
197
+ requirementContains?: string;
198
+ limit?: number;
199
+ }): ActivityLogResult;
200
+ export interface RecordMessageParams {
201
+ session_id: string;
202
+ developer: string | null;
203
+ /** Explicit continuation target — set when the caller already knows which message this extends. */
204
+ message_id?: string;
205
+ /** The user's original request text, when the caller has it. */
206
+ request?: string | null;
207
+ summary: string;
208
+ edits: MessageEdit[];
209
+ }
210
+ /**
211
+ * Records a commit's edits against a message, creating or continuing one.
212
+ *
213
+ * Continuation, in priority order: an explicit `message_id`; else the session's newest message,
214
+ * when its `request` text matches this commit's (consecutive saves for the same ask collapse into
215
+ * one message); else a new message. A caller with no `request` at all always gets a new message
216
+ * per commit — there is nothing to match on, so grouping degrades to "one message per save" rather
217
+ * than guessing.
218
+ */
219
+ export declare function recordMessage(devmindPath: string, params: RecordMessageParams): ActivityMessage;
220
+ /** Persists a message after its status/edits were mutated in place (by the revert engine). */
221
+ export declare function saveMessage(devmindPath: string, message: ActivityMessage): void;