devsmind-mcp 2.3.0 → 3.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 +326 -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 +128 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +30 -0
- package/dist/cli/init.js +98 -18
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/integrations/memory-topics.d.ts +44 -0
- package/dist/cli/integrations/memory-topics.js +295 -0
- package/dist/cli/integrations/memory-topics.js.map +1 -0
- package/dist/cli/integrations/memory.d.ts +11 -5
- package/dist/cli/integrations/memory.js +132 -36
- 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 +19 -5
- package/dist/cli/integrations/registry.js +23 -13
- 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/prune.js +4 -3
- package/dist/cli/prune.js.map +1 -1
- package/dist/cli/rule.d.ts +22 -2
- package/dist/cli/rule.js +116 -56
- 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.d.ts +200 -0
- package/dist/db/activity.js +347 -0
- package/dist/db/activity.js.map +1 -0
- package/dist/db/analyze.js +25 -9
- package/dist/db/analyze.js.map +1 -1
- package/dist/db/database.d.ts +607 -79
- package/dist/db/database.js +1936 -488
- 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/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 +90 -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 +2755 -745
- 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 +187 -0
- package/dist/utils/ast.js +640 -20
- 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 +47 -0
- package/dist/utils/edit.js +196 -0
- package/dist/utils/edit.js.map +1 -0
- package/dist/utils/scanner.js +12 -4
- 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,66 @@
|
|
|
1
|
+
import { ExtractionCandidate } from '../utils/ast';
|
|
2
|
+
import { LlmCredentials } from './llm-client';
|
|
3
|
+
/**
|
|
4
|
+
* Agentic node-extraction curation — the judgment half of Phase D. `enumerateFileCandidates`
|
|
5
|
+
* (deterministic, no LLM) already found every candidate and split them by whether their EXISTENCE
|
|
6
|
+
* is unambiguous (`isExported`). This module handles only what's left: for candidates whose
|
|
7
|
+
* significance genuinely requires judgment (an unexported helper, an anonymous default, a tiny
|
|
8
|
+
* inline callback), a multi-turn tool-calling agent decides keep/drop/merge/rename. Existence is
|
|
9
|
+
* never delegated to the model — only significance is, which is the whole reason this is more
|
|
10
|
+
* reliable than the old "dump the whole file to an LLM and hope it finds everything" extraction.
|
|
11
|
+
*/
|
|
12
|
+
export type CurationDecisionKind = 'keep' | 'drop' | 'merge' | 'rename';
|
|
13
|
+
export interface CurationDecision {
|
|
14
|
+
qualified: string;
|
|
15
|
+
decision: CurationDecisionKind;
|
|
16
|
+
/** Present when `decision === 'merge'` — the candidate this one conceptually belongs to. */
|
|
17
|
+
mergeInto?: string;
|
|
18
|
+
/** Present when `decision === 'rename'` — the better name for this candidate. */
|
|
19
|
+
renameTo?: string;
|
|
20
|
+
reason?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface CurationResult {
|
|
23
|
+
decisions: CurationDecision[];
|
|
24
|
+
/** True when the turn budget ran out before the model called `submit_decisions` — every
|
|
25
|
+
* candidate defaulted to "keep" (see {@link curateAmbiguousCandidates}'s doc for why). */
|
|
26
|
+
timedOut: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Runs the agentic curation loop over one file's AMBIGUOUS candidates only (the `isExported:false`
|
|
30
|
+
* subset of `enumerateFileCandidates` — the caller is responsible for that split, see
|
|
31
|
+
* {@link extractFileWithCuration}). Multi-turn: the model may call `get_file_imports` any number
|
|
32
|
+
* of times before it must call `submit_decisions` to finish. `opts.onTurn` receives one line per
|
|
33
|
+
* model turn — nothing about this loop happens silently.
|
|
34
|
+
*
|
|
35
|
+
* If the turn budget is exhausted without a `submit_decisions` call, every candidate defaults to
|
|
36
|
+
* "keep" — silently dropping a real candidate is a worse failure than over-including one, and
|
|
37
|
+
* everything reaching this function already passed the deterministic auto-accept gate, so "keep,
|
|
38
|
+
* unreviewed" is the safe fallback, not a crash or a lost node.
|
|
39
|
+
*/
|
|
40
|
+
export declare function curateAmbiguousCandidates(creds: LlmCredentials, filePath: string, candidates: ExtractionCandidate[], opts?: {
|
|
41
|
+
maxTurns?: number;
|
|
42
|
+
rpm?: number;
|
|
43
|
+
onTurn?: (line: string) => void;
|
|
44
|
+
}): Promise<CurationResult>;
|
|
45
|
+
/** The shape `runBackgroundIndexing`/`runBackgroundReindexing` already consume from the old
|
|
46
|
+
* `extractNodesFromCode` — matched exactly so wiring this in is a drop-in call-site swap. */
|
|
47
|
+
export interface CuratedExtractionResult {
|
|
48
|
+
nodes: {
|
|
49
|
+
node_id: string;
|
|
50
|
+
name: string;
|
|
51
|
+
type: string;
|
|
52
|
+
signature?: string;
|
|
53
|
+
code_snapshot: string;
|
|
54
|
+
}[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The full Phase D per-file extraction: deterministic enumeration + auto-accept for exported
|
|
58
|
+
* candidates (zero LLM turns), agentic curation only for the ambiguous remainder. This is the
|
|
59
|
+
* cost control the whole design rests on — on a typical file most candidates ARE exported, so most
|
|
60
|
+
* files touch the LLM only for a handful of borderline cases, or not at all.
|
|
61
|
+
*/
|
|
62
|
+
export declare function extractFileWithCuration(creds: LlmCredentials, filePath: string, opts?: {
|
|
63
|
+
maxTurns?: number;
|
|
64
|
+
rpm?: number;
|
|
65
|
+
onLog?: (line: string) => void;
|
|
66
|
+
}): Promise<CuratedExtractionResult>;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.curateAmbiguousCandidates = curateAmbiguousCandidates;
|
|
4
|
+
exports.extractFileWithCuration = extractFileWithCuration;
|
|
5
|
+
const ast_1 = require("../utils/ast");
|
|
6
|
+
const llm_client_1 = require("./llm-client");
|
|
7
|
+
const runner_1 = require("./runner");
|
|
8
|
+
const SUBMIT_TOOL_NAME = 'submit_decisions';
|
|
9
|
+
const GET_IMPORTS_TOOL_NAME = 'get_file_imports';
|
|
10
|
+
function buildTools() {
|
|
11
|
+
return [
|
|
12
|
+
{
|
|
13
|
+
name: GET_IMPORTS_TOOL_NAME,
|
|
14
|
+
description: "Returns this file's own import statements (module specifier + imported name). Useful to judge whether an ambiguous candidate is a real, independently meaningful entity or just a trivial local detail — e.g. whether it mirrors something worth tracking alongside a specific imported dependency, versus pure local plumbing.",
|
|
15
|
+
parameters: { type: 'object', properties: {}, additionalProperties: false }
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: SUBMIT_TOOL_NAME,
|
|
19
|
+
description: 'REQUIRED — call this exactly once, when you have a decision for every candidate, to finish the task. This is the ONLY way to finish; do not stop without calling it.',
|
|
20
|
+
parameters: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
decisions: {
|
|
24
|
+
type: 'array',
|
|
25
|
+
items: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
qualified: { type: 'string', description: 'The candidate\'s "qualified" name, copied exactly as given.' },
|
|
29
|
+
decision: { type: 'string', enum: ['keep', 'drop', 'merge', 'rename'] },
|
|
30
|
+
merge_into: { type: 'string', description: 'Required when decision is "merge" — the qualified name of the candidate this one belongs to.' },
|
|
31
|
+
rename_to: { type: 'string', description: 'Required when decision is "rename" — the better name for this candidate.' },
|
|
32
|
+
reason: { type: 'string', description: 'One short sentence: why.' }
|
|
33
|
+
},
|
|
34
|
+
required: ['qualified', 'decision']
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
required: ['decisions']
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
function buildSystemPrompt() {
|
|
44
|
+
return [
|
|
45
|
+
'You are curating a list of code-graph node CANDIDATES for one source file.',
|
|
46
|
+
"These candidates were found deterministically by parsing the file's AST — every one of them genuinely exists in the code, at the exact line numbers given. Nothing about their EXISTENCE is in question here.",
|
|
47
|
+
'',
|
|
48
|
+
'Your ONLY job is judgment. For EACH candidate, decide:',
|
|
49
|
+
'- "keep" — a real, independently meaningful entity worth its own node.',
|
|
50
|
+
'- "drop" — trivial noise (e.g. a tiny inline callback, a throwaway local) not worth tracking on its own.',
|
|
51
|
+
'- "merge" — this is really part of another candidate\'s concept, not a distinct entity (give merge_into).',
|
|
52
|
+
"- \"rename\" — the extractor's name is wrong, unclear, or anonymous and you have a genuinely better one (give rename_to).",
|
|
53
|
+
'',
|
|
54
|
+
`Call ${GET_IMPORTS_TOOL_NAME} if the file's imports would help you judge significance — as many times as you like, though it never changes between calls.`,
|
|
55
|
+
`When you have a decision for EVERY candidate, call ${SUBMIT_TOOL_NAME} exactly once with the full list. Keep any "reason" to one short sentence. Do not respond with plain text instead of a tool call.`
|
|
56
|
+
].join('\n');
|
|
57
|
+
}
|
|
58
|
+
function buildInitialUserMessage(filePath, candidates) {
|
|
59
|
+
const blocks = candidates.map(c => `- qualified: "${c.qualified}"\n type: ${c.type}\n lines: ${c.startLine}-${c.endLine}\n code:\n${c.codeSnapshot.split('\n').map(l => ' ' + l).join('\n')}`);
|
|
60
|
+
return `File: ${filePath}\n\n${candidates.length} candidate(s) to curate:\n\n${blocks.join('\n\n')}`;
|
|
61
|
+
}
|
|
62
|
+
function parseDecisions(args, candidates) {
|
|
63
|
+
const raw = Array.isArray(args.decisions) ? args.decisions : [];
|
|
64
|
+
const validQualified = new Set(candidates.map(c => c.qualified));
|
|
65
|
+
const decisions = [];
|
|
66
|
+
const seen = new Set();
|
|
67
|
+
for (const entry of raw) {
|
|
68
|
+
const d = entry;
|
|
69
|
+
if (!d || typeof d.qualified !== 'string' || !validQualified.has(d.qualified))
|
|
70
|
+
continue;
|
|
71
|
+
if (seen.has(d.qualified))
|
|
72
|
+
continue; // one decision per candidate — a duplicate is ignored
|
|
73
|
+
seen.add(d.qualified);
|
|
74
|
+
const decisionKind = d.decision === 'drop' || d.decision === 'merge' || d.decision === 'rename' ? d.decision : 'keep';
|
|
75
|
+
decisions.push({
|
|
76
|
+
qualified: d.qualified,
|
|
77
|
+
decision: decisionKind,
|
|
78
|
+
mergeInto: typeof d.merge_into === 'string' ? d.merge_into : undefined,
|
|
79
|
+
renameTo: typeof d.rename_to === 'string' ? d.rename_to : undefined,
|
|
80
|
+
reason: typeof d.reason === 'string' ? d.reason : undefined
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// Any candidate the model never mentioned still needs a decision — same conservative default
|
|
84
|
+
// as a turn-budget timeout (see curateAmbiguousCandidates): keep, not silently drop.
|
|
85
|
+
for (const c of candidates) {
|
|
86
|
+
if (!seen.has(c.qualified)) {
|
|
87
|
+
decisions.push({ qualified: c.qualified, decision: 'keep', reason: 'not mentioned by the model — conservative default' });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return decisions;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Runs the agentic curation loop over one file's AMBIGUOUS candidates only (the `isExported:false`
|
|
94
|
+
* subset of `enumerateFileCandidates` — the caller is responsible for that split, see
|
|
95
|
+
* {@link extractFileWithCuration}). Multi-turn: the model may call `get_file_imports` any number
|
|
96
|
+
* of times before it must call `submit_decisions` to finish. `opts.onTurn` receives one line per
|
|
97
|
+
* model turn — nothing about this loop happens silently.
|
|
98
|
+
*
|
|
99
|
+
* If the turn budget is exhausted without a `submit_decisions` call, every candidate defaults to
|
|
100
|
+
* "keep" — silently dropping a real candidate is a worse failure than over-including one, and
|
|
101
|
+
* everything reaching this function already passed the deterministic auto-accept gate, so "keep,
|
|
102
|
+
* unreviewed" is the safe fallback, not a crash or a lost node.
|
|
103
|
+
*/
|
|
104
|
+
async function curateAmbiguousCandidates(creds, filePath, candidates, opts = {}) {
|
|
105
|
+
if (candidates.length === 0)
|
|
106
|
+
return { decisions: [], timedOut: false };
|
|
107
|
+
const maxTurns = opts.maxTurns ?? 4;
|
|
108
|
+
const tools = buildTools();
|
|
109
|
+
const systemPrompt = buildSystemPrompt();
|
|
110
|
+
const messages = [{ role: 'user', content: buildInitialUserMessage(filePath, candidates) }];
|
|
111
|
+
const log = opts.onTurn ?? (() => { });
|
|
112
|
+
for (let turn = 1; turn <= maxTurns; turn++) {
|
|
113
|
+
await (0, runner_1.throttleRpm)(opts.rpm);
|
|
114
|
+
const result = await (0, llm_client_1.sendConversationTurnWithRetry)(creds, systemPrompt, messages, tools, {
|
|
115
|
+
onRetry: (m) => log(` ⚠ ${m}`)
|
|
116
|
+
});
|
|
117
|
+
const submitCall = result.toolCalls.find(tc => tc.name === SUBMIT_TOOL_NAME);
|
|
118
|
+
if (submitCall) {
|
|
119
|
+
const decisions = parseDecisions(submitCall.args, candidates);
|
|
120
|
+
log(` ✓ turn ${turn}: ${SUBMIT_TOOL_NAME} — ${decisions.length} decision(s)`);
|
|
121
|
+
return { decisions, timedOut: false };
|
|
122
|
+
}
|
|
123
|
+
const importsCall = result.toolCalls.find(tc => tc.name === GET_IMPORTS_TOOL_NAME);
|
|
124
|
+
if (importsCall) {
|
|
125
|
+
log(` → turn ${turn}: ${GET_IMPORTS_TOOL_NAME}`);
|
|
126
|
+
messages.push({ role: 'assistant', content: result.text, toolCalls: result.toolCalls });
|
|
127
|
+
messages.push({
|
|
128
|
+
role: 'tool',
|
|
129
|
+
toolCallId: importsCall.id,
|
|
130
|
+
toolName: GET_IMPORTS_TOOL_NAME,
|
|
131
|
+
content: JSON.stringify((0, ast_1.listFileImports)(filePath))
|
|
132
|
+
});
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
// No recognized tool call this turn — nudge once rather than loop silently on a model that
|
|
136
|
+
// isn't cooperating; if it still won't call a tool, the turn budget above ends this cleanly.
|
|
137
|
+
log(` ? turn ${turn}: no tool call — nudging`);
|
|
138
|
+
messages.push({ role: 'assistant', content: result.text, toolCalls: [] });
|
|
139
|
+
messages.push({
|
|
140
|
+
role: 'user',
|
|
141
|
+
content: `You must call ${SUBMIT_TOOL_NAME} to finish (or ${GET_IMPORTS_TOOL_NAME} first if you need it). Do not respond with plain text.`
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
log(` ⚠ turn budget (${maxTurns}) exhausted — defaulting all ${candidates.length} candidate(s) to "keep"`);
|
|
145
|
+
return {
|
|
146
|
+
decisions: candidates.map(c => ({ qualified: c.qualified, decision: 'keep', reason: 'curation turn budget exhausted — conservative default' })),
|
|
147
|
+
timedOut: true
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The full Phase D per-file extraction: deterministic enumeration + auto-accept for exported
|
|
152
|
+
* candidates (zero LLM turns), agentic curation only for the ambiguous remainder. This is the
|
|
153
|
+
* cost control the whole design rests on — on a typical file most candidates ARE exported, so most
|
|
154
|
+
* files touch the LLM only for a handful of borderline cases, or not at all.
|
|
155
|
+
*/
|
|
156
|
+
async function extractFileWithCuration(creds, filePath, opts = {}) {
|
|
157
|
+
const log = opts.onLog ?? (() => { });
|
|
158
|
+
const candidates = (0, ast_1.enumerateFileCandidates)(filePath);
|
|
159
|
+
const exported = candidates.filter(c => c.isExported);
|
|
160
|
+
const ambiguous = candidates.filter(c => !c.isExported);
|
|
161
|
+
log(` ${exported.length} exported (auto-accepted, 0 LLM turns), ${ambiguous.length} ambiguous`);
|
|
162
|
+
const nodes = exported.map(c => ({
|
|
163
|
+
node_id: c.qualified,
|
|
164
|
+
name: c.name,
|
|
165
|
+
type: c.type,
|
|
166
|
+
signature: c.signature ?? undefined,
|
|
167
|
+
code_snapshot: c.codeSnapshot
|
|
168
|
+
}));
|
|
169
|
+
if (ambiguous.length === 0)
|
|
170
|
+
return { nodes };
|
|
171
|
+
const result = await curateAmbiguousCandidates(creds, filePath, ambiguous, {
|
|
172
|
+
maxTurns: opts.maxTurns,
|
|
173
|
+
rpm: opts.rpm,
|
|
174
|
+
onTurn: log
|
|
175
|
+
});
|
|
176
|
+
const byQualified = new Map(ambiguous.map(c => [c.qualified, c]));
|
|
177
|
+
for (const d of result.decisions) {
|
|
178
|
+
const c = byQualified.get(d.qualified);
|
|
179
|
+
if (!c)
|
|
180
|
+
continue;
|
|
181
|
+
// "merge" folds this candidate's SIGNIFICANCE into its target conceptually — it does not
|
|
182
|
+
// become a separate node. The merge target (if it's also a real candidate) is handled by its
|
|
183
|
+
// own decision entry; this candidate's code simply isn't extracted as its own node.
|
|
184
|
+
if (d.decision === 'drop' || d.decision === 'merge')
|
|
185
|
+
continue;
|
|
186
|
+
const name = d.decision === 'rename' && d.renameTo ? d.renameTo : c.name;
|
|
187
|
+
const nodeId = d.decision === 'rename' && d.renameTo ? d.renameTo : c.qualified;
|
|
188
|
+
nodes.push({ node_id: nodeId, name, type: c.type, signature: c.signature ?? undefined, code_snapshot: c.codeSnapshot });
|
|
189
|
+
}
|
|
190
|
+
return { nodes };
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=extract-agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-agent.js","sourceRoot":"","sources":["../../src/cli/extract-agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `devsmind feedback` — the human-facing read of the local, gitignored feedback log that
|
|
3
|
+
* `commit_changes`' `feedback` param writes to (see db/feedback.ts). Read-only, same store
|
|
4
|
+
* `read_graph_feedback` (an agent's own batch graph-fix session) and the AI-only product log
|
|
5
|
+
* draw from — this is the first surface a person has ever had to actually see any of it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function handleFeedback(opts: {
|
|
8
|
+
path?: string;
|
|
9
|
+
since?: string;
|
|
10
|
+
all?: boolean;
|
|
11
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleFeedback = handleFeedback;
|
|
4
|
+
const config_1 = require("../utils/config");
|
|
5
|
+
const feedback_1 = require("../db/feedback");
|
|
6
|
+
const DIM = '\x1b[2m';
|
|
7
|
+
const BOLD = '\x1b[1m';
|
|
8
|
+
const GREEN = '\x1b[32m';
|
|
9
|
+
const YELLOW = '\x1b[33m';
|
|
10
|
+
const RESET = '\x1b[0m';
|
|
11
|
+
function sinceCutoff(sinceDays) {
|
|
12
|
+
const n = sinceDays ? parseInt(sinceDays, 10) : undefined;
|
|
13
|
+
return n !== undefined && !Number.isNaN(n) ? Date.now() - n * 24 * 60 * 60 * 1000 : null;
|
|
14
|
+
}
|
|
15
|
+
function printGraphCluster(c) {
|
|
16
|
+
const confidenceTag = c.confidence === 'confirmed' ? `${GREEN}confirmed${RESET}` : `${YELLOW}suspected${RESET}`;
|
|
17
|
+
console.log(` ${BOLD}${c.count}x${RESET} ${c.category} ${DIM}${c.node_id}${RESET} (${confidenceTag})`);
|
|
18
|
+
// One representative line of text per cluster keeps this readable even when the same report
|
|
19
|
+
// repeated 30 times — the individual entries (with evidence) are what read_graph_feedback
|
|
20
|
+
// exposes to the AI's own batch graph-fix session, not what a human skim needs here.
|
|
21
|
+
console.log(` ${c.entries[0].text}`);
|
|
22
|
+
if (c.entries[0].evidence) {
|
|
23
|
+
const e = c.entries[0].evidence;
|
|
24
|
+
console.log(` ${DIM}evidence: ${e.file}${e.line ? ':' + e.line : ''}${RESET}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function printProductEntries(entries, category) {
|
|
28
|
+
const filtered = entries.filter(e => e.category === category);
|
|
29
|
+
if (!filtered.length)
|
|
30
|
+
return;
|
|
31
|
+
const label = { tools_used: 'What helped', dropped_and_why: 'What got dropped', devsmind_better: 'How to improve' }[category];
|
|
32
|
+
console.log(` ${BOLD}${label}${RESET}`);
|
|
33
|
+
for (const e of filtered) {
|
|
34
|
+
console.log(` ${DIM}${new Date(e.ts).toLocaleDateString()}${RESET} ${e.text}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* `devsmind feedback` — the human-facing read of the local, gitignored feedback log that
|
|
39
|
+
* `commit_changes`' `feedback` param writes to (see db/feedback.ts). Read-only, same store
|
|
40
|
+
* `read_graph_feedback` (an agent's own batch graph-fix session) and the AI-only product log
|
|
41
|
+
* draw from — this is the first surface a person has ever had to actually see any of it.
|
|
42
|
+
*/
|
|
43
|
+
async function handleFeedback(opts) {
|
|
44
|
+
const devmindDir = (0, config_1.resolveDevmindDir)(opts.path);
|
|
45
|
+
if (!devmindDir) {
|
|
46
|
+
console.error(`❌ No .devmind directory found.\n` +
|
|
47
|
+
` Run from inside a DevsMind brain folder, or pass --path <devmind_path>.`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const cutoff = sinceCutoff(opts.since);
|
|
51
|
+
const inWindow = (ts) => cutoff === null || new Date(ts).getTime() >= cutoff;
|
|
52
|
+
const graphEntries = (0, feedback_1.readGraphFeedback)(devmindDir, { includeProcessed: !!opts.all }).filter(e => inWindow(e.ts));
|
|
53
|
+
const productEntries = (0, feedback_1.readProductFeedback)(devmindDir).filter(e => inWindow(e.ts));
|
|
54
|
+
const ruleCandidates = (0, feedback_1.readIndexerRuleCandidates)(devmindDir).filter(e => inWindow(e.ts));
|
|
55
|
+
if (!graphEntries.length && !productEntries.length && !ruleCandidates.length) {
|
|
56
|
+
console.log(`\n📭 No feedback recorded${cutoff !== null ? ` in the last ${opts.since} day(s)` : ''}.\n` +
|
|
57
|
+
` commit_changes asks for feedback on every commit — "none" is a valid answer and writes\n` +
|
|
58
|
+
` nothing here, so an empty log usually just means every recent commit answered "none".\n`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
console.log('');
|
|
62
|
+
if (graphEntries.length) {
|
|
63
|
+
const clusters = (0, feedback_1.clusterGraphFeedback)(graphEntries);
|
|
64
|
+
console.log(`${BOLD}Graph feedback${RESET} ${DIM}${graphEntries.length} report(s) in ${clusters.length} cluster(s)${opts.all ? '' : ' — unprocessed only, pass --all to include processed'}${RESET}`);
|
|
65
|
+
for (const c of clusters)
|
|
66
|
+
printGraphCluster(c);
|
|
67
|
+
console.log('');
|
|
68
|
+
}
|
|
69
|
+
if (productEntries.length) {
|
|
70
|
+
console.log(`${BOLD}Product feedback${RESET} ${DIM}${productEntries.length} entries — never read by an agent, for you to act on${RESET}`);
|
|
71
|
+
printProductEntries(productEntries, 'devsmind_better');
|
|
72
|
+
printProductEntries(productEntries, 'dropped_and_why');
|
|
73
|
+
printProductEntries(productEntries, 'tools_used');
|
|
74
|
+
console.log('');
|
|
75
|
+
}
|
|
76
|
+
if (ruleCandidates.length) {
|
|
77
|
+
console.log(`${BOLD}Indexer rule candidates${RESET} ${DIM}recurring patterns worth a permanent detector${RESET}`);
|
|
78
|
+
for (const r of ruleCandidates) {
|
|
79
|
+
console.log(` ${BOLD}${r.evidence_count}x${RESET} ${r.pattern}`);
|
|
80
|
+
for (const ex of r.examples.slice(0, 3))
|
|
81
|
+
console.log(` ${DIM}${ex}${RESET}`);
|
|
82
|
+
}
|
|
83
|
+
console.log('');
|
|
84
|
+
}
|
|
85
|
+
if (graphEntries.length) {
|
|
86
|
+
console.log(`${DIM}Graph feedback is meant to be resolved by an AI agent's own batch graph-fix session`);
|
|
87
|
+
console.log(`(ask it to "run a graph-fix session" — it calls read_graph_feedback / mark_graph_feedback_processed).${RESET}\n`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=feedback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback.js","sourceRoot":"","sources":["../../src/cli/feedback.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -8,16 +8,22 @@ const view_1 = require("./view");
|
|
|
8
8
|
const prune_1 = require("./prune");
|
|
9
9
|
const sync_1 = require("./sync");
|
|
10
10
|
const analyze_1 = require("./analyze");
|
|
11
|
+
const diff_1 = require("./diff");
|
|
12
|
+
const activity_1 = require("./activity");
|
|
13
|
+
const feedback_1 = require("./feedback");
|
|
11
14
|
const workflow_1 = require("./workflow");
|
|
12
15
|
const mcp_1 = require("./integrations/mcp");
|
|
13
16
|
const memory_1 = require("./integrations/memory");
|
|
14
17
|
const runner_1 = require("./runner");
|
|
18
|
+
const describe_1 = require("./describe");
|
|
19
|
+
const embed_1 = require("./embed");
|
|
15
20
|
const server_1 = require("../mcp/server");
|
|
21
|
+
const version_1 = require("../utils/version");
|
|
16
22
|
const program = new commander_1.Command();
|
|
17
23
|
program
|
|
18
24
|
.name('devsmind')
|
|
19
25
|
.description('DevsMind — Team AI Brain CLI')
|
|
20
|
-
.version(
|
|
26
|
+
.version(version_1.DEVSMIND_VERSION, '-v, --version');
|
|
21
27
|
program
|
|
22
28
|
.command('init')
|
|
23
29
|
.description('Initialize a new DevsMind brain or update repository paths')
|
|
@@ -37,7 +43,7 @@ program
|
|
|
37
43
|
` IDEs connect via: http://localhost:${server_1.DEVSMIND_PORT}/mcp`)
|
|
38
44
|
.option('--stdio', 'Use stdio transport instead of HTTP (for direct IDE process injection)')
|
|
39
45
|
.option('-p, --port <number>', `HTTP port to listen on (default: ${server_1.DEVSMIND_PORT})`, String(server_1.DEVSMIND_PORT))
|
|
40
|
-
.option('--path <devmind_path>', 'Explicit path to the .devmind directory (auto-detected from cwd by default)
|
|
46
|
+
.option('--path <devmind_path>', 'Explicit path to the .devmind directory the server binds to (auto-detected from cwd by default). The server serves this one project, so callers never pass a path.')
|
|
41
47
|
.option('--sync', 'Run devsmind sync before starting the server')
|
|
42
48
|
.option('--analyze', 'Run devsmind analyze before starting the server')
|
|
43
49
|
.option('--fix', 'With --analyze, also apply safe automatic fixes')
|
|
@@ -56,7 +62,7 @@ program
|
|
|
56
62
|
}
|
|
57
63
|
if (opts.stdio) {
|
|
58
64
|
// Stdio mode: IDE manages the process directly
|
|
59
|
-
(0, server_1.runStdioMcpServer)();
|
|
65
|
+
(0, server_1.runStdioMcpServer)(opts.path);
|
|
60
66
|
}
|
|
61
67
|
else {
|
|
62
68
|
// HTTP mode: IDE connects over the network
|
|
@@ -66,7 +72,7 @@ program
|
|
|
66
72
|
process.exit(1);
|
|
67
73
|
}
|
|
68
74
|
try {
|
|
69
|
-
await (0, server_1.runHttpMcpServer)(port);
|
|
75
|
+
await (0, server_1.runHttpMcpServer)(port, opts.path);
|
|
70
76
|
}
|
|
71
77
|
catch (err) {
|
|
72
78
|
const msg = err.message;
|
|
@@ -85,6 +91,7 @@ program
|
|
|
85
91
|
.description('Get the AI workspace rule and place it in your tool (guided), or print it')
|
|
86
92
|
.option('-p, --path <devmind_path>', 'Explicit path to the .devmind directory (auto-detected from cwd by default)')
|
|
87
93
|
.option('--print', 'Just print the rule to stdout (no interactive placement)')
|
|
94
|
+
.option('--manual', 'Manual workflow style: AI searches/reads freely but only stages or commits when explicitly asked (default is automatic — stages and commits without being asked). Only needed with --print/non-interactive; the interactive flow asks.')
|
|
88
95
|
.action(async (opts) => {
|
|
89
96
|
try {
|
|
90
97
|
await (0, rule_1.handleRule)(opts);
|
|
@@ -109,8 +116,10 @@ program
|
|
|
109
116
|
});
|
|
110
117
|
program
|
|
111
118
|
.command('memory')
|
|
112
|
-
.description('Seed a tool\'s own persistent agent-memory/skills store (guided, per-tool)')
|
|
119
|
+
.description('Seed a tool\'s own persistent agent-memory/skills store (guided, per-tool), or print it')
|
|
113
120
|
.option('-p, --path <devmind_path>', 'Explicit path to the .devmind directory (auto-detected from cwd by default)')
|
|
121
|
+
.option('--print', 'Just print the memory files to stdout (no interactive placement)')
|
|
122
|
+
.option('--tool <id>', 'Which tool\'s memory shape to print (claude-code, antigravity, antigravity-cli). Only used with --print/non-interactive; the interactive flow asks.')
|
|
114
123
|
.action(async (opts) => {
|
|
115
124
|
try {
|
|
116
125
|
await (0, memory_1.handleMemory)(opts);
|
|
@@ -151,6 +160,112 @@ program
|
|
|
151
160
|
process.exit(1);
|
|
152
161
|
}
|
|
153
162
|
});
|
|
163
|
+
program
|
|
164
|
+
.command('describe')
|
|
165
|
+
.description('Backfill natural-language descriptions for existing nodes that predate the description requirement — what search_nodes needs to find code by natural language. Always safe to re-run (work queue is just "nodes with no description yet"); new nodes going forward get described via commit_changes\' gate + add_description instead, not this command.')
|
|
166
|
+
.option('-p, --path <devmind_path>', 'Explicit path to the .devmind directory (auto-detected from cwd by default)')
|
|
167
|
+
.option('--provider <provider>', 'LLM provider: "gemini", "vertex", or "ollama"', 'gemini')
|
|
168
|
+
.option('--model <name>', 'Model identifier (default: "gemini-2.0-flash", "gemini-1.5-flash", or "qwen2.5-coder")')
|
|
169
|
+
.option('--key <api_key>', 'API Key or Service Account file path (overrides GEMINI_API_KEY / GOOGLE_APPLICATION_CREDENTIALS)')
|
|
170
|
+
.option('--url <url>', 'Ollama server endpoint (default: "http://localhost:11434")')
|
|
171
|
+
.option('--rpm <number>', 'Max LLM requests per minute, proactively paced (default: unthrottled)')
|
|
172
|
+
.option('--batch-size <number>', 'Nodes described per LLM call (default: 25)')
|
|
173
|
+
.option('--dry-run', 'List pending nodes without calling the LLM or writing anything')
|
|
174
|
+
.action(async (opts) => {
|
|
175
|
+
try {
|
|
176
|
+
await (0, describe_1.handleDescribe)({
|
|
177
|
+
path: opts.path,
|
|
178
|
+
provider: opts.provider,
|
|
179
|
+
model: opts.model,
|
|
180
|
+
key: opts.key,
|
|
181
|
+
url: opts.url,
|
|
182
|
+
rpm: opts.rpm ? parseInt(opts.rpm, 10) : undefined,
|
|
183
|
+
batchSize: opts.batchSize ? parseInt(opts.batchSize, 10) : undefined,
|
|
184
|
+
dryRun: !!opts.dryRun
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
console.error(`❌ Describe failed: ${err.message}`);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
program
|
|
193
|
+
.command('embed')
|
|
194
|
+
.description('Compute semantic (vector) search embeddings for every described node that lacks a current one. Fully local/offline — no LLM credentials needed, inference runs on-device. Always safe to re-run (work queue is "needs a vector"); new/edited descriptions going forward get auto-embedded via commit_changes/add_description/describe instead, not this command — it\'s only for clearing an existing backlog or a model upgrade (--force).')
|
|
195
|
+
.option('-p, --path <devmind_path>', 'Explicit path to the .devmind directory (auto-detected from cwd by default)')
|
|
196
|
+
.option('--batch-size <number>', 'Nodes embedded per inference call (default: 32)')
|
|
197
|
+
.option('--dry-run', 'List pending nodes without running inference or writing anything')
|
|
198
|
+
.option('--force', 'Re-embed every described node regardless of current vector state (use after a model upgrade)')
|
|
199
|
+
.action(async (opts) => {
|
|
200
|
+
try {
|
|
201
|
+
await (0, embed_1.handleEmbed)({
|
|
202
|
+
path: opts.path,
|
|
203
|
+
batchSize: opts.batchSize ? parseInt(opts.batchSize, 10) : undefined,
|
|
204
|
+
dryRun: !!opts.dryRun,
|
|
205
|
+
force: !!opts.force
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
console.error(`❌ Embed failed: ${err.message}`);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
program
|
|
214
|
+
.command('diff <node_id>')
|
|
215
|
+
.description('Show what changed in an entity, red/green, with the reasoning recorded for it')
|
|
216
|
+
.option('-p, --path <devmind_path>', 'Explicit path to the .devmind directory (auto-detected from cwd by default)')
|
|
217
|
+
.action(async (nodeId, opts) => {
|
|
218
|
+
try {
|
|
219
|
+
await (0, diff_1.handleDiff)(nodeId, opts);
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
console.error(`❌ Diff failed: ${err.message}`);
|
|
223
|
+
process.exit(1);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
program
|
|
227
|
+
.command('revert <node_id>')
|
|
228
|
+
.description('Undo an entity\'s most recent recorded edit and erase it from history')
|
|
229
|
+
.option('-p, --path <devmind_path>', 'Explicit path to the .devmind directory (auto-detected from cwd by default)')
|
|
230
|
+
.option('-y, --yes', 'Skip the confirmation prompt')
|
|
231
|
+
.action(async (nodeId, opts) => {
|
|
232
|
+
try {
|
|
233
|
+
await (0, diff_1.handleRevert)(nodeId, opts);
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
console.error(`❌ Revert failed: ${err.message}`);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
program
|
|
241
|
+
.command('activity')
|
|
242
|
+
.description('Show your local activity timeline — sessions and messages by day (local only, never pushed)')
|
|
243
|
+
.option('-p, --path <devmind_path>', 'Explicit path to the .devmind directory (auto-detected from cwd by default)')
|
|
244
|
+
.option('--since <days>', 'Only show messages from the last N days')
|
|
245
|
+
.action(async (opts) => {
|
|
246
|
+
try {
|
|
247
|
+
await (0, activity_1.handleActivity)(opts);
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
console.error(`❌ Activity failed: ${err.message}`);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
program
|
|
255
|
+
.command('feedback')
|
|
256
|
+
.description('Show feedback recorded from commit_changes — graph problems, product feedback, and indexer rule candidates (local only, never pushed)')
|
|
257
|
+
.option('-p, --path <devmind_path>', 'Explicit path to the .devmind directory (auto-detected from cwd by default)')
|
|
258
|
+
.option('--since <days>', 'Only show entries from the last N days')
|
|
259
|
+
.option('--all', 'Include graph-feedback entries already marked processed (default: unprocessed only)')
|
|
260
|
+
.action(async (opts) => {
|
|
261
|
+
try {
|
|
262
|
+
await (0, feedback_1.handleFeedback)(opts);
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
console.error(`❌ Feedback failed: ${err.message}`);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
154
269
|
program
|
|
155
270
|
.command('workflow')
|
|
156
271
|
.description('List, view, pause, and resume persistent feature workflows (interactive)')
|
|
@@ -200,6 +315,8 @@ program
|
|
|
200
315
|
.option('--from-scratch', 'Wipe ALL nodes, connections, history, and graph/history folders, then reindex from zero. Asks for confirmation unless --yes is passed.')
|
|
201
316
|
.option('--nodes-only', 'Only run Phase 1 (node/code extraction). No connections are built or touched.')
|
|
202
317
|
.option('--edges-only', 'Only run Phase 2 (connection resolution). Wipes existing connections and rebuilds them fresh across all current nodes. Requires nodes to already exist.')
|
|
318
|
+
.option('--describe', 'Only meaningful with --nodes-only: also run Phase 3 (description backfill) right after that structure-only extraction, using the SAME credentials — an optional extension when you want a --nodes-only run to be searchable immediately instead of running `devsmind describe` separately later. On a FULL run (neither --nodes-only nor --edges-only), Phase 3 always runs regardless of this flag — descriptions are mandatory there, since Phase 1/2 never write one and an undescribed "finished" index is not actually searchable by search_nodes\' description/vector layers. Not allowed with --edges-only (which never extracts nodes or resolves credentials).')
|
|
319
|
+
.option('--describe-batch-size <number>', 'Nodes described per LLM call during Phase 3 (default: 25)')
|
|
203
320
|
.option('--repos <names>', 'Comma-separated repo names to restrict this run to (standalone mode only). Composes with --nodes-only / --edges-only, or full. Not allowed with --from-scratch.')
|
|
204
321
|
.option('--rpm <number>', 'Max LLM requests per minute, paced proactively to avoid 429s (default: unthrottled — fires as fast as possible)')
|
|
205
322
|
.option('--yes', 'Skip the confirmation prompt for --from-scratch')
|
|
@@ -215,6 +332,10 @@ program
|
|
|
215
332
|
console.error('❌ Error: --from-scratch and --edges-only cannot be used together — --from-scratch wipes nodes, so there would be nothing to build edges from. Use --from-scratch alone, or --from-scratch --nodes-only, then --edges-only separately.');
|
|
216
333
|
process.exit(1);
|
|
217
334
|
}
|
|
335
|
+
if (opts.describe && opts.edgesOnly) {
|
|
336
|
+
console.error('❌ Error: --describe and --edges-only cannot be used together — --edges-only never extracts nodes or resolves LLM credentials, so there is nothing new to describe. Run `devsmind describe` separately if you need to backfill descriptions after an --edges-only run.');
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
218
339
|
try {
|
|
219
340
|
await (0, runner_1.runBackgroundIndexing)({
|
|
220
341
|
devmindPath,
|
|
@@ -228,6 +349,8 @@ program
|
|
|
228
349
|
fromScratch: !!opts.fromScratch,
|
|
229
350
|
nodesOnly: !!opts.nodesOnly,
|
|
230
351
|
edgesOnly: !!opts.edgesOnly,
|
|
352
|
+
describe: !!opts.describe,
|
|
353
|
+
describeBatchSize: opts.describeBatchSize ? parseInt(opts.describeBatchSize, 10) : undefined,
|
|
231
354
|
repos: opts.repos ? opts.repos.split(',').map(s => s.trim()).filter(Boolean) : undefined,
|
|
232
355
|
rpm: opts.rpm ? parseInt(opts.rpm, 10) : undefined,
|
|
233
356
|
yes: !!opts.yes
|