minovative-mind-cli 1.5.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +51 -45
  2. package/dist/commands/chat.js +10 -5
  3. package/dist/services/agent/slashCommands.js +163 -37
  4. package/dist/services/agent/toolLoop.d.ts +1 -1
  5. package/dist/services/agent/toolLoop.js +7 -2
  6. package/dist/services/agent/types.d.ts +2 -0
  7. package/dist/services/agent-tools.d.ts +9 -4
  8. package/dist/services/agent-tools.js +145 -21
  9. package/dist/services/agent.d.ts +8 -0
  10. package/dist/services/agent.js +294 -38
  11. package/dist/services/ai.d.ts +19 -5
  12. package/dist/services/ai.js +167 -35
  13. package/dist/services/changeLogger.d.ts +142 -0
  14. package/dist/services/changeLogger.js +132 -3
  15. package/dist/services/contextAgent.d.ts +6 -1
  16. package/dist/services/contextAgent.js +95 -14
  17. package/dist/services/embeddingIndex.d.ts +82 -0
  18. package/dist/services/embeddingIndex.js +613 -0
  19. package/dist/services/investigationComplexity.d.ts +45 -0
  20. package/dist/services/investigationComplexity.js +91 -0
  21. package/dist/services/metrics.d.ts +18 -0
  22. package/dist/services/metrics.js +7 -0
  23. package/dist/services/orchestration/fileLockRegistry.d.ts +125 -0
  24. package/dist/services/orchestration/fileLockRegistry.js +276 -0
  25. package/dist/services/orchestration/investigationAgent.d.ts +85 -0
  26. package/dist/services/orchestration/investigationAgent.js +359 -0
  27. package/dist/services/orchestration/investigationOrchestrator.d.ts +53 -0
  28. package/dist/services/orchestration/investigationOrchestrator.js +180 -0
  29. package/dist/services/orchestration/messageBus.d.ts +162 -0
  30. package/dist/services/orchestration/messageBus.js +225 -0
  31. package/dist/services/orchestration/orchestrator.d.ts +45 -0
  32. package/dist/services/orchestration/orchestrator.js +214 -0
  33. package/dist/services/orchestration/readCache.d.ts +79 -0
  34. package/dist/services/orchestration/readCache.js +108 -0
  35. package/dist/services/orchestration/scopedTools.d.ts +57 -0
  36. package/dist/services/orchestration/scopedTools.js +172 -0
  37. package/dist/services/orchestration/subAgent.d.ts +58 -0
  38. package/dist/services/orchestration/subAgent.js +187 -0
  39. package/dist/services/orchestration/taskGraph.d.ts +129 -0
  40. package/dist/services/orchestration/taskGraph.js +254 -0
  41. package/dist/services/proxyClient.d.ts +25 -0
  42. package/dist/services/proxyClient.js +60 -0
  43. package/dist/utils/asyncContext.d.ts +16 -0
  44. package/dist/utils/asyncContext.js +25 -0
  45. package/dist/utils/config.d.ts +3 -1
  46. package/dist/utils/config.js +3 -1
  47. package/dist/utils/contextPrompts.js +3 -2
  48. package/dist/utils/dependencyTracer/modules/api.d.ts +9 -0
  49. package/dist/utils/dependencyTracer/modules/api.js +62 -0
  50. package/dist/utils/dependencyTracer/modules/graph.d.ts +9 -0
  51. package/dist/utils/dependencyTracer/modules/graph.js +23 -0
  52. package/dist/utils/dependencyTracer/modules/profiles.d.ts +7 -0
  53. package/dist/utils/dependencyTracer/modules/profiles.js +120 -0
  54. package/dist/utils/dependencyTracer/modules/resolver.d.ts +7 -0
  55. package/dist/utils/dependencyTracer/modules/resolver.js +51 -0
  56. package/dist/utils/dependencyTracer/modules/types.d.ts +4 -0
  57. package/dist/utils/dependencyTracer/modules/types.js +1 -0
  58. package/dist/utils/dependencyTracer/modules/walker.d.ts +1 -0
  59. package/dist/utils/dependencyTracer/modules/walker.js +48 -0
  60. package/dist/utils/dependencyTracer.js +31 -17
  61. package/dist/utils/excludedExtensions.js +0 -1
  62. package/dist/utils/historyPrompt.d.ts +9 -0
  63. package/dist/utils/historyPrompt.js +87 -0
  64. package/dist/utils/logo.d.ts +2 -0
  65. package/dist/utils/logo.js +31 -10
  66. package/dist/utils/paste.d.ts +21 -0
  67. package/dist/utils/paste.js +22 -1
  68. package/dist/utils/profiles.d.ts +2 -0
  69. package/dist/utils/profiles.js +44 -0
  70. package/dist/utils/projectStorage.js +10 -7
  71. package/dist/utils/systemPrompts.d.ts +6 -3
  72. package/dist/utils/systemPrompts.js +106 -5
  73. package/dist/utils/types.d.ts +33 -0
  74. package/dist/utils/types.js +1 -0
  75. package/oclif.manifest.json +2 -2
  76. package/package.json +5 -3
