monomind 1.17.0 → 1.17.2

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 (94) hide show
  1. package/.claude/agents/engineering/engineering-security-engineer.md +1 -1
  2. package/.claude/commands/mastermind/_repeat.md +4 -0
  3. package/.claude/commands/mastermind/master.md +52 -1
  4. package/.claude/scheduled_tasks.lock +1 -1
  5. package/.claude/skills/mastermind/_repeat.md +2 -0
  6. package/package.json +1 -1
  7. package/packages/@monomind/cli/.claude/agents/engineering/engineering-security-engineer.md +1 -1
  8. package/packages/@monomind/cli/.claude/commands/mastermind/_repeat.md +4 -0
  9. package/packages/@monomind/cli/.claude/commands/mastermind/master.md +52 -1
  10. package/packages/@monomind/cli/.claude/skills/mastermind/_repeat.md +2 -0
  11. package/packages/@monomind/cli/dist/src/__tests__/browse-analyzer.test.js +42 -59
  12. package/packages/@monomind/cli/dist/src/agents/registry-builder.d.ts +8 -0
  13. package/packages/@monomind/cli/dist/src/agents/registry-builder.js +22 -0
  14. package/packages/@monomind/cli/dist/src/browser/dashboard/server.js +18 -0
  15. package/packages/@monomind/cli/dist/src/browser/dashboard/ui.html +37 -125
  16. package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.d.ts +17 -0
  17. package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +320 -0
  18. package/packages/@monomind/cli/dist/src/commands/agent-ops.d.ts +9 -0
  19. package/packages/@monomind/cli/dist/src/commands/agent-ops.js +329 -0
  20. package/packages/@monomind/cli/dist/src/commands/agent.js +5 -907
  21. package/packages/@monomind/cli/dist/src/commands/analyze-ast.d.ts +26 -0
  22. package/packages/@monomind/cli/dist/src/commands/analyze-ast.js +284 -0
  23. package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.d.ts +14 -0
  24. package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.js +295 -0
  25. package/packages/@monomind/cli/dist/src/commands/analyze-diff.d.ts +8 -0
  26. package/packages/@monomind/cli/dist/src/commands/analyze-diff.js +395 -0
  27. package/packages/@monomind/cli/dist/src/commands/analyze-graph.d.ts +14 -0
  28. package/packages/@monomind/cli/dist/src/commands/analyze-graph.js +304 -0
  29. package/packages/@monomind/cli/dist/src/commands/analyze-imports.d.ts +11 -0
  30. package/packages/@monomind/cli/dist/src/commands/analyze-imports.js +287 -0
  31. package/packages/@monomind/cli/dist/src/commands/analyze-symbols.d.ts +14 -0
  32. package/packages/@monomind/cli/dist/src/commands/analyze-symbols.js +302 -0
  33. package/packages/@monomind/cli/dist/src/commands/analyze.d.ts +38 -0
  34. package/packages/@monomind/cli/dist/src/commands/analyze.js +12 -1827
  35. package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.d.ts +26 -0
  36. package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.js +189 -0
  37. package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.d.ts +20 -0
  38. package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +432 -0
  39. package/packages/@monomind/cli/dist/src/commands/doctor.js +54 -943
  40. package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.d.ts +11 -0
  41. package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.js +242 -0
  42. package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.d.ts +35 -0
  43. package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.js +203 -0
  44. package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.d.ts +8 -0
  45. package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.js +233 -0
  46. package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.d.ts +12 -0
  47. package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.js +274 -0
  48. package/packages/@monomind/cli/dist/src/commands/hive-mind.js +10 -1129
  49. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.d.ts +4 -4
  50. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +19 -819
  51. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.d.ts +7 -0
  52. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.js +334 -0
  53. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.d.ts +7 -0
  54. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.js +399 -0
  55. package/packages/@monomind/cli/dist/src/commands/init-subcommands.d.ts +8 -0
  56. package/packages/@monomind/cli/dist/src/commands/init-subcommands.js +156 -0
  57. package/packages/@monomind/cli/dist/src/commands/init-upgrade.d.ts +6 -0
  58. package/packages/@monomind/cli/dist/src/commands/init-upgrade.js +203 -0
  59. package/packages/@monomind/cli/dist/src/commands/init-wizard.d.ts +6 -0
  60. package/packages/@monomind/cli/dist/src/commands/init-wizard.js +246 -0
  61. package/packages/@monomind/cli/dist/src/commands/init.js +6 -623
  62. package/packages/@monomind/cli/dist/src/commands/memory-admin.d.ts +10 -0
  63. package/packages/@monomind/cli/dist/src/commands/memory-admin.js +433 -0
  64. package/packages/@monomind/cli/dist/src/commands/memory-crud.d.ts +9 -0
  65. package/packages/@monomind/cli/dist/src/commands/memory-crud.js +342 -0
  66. package/packages/@monomind/cli/dist/src/commands/memory-list.d.ts +10 -0
  67. package/packages/@monomind/cli/dist/src/commands/memory-list.js +321 -0
  68. package/packages/@monomind/cli/dist/src/commands/memory-transfer.d.ts +9 -0
  69. package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +372 -0
  70. package/packages/@monomind/cli/dist/src/commands/memory.d.ts +6 -0
  71. package/packages/@monomind/cli/dist/src/commands/memory.js +10 -1441
  72. package/packages/@monomind/cli/dist/src/commands/neural-core.d.ts +8 -0
  73. package/packages/@monomind/cli/dist/src/commands/neural-core.js +274 -0
  74. package/packages/@monomind/cli/dist/src/commands/neural-optimize.d.ts +7 -0
  75. package/packages/@monomind/cli/dist/src/commands/neural-optimize.js +332 -0
  76. package/packages/@monomind/cli/dist/src/commands/neural-registry.d.ts +7 -0
  77. package/packages/@monomind/cli/dist/src/commands/neural-registry.js +290 -0
  78. package/packages/@monomind/cli/dist/src/commands/neural.js +3 -974
  79. package/packages/@monomind/cli/dist/src/commands/platforms.js +327 -7
  80. package/packages/@monomind/cli/dist/src/commands/security-cve.d.ts +6 -0
  81. package/packages/@monomind/cli/dist/src/commands/security-cve.js +310 -0
  82. package/packages/@monomind/cli/dist/src/commands/security-misc.d.ts +9 -0
  83. package/packages/@monomind/cli/dist/src/commands/security-misc.js +293 -0
  84. package/packages/@monomind/cli/dist/src/commands/security-scan.d.ts +18 -0
  85. package/packages/@monomind/cli/dist/src/commands/security-scan.js +328 -0
  86. package/packages/@monomind/cli/dist/src/commands/security.js +3 -958
  87. package/packages/@monomind/cli/dist/src/commands/session.js +1 -1
  88. package/packages/@monomind/cli/dist/src/commands/swarm.js +23 -17
  89. package/packages/@monomind/cli/dist/src/index.js +8 -37
  90. package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +77 -0
  91. package/packages/@monomind/cli/dist/src/parser.js +11 -6
  92. package/packages/@monomind/cli/dist/src/routing/llm-caller.js +1 -2
  93. package/packages/@monomind/cli/package.json +2 -3
  94. package/packages/@monomind/cli/scripts/understand-analyze.mjs +1 -1
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Analyze AST subcommand
3
+ * AST-based code analysis using monovector tree-sitter with regex fallback
4
+ */
5
+ import type { Command } from '../types.js';
6
+ /**
7
+ * Helper: Truncate file path for display
8
+ */
9
+ export declare function truncatePathAst(filePath: string, maxLen?: number): string;
10
+ /**
11
+ * Helper: Format complexity value with color coding
12
+ */
13
+ export declare function formatComplexityValueAst(value: number): string;
14
+ /**
15
+ * Helper: Get type marker for symbols
16
+ */
17
+ export declare function getTypeMarkerAst(type: string): string;
18
+ /**
19
+ * Helper: Get complexity rating text
20
+ */
21
+ export declare function getComplexityRatingAst(value: number): string;
22
+ /**
23
+ * AST analysis subcommand
24
+ */
25
+ export declare const astCommand: Command;
26
+ //# sourceMappingURL=analyze-ast.d.ts.map
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Analyze AST subcommand
3
+ * AST-based code analysis using monovector tree-sitter with regex fallback
4
+ */
5
+ import { output } from '../output.js';
6
+ import * as fs from 'fs/promises';
7
+ import { resolve } from 'path';
8
+ import { getASTAnalyzer, safeWriteOutputFile, scanSourceFiles, fallbackAnalyze } from './analyze.js';
9
+ /**
10
+ * Helper: Truncate file path for display
11
+ */
12
+ export function truncatePathAst(filePath, maxLen = 45) {
13
+ if (filePath.length <= maxLen)
14
+ return filePath;
15
+ return '...' + filePath.slice(-(maxLen - 3));
16
+ }
17
+ /**
18
+ * Helper: Format complexity value with color coding
19
+ */
20
+ export function formatComplexityValueAst(value) {
21
+ if (value <= 5)
22
+ return output.success(String(value));
23
+ if (value <= 10)
24
+ return output.warning(String(value));
25
+ return output.error(String(value));
26
+ }
27
+ /**
28
+ * Helper: Get type marker for symbols
29
+ */
30
+ export function getTypeMarkerAst(type) {
31
+ switch (type) {
32
+ case 'function': return output.success('fn');
33
+ case 'class': return output.info('class');
34
+ case 'variable': return output.dim('var');
35
+ case 'type': return output.highlight('type');
36
+ case 'interface': return output.highlight('iface');
37
+ default: return output.dim(type.slice(0, 5));
38
+ }
39
+ }
40
+ /**
41
+ * Helper: Get complexity rating text
42
+ */
43
+ export function getComplexityRatingAst(value) {
44
+ if (value <= 5)
45
+ return output.success('Simple');
46
+ if (value <= 10)
47
+ return output.warning('Moderate');
48
+ if (value <= 20)
49
+ return output.error('Complex');
50
+ return output.error(output.bold('Very Complex'));
51
+ }
52
+ /**
53
+ * AST analysis subcommand
54
+ */
55
+ export const astCommand = {
56
+ name: 'ast',
57
+ description: 'Analyze code using AST parsing (tree-sitter via monovector)',
58
+ options: [
59
+ {
60
+ name: 'complexity',
61
+ short: 'c',
62
+ description: 'Include complexity metrics',
63
+ type: 'boolean',
64
+ default: false,
65
+ },
66
+ {
67
+ name: 'symbols',
68
+ short: 's',
69
+ description: 'Include symbol extraction',
70
+ type: 'boolean',
71
+ default: false,
72
+ },
73
+ {
74
+ name: 'format',
75
+ short: 'f',
76
+ description: 'Output format (text, json, table)',
77
+ type: 'string',
78
+ default: 'text',
79
+ choices: ['text', 'json', 'table'],
80
+ },
81
+ {
82
+ name: 'output',
83
+ short: 'o',
84
+ description: 'Output file path',
85
+ type: 'string',
86
+ },
87
+ {
88
+ name: 'verbose',
89
+ short: 'v',
90
+ description: 'Show detailed analysis',
91
+ type: 'boolean',
92
+ default: false,
93
+ },
94
+ ],
95
+ examples: [
96
+ { command: 'monomind analyze ast src/', description: 'Analyze all files in src/' },
97
+ { command: 'monomind analyze ast src/index.ts --complexity', description: 'Analyze with complexity' },
98
+ { command: 'monomind analyze ast . --format json', description: 'JSON output' },
99
+ { command: 'monomind analyze ast src/ --symbols', description: 'Extract symbols' },
100
+ ],
101
+ action: async (ctx) => {
102
+ const targetPath = ctx.args[0] || ctx.cwd;
103
+ const showComplexity = ctx.flags.complexity;
104
+ const showSymbols = ctx.flags.symbols;
105
+ const formatType = ctx.flags.format || 'text';
106
+ const outputFile = ctx.flags.output;
107
+ const verbose = ctx.flags.verbose;
108
+ // If no specific flags, show summary
109
+ const showAll = !showComplexity && !showSymbols;
110
+ output.printInfo(`Analyzing: ${output.highlight(targetPath)}`);
111
+ output.writeln();
112
+ const spinner = output.createSpinner({ text: 'Parsing AST...', spinner: 'dots' });
113
+ spinner.start();
114
+ try {
115
+ const astModule = await getASTAnalyzer();
116
+ if (!astModule) {
117
+ spinner.stop();
118
+ output.printWarning('AST analyzer not available, using regex fallback');
119
+ }
120
+ // Resolve path and check if file or directory
121
+ const resolvedPath = resolve(targetPath);
122
+ const stat = await fs.stat(resolvedPath);
123
+ const isDirectory = stat.isDirectory();
124
+ let results = [];
125
+ if (isDirectory) {
126
+ // Scan directory for source files
127
+ const files = await scanSourceFiles(resolvedPath);
128
+ spinner.stop();
129
+ output.printInfo(`Found ${files.length} source files`);
130
+ spinner.start();
131
+ for (const file of files.slice(0, 100)) {
132
+ try {
133
+ const content = await fs.readFile(file, 'utf-8');
134
+ if (astModule) {
135
+ const analyzer = astModule.createASTAnalyzer();
136
+ const analysis = analyzer.analyze(content, file);
137
+ results.push(analysis);
138
+ }
139
+ else {
140
+ // Fallback analysis
141
+ results.push(fallbackAnalyze(content, file));
142
+ }
143
+ }
144
+ catch {
145
+ // Skip files that can't be analyzed
146
+ }
147
+ }
148
+ }
149
+ else {
150
+ // Single file
151
+ const content = await fs.readFile(resolvedPath, 'utf-8');
152
+ if (astModule) {
153
+ const analyzer = astModule.createASTAnalyzer();
154
+ const analysis = analyzer.analyze(content, resolvedPath);
155
+ results.push(analysis);
156
+ }
157
+ else {
158
+ results.push(fallbackAnalyze(content, resolvedPath));
159
+ }
160
+ }
161
+ spinner.stop();
162
+ if (results.length === 0) {
163
+ output.printWarning('No files analyzed');
164
+ return { success: true };
165
+ }
166
+ // Calculate totals
167
+ const totals = {
168
+ files: results.length,
169
+ functions: results.reduce((sum, r) => sum + r.functions.length, 0),
170
+ classes: results.reduce((sum, r) => sum + r.classes.length, 0),
171
+ imports: results.reduce((sum, r) => sum + r.imports.length, 0),
172
+ avgComplexity: results.reduce((sum, r) => sum + r.complexity.cyclomatic, 0) / results.length,
173
+ totalLoc: results.reduce((sum, r) => sum + r.complexity.loc, 0),
174
+ };
175
+ // JSON output
176
+ if (formatType === 'json') {
177
+ const jsonOutput = { files: results, totals };
178
+ if (outputFile) {
179
+ await safeWriteOutputFile(outputFile, JSON.stringify(jsonOutput, null, 2));
180
+ output.printSuccess(`Results written to ${outputFile}`);
181
+ }
182
+ else {
183
+ output.printJson(jsonOutput);
184
+ }
185
+ return { success: true, data: jsonOutput };
186
+ }
187
+ // Summary box
188
+ output.printBox([
189
+ `Files analyzed: ${totals.files}`,
190
+ `Functions: ${totals.functions}`,
191
+ `Classes: ${totals.classes}`,
192
+ `Total LOC: ${totals.totalLoc}`,
193
+ `Avg Complexity: ${formatComplexityValueAst(Math.round(totals.avgComplexity))}`,
194
+ ].join('\n'), 'AST Analysis Summary');
195
+ // Complexity view
196
+ if (showComplexity || showAll) {
197
+ output.writeln();
198
+ output.writeln(output.bold('Complexity by File'));
199
+ output.writeln(output.dim('-'.repeat(60)));
200
+ const complexityData = results
201
+ .map(r => ({
202
+ file: truncatePathAst(r.filePath),
203
+ cyclomatic: r.complexity.cyclomatic,
204
+ cognitive: r.complexity.cognitive,
205
+ loc: r.complexity.loc,
206
+ rating: getComplexityRatingAst(r.complexity.cyclomatic),
207
+ }))
208
+ .sort((a, b) => b.cyclomatic - a.cyclomatic)
209
+ .slice(0, 15);
210
+ output.printTable({
211
+ columns: [
212
+ { key: 'file', header: 'File', width: 40 },
213
+ { key: 'cyclomatic', header: 'Cyclo', width: 8, align: 'right', format: (v) => formatComplexityValueAst(v) },
214
+ { key: 'cognitive', header: 'Cogni', width: 8, align: 'right' },
215
+ { key: 'loc', header: 'LOC', width: 8, align: 'right' },
216
+ { key: 'rating', header: 'Rating', width: 15 },
217
+ ],
218
+ data: complexityData,
219
+ });
220
+ if (results.length > 15) {
221
+ output.writeln(output.dim(` ... and ${results.length - 15} more files`));
222
+ }
223
+ }
224
+ // Symbols view
225
+ if (showSymbols || showAll) {
226
+ output.writeln();
227
+ output.writeln(output.bold('Extracted Symbols'));
228
+ output.writeln(output.dim('-'.repeat(60)));
229
+ const allSymbols = [];
230
+ for (const r of results) {
231
+ for (const fn of r.functions) {
232
+ allSymbols.push({ name: fn.name, type: 'function', file: truncatePathAst(r.filePath, 30), line: fn.startLine });
233
+ }
234
+ for (const cls of r.classes) {
235
+ allSymbols.push({ name: cls.name, type: 'class', file: truncatePathAst(r.filePath, 30), line: cls.startLine });
236
+ }
237
+ }
238
+ const displaySymbols = allSymbols.slice(0, 20);
239
+ output.printTable({
240
+ columns: [
241
+ { key: 'type', header: 'Type', width: 8, format: (v) => getTypeMarkerAst(v) },
242
+ { key: 'name', header: 'Symbol', width: 30 },
243
+ { key: 'file', header: 'File', width: 35 },
244
+ { key: 'line', header: 'Line', width: 8, align: 'right' },
245
+ ],
246
+ data: displaySymbols,
247
+ });
248
+ if (allSymbols.length > 20) {
249
+ output.writeln(output.dim(` ... and ${allSymbols.length - 20} more symbols`));
250
+ }
251
+ }
252
+ // Verbose output
253
+ if (verbose) {
254
+ output.writeln();
255
+ output.writeln(output.bold('Import Analysis'));
256
+ output.writeln(output.dim('-'.repeat(60)));
257
+ const importCounts = new Map();
258
+ for (const r of results) {
259
+ for (const imp of r.imports) {
260
+ importCounts.set(imp, (importCounts.get(imp) || 0) + 1);
261
+ }
262
+ }
263
+ const topImports = Array.from(importCounts.entries())
264
+ .sort((a, b) => b[1] - a[1])
265
+ .slice(0, 10);
266
+ for (const [imp, count] of topImports) {
267
+ output.writeln(` ${output.highlight(count.toString().padStart(3))} ${imp}`);
268
+ }
269
+ }
270
+ if (outputFile) {
271
+ await safeWriteOutputFile(outputFile, JSON.stringify({ files: results, totals }, null, 2));
272
+ output.printSuccess(`Results written to ${outputFile}`);
273
+ }
274
+ return { success: true, data: { files: results, totals } };
275
+ }
276
+ catch (error) {
277
+ spinner.stop();
278
+ const message = error instanceof Error ? error.message : String(error);
279
+ output.printError(`AST analysis failed: ${message}`);
280
+ return { success: false, exitCode: 1 };
281
+ }
282
+ },
283
+ };
284
+ //# sourceMappingURL=analyze-ast.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Analyze boundaries and modules subcommands
3
+ * MinCut boundary detection and Louvain community detection
4
+ */
5
+ import type { Command } from '../types.js';
6
+ /**
7
+ * Analyze code boundaries using MinCut algorithm
8
+ */
9
+ export declare const boundariesCommand: Command;
10
+ /**
11
+ * Analyze modules/communities using Louvain algorithm
12
+ */
13
+ export declare const modulesCommand: Command;
14
+ //# sourceMappingURL=analyze-boundaries.d.ts.map
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Analyze boundaries and modules subcommands
3
+ * MinCut boundary detection and Louvain community detection
4
+ */
5
+ import { output } from '../output.js';
6
+ import { resolve } from 'path';
7
+ import { getGraphAnalyzer, safeWriteOutputFile } from './analyze.js';
8
+ /**
9
+ * Analyze code boundaries using MinCut algorithm
10
+ */
11
+ export const boundariesCommand = {
12
+ name: 'boundaries',
13
+ aliases: ['boundary', 'mincut'],
14
+ description: 'Find natural code boundaries using MinCut algorithm',
15
+ options: [
16
+ {
17
+ name: 'partitions',
18
+ short: 'p',
19
+ description: 'Number of partitions to find',
20
+ type: 'number',
21
+ default: 2,
22
+ },
23
+ {
24
+ name: 'output',
25
+ short: 'o',
26
+ description: 'Output file path',
27
+ type: 'string',
28
+ },
29
+ {
30
+ name: 'format',
31
+ short: 'f',
32
+ description: 'Output format (text, json, dot)',
33
+ type: 'string',
34
+ default: 'text',
35
+ choices: ['text', 'json', 'dot'],
36
+ },
37
+ ],
38
+ examples: [
39
+ { command: 'monomind analyze boundaries src/', description: 'Find code boundaries in src/' },
40
+ { command: 'monomind analyze boundaries -p 3 src/', description: 'Find 3 partitions' },
41
+ { command: 'monomind analyze boundaries -f dot -o graph.dot src/', description: 'Export to DOT format' },
42
+ ],
43
+ action: async (ctx) => {
44
+ const targetDir = ctx.args[0] || ctx.cwd;
45
+ const rawPartitions = ctx.flags.partitions || 2;
46
+ const numPartitions = Number.isFinite(rawPartitions) ? Math.max(1, Math.min(rawPartitions, 100)) : 2;
47
+ const outputFile = ctx.flags.output;
48
+ const format = ctx.flags.format || 'text';
49
+ output.printInfo(`Analyzing code boundaries in: ${output.highlight(targetDir)}`);
50
+ output.writeln();
51
+ const spinner = output.createSpinner({ text: 'Building dependency graph...', spinner: 'dots' });
52
+ spinner.start();
53
+ try {
54
+ const analyzer = await getGraphAnalyzer();
55
+ if (!analyzer) {
56
+ spinner.stop();
57
+ output.printError('Graph analyzer module not available');
58
+ return { success: false, exitCode: 1 };
59
+ }
60
+ const result = await analyzer.analyzeGraph(resolve(targetDir), {
61
+ includeBoundaries: true,
62
+ includeModules: false,
63
+ numPartitions,
64
+ });
65
+ spinner.stop();
66
+ // Handle different output formats
67
+ if (format === 'json') {
68
+ const jsonOutput = {
69
+ boundaries: result.boundaries,
70
+ statistics: result.statistics,
71
+ circularDependencies: result.circularDependencies,
72
+ };
73
+ if (outputFile) {
74
+ await safeWriteOutputFile(outputFile, JSON.stringify(jsonOutput, null, 2));
75
+ output.printSuccess(`Results written to ${outputFile}`);
76
+ }
77
+ else {
78
+ output.printJson(jsonOutput);
79
+ }
80
+ return { success: true, data: jsonOutput };
81
+ }
82
+ if (format === 'dot') {
83
+ const dotOutput = analyzer.exportToDot(result, {
84
+ includeLabels: true,
85
+ highlightCycles: true,
86
+ });
87
+ if (outputFile) {
88
+ await safeWriteOutputFile(outputFile, dotOutput);
89
+ output.printSuccess(`DOT graph written to ${outputFile}`);
90
+ output.writeln(output.dim('Visualize with: dot -Tpng -o graph.png ' + outputFile));
91
+ }
92
+ else {
93
+ output.writeln(dotOutput);
94
+ }
95
+ return { success: true };
96
+ }
97
+ // Text format (default)
98
+ output.printBox([
99
+ `Files analyzed: ${result.statistics.nodeCount}`,
100
+ `Dependencies: ${result.statistics.edgeCount}`,
101
+ `Avg degree: ${result.statistics.avgDegree.toFixed(2)}`,
102
+ `Density: ${(result.statistics.density * 100).toFixed(2)}%`,
103
+ `Components: ${result.statistics.componentCount}`,
104
+ ].join('\n'), 'Graph Statistics');
105
+ if (result.boundaries && result.boundaries.length > 0) {
106
+ output.writeln();
107
+ output.writeln(output.bold('MinCut Boundaries'));
108
+ output.writeln();
109
+ for (let i = 0; i < result.boundaries.length; i++) {
110
+ const boundary = result.boundaries[i];
111
+ output.writeln(output.bold(`Boundary ${i + 1} (cut value: ${boundary.cutValue})`));
112
+ output.writeln();
113
+ output.writeln(output.dim('Partition 1:'));
114
+ const p1Display = boundary.partition1.slice(0, 10);
115
+ output.printList(p1Display);
116
+ if (boundary.partition1.length > 10) {
117
+ output.writeln(output.dim(` ... and ${boundary.partition1.length - 10} more files`));
118
+ }
119
+ output.writeln();
120
+ output.writeln(output.dim('Partition 2:'));
121
+ const p2Display = boundary.partition2.slice(0, 10);
122
+ output.printList(p2Display);
123
+ if (boundary.partition2.length > 10) {
124
+ output.writeln(output.dim(` ... and ${boundary.partition2.length - 10} more files`));
125
+ }
126
+ output.writeln();
127
+ output.writeln(output.success('Suggestion:'));
128
+ output.writeln(` ${boundary.suggestion}`);
129
+ output.writeln();
130
+ }
131
+ }
132
+ // Show circular dependencies
133
+ if (result.circularDependencies.length > 0) {
134
+ output.writeln();
135
+ output.writeln(output.bold(output.warning('Circular Dependencies Detected')));
136
+ output.writeln();
137
+ for (const cycle of result.circularDependencies.slice(0, 5)) {
138
+ const severityColor = cycle.severity === 'high' ? output.error : cycle.severity === 'medium' ? output.warning : output.dim;
139
+ output.writeln(`${severityColor(`[${cycle.severity.toUpperCase()}]`)} ${cycle.cycle.join(' -> ')}`);
140
+ output.writeln(output.dim(` ${cycle.suggestion}`));
141
+ output.writeln();
142
+ }
143
+ if (result.circularDependencies.length > 5) {
144
+ output.writeln(output.dim(`... and ${result.circularDependencies.length - 5} more cycles`));
145
+ }
146
+ }
147
+ if (outputFile) {
148
+ await safeWriteOutputFile(outputFile, JSON.stringify(result, null, 2));
149
+ output.printSuccess(`Full results written to ${outputFile}`);
150
+ }
151
+ return { success: true, data: result };
152
+ }
153
+ catch (error) {
154
+ spinner.stop();
155
+ const message = error instanceof Error ? error.message : String(error);
156
+ output.printError(`Analysis failed: ${message}`);
157
+ return { success: false, exitCode: 1 };
158
+ }
159
+ },
160
+ };
161
+ /**
162
+ * Analyze modules/communities using Louvain algorithm
163
+ */
164
+ export const modulesCommand = {
165
+ name: 'modules',
166
+ aliases: ['communities', 'louvain'],
167
+ description: 'Detect module communities using Louvain algorithm',
168
+ options: [
169
+ {
170
+ name: 'output',
171
+ short: 'o',
172
+ description: 'Output file path',
173
+ type: 'string',
174
+ },
175
+ {
176
+ name: 'format',
177
+ short: 'f',
178
+ description: 'Output format (text, json, dot)',
179
+ type: 'string',
180
+ default: 'text',
181
+ choices: ['text', 'json', 'dot'],
182
+ },
183
+ {
184
+ name: 'min-size',
185
+ short: 'm',
186
+ description: 'Minimum community size to display',
187
+ type: 'number',
188
+ default: 2,
189
+ },
190
+ ],
191
+ examples: [
192
+ { command: 'monomind analyze modules src/', description: 'Detect module communities' },
193
+ { command: 'monomind analyze modules -f dot -o modules.dot src/', description: 'Export colored DOT graph' },
194
+ { command: 'monomind analyze modules -m 3 src/', description: 'Only show communities with 3+ files' },
195
+ ],
196
+ action: async (ctx) => {
197
+ const targetDir = ctx.args[0] || ctx.cwd;
198
+ const outputFile = ctx.flags.output;
199
+ const format = ctx.flags.format || 'text';
200
+ const minSize = ctx.flags['min-size'] || 2;
201
+ output.printInfo(`Detecting module communities in: ${output.highlight(targetDir)}`);
202
+ output.writeln();
203
+ const spinner = output.createSpinner({ text: 'Building dependency graph...', spinner: 'dots' });
204
+ spinner.start();
205
+ try {
206
+ const analyzer = await getGraphAnalyzer();
207
+ if (!analyzer) {
208
+ spinner.stop();
209
+ output.printError('Graph analyzer module not available');
210
+ return { success: false, exitCode: 1 };
211
+ }
212
+ const result = await analyzer.analyzeGraph(resolve(targetDir), {
213
+ includeBoundaries: false,
214
+ includeModules: true,
215
+ });
216
+ spinner.stop();
217
+ // Filter communities by size
218
+ const communities = result.communities?.filter(c => c.members.length >= minSize) || [];
219
+ // Handle different output formats
220
+ if (format === 'json') {
221
+ const jsonOutput = {
222
+ communities,
223
+ statistics: result.statistics,
224
+ };
225
+ if (outputFile) {
226
+ await safeWriteOutputFile(outputFile, JSON.stringify(jsonOutput, null, 2));
227
+ output.printSuccess(`Results written to ${outputFile}`);
228
+ }
229
+ else {
230
+ output.printJson(jsonOutput);
231
+ }
232
+ return { success: true, data: jsonOutput };
233
+ }
234
+ if (format === 'dot') {
235
+ const dotOutput = analyzer.exportToDot(result, {
236
+ includeLabels: true,
237
+ colorByCommunity: true,
238
+ highlightCycles: true,
239
+ });
240
+ if (outputFile) {
241
+ await safeWriteOutputFile(outputFile, dotOutput);
242
+ output.printSuccess(`DOT graph written to ${outputFile}`);
243
+ output.writeln(output.dim('Visualize with: dot -Tpng -o modules.png ' + outputFile));
244
+ }
245
+ else {
246
+ output.writeln(dotOutput);
247
+ }
248
+ return { success: true };
249
+ }
250
+ // Text format (default)
251
+ output.printBox([
252
+ `Files analyzed: ${result.statistics.nodeCount}`,
253
+ `Dependencies: ${result.statistics.edgeCount}`,
254
+ `Communities found: ${result.communities?.length || 0}`,
255
+ `Showing: ${communities.length} (min size: ${minSize})`,
256
+ ].join('\n'), 'Module Detection Results');
257
+ if (communities.length > 0) {
258
+ output.writeln();
259
+ output.writeln(output.bold('Detected Communities'));
260
+ output.writeln();
261
+ for (const community of communities.slice(0, 10)) {
262
+ const cohesionIndicator = community.cohesion > 0.5 ? output.success('High') :
263
+ community.cohesion > 0.2 ? output.warning('Medium') : output.dim('Low');
264
+ output.writeln(output.bold(`Community ${community.id}: ${community.suggestedName || 'unnamed'}`));
265
+ output.writeln(` ${output.dim('Cohesion:')} ${cohesionIndicator} (${(community.cohesion * 100).toFixed(1)}%)`);
266
+ output.writeln(` ${output.dim('Central node:')} ${community.centralNode || 'none'}`);
267
+ output.writeln(` ${output.dim('Members:')} ${community.members.length} files`);
268
+ const displayMembers = community.members.slice(0, 5);
269
+ for (const member of displayMembers) {
270
+ output.writeln(` - ${member}`);
271
+ }
272
+ if (community.members.length > 5) {
273
+ output.writeln(output.dim(` ... and ${community.members.length - 5} more`));
274
+ }
275
+ output.writeln();
276
+ }
277
+ if (communities.length > 10) {
278
+ output.writeln(output.dim(`... and ${communities.length - 10} more communities`));
279
+ }
280
+ }
281
+ if (outputFile) {
282
+ await safeWriteOutputFile(outputFile, JSON.stringify(result, null, 2));
283
+ output.printSuccess(`Full results written to ${outputFile}`);
284
+ }
285
+ return { success: true, data: result };
286
+ }
287
+ catch (error) {
288
+ spinner.stop();
289
+ const message = error instanceof Error ? error.message : String(error);
290
+ output.printError(`Analysis failed: ${message}`);
291
+ return { success: false, exitCode: 1 };
292
+ }
293
+ },
294
+ };
295
+ //# sourceMappingURL=analyze-boundaries.js.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Analyze diff and code subcommands
3
+ * Handles git diff risk assessment and static code quality analysis
4
+ */
5
+ import type { Command } from '../types.js';
6
+ export declare const diffCommand: Command;
7
+ export declare const codeCommand: Command;
8
+ //# sourceMappingURL=analyze-diff.d.ts.map