minovative-mind-cli 1.5.1 → 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.
- package/README.md +51 -45
- package/dist/commands/chat.js +7 -2
- package/dist/services/agent/slashCommands.js +156 -30
- package/dist/services/agent/toolLoop.d.ts +1 -1
- package/dist/services/agent/toolLoop.js +7 -2
- package/dist/services/agent/types.d.ts +2 -0
- package/dist/services/agent-tools.d.ts +9 -4
- package/dist/services/agent-tools.js +145 -21
- package/dist/services/agent.d.ts +8 -0
- package/dist/services/agent.js +285 -40
- package/dist/services/ai.d.ts +19 -5
- package/dist/services/ai.js +167 -35
- package/dist/services/changeLogger.d.ts +142 -0
- package/dist/services/changeLogger.js +132 -3
- package/dist/services/contextAgent.d.ts +6 -1
- package/dist/services/contextAgent.js +95 -14
- package/dist/services/embeddingIndex.d.ts +82 -0
- package/dist/services/embeddingIndex.js +613 -0
- package/dist/services/investigationComplexity.d.ts +45 -0
- package/dist/services/investigationComplexity.js +91 -0
- package/dist/services/metrics.d.ts +18 -0
- package/dist/services/metrics.js +7 -0
- package/dist/services/orchestration/fileLockRegistry.d.ts +125 -0
- package/dist/services/orchestration/fileLockRegistry.js +276 -0
- package/dist/services/orchestration/investigationAgent.d.ts +85 -0
- package/dist/services/orchestration/investigationAgent.js +359 -0
- package/dist/services/orchestration/investigationOrchestrator.d.ts +53 -0
- package/dist/services/orchestration/investigationOrchestrator.js +180 -0
- package/dist/services/orchestration/messageBus.d.ts +162 -0
- package/dist/services/orchestration/messageBus.js +225 -0
- package/dist/services/orchestration/orchestrator.d.ts +45 -0
- package/dist/services/orchestration/orchestrator.js +214 -0
- package/dist/services/orchestration/readCache.d.ts +79 -0
- package/dist/services/orchestration/readCache.js +108 -0
- package/dist/services/orchestration/scopedTools.d.ts +57 -0
- package/dist/services/orchestration/scopedTools.js +172 -0
- package/dist/services/orchestration/subAgent.d.ts +58 -0
- package/dist/services/orchestration/subAgent.js +187 -0
- package/dist/services/orchestration/taskGraph.d.ts +129 -0
- package/dist/services/orchestration/taskGraph.js +254 -0
- package/dist/services/proxyClient.d.ts +25 -0
- package/dist/services/proxyClient.js +60 -0
- package/dist/utils/asyncContext.d.ts +16 -0
- package/dist/utils/asyncContext.js +25 -0
- package/dist/utils/config.d.ts +3 -1
- package/dist/utils/config.js +3 -1
- package/dist/utils/contextPrompts.js +3 -2
- package/dist/utils/dependencyTracer/modules/api.d.ts +9 -0
- package/dist/utils/dependencyTracer/modules/api.js +62 -0
- package/dist/utils/dependencyTracer/modules/graph.d.ts +9 -0
- package/dist/utils/dependencyTracer/modules/graph.js +23 -0
- package/dist/utils/dependencyTracer/modules/profiles.d.ts +7 -0
- package/dist/utils/dependencyTracer/modules/profiles.js +120 -0
- package/dist/utils/dependencyTracer/modules/resolver.d.ts +7 -0
- package/dist/utils/dependencyTracer/modules/resolver.js +51 -0
- package/dist/utils/dependencyTracer/modules/types.d.ts +4 -0
- package/dist/utils/dependencyTracer/modules/types.js +1 -0
- package/dist/utils/dependencyTracer/modules/walker.d.ts +1 -0
- package/dist/utils/dependencyTracer/modules/walker.js +48 -0
- package/dist/utils/dependencyTracer.js +31 -17
- package/dist/utils/excludedExtensions.js +0 -1
- package/dist/utils/historyPrompt.d.ts +9 -0
- package/dist/utils/historyPrompt.js +87 -0
- package/dist/utils/logo.js +7 -7
- package/dist/utils/paste.d.ts +21 -0
- package/dist/utils/paste.js +22 -1
- package/dist/utils/profiles.d.ts +2 -0
- package/dist/utils/profiles.js +44 -0
- package/dist/utils/projectStorage.js +10 -7
- package/dist/utils/systemPrompts.d.ts +6 -3
- package/dist/utils/systemPrompts.js +106 -5
- package/dist/utils/types.d.ts +33 -0
- package/dist/utils/types.js +1 -0
- package/oclif.manifest.json +2 -2
- package/package.json +4 -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 @@
|
|
|
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
|
|
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',
|
|
286
|
-
'.
|
|
287
|
-
'.
|
|
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',
|
|
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
|
];
|
|
@@ -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
|
+
};
|
package/dist/utils/logo.js
CHANGED
|
@@ -6,19 +6,19 @@ const dotG = gradient(['#222', '#0044ff']); // Centered neon core
|
|
|
6
6
|
const textG = gradient(['#0052ff', '#222']); // Electric blue-to-cyan shift for typography
|
|
7
7
|
export const LOGO = [
|
|
8
8
|
// Line 1
|
|
9
|
-
` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ██
|
|
9
|
+
` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ██ ██ ██ ████████ ██ ██████')}`,
|
|
10
10
|
// Line 2
|
|
11
|
-
` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('████ ████
|
|
11
|
+
` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('████ ████ ████ ████ ██ ██ ██ ')}`,
|
|
12
12
|
// Line 3
|
|
13
|
-
` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ████ ██
|
|
13
|
+
` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ████ ██ ██ ████ ██ ██ ██ ██ ')}`,
|
|
14
14
|
// Line 4
|
|
15
|
-
`${leftG('████')} ${leftG('████')} ${dotG('██')} ${rightG('████')} ${rightG('████')} ${textG('██ ██ ██
|
|
15
|
+
`${leftG('████')} ${leftG('████')} ${dotG('██')} ${rightG('████')} ${rightG('████')} ${textG('██ ██ ██ ██ ██ ██ ██ ██ ██ ')}`,
|
|
16
16
|
// Line 5
|
|
17
|
-
` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ██
|
|
17
|
+
` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ██ ██ ██ ██ ██ ██ ')}`,
|
|
18
18
|
// Line 6
|
|
19
|
-
` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ██
|
|
19
|
+
` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ██ ██ ██ ██ ██ ██ ')}`,
|
|
20
20
|
// Line 7
|
|
21
|
-
` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ██
|
|
21
|
+
` ${leftG('████')} ${leftG('████')} ${rightG('████')} ${rightG('████')} ${textG('██ ██ ██ ██ ████████ ██████████ ██████')}`,
|
|
22
22
|
].join('\n');
|
|
23
23
|
export function printLogo() {
|
|
24
24
|
console.log('\n' + LOGO + '\n');
|
package/dist/utils/paste.d.ts
CHANGED
|
@@ -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>;
|
package/dist/utils/paste.js
CHANGED
|
@@ -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,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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
}
|