@@ -0,0 +1,23 @@
1
+ export function bfsTraverse(nodes, startFile, direction, maxDepth) {
2
+ const visited = new Set();
3
+ visited.add(startFile);
4
+ const queue = [{ file: startFile, depth: 0 }];
5
+ const result = [];
6
+ while (queue.length > 0) {
7
+ const { file, depth } = queue.shift();
8
+ if (depth >= maxDepth)
9
+ continue;
10
+ const node = nodes.get(file);
11
+ if (!node)
12
+ continue;
13
+ const neighbors = node[direction];
14
+ for (const neighbor of neighbors) {
15
+ if (!visited.has(neighbor)) {
16
+ visited.add(neighbor);
17
+ result.push(neighbor);
18
+ queue.push({ file: neighbor, depth: depth + 1 });
19
+ }
20
+ }
21
+ }
22
+ return result;
23
+ }
@@ -0,0 +1,7 @@
1
+ export interface LanguageProfile {
2
+ extensions: string[];
3
+ patterns: RegExp[];
4
+ normalizeSpecifier?: (specifier: string, sourceFile: string) => string;
5
+ }
6
+ export declare const LANGUAGE_PROFILES: LanguageProfile[];
7
+ export declare const PROFILE_BY_EXT: Map<string, LanguageProfile[]>;
@@ -0,0 +1,120 @@
1
+ export const LANGUAGE_PROFILES = [
2
+ {
3
+ extensions: ['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs', '.mts', '.cts'],
4
+ patterns: [
5
+ /import\s+(?:[\s\S]*?\s+from\s+)?['"](?<specifier>[^'"]+)['"]/gm,
6
+ /import\s*\(\s*['"](?<specifier>[^'"]+)['"]\s*\)/gm,
7
+ /require\s*\(\s*['"](?<specifier>[^'"]+)['"]\s*\)/gm,
8
+ /export\s+(?:[\s\S]*?\s+from\s+)['"](?<specifier>[^'"]+)['"]/gm,
9
+ ],
10
+ },
11
+ {
12
+ extensions: ['.py'],
13
+ patterns: [
14
+ /^from\s+(?<specifier>[^\s]+)\s+import\s/gm,
15
+ /^import\s+(?!.*\sfrom\s)(?<specifier>[^\s,]+)/gm,
16
+ ],
17
+ normalizeSpecifier: (spec) => {
18
+ if (spec.startsWith('.')) {
19
+ const dots = spec.match(/^\.+/)[0];
20
+ const rest = spec.slice(dots.length);
21
+ const prefix = dots.length === 1 ? './' : '../'.repeat(dots.length - 1);
22
+ return rest ? `${prefix}${rest.replace(/\./g, '/')}.py` : prefix.slice(0, -1);
23
+ }
24
+ return spec.replace(/\./g, '/') + '.py';
25
+ },
26
+ },
27
+ {
28
+ extensions: ['.rs'],
29
+ patterns: [
30
+ /use\s+(?<specifier>(?:crate|self|super)(?:::[a-zA-Z_][a-zA-Z0-9_]*)+)/gm,
31
+ /mod\s+(?<specifier>[a-zA-Z_][a-zA-Z0-9_]*)\s*;/gm,
32
+ ],
33
+ normalizeSpecifier: (spec) => {
34
+ if (!spec.includes('::')) {
35
+ return spec + '.rs';
36
+ }
37
+ const parts = spec
38
+ .replace(/^crate::/, '')
39
+ .replace(/^(self|super)::/, '')
40
+ .split('::');
41
+ return parts.join('/') + '.rs';
42
+ },
43
+ },
44
+ {
45
+ extensions: ['.go'],
46
+ patterns: [
47
+ /import\s+(?:[a-zA-Z_][a-zA-Z0-9_]*\s+)?["'](?<specifier>[^"']+)["']/gm,
48
+ /^\s*(?:[a-zA-Z_][a-zA-Z0-9_]*\s+)?["'](?<specifier>[^"']+)["']\s*$/gm,
49
+ ],
50
+ },
51
+ {
52
+ extensions: ['.c', '.h', '.cpp', '.hpp', '.cc', '.cxx', '.hh', '.hxx'],
53
+ patterns: [
54
+ /^\s*#\s*include\s+"(?<specifier>[^"]+)"/gm,
55
+ /^\s*#\s*include\s+<(?<specifier>[^>]+)>/gm,
56
+ ],
57
+ },
58
+ {
59
+ extensions: ['.java', '.kt', '.kts'],
60
+ patterns: [
61
+ /^import\s+(?:static\s+)?(?<specifier>[a-zA-Z_][a-zA-Z0-9_.]*\.[a-zA-Z_][a-zA-Z0-9_]*)/gm,
62
+ ],
63
+ normalizeSpecifier: (spec) => {
64
+ return spec.replace(/\./g, '/') + '.java';
65
+ },
66
+ },
67
+ {
68
+ extensions: ['.css', '.scss', '.sass', '.less', '.styl'],
69
+ patterns: [
70
+ /@import\s+(?:url\s*\(\s*)?['"](?<specifier>[^'"]+)['"]/gm,
71
+ /@use\s+['"](?<specifier>[^'"]+)['"]/gm,
72
+ /@forward\s+['"](?<specifier>[^'"]+)['"]/gm,
73
+ ],
74
+ },
75
+ {
76
+ extensions: ['.rb'],
77
+ patterns: [
78
+ /require\s+['"](?<specifier>[^'"]+)['"]/gm,
79
+ /require_relative\s+['"](?<specifier>[^'"]+)['"]/gm,
80
+ /load\s+['"](?<specifier>[^'"]+)['"]/gm,
81
+ ],
82
+ },
83
+ {
84
+ extensions: ['.php'],
85
+ patterns: [
86
+ /use\s+(?<specifier>[A-Z][a-zA-Z0-9_\\]+)/gm,
87
+ /(?:include|require)(?:_once)?\s+['"](?<specifier>[^'"]+)['"]/gm,
88
+ /(?:include|require)(?:_once)?\s*\(\s*['"](?<specifier>[^'"]+)['"]\s*\)/gm,
89
+ ],
90
+ normalizeSpecifier: (spec) => {
91
+ if (spec.includes('\\')) {
92
+ return spec.replace(/\\/g, '/') + '.php';
93
+ }
94
+ return spec;
95
+ },
96
+ },
97
+ {
98
+ extensions: ['.swift'],
99
+ patterns: [
100
+ /^import\s+(?:class\s+|struct\s+|enum\s+|protocol\s+|typealias\s+|func\s+|var\s+|let\s+)?(?<specifier>[a-zA-Z_][a-zA-Z0-9_.]*)/gm,
101
+ ],
102
+ },
103
+ {
104
+ extensions: ['.dart'],
105
+ patterns: [
106
+ /import\s+['"](?<specifier>[^'"]+)['"]/gm,
107
+ /export\s+['"](?<specifier>[^'"]+)['"]/gm,
108
+ /part\s+['"](?<specifier>[^'"]+)['"]/gm,
109
+ /part\s+of\s+['"](?<specifier>[^'"]+)['"]/gm,
110
+ ],
111
+ },
112
+ ];
113
+ export const PROFILE_BY_EXT = new Map();
114
+ for (const profile of LANGUAGE_PROFILES) {
115
+ for (const ext of profile.extensions) {
116
+ const existing = PROFILE_BY_EXT.get(ext) || [];
117
+ existing.push(profile);
118
+ PROFILE_BY_EXT.set(ext, existing);
119
+ }
120
+ }
@@ -0,0 +1,7 @@
1
+ export interface AliasMapping {
2
+ prefix: string;
3
+ targets: string[];
4
+ }
5
+ export declare function loadPathAliases(workspaceRoot: string): Promise<AliasMapping[]>;
6
+ export declare function fileExists(filePath: string): Promise<boolean>;
7
+ export declare function normalizeSlashes(p: string): string;
@@ -0,0 +1,51 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ let cachedAliases = null;
4
+ let cachedAliasRoot = null;
5
+ export async function loadPathAliases(workspaceRoot) {
6
+ if (cachedAliasRoot === workspaceRoot && cachedAliases !== null) {
7
+ return cachedAliases;
8
+ }
9
+ const aliases = [];
10
+ for (const configName of ['tsconfig.json', 'jsconfig.json']) {
11
+ try {
12
+ const configPath = path.join(workspaceRoot, configName);
13
+ const raw = await fs.readFile(configPath, 'utf-8');
14
+ const cleaned = raw.replace(/\/\/.*$/gm, '').replace(/,\s*([\]}])/g, '');
15
+ const config = JSON.parse(cleaned);
16
+ const paths = config?.compilerOptions?.paths;
17
+ const baseUrl = config?.compilerOptions?.baseUrl || '.';
18
+ if (paths && typeof paths === 'object') {
19
+ for (const [pattern, targets] of Object.entries(paths)) {
20
+ if (!Array.isArray(targets))
21
+ continue;
22
+ const prefix = pattern.replace(/\*$/, '');
23
+ const resolvedTargets = targets.map((t) => {
24
+ const targetBase = t.replace(/\*$/, '');
25
+ return path.join(baseUrl, targetBase);
26
+ });
27
+ aliases.push({ prefix, targets: resolvedTargets });
28
+ }
29
+ }
30
+ break;
31
+ }
32
+ catch {
33
+ // Config doesn't exist or is invalid — try the next one
34
+ }
35
+ }
36
+ cachedAliases = aliases;
37
+ cachedAliasRoot = workspaceRoot;
38
+ return aliases;
39
+ }
40
+ export async function fileExists(filePath) {
41
+ try {
42
+ await fs.access(filePath);
43
+ return true;
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
49
+ export function normalizeSlashes(p) {
50
+ return p.replace(/\\/g, '/');
51
+ }
@@ -0,0 +1,4 @@
1
+ export interface DependencyNode {
2
+ imports: Set<string>;
3
+ importedBy: Set<string>;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare function probeFilePath(candidate: string): Promise<string | null>;
@@ -0,0 +1,48 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileExists } from './resolver.js';
4
+ const PROBE_EXTENSIONS = [
5
+ '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts',
6
+ '.py', '.rs', '.go', '.rb', '.php', '.swift', '.dart',
7
+ '.css', '.scss', '.sass', '.less', '.json',
8
+ ];
9
+ const INDEX_FILES = [
10
+ 'index.ts', 'index.tsx', 'index.js', 'index.jsx', 'index.mjs',
11
+ 'mod.rs', '__init__.py',
12
+ ];
13
+ export async function probeFilePath(candidate) {
14
+ if (await fileExists(candidate)) {
15
+ const stat = await fs.stat(candidate);
16
+ if (stat.isFile())
17
+ return candidate;
18
+ }
19
+ for (const ext of PROBE_EXTENSIONS) {
20
+ const withExt = candidate + ext;
21
+ if (await fileExists(withExt)) {
22
+ return withExt;
23
+ }
24
+ }
25
+ for (const indexFile of INDEX_FILES) {
26
+ const withIndex = path.join(candidate, indexFile);
27
+ if (await fileExists(withIndex)) {
28
+ return withIndex;
29
+ }
30
+ }
31
+ const basename = path.basename(candidate);
32
+ if (!basename.startsWith('_')) {
33
+ const dir = path.dirname(candidate);
34
+ for (const ext of ['.scss', '.sass', '.less', '.css']) {
35
+ const partial = path.join(dir, `_${basename}${ext}`);
36
+ if (await fileExists(partial)) {
37
+ return partial;
38
+ }
39
+ if (basename.endsWith(ext)) {
40
+ const partialWithExt = path.join(dir, `_${basename}`);
41
+ if (await fileExists(partialWithExt)) {
42
+ return partialWithExt;
43
+ }
44
+ }
45
+ }
46
+ }
47
+ return null;
48
+ }
@@ -62,7 +62,10 @@ const LANGUAGE_PROFILES = [
62
62
  return spec + '.rs';
63
63
  }
64
64
  // crate::foo::bar → src/foo/bar.rs (strip 'crate::' prefix)
65
- const parts = spec.replace(/^crate::/, '').replace(/^(self|super)::/, '').split('::');
65
+ const parts = spec
66
+ .replace(/^crate::/, '')
67
+ .replace(/^(self|super)::/, '')
68
+ .split('::');
66
69
  return parts.join('/') + '.rs';
67
70
  },
68
71
  },
