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.
- package/README.md +317 -234
- package/dist/cli/activity.d.ts +9 -0
- package/dist/cli/activity.js +69 -0
- package/dist/cli/activity.js.map +1 -0
- package/dist/cli/describe.d.ts +31 -0
- package/dist/cli/describe.js +201 -0
- package/dist/cli/describe.js.map +1 -0
- package/dist/cli/diff.d.ts +14 -0
- package/dist/cli/diff.js +161 -0
- package/dist/cli/diff.js.map +1 -0
- package/dist/cli/embed.d.ts +6 -0
- package/dist/cli/embed.js +109 -0
- package/dist/cli/embed.js.map +1 -0
- package/dist/cli/extract-agent.d.ts +66 -0
- package/dist/cli/extract-agent.js +192 -0
- package/dist/cli/extract-agent.js.map +1 -0
- package/dist/cli/feedback.d.ts +11 -0
- package/dist/cli/feedback.js +90 -0
- package/dist/cli/feedback.js.map +1 -0
- package/dist/cli/index.js +139 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +30 -0
- package/dist/cli/init.js +89 -18
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/integrations/memory-topics.d.ts +55 -0
- package/dist/cli/integrations/memory-topics.js +318 -0
- package/dist/cli/integrations/memory-topics.js.map +1 -0
- package/dist/cli/integrations/memory.d.ts +17 -6
- package/dist/cli/integrations/memory.js +52 -139
- package/dist/cli/integrations/memory.js.map +1 -1
- package/dist/cli/integrations/prompt.d.ts +8 -0
- package/dist/cli/integrations/prompt.js +24 -3
- package/dist/cli/integrations/prompt.js.map +1 -1
- package/dist/cli/integrations/registry.d.ts +26 -9
- package/dist/cli/integrations/registry.js +61 -30
- package/dist/cli/integrations/registry.js.map +1 -1
- package/dist/cli/llm-client.d.ts +116 -0
- package/dist/cli/llm-client.js +322 -0
- package/dist/cli/llm-client.js.map +1 -0
- package/dist/cli/rule.d.ts +22 -2
- package/dist/cli/rule.js +110 -34
- package/dist/cli/rule.js.map +1 -1
- package/dist/cli/runner.d.ts +21 -0
- package/dist/cli/runner.js +94 -276
- package/dist/cli/runner.js.map +1 -1
- package/dist/cli/sync-progress.d.ts +9 -0
- package/dist/cli/sync-progress.js +41 -0
- package/dist/cli/sync-progress.js.map +1 -0
- package/dist/cli/sync.js +5 -2
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/view.js +3 -2
- package/dist/cli/view.js.map +1 -1
- package/dist/cli/workflow.js +26 -20
- package/dist/cli/workflow.js.map +1 -1
- package/dist/db/activity-graph.d.ts +55 -0
- package/dist/db/activity-graph.js +314 -0
- package/dist/db/activity-graph.js.map +1 -0
- package/dist/db/activity.d.ts +221 -0
- package/dist/db/activity.js +348 -0
- package/dist/db/activity.js.map +1 -0
- package/dist/db/analyze.js +18 -6
- package/dist/db/analyze.js.map +1 -1
- package/dist/db/database.d.ts +654 -102
- package/dist/db/database.js +1911 -558
- package/dist/db/database.js.map +1 -1
- package/dist/db/edges.d.ts +24 -0
- package/dist/db/edges.js +84 -0
- package/dist/db/edges.js.map +1 -1
- package/dist/db/embedder.d.ts +39 -0
- package/dist/db/embedder.js +0 -0
- package/dist/db/embedder.js.map +1 -0
- package/dist/db/feedback.d.ts +128 -0
- package/dist/db/feedback.js +182 -0
- package/dist/db/feedback.js.map +1 -0
- package/dist/db/file-diff.d.ts +32 -0
- package/dist/db/file-diff.js +110 -0
- package/dist/db/file-diff.js.map +1 -0
- package/dist/db/grep.d.ts +78 -0
- package/dist/db/grep.js +475 -0
- package/dist/db/grep.js.map +1 -0
- package/dist/db/index-build.d.ts +75 -0
- package/dist/db/index-build.js +177 -0
- package/dist/db/index-build.js.map +1 -0
- package/dist/db/message-revert.d.ts +63 -0
- package/dist/db/message-revert.js +258 -0
- package/dist/db/message-revert.js.map +1 -0
- package/dist/db/revert.d.ts +31 -0
- package/dist/db/revert.js +108 -0
- package/dist/db/revert.js.map +1 -0
- package/dist/db/schema.d.ts +49 -4
- package/dist/db/schema.js +125 -73
- package/dist/db/schema.js.map +1 -1
- package/dist/db/search-index.d.ts +65 -0
- package/dist/db/search-index.js +74 -0
- package/dist/db/search-index.js.map +1 -0
- package/dist/db/staging.d.ts +91 -5
- package/dist/db/staging.js +148 -22
- package/dist/db/staging.js.map +1 -1
- package/dist/mcp/server.d.ts +29 -7
- package/dist/mcp/server.js +2637 -878
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/vendor/3d-force-graph.min.js +5 -0
- package/dist/mcp/vendor/force-graph.min.js +5 -0
- package/dist/mcp/vendor/model/model_int8.onnx +0 -0
- package/dist/mcp/vendor/model/vocab.txt +30522 -0
- package/dist/mcp/vendor/three.min.js +7 -0
- package/dist/mcp/view.css +419 -0
- package/dist/mcp/view.html +161 -0
- package/dist/mcp/view.js +245 -0
- package/dist/mcp/view_chat.js +382 -0
- package/dist/mcp/view_graph.js +576 -0
- package/dist/mcp/visualizer.d.ts +25 -2
- package/dist/mcp/visualizer.js +31 -4
- package/dist/mcp/visualizer.js.map +1 -1
- package/dist/utils/ast.d.ts +89 -0
- package/dist/utils/ast.js +378 -10
- package/dist/utils/ast.js.map +1 -1
- package/dist/utils/diff.d.ts +44 -0
- package/dist/utils/diff.js +78 -0
- package/dist/utils/diff.js.map +1 -0
- package/dist/utils/edit.d.ts +6 -0
- package/dist/utils/edit.js +47 -14
- package/dist/utils/edit.js.map +1 -1
- package/dist/utils/scanner.d.ts +6 -4
- package/dist/utils/scanner.js +18 -8
- package/dist/utils/scanner.js.map +1 -1
- package/dist/utils/tokenize.d.ts +45 -0
- package/dist/utils/tokenize.js +129 -0
- package/dist/utils/tokenize.js.map +1 -0
- package/dist/utils/version.d.ts +14 -0
- package/dist/utils/version.js +61 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +18 -5
- package/dist/mcp/visualizer_2d.html +0 -635
- package/dist/mcp/visualizer_3d.html +0 -613
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractFilesIntoGraph = extractFilesIntoGraph;
|
|
4
|
+
exports.pendingDescriptionNodes = pendingDescriptionNodes;
|
|
5
|
+
exports.resolveEdgesIncrementally = resolveEdgesIncrementally;
|
|
6
|
+
const indexer_1 = require("./indexer");
|
|
7
|
+
const ast_1 = require("../utils/ast");
|
|
8
|
+
const edges_1 = require("./edges");
|
|
9
|
+
/**
|
|
10
|
+
* How much of a node's code ships in an indexing batch response. Deliberately tighter than
|
|
11
|
+
* `describe.ts`'s 2000-char one-shot-LLM budget: the chat model driving in-chat indexing already
|
|
12
|
+
* has the surrounding conversation as context, so it needs less code per node to write a good
|
|
13
|
+
* description, and every char here is repeated once per node across a whole repo.
|
|
14
|
+
*/
|
|
15
|
+
const CODE_CHARS_PER_NODE = 1200;
|
|
16
|
+
/** Default per-call extraction budgets — whichever trips first ends the batch. Files are always
|
|
17
|
+
* processed to completion once started, so `last_file_indexed` never points mid-file. */
|
|
18
|
+
const DEFAULT_FILE_BUDGET = 40;
|
|
19
|
+
const DEFAULT_NODE_BUDGET = 25;
|
|
20
|
+
const DEFAULT_CHAR_BUDGET = 30000;
|
|
21
|
+
/** Default wall-clock budget for one `resolveEdgesIncrementally` call. */
|
|
22
|
+
const DEFAULT_EDGE_TIME_BUDGET_MS = 25000;
|
|
23
|
+
/**
|
|
24
|
+
* Extracts structure from `files` deterministically via local AST — no LLM, so nothing is
|
|
25
|
+
* silently missed or hallucinated the way whole-file-to-an-LLM extraction could be. Writes each
|
|
26
|
+
* candidate straight to the graph (`upsertNode` + `updateHistory`, deliberately with no
|
|
27
|
+
* `code_before` — this is an index snapshot, not an edit, so it gets no diff/revert, exactly
|
|
28
|
+
* like `devsmind index --run`'s own Phase 1 write in `runner.ts:781-802`). Stops once ANY of the
|
|
29
|
+
* three budgets trips, but always finishes the file it's mid-way through first — the AI's cursor
|
|
30
|
+
* (`files_done`/`last_file_indexed`) must only ever advance past a FULLY extracted file, or a
|
|
31
|
+
* later `index_continue` silently skips whatever was left unprocessed in it.
|
|
32
|
+
*
|
|
33
|
+
* Node ids use the same `${repoRelPath}#${qualified}` formula as `runner.ts:778-779` and
|
|
34
|
+
* `staging.ts`'s `resolveEntryId` — required so a node created here is the same node an
|
|
35
|
+
* `edit_node` on the same symbol later resolves to, not a duplicate.
|
|
36
|
+
*/
|
|
37
|
+
function extractFilesIntoGraph(db, files, opts = {}) {
|
|
38
|
+
const fileBudget = opts.fileBudget ?? DEFAULT_FILE_BUDGET;
|
|
39
|
+
const nodeBudget = opts.nodeBudget ?? DEFAULT_NODE_BUDGET;
|
|
40
|
+
const charBudget = opts.charBudget ?? DEFAULT_CHAR_BUDGET;
|
|
41
|
+
const codeCharsPerNode = opts.codeCharsPerNode ?? CODE_CHARS_PER_NODE;
|
|
42
|
+
const filesExtracted = [];
|
|
43
|
+
const nodes = [];
|
|
44
|
+
const fileImports = {};
|
|
45
|
+
let nodesCreated = 0;
|
|
46
|
+
let charsUsed = 0;
|
|
47
|
+
let cursor = null;
|
|
48
|
+
for (const filePath of files) {
|
|
49
|
+
if (filesExtracted.length > 0 &&
|
|
50
|
+
(filesExtracted.length >= fileBudget || nodes.length >= nodeBudget || charsUsed >= charBudget)) {
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
// Never throws: returns [] for a non-parseable extension, an unreadable file, or a parse
|
|
54
|
+
// failure — all three are legitimate "nothing to extract here" outcomes, not batch-aborting
|
|
55
|
+
// errors. A file that yields nothing still counts as fully processed below.
|
|
56
|
+
const candidates = (0, ast_1.enumerateFileCandidates)(filePath);
|
|
57
|
+
const repoRelPath = db.toRepoRelativePath(filePath);
|
|
58
|
+
for (const c of candidates) {
|
|
59
|
+
const nodeId = `${repoRelPath}#${c.qualified}`;
|
|
60
|
+
db.upsertNode({ id: nodeId, name: c.name, type: c.type, file_path: filePath, signature: c.signature });
|
|
61
|
+
db.updateHistory({
|
|
62
|
+
node_id: nodeId,
|
|
63
|
+
code_snapshot: c.codeSnapshot,
|
|
64
|
+
reasoning: {
|
|
65
|
+
what_changed: 'Initial code extraction during in-chat indexing',
|
|
66
|
+
why: 'Initial index setup',
|
|
67
|
+
goal: 'Establish baseline codebase knowledge graph',
|
|
68
|
+
model: 'ast'
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
nodesCreated++;
|
|
72
|
+
const truncated = c.codeSnapshot.length > codeCharsPerNode;
|
|
73
|
+
const code = truncated ? c.codeSnapshot.slice(0, codeCharsPerNode) : c.codeSnapshot;
|
|
74
|
+
charsUsed += code.length;
|
|
75
|
+
nodes.push({
|
|
76
|
+
node_id: nodeId,
|
|
77
|
+
name: c.name,
|
|
78
|
+
type: c.type,
|
|
79
|
+
signature: c.signature,
|
|
80
|
+
file_path: filePath,
|
|
81
|
+
start_line: c.startLine,
|
|
82
|
+
end_line: c.endLine,
|
|
83
|
+
exported: c.isExported,
|
|
84
|
+
code,
|
|
85
|
+
code_truncated: truncated
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (candidates.length > 0) {
|
|
89
|
+
const imports = (0, ast_1.listFileImports)(filePath).map(i => i.moduleSpecifier);
|
|
90
|
+
if (imports.length > 0)
|
|
91
|
+
fileImports[repoRelPath] = [...new Set(imports)];
|
|
92
|
+
}
|
|
93
|
+
filesExtracted.push(filePath);
|
|
94
|
+
cursor = filePath;
|
|
95
|
+
}
|
|
96
|
+
return { filesExtracted, nodesCreated, nodes, fileImports, cursor };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Nodes with no description yet, oldest-created first — the same `WHERE description IS NULL`
|
|
100
|
+
* predicate `describe.ts:67`'s backlog-clearing loop uses. Re-served by `index_continue` ahead of
|
|
101
|
+
* a fresh batch so a still-undescribed node from an earlier batch is never silently dropped just
|
|
102
|
+
* because the AI moved on to the next one; it grows the visible backlog instead of losing it.
|
|
103
|
+
*/
|
|
104
|
+
function pendingDescriptionNodes(db, limit) {
|
|
105
|
+
const pending = db.getAllNodes().filter(n => !n.deprecated && !n.description).slice(0, limit);
|
|
106
|
+
const nodes = [];
|
|
107
|
+
for (const n of pending) {
|
|
108
|
+
const latest = db.getLatestCode(n.id);
|
|
109
|
+
const full = latest?.code_snapshot ?? '';
|
|
110
|
+
const truncated = full.length > CODE_CHARS_PER_NODE;
|
|
111
|
+
nodes.push({
|
|
112
|
+
node_id: n.id,
|
|
113
|
+
name: n.name,
|
|
114
|
+
type: n.type,
|
|
115
|
+
signature: n.signature ?? null,
|
|
116
|
+
file_path: n.file_path,
|
|
117
|
+
code: truncated ? full.slice(0, CODE_CHARS_PER_NODE) : full,
|
|
118
|
+
code_truncated: truncated
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return nodes;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Resumable, time-budgeted full-graph connection pass — the same `phase`/`nodes_done`/
|
|
125
|
+
* `nodes_total` state machine `runner.ts:834-839` and `runner.ts:604-661` already use for the
|
|
126
|
+
* CLI's Phase 2. A node extracted in an early batch can be the TARGET of a node extracted much
|
|
127
|
+
* later, so edges are never resolved per-batch during extraction — only here, once, over the
|
|
128
|
+
* WHOLE graph, exactly as `runner.ts:1223-1231` documents for the same reason.
|
|
129
|
+
*
|
|
130
|
+
* Persists `nodes_done` to the scratchpad after every node (not just on return), so a crash mid
|
|
131
|
+
* -pass loses at most one node's edges, not the whole call. Missing-node fill only runs once the
|
|
132
|
+
* WHOLE pass completes within a single call — a `resolve` that keeps expiring its time budget
|
|
133
|
+
* across many `index_complete` calls will still get every edge, just not missing-node fill until
|
|
134
|
+
* the pass that finally finishes; this is the same characteristic `devsmind index --run` itself
|
|
135
|
+
* has across a restarted process, not a new limitation introduced here.
|
|
136
|
+
*/
|
|
137
|
+
function resolveEdgesIncrementally(db, devmindPath, pad, opts = {}) {
|
|
138
|
+
const deadline = Date.now() + (opts.timeBudgetMs ?? DEFAULT_EDGE_TIME_BUDGET_MS);
|
|
139
|
+
// Alias detection BEFORE the candidate pool is fetched, so this pass's own freshly-attached
|
|
140
|
+
// aliases (RTK Query generated hook names, etc.) are visible to the resolver below in the SAME
|
|
141
|
+
// pass rather than needing a second run — mirrors edges.ts's `resolveEdgesForNodes` ordering.
|
|
142
|
+
(0, edges_1.applyDeterministicAliases)(db, db.listNodes().map(n => n.id));
|
|
143
|
+
const allNodes = db.listNodes();
|
|
144
|
+
const allIds = new Set(allNodes.map(n => n.id));
|
|
145
|
+
if (pad.phase !== 2) {
|
|
146
|
+
pad.phase = 2;
|
|
147
|
+
pad.nodes_done = 0;
|
|
148
|
+
pad.nodes_total = allNodes.length;
|
|
149
|
+
pad.connections_created = 0;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
pad.nodes_total = allNodes.length;
|
|
153
|
+
}
|
|
154
|
+
const { missing, onMissing } = (0, edges_1.createMissingCollector)();
|
|
155
|
+
let i = pad.nodes_done;
|
|
156
|
+
for (; i < allNodes.length; i++) {
|
|
157
|
+
if (Date.now() > deadline)
|
|
158
|
+
break;
|
|
159
|
+
const node = allNodes[i];
|
|
160
|
+
const latest = db.getLatestCode(node.id);
|
|
161
|
+
if (latest?.code_snapshot && latest.code_snapshot.trim().length > 0) {
|
|
162
|
+
for (const targetId of (0, ast_1.resolveConnectionsLocally)(node.id, node.file_path, allNodes, devmindPath, onMissing)) {
|
|
163
|
+
if (allIds.has(targetId)) {
|
|
164
|
+
db.addConnection(node.id, targetId);
|
|
165
|
+
pad.connections_created++;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
pad.nodes_done = i + 1;
|
|
170
|
+
pad.updated_at = new Date().toISOString();
|
|
171
|
+
(0, indexer_1.writeScratchpad)(devmindPath, pad);
|
|
172
|
+
}
|
|
173
|
+
const done = i >= allNodes.length;
|
|
174
|
+
const missingFilled = done ? (0, edges_1.finalizeMissingNodes)(devmindPath, db, missing, { writeReport: false, quiet: true }) : 0;
|
|
175
|
+
return { done, nodesDone: pad.nodes_done, nodesTotal: pad.nodes_total, edgesAdded: pad.connections_created, missingFilled };
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=index-build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-build.js","sourceRoot":"","sources":["../../src/db/index-build.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export interface RevertOutcome {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
/** Messages actually flipped to 'reverted' this call, oldest-affected-first. */
|
|
4
|
+
reverted: string[];
|
|
5
|
+
/** Set when the cascade stopped before finishing everything requested. */
|
|
6
|
+
blocked_at?: {
|
|
7
|
+
message_id: string;
|
|
8
|
+
reason: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export interface UnrevertOutcome {
|
|
12
|
+
ok: boolean;
|
|
13
|
+
error?: string;
|
|
14
|
+
message_id?: string;
|
|
15
|
+
}
|
|
16
|
+
/** Outcome of reverting/un-reverting one file's edits within one message. */
|
|
17
|
+
export interface FileRevertOutcome {
|
|
18
|
+
ok: boolean;
|
|
19
|
+
error?: string;
|
|
20
|
+
/** Ids of the edits actually flipped this call. */
|
|
21
|
+
edit_ids?: string[];
|
|
22
|
+
}
|
|
23
|
+
/** Outcome of reverting/un-reverting exactly one edit. */
|
|
24
|
+
export interface EditRevertOutcome {
|
|
25
|
+
ok: boolean;
|
|
26
|
+
error?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Reverts `messageId` and every message after it that isn't already fully reverted — the stack
|
|
30
|
+
* model: later messages were built on top of earlier ones, so undoing one partway through the
|
|
31
|
+
* stack undoes everything above it too, newest first. Stops at the first message that can't be
|
|
32
|
+
* fully reverted (a drifted edit) and reports which; everything reverted before that point in
|
|
33
|
+
* this call stays reverted. A `partial` message (touched by an earlier file/edit-level revert)
|
|
34
|
+
* counts as "not yet fully reverted" and is swept up here too, finishing the job on it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function revertMessage(devmindPath: string, messageId: string): RevertOutcome;
|
|
37
|
+
/**
|
|
38
|
+
* Re-applies exactly one message: the OLDEST currently-**fully**-reverted one. Un-revert restores
|
|
39
|
+
* up the stack in the order things came off it — out-of-order restoration would recreate the same
|
|
40
|
+
* "later edit built on code that isn't there yet" hazard the revert direction guards against, so
|
|
41
|
+
* it's refused rather than attempted. A `partial` message wasn't produced by that cascade — it was
|
|
42
|
+
* touched by a surgical file/edit-level revert — so it can be un-reverted independently of the
|
|
43
|
+
* fully-reverted stack's ordering.
|
|
44
|
+
*/
|
|
45
|
+
export declare function unrevertMessage(devmindPath: string, messageId: string): UnrevertOutcome;
|
|
46
|
+
/**
|
|
47
|
+
* Reverts just the edits ONE file picked up within ONE message, leaving the message's other
|
|
48
|
+
* files untouched. Newest-edit-first within that file, atomic (rolls back this operation's own
|
|
49
|
+
* partial progress on failure, same as the whole-message path) — never leaves this one file
|
|
50
|
+
* half-reverted, though the message as a whole is expected to end up `partial`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function revertMessageFile(devmindPath: string, messageId: string, filePath: string): FileRevertOutcome;
|
|
53
|
+
/** Symmetric to revertMessageFile: oldest-edit-first, same atomic rollback. */
|
|
54
|
+
export declare function unrevertMessageFile(devmindPath: string, messageId: string, filePath: string): FileRevertOutcome;
|
|
55
|
+
/**
|
|
56
|
+
* Reverts exactly ONE edit, identified by its stable `id`, regardless of what else in the message
|
|
57
|
+
* or file is applied. Safe by construction: if a later edit (to the same node, in this message or
|
|
58
|
+
* a later one) already changed this text further, this edit's `after` string won't be found on
|
|
59
|
+
* disk anymore and the exact-match guard in applyEdit refuses rather than guessing.
|
|
60
|
+
*/
|
|
61
|
+
export declare function revertMessageEdit(devmindPath: string, messageId: string, editId: string): EditRevertOutcome;
|
|
62
|
+
/** Symmetric to revertMessageEdit. */
|
|
63
|
+
export declare function unrevertMessageEdit(devmindPath: string, messageId: string, editId: string): EditRevertOutcome;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.revertMessage = revertMessage;
|
|
4
|
+
exports.unrevertMessage = unrevertMessage;
|
|
5
|
+
exports.revertMessageFile = revertMessageFile;
|
|
6
|
+
exports.unrevertMessageFile = unrevertMessageFile;
|
|
7
|
+
exports.revertMessageEdit = revertMessageEdit;
|
|
8
|
+
exports.unrevertMessageEdit = unrevertMessageEdit;
|
|
9
|
+
const activity_1 = require("./activity");
|
|
10
|
+
const edit_1 = require("../utils/edit");
|
|
11
|
+
const ast_1 = require("../utils/ast");
|
|
12
|
+
/** All messages, oldest-created first — the timeline order the stack model operates over. */
|
|
13
|
+
function timeline(devmindPath) {
|
|
14
|
+
return (0, activity_1.listMessages)(devmindPath).slice().sort((a, b) => a.created_at.localeCompare(b.created_at));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Applies one edit's `from` -> `to` text via the same exact-match, atomic-write primitive
|
|
18
|
+
* `edit_node` uses. A failure here means the file has drifted since this edit was recorded — a
|
|
19
|
+
* hand-edit, a pull, a later message's edit to the same text, anything — and is reported rather
|
|
20
|
+
* than forced. This exact-match requirement is also what makes file-level and single-edit revert
|
|
21
|
+
* safe without any separate "is this the newest" bookkeeping: an edit whose recorded text isn't
|
|
22
|
+
* uniquely present anymore simply can't be applied, whatever the reason.
|
|
23
|
+
*/
|
|
24
|
+
function applyEdit(edit, from, to) {
|
|
25
|
+
if (from === to)
|
|
26
|
+
return { ok: true }; // a no-op edit (e.g. before === after) has nothing to write
|
|
27
|
+
const r = (0, edit_1.replaceTextInFile)(edit.file_path, from, to, false);
|
|
28
|
+
if (!r.ok)
|
|
29
|
+
return { ok: false, error: r.error };
|
|
30
|
+
(0, ast_1.invalidateParsedFile)(edit.file_path);
|
|
31
|
+
return { ok: true };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Reverts one message's edits as a single atomic unit: newest edit first (later edits to the same
|
|
35
|
+
* node were built on earlier ones, so they must come off in reverse), and if any edit can't be
|
|
36
|
+
* applied, everything from this same message that DID succeed in this attempt is rolled back
|
|
37
|
+
* immediately — a message is never left half-reverted by THIS call. (It can still end up
|
|
38
|
+
* `partial` from an earlier, separate file/edit-level revert — this function skips edits already
|
|
39
|
+
* in the target state rather than re-applying them, so it composes safely with that.)
|
|
40
|
+
*/
|
|
41
|
+
function revertOneMessageAtomically(message) {
|
|
42
|
+
const newestFirst = message.edits.slice().reverse();
|
|
43
|
+
const applied = [];
|
|
44
|
+
for (const edit of newestFirst) {
|
|
45
|
+
if (edit.reverted)
|
|
46
|
+
continue; // already reverted via a granular action — nothing to do
|
|
47
|
+
const r = applyEdit(edit, edit.after, edit.before);
|
|
48
|
+
if (!r.ok) {
|
|
49
|
+
// Undo in LIFO order — the most recently reverted edit must come off first. `applied` was
|
|
50
|
+
// built newest-edit-first, so `applied[0]` was reverted first and must be un-reverted LAST;
|
|
51
|
+
// reversing the array puts the most recent action first, exactly as a proper undo requires.
|
|
52
|
+
for (const undo of applied.slice().reverse()) {
|
|
53
|
+
applyEdit(undo, undo.before, undo.after); // best-effort; started from a known-good state
|
|
54
|
+
undo.reverted = false;
|
|
55
|
+
}
|
|
56
|
+
return { ok: false, error: `${edit.node_id}: ${r.error}` };
|
|
57
|
+
}
|
|
58
|
+
edit.reverted = true;
|
|
59
|
+
applied.push(edit);
|
|
60
|
+
}
|
|
61
|
+
return { ok: true };
|
|
62
|
+
}
|
|
63
|
+
/** Symmetric to revertOneMessageAtomically: oldest edit first, same rollback-on-failure guarantee. */
|
|
64
|
+
function unrevertOneMessageAtomically(message) {
|
|
65
|
+
const oldestFirst = message.edits;
|
|
66
|
+
const applied = [];
|
|
67
|
+
for (const edit of oldestFirst) {
|
|
68
|
+
if (!edit.reverted)
|
|
69
|
+
continue; // already applied — nothing to restore
|
|
70
|
+
const r = applyEdit(edit, edit.before, edit.after);
|
|
71
|
+
if (!r.ok) {
|
|
72
|
+
for (const undo of applied.slice().reverse()) {
|
|
73
|
+
applyEdit(undo, undo.after, undo.before);
|
|
74
|
+
undo.reverted = true;
|
|
75
|
+
}
|
|
76
|
+
return { ok: false, error: `${edit.node_id}: ${r.error}` };
|
|
77
|
+
}
|
|
78
|
+
edit.reverted = false;
|
|
79
|
+
applied.push(edit);
|
|
80
|
+
}
|
|
81
|
+
return { ok: true };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Reverts `messageId` and every message after it that isn't already fully reverted — the stack
|
|
85
|
+
* model: later messages were built on top of earlier ones, so undoing one partway through the
|
|
86
|
+
* stack undoes everything above it too, newest first. Stops at the first message that can't be
|
|
87
|
+
* fully reverted (a drifted edit) and reports which; everything reverted before that point in
|
|
88
|
+
* this call stays reverted. A `partial` message (touched by an earlier file/edit-level revert)
|
|
89
|
+
* counts as "not yet fully reverted" and is swept up here too, finishing the job on it.
|
|
90
|
+
*/
|
|
91
|
+
function revertMessage(devmindPath, messageId) {
|
|
92
|
+
const target = (0, activity_1.readMessage)(devmindPath, messageId);
|
|
93
|
+
if (!target)
|
|
94
|
+
return { ok: false, reverted: [], blocked_at: { message_id: messageId, reason: 'message not found' } };
|
|
95
|
+
if (target.status === 'reverted') {
|
|
96
|
+
return { ok: false, reverted: [], blocked_at: { message_id: messageId, reason: 'already reverted' } };
|
|
97
|
+
}
|
|
98
|
+
const all = timeline(devmindPath);
|
|
99
|
+
const targetIndex = all.findIndex(m => m.id === messageId);
|
|
100
|
+
// Newest-first: everything from the end of the timeline down to (and including) the target.
|
|
101
|
+
const candidates = all.slice(targetIndex).filter(m => m.status !== 'reverted').reverse();
|
|
102
|
+
const reverted = [];
|
|
103
|
+
for (const message of candidates) {
|
|
104
|
+
const result = revertOneMessageAtomically(message);
|
|
105
|
+
if (!result.ok) {
|
|
106
|
+
message.status = (0, activity_1.deriveStatus)(message.edits);
|
|
107
|
+
(0, activity_1.saveMessage)(devmindPath, message);
|
|
108
|
+
return { ok: reverted.length > 0, reverted, blocked_at: { message_id: message.id, reason: result.error } };
|
|
109
|
+
}
|
|
110
|
+
message.status = (0, activity_1.deriveStatus)(message.edits);
|
|
111
|
+
message.updated_at = new Date().toISOString();
|
|
112
|
+
(0, activity_1.saveMessage)(devmindPath, message);
|
|
113
|
+
reverted.push(message.id);
|
|
114
|
+
}
|
|
115
|
+
return { ok: true, reverted };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Re-applies exactly one message: the OLDEST currently-**fully**-reverted one. Un-revert restores
|
|
119
|
+
* up the stack in the order things came off it — out-of-order restoration would recreate the same
|
|
120
|
+
* "later edit built on code that isn't there yet" hazard the revert direction guards against, so
|
|
121
|
+
* it's refused rather than attempted. A `partial` message wasn't produced by that cascade — it was
|
|
122
|
+
* touched by a surgical file/edit-level revert — so it can be un-reverted independently of the
|
|
123
|
+
* fully-reverted stack's ordering.
|
|
124
|
+
*/
|
|
125
|
+
function unrevertMessage(devmindPath, messageId) {
|
|
126
|
+
const target = (0, activity_1.readMessage)(devmindPath, messageId);
|
|
127
|
+
if (!target)
|
|
128
|
+
return { ok: false, error: 'message not found' };
|
|
129
|
+
if (target.status === 'applied')
|
|
130
|
+
return { ok: false, error: 'message is not reverted — nothing to restore' };
|
|
131
|
+
if (target.status === 'reverted') {
|
|
132
|
+
const reverted = timeline(devmindPath).filter(m => m.status === 'reverted');
|
|
133
|
+
const oldest = reverted[0];
|
|
134
|
+
/* istanbul ignore next -- unreachable: `target.status === 'reverted'` (just checked above) means
|
|
135
|
+
`target` itself is in `timeline(devmindPath)` with status 'reverted', so `reverted` can never
|
|
136
|
+
be empty here. Kept as a real guard (not just a comment) in case that invariant ever breaks. */
|
|
137
|
+
if (!oldest)
|
|
138
|
+
return { ok: false, error: 'no reverted messages found' };
|
|
139
|
+
if (oldest.id !== messageId) {
|
|
140
|
+
return {
|
|
141
|
+
ok: false,
|
|
142
|
+
error: `un-revert must restore the oldest reverted message first: "${oldest.summary}" (${oldest.id}). Restore that one before this one.`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const result = unrevertOneMessageAtomically(target);
|
|
147
|
+
if (!result.ok) {
|
|
148
|
+
target.status = (0, activity_1.deriveStatus)(target.edits);
|
|
149
|
+
(0, activity_1.saveMessage)(devmindPath, target);
|
|
150
|
+
return { ok: false, error: result.error };
|
|
151
|
+
}
|
|
152
|
+
target.status = (0, activity_1.deriveStatus)(target.edits);
|
|
153
|
+
target.updated_at = new Date().toISOString();
|
|
154
|
+
(0, activity_1.saveMessage)(devmindPath, target);
|
|
155
|
+
return { ok: true, message_id: target.id };
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Reverts just the edits ONE file picked up within ONE message, leaving the message's other
|
|
159
|
+
* files untouched. Newest-edit-first within that file, atomic (rolls back this operation's own
|
|
160
|
+
* partial progress on failure, same as the whole-message path) — never leaves this one file
|
|
161
|
+
* half-reverted, though the message as a whole is expected to end up `partial`.
|
|
162
|
+
*/
|
|
163
|
+
function revertMessageFile(devmindPath, messageId, filePath) {
|
|
164
|
+
const message = (0, activity_1.readMessage)(devmindPath, messageId);
|
|
165
|
+
if (!message)
|
|
166
|
+
return { ok: false, error: 'message not found' };
|
|
167
|
+
const fileEdits = message.edits.filter(e => e.file_path === filePath && !e.reverted);
|
|
168
|
+
if (!fileEdits.length)
|
|
169
|
+
return { ok: false, error: 'no applied edits for this file in this message' };
|
|
170
|
+
const newestFirst = fileEdits.slice().reverse();
|
|
171
|
+
const applied = [];
|
|
172
|
+
for (const edit of newestFirst) {
|
|
173
|
+
const r = applyEdit(edit, edit.after, edit.before);
|
|
174
|
+
if (!r.ok) {
|
|
175
|
+
for (const undo of applied.slice().reverse()) {
|
|
176
|
+
applyEdit(undo, undo.before, undo.after);
|
|
177
|
+
undo.reverted = false;
|
|
178
|
+
}
|
|
179
|
+
return { ok: false, error: `${edit.node_id}: ${r.error}` };
|
|
180
|
+
}
|
|
181
|
+
edit.reverted = true;
|
|
182
|
+
applied.push(edit);
|
|
183
|
+
}
|
|
184
|
+
message.status = (0, activity_1.deriveStatus)(message.edits);
|
|
185
|
+
message.updated_at = new Date().toISOString();
|
|
186
|
+
(0, activity_1.saveMessage)(devmindPath, message);
|
|
187
|
+
return { ok: true, edit_ids: applied.map(e => e.id) };
|
|
188
|
+
}
|
|
189
|
+
/** Symmetric to revertMessageFile: oldest-edit-first, same atomic rollback. */
|
|
190
|
+
function unrevertMessageFile(devmindPath, messageId, filePath) {
|
|
191
|
+
const message = (0, activity_1.readMessage)(devmindPath, messageId);
|
|
192
|
+
if (!message)
|
|
193
|
+
return { ok: false, error: 'message not found' };
|
|
194
|
+
const fileEdits = message.edits.filter(e => e.file_path === filePath && e.reverted);
|
|
195
|
+
if (!fileEdits.length)
|
|
196
|
+
return { ok: false, error: 'no reverted edits for this file in this message' };
|
|
197
|
+
const applied = [];
|
|
198
|
+
for (const edit of fileEdits) {
|
|
199
|
+
const r = applyEdit(edit, edit.before, edit.after);
|
|
200
|
+
if (!r.ok) {
|
|
201
|
+
for (const undo of applied.slice().reverse()) {
|
|
202
|
+
applyEdit(undo, undo.after, undo.before);
|
|
203
|
+
undo.reverted = true;
|
|
204
|
+
}
|
|
205
|
+
return { ok: false, error: `${edit.node_id}: ${r.error}` };
|
|
206
|
+
}
|
|
207
|
+
edit.reverted = false;
|
|
208
|
+
applied.push(edit);
|
|
209
|
+
}
|
|
210
|
+
message.status = (0, activity_1.deriveStatus)(message.edits);
|
|
211
|
+
message.updated_at = new Date().toISOString();
|
|
212
|
+
(0, activity_1.saveMessage)(devmindPath, message);
|
|
213
|
+
return { ok: true, edit_ids: applied.map(e => e.id) };
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Reverts exactly ONE edit, identified by its stable `id`, regardless of what else in the message
|
|
217
|
+
* or file is applied. Safe by construction: if a later edit (to the same node, in this message or
|
|
218
|
+
* a later one) already changed this text further, this edit's `after` string won't be found on
|
|
219
|
+
* disk anymore and the exact-match guard in applyEdit refuses rather than guessing.
|
|
220
|
+
*/
|
|
221
|
+
function revertMessageEdit(devmindPath, messageId, editId) {
|
|
222
|
+
const message = (0, activity_1.readMessage)(devmindPath, messageId);
|
|
223
|
+
if (!message)
|
|
224
|
+
return { ok: false, error: 'message not found' };
|
|
225
|
+
const edit = message.edits.find(e => e.id === editId);
|
|
226
|
+
if (!edit)
|
|
227
|
+
return { ok: false, error: 'edit not found in this message' };
|
|
228
|
+
if (edit.reverted)
|
|
229
|
+
return { ok: false, error: 'already reverted' };
|
|
230
|
+
const r = applyEdit(edit, edit.after, edit.before);
|
|
231
|
+
if (!r.ok)
|
|
232
|
+
return { ok: false, error: r.error };
|
|
233
|
+
edit.reverted = true;
|
|
234
|
+
message.status = (0, activity_1.deriveStatus)(message.edits);
|
|
235
|
+
message.updated_at = new Date().toISOString();
|
|
236
|
+
(0, activity_1.saveMessage)(devmindPath, message);
|
|
237
|
+
return { ok: true };
|
|
238
|
+
}
|
|
239
|
+
/** Symmetric to revertMessageEdit. */
|
|
240
|
+
function unrevertMessageEdit(devmindPath, messageId, editId) {
|
|
241
|
+
const message = (0, activity_1.readMessage)(devmindPath, messageId);
|
|
242
|
+
if (!message)
|
|
243
|
+
return { ok: false, error: 'message not found' };
|
|
244
|
+
const edit = message.edits.find(e => e.id === editId);
|
|
245
|
+
if (!edit)
|
|
246
|
+
return { ok: false, error: 'edit not found in this message' };
|
|
247
|
+
if (!edit.reverted)
|
|
248
|
+
return { ok: false, error: 'not reverted' };
|
|
249
|
+
const r = applyEdit(edit, edit.before, edit.after);
|
|
250
|
+
if (!r.ok)
|
|
251
|
+
return { ok: false, error: r.error };
|
|
252
|
+
edit.reverted = false;
|
|
253
|
+
message.status = (0, activity_1.deriveStatus)(message.edits);
|
|
254
|
+
message.updated_at = new Date().toISOString();
|
|
255
|
+
(0, activity_1.saveMessage)(devmindPath, message);
|
|
256
|
+
return { ok: true };
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=message-revert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-revert.js","sourceRoot":"","sources":["../../src/db/message-revert.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { DevMindDatabase } from './database';
|
|
2
|
+
export interface RevertResult {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
error?: string;
|
|
5
|
+
node_id?: string;
|
|
6
|
+
file_path?: string;
|
|
7
|
+
/** True when the change was still staged, so there was no recorded history to unwind. */
|
|
8
|
+
was_staged?: boolean;
|
|
9
|
+
entry_deleted?: boolean;
|
|
10
|
+
/** Set when the code was restored but the history entry could not be fully removed. */
|
|
11
|
+
note?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Undoes the most recent recorded edit to one entity: restores the file, then erases what was
|
|
15
|
+
* written about it.
|
|
16
|
+
*
|
|
17
|
+
* Only the newest edit is reversible. An older one cannot be undone in isolation — every edit
|
|
18
|
+
* after it was made against the code it produced, so putting its "before" back would overwrite
|
|
19
|
+
* work that has nothing to do with it. Git is the tool for that, and it already does it properly.
|
|
20
|
+
*
|
|
21
|
+
* `expectedHistoryId`, when given, is the specific entry the caller intended to revert (e.g. the
|
|
22
|
+
* one a "Revert this change" button was clicked on). It must match the node's actual latest entry
|
|
23
|
+
* — if it doesn't (a newer entry was recorded since, perhaps from another session), the revert is
|
|
24
|
+
* refused rather than silently reverting that newer entry instead, which would undo the wrong
|
|
25
|
+
* change from the caller's point of view.
|
|
26
|
+
*
|
|
27
|
+
* The file is restored by swapping the recorded `after` back to the recorded `before`, which
|
|
28
|
+
* routes through the same exact-match write `edit_node` uses: if that text is no longer uniquely
|
|
29
|
+
* present, the write is refused rather than guessed at.
|
|
30
|
+
*/
|
|
31
|
+
export declare function revertLastEdit(db: DevMindDatabase, devmindPath: string, nodeId: string, expectedHistoryId?: string): RevertResult;
|