@@ -198,12 +201,7 @@ const WALK_IGNORED_DIRS = new Set([
198
201
  '.gradle',
199
202
  '.idea',
200
203
  ]);
201
- const WALK_IGNORED_FILES = new Set([
202
- 'package-lock.json',
203
- 'yarn.lock',
204
- 'pnpm-lock.yaml',
205
- '.DS_Store',
206
- ]);
204
+ const WALK_IGNORED_FILES = new Set(['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', '.DS_Store']);
207
205
  // Build a set of bare extensions from EXCLUDED_EXTENSIONS for fast lookup
208
206
  const EXCLUDED_EXT_SET = new Set(EXCLUDED_EXTENSIONS.map((glob) => glob.replace('*', '')));
209
207
  let cachedAliases = null;
@@ -222,9 +220,7 @@ async function loadPathAliases(workspaceRoot) {
222
220
  const configPath = path.join(workspaceRoot, configName);
223
221
  const raw = await fs.readFile(configPath, 'utf-8');
224
222
  // Strip single-line comments (// ...) and trailing commas for lenient JSON parsing
225
- const cleaned = raw
226
- .replace(/\/\/.*$/gm, '')
227
- .replace(/,\s*([\]}])/g, '$1');
223
+ const cleaned = raw.replace(/\/\/.*$/gm, '').replace(/,\s*([\]}])/g, '$1');
228
224
  const config = JSON.parse(cleaned);
229
225
  const paths = config?.compilerOptions?.paths;
230
226
  const baseUrl = config?.compilerOptions?.baseUrl || '.';
@@ -269,9 +265,7 @@ function extractImports(content, ext) {
269
265
  for (const match of content.matchAll(pattern)) {
270
266
  const raw = match.groups?.specifier;
271
267
  if (raw && raw.trim().length > 0) {
272
- const normalized = profile.normalizeSpecifier
273
- ? profile.normalizeSpecifier(raw.trim(), '')
274
- : raw.trim();
268
+ const normalized = profile.normalizeSpecifier ? profile.normalizeSpecifier(raw.trim(), '') : raw.trim();
275
269
  specifiers.add(normalized);
276
270
  }
277
271
  }
@@ -282,14 +276,34 @@ function extractImports(content, ext) {
282
276
  // ─── Path Resolution ─────────────────────────────────────────────────
283
277
  /** Extensions to probe when the import specifier has no extension */
284
278
  const PROBE_EXTENSIONS = [
285
- '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts',
286
- '.py', '.rs', '.go', '.rb', '.php', '.swift', '.dart',
287
- '.css', '.scss', '.sass', '.less',
279
+ '.ts',
280
+ '.tsx',
281
+ '.js',
282
+ '.jsx',
283
+ '.mjs',
284
+ '.cjs',
285
+ '.mts',
286
+ '.cts',
287
+ '.py',
288
+ '.rs',
289
+ '.go',
290
+ '.rb',
291
+ '.php',
292
+ '.swift',
293
+ '.dart',
294
+ '.css',
295
+ '.scss',
296
+ '.sass',
297
+ '.less',
288
298
  '.json',
289
299
  ];
290
300
  /** Index file basenames to probe for directory imports */
291
301
  const INDEX_FILES = [
292
- 'index.ts', 'index.tsx', 'index.js', 'index.jsx', 'index.mjs',
302
+ 'index.ts',
303
+ 'index.tsx',
304
+ 'index.js',
305
+ 'index.jsx',
306
+ 'index.mjs',
293
307
  'mod.rs', // Rust convention
294
308
  '__init__.py', // Python convention
295
309
  ];
@@ -43,7 +43,6 @@ export const EXCLUDED_EXTENSIONS = [
43
43
  "*.gz",
44
44
  "*.tgz",
45
45
  // --- Documents ---
46
- "*.pdf",
47
46
  "*.doc",
48
47
  "*.docx",
49
48
  "*.ppt",
@@ -0,0 +1,9 @@
1
+ export interface HistoryTextOptions {
2
+ message: string;
3
+ placeholder?: string;
4
+ defaultValue?: string;
5
+ initialValue?: string;
6
+ history?: string[];
7
+ validate?: (value: string) => string | Error | undefined;
8
+ }
9
+ export declare const historyText: (opts: HistoryTextOptions) => Promise<string | symbol>;
@@ -0,0 +1,87 @@
1
+ import { TextPrompt } from '@clack/core';
2
+ import pc from 'picocolors';
3
+ // Emulate clack/prompts styling characters
4
+ const S_BAR = '│';
5
+ const S_BAR_END = '└';
6
+ const S_STEP_ACTIVE = '◆';
7
+ const S_STEP_CANCEL = '■';
8
+ const S_STEP_ERROR = '▲';
9
+ const S_STEP_SUBMIT = '◇';
10
+ function symbol(state) {
11
+ switch (state) {
12
+ case 'initial':
13
+ case 'active':
14
+ return pc.cyan(S_STEP_ACTIVE);
15
+ case 'cancel':
16
+ return pc.red(S_STEP_CANCEL);
17
+ case 'error':
18
+ return pc.yellow(S_STEP_ERROR);
19
+ case 'submit':
20
+ return pc.green(S_STEP_SUBMIT);
21
+ default:
22
+ return pc.cyan(S_STEP_ACTIVE);
23
+ }
24
+ }
25
+ export const historyText = (opts) => {
26
+ const history = opts.history || [];
27
+ let historyIndex = -1;
28
+ const prompt = new TextPrompt({
29
+ validate: opts.validate,
30
+ placeholder: opts.placeholder,
31
+ defaultValue: opts.defaultValue,
32
+ initialValue: opts.initialValue,
33
+ render() {
34
+ const title = `${pc.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`;
35
+ const placeholder = opts.placeholder
36
+ ? pc.inverse(opts.placeholder[0]) + pc.dim(opts.placeholder.slice(1))
37
+ : pc.inverse(pc.hidden('_'));
38
+ const value = !this.value ? placeholder : this.valueWithCursor;
39
+ switch (this.state) {
40
+ case 'error':
41
+ return `${title.trim()}\n${pc.yellow(S_BAR)} ${value}\n${pc.yellow(S_BAR_END)} ${pc.yellow(this.error)}\n`;
42
+ case 'submit': {
43
+ const submittedText = this.value || opts.placeholder || '';
44
+ return `${title}${pc.gray(S_BAR)} ${pc.bgBlue(pc.white(` ${submittedText} `))}`;
45
+ }
46
+ case 'cancel':
47
+ return `${title}${pc.gray(S_BAR)} ${pc.strikethrough(pc.dim(this.value ?? ''))}${this.value?.trim() ? `\n${pc.gray(S_BAR)}` : ''}`;
48
+ default:
49
+ return `${title}${pc.cyan(S_BAR)} ${value}\n${pc.cyan(S_BAR_END)}\n`;
50
+ }
51
+ },
52
+ });
53
+ prompt.on('cursor', (key) => {
54
+ if (key === 'up') {
55
+ if (history.length > 0 && historyIndex < history.length - 1) {
56
+ historyIndex++;
57
+ prompt.value = history[history.length - 1 - historyIndex];
58
+ const rl = prompt.rl;
59
+ if (rl) {
60
+ rl.line = prompt.value;
61
+ rl.cursor = prompt.value.length;
62
+ }
63
+ }
64
+ }
65
+ else if (key === 'down') {
66
+ if (historyIndex > 0) {
67
+ historyIndex--;
68
+ prompt.value = history[history.length - 1 - historyIndex];
69
+ const rl = prompt.rl;
70
+ if (rl) {
71
+ rl.line = prompt.value;
72
+ rl.cursor = prompt.value.length;
73
+ }
74
+ }
75
+ else if (historyIndex === 0) {
76
+ historyIndex = -1;
77
+ prompt.value = '';
78
+ const rl = prompt.rl;
79
+ if (rl) {
80
+ rl.line = '';
81
+ rl.cursor = 0;
82
+ }
83
+ }
84
+ }
85
+ });
86
+ return prompt.prompt();
87
+ };
@@ -1,2 +1,4 @@
1
1
  export declare const LOGO: string;
2
2
  export declare function printLogo(): void;
3
+ export declare const brandBg: (text: string) => string;
4
+ export declare const brandFg: (text: any) => string;
@@ -1,12 +1,33 @@
1
- import pc from 'picocolors';
2
- export const LOGO = `
3
- ${pc.blue('///')} ${pc.cyan('//')} ${pc.cyan('\\\\')} ${pc.blue('\\\\\\')} ${pc.blue(' __ __ __ __ ____ _ ___ ')}
4
- ${pc.blue('///')} ${pc.cyan('//')} ${pc.cyan('\\\\')} ${pc.blue('\\\\\\')} ${pc.blue(' | \\/ || \\/ | / ___| | |_ _|')}
5
- ${pc.blue('///')} ${pc.cyan('//')} ${pc.cyan('●')} ${pc.cyan('\\\\')} ${pc.blue('\\\\\\')} ${pc.blue(' | |\\/| || |\\/| | | | | | | | ')}
6
- ${pc.blue('\\\\\\')} ${pc.cyan('\\\\')} ${pc.cyan('//')} ${pc.blue('///')} ${pc.blue(' | | | || | | | | |___| |___ | | ')}
7
- ${pc.blue('\\\\\\')} ${pc.cyan('\\\\')} ${pc.cyan('//')} ${pc.blue('///')} ${pc.blue(' |_| |_||_| |_| \\____|_____|___|')}
8
- ${pc.blue('\\\\\\')} ${pc.cyan('\\\\')} ${pc.cyan('//')} ${pc.blue('///')}
9
- `;
1
+ import gradient from 'gradient-string';
2
+ // Premium hex profiles sampled directly from mino-logo-dark.png
3
+ const leftG = gradient(['#222', '#0044ff']); // Cyan to deep royal blue (inward flow)
4
+ const rightG = gradient(['#0044ff', '#222']); // Deep royal blue to cyan (outward flow)
5
+ const dotG = gradient(['#222', '#0044ff']); // Centered neon core
6
+ const textG = gradient(['#0052ff', '#222']); // Electric blue-to-cyan shift for typography
7
+ export const LOGO = [
8
+ // Line 1
9
+ ` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ██ ██ ██ ████████ ██ ██████')}`,
10
+ // Line 2
11
+ ` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('████ ████ ████ ████ ██ ██ ██ ')}`,
12
+ // Line 3
13
+ ` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ████ ██ ██ ████ ██ ██ ██ ██ ')}`,
14
+ // Line 4
15
+ `${leftG('████')} ${leftG('████')} ${dotG('██')} ${rightG('████')} ${rightG('████')} ${textG('██ ██ ██ ██ ██ ██ ██ ██ ██ ')}`,
16
+ // Line 5
17
+ ` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ██ ██ ██ ██ ██ ██ ')}`,
18
+ // Line 6
19
+ ` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ██ ██ ██ ██ ██ ██ ')}`,
20
+ // Line 7
21
+ ` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ██ ██ ██ ████████ ██████████ ██████')}`,
22
+ ].join('\n');
10
23
  export function printLogo() {
11
- console.log(pc.bold(LOGO));
24
+ console.log('\n' + LOGO + '\n');
12
25
  }
26
+ import pc from 'picocolors';
27
+ // Override picocolors cyan globally to use our custom hex brand color for the entire UI
28
+ const customCyan = (text) => `\x1b[38;2;0;82;255m${text}\x1b[0m`;
29
+ const customBgCyan = (text) => `\x1b[48;2;0;82;255m${text}\x1b[0m`;
30
+ pc.cyan = customCyan;
31
+ pc.bgCyan = customBgCyan;
32
+ export const brandBg = (text) => `\x1b[48;2;0;82;255m${pc.black(text)}\x1b[0m`;
33
+ export const brandFg = customCyan;
@@ -1 +1,22 @@
1
+ /**
2
+ * Reads multi-line content from the standard input (stdin) stream.
3
+ *
4
+ * This function utilizes the `node:readline` module to collect input line-by-line
5
+ * in an interactive environment. It is designed to capture large blocks of text
6
+ * pasted into the terminal by a user.
7
+ *
8
+ * ### Behavior:
9
+ * - Aggregates all incoming lines into an internal array.
10
+ * - Trims leading and trailing whitespace from the final joined result upon `EOF` (End of File).
11
+ * - Gracefully handles `SIGINT` (e.g., Ctrl+C) by canceling the collection process
12
+ * and returning `null`, preventing process termination if undesired.
13
+ *
14
+ * ### Usage:
15
+ * Useful for scenarios where a CLI tool requires the user to provide a text
16
+ * payload that is too large or inconvenient to pass via a simple CLI argument.
17
+ *
18
+ * @returns {Promise<string | null>} A promise that resolves to:
19
+ * - A `string` containing the complete, trimmed input content upon successful stream completion.
20
+ * - `null` if the user interrupts the operation using `SIGINT`.
21
+ */
1
22
  export declare function readPaste(): Promise<string | null>;
@@ -1,10 +1,31 @@
1
1
  import * as readline from 'node:readline';
2
+ /**
3
+ * Reads multi-line content from the standard input (stdin) stream.
4
+ *
5
+ * This function utilizes the `node:readline` module to collect input line-by-line
6
+ * in an interactive environment. It is designed to capture large blocks of text
7
+ * pasted into the terminal by a user.
8
+ *
9
+ * ### Behavior:
10
+ * - Aggregates all incoming lines into an internal array.
11
+ * - Trims leading and trailing whitespace from the final joined result upon `EOF` (End of File).
12
+ * - Gracefully handles `SIGINT` (e.g., Ctrl+C) by canceling the collection process
13
+ * and returning `null`, preventing process termination if undesired.
14
+ *
15
+ * ### Usage:
16
+ * Useful for scenarios where a CLI tool requires the user to provide a text
17
+ * payload that is too large or inconvenient to pass via a simple CLI argument.
18
+ *
19
+ * @returns {Promise<string | null>} A promise that resolves to:
20
+ * - A `string` containing the complete, trimmed input content upon successful stream completion.
21
+ * - `null` if the user interrupts the operation using `SIGINT`.
22
+ */
2
23
  export async function readPaste() {
3
24
  return new Promise((resolve) => {
4
25
  const rl = readline.createInterface({
5
26
  input: process.stdin,
6
27
  output: process.stdout,
7
- terminal: true
28
+ terminal: true,
8
29
  });
9
30
  const content = [];
10
31
  let isCanceled = false;
@@ -0,0 +1,2 @@
1
+ import { LanguageProfile } from './types.js';
2
+ export declare const LANGUAGE_PROFILES: LanguageProfile[];
@@ -0,0 +1,44 @@
1
+ export const LANGUAGE_PROFILES = [
2
+ {
3
+ extensions: ['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs', '.mts', '.cts'],
4
+ patterns: [
5
+ /import\s+(?:[\s\S]*?\s+from\s+)?['"](?<specifier>[^'"]+)['"]/gm,
6
+ /import\s*\(\s*['"](?<specifier>[^'"]+)['"]\s*\)/gm,
7
+ /require\s*\(\s*['"](?<specifier>[^'"]+)['"]\s*\)/gm,
8
+ /export\s+(?:[\s\S]*?\s+from\s+)['"](?<specifier>[^'"]+)['"]/gm,
9
+ ],
10
+ },
11
+ {
12
+ extensions: ['.py'],
13
+ patterns: [
14
+ /^from\s+(?<specifier>[^\s]+)\s+import\s/gm,
15
+ /^import\s+(?!.*\sfrom\s)(?<specifier>[^\s,]+)/gm,
16
+ ],
17
+ normalizeSpecifier: (spec) => {
18
+ if (spec.startsWith('.')) {
19
+ const dots = spec.match(/^\.+/)[0];
20
+ const rest = spec.slice(dots.length);
21
+ const prefix = dots.length === 1 ? './' : '../'.repeat(dots.length - 1);
22
+ return rest ? `${prefix}${rest.replace(/\./g, '/')}.py` : prefix.slice(0, -1);
23
+ }
24
+ return spec.replace(/\./g, '/') + '.py';
25
+ },
26
+ },
27
+ {
28
+ extensions: ['.rs'],
29
+ patterns: [
30
+ /use\s+(?<specifier>(?:crate|self|super)(?:::[a-zA-Z_][a-zA-Z0-9_]*)+)/gm,
31
+ /mod\s+(?<specifier>[a-zA-Z_][a-zA-Z0-9_]*)\s*;/gm,
32
+ ],
33
+ normalizeSpecifier: (spec, sourceFile) => {
34
+ if (!spec.includes('::')) {
35
+ return spec + '.rs';
36
+ }
37
+ const parts = spec
38
+ .replace(/^crate::/, '')
39
+ .replace(/^(self|super)::/, '')
40
+ .split('::');
41
+ return parts.join('/') + '.rs';
42
+ },
43
+ }
44
+ ];
@@ -14,13 +14,16 @@ export function getProjectStorageDir(workspaceRoot) {
14
14
  */
15
15
  export function ensureProjectStorage(workspaceRoot) {
16
16
  const storageDir = getProjectStorageDir(workspaceRoot);
17
- if (!fs.existsSync(storageDir)) {
18
- try {
19
- fs.mkdirSync(storageDir, { recursive: true });
20
- }
21
- catch (err) {
22
- // Non-fatal error, CLI can proceed without cache if permissions fail
23
- console.warn(pc.yellow(`Failed to create .minovativemind directory: ${err instanceof Error ? err.message : String(err)}`));
17
+ const embeddingsDir = path.join(storageDir, 'embeddings');
18
+ for (const dir of [storageDir, embeddingsDir]) {
19
+ if (!fs.existsSync(dir)) {
20
+ try {
21
+ fs.mkdirSync(dir, { recursive: true });
22
+ }
23
+ catch (err) {
24
+ // Non-fatal error, CLI can proceed without cache if permissions fail
25
+ console.warn(pc.yellow(`Failed to create directory: ${err instanceof Error ? err.message : String(err)}`));
26
+ }
24
27
  }
25
28
  }
26
29
  }