@latentforce/shift 1.0.14 → 1.0.16
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/build/cli/commands/init.js +8 -5
- package/build/daemon/tools-executor.js +146 -0
- package/build/mcp-server.js +18 -0
- package/package.json +1 -1
|
@@ -242,14 +242,20 @@ export async function initCommand(options = {}) {
|
|
|
242
242
|
scan_timestamp: new Date().toISOString(),
|
|
243
243
|
project_name: project.projectName,
|
|
244
244
|
},
|
|
245
|
-
scan_targets: scanTargets
|
|
245
|
+
scan_targets: scanTargets,
|
|
246
246
|
total_loc: totalLOC,
|
|
247
247
|
files: filesWithContent.length > 0 ? filesWithContent : undefined,
|
|
248
248
|
};
|
|
249
249
|
try {
|
|
250
250
|
const response = await sendInitScan(apiKey, project.projectId, payload);
|
|
251
251
|
console.log('[Init] ✓ Backend initialization completed');
|
|
252
|
-
|
|
252
|
+
const count = response.source_files_count ?? response.files_read;
|
|
253
|
+
if (count != null) {
|
|
254
|
+
console.log(`[Init] Source files queued: ${count}`);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
console.log(`[Init] (backend did not return file count)`);
|
|
258
|
+
}
|
|
253
259
|
// Update local config with agent info (matching extension)
|
|
254
260
|
if (response.agents_created?.theme_planner) {
|
|
255
261
|
const agentInfo = {
|
|
@@ -263,9 +269,6 @@ export async function initCommand(options = {}) {
|
|
|
263
269
|
writeProjectConfig(projectConfig, projectRoot);
|
|
264
270
|
console.log(`[Init] ✓ Agent info saved: ${agentInfo.agent_id}`);
|
|
265
271
|
}
|
|
266
|
-
if (response.files_read === 0 && response.files_failed > 0) {
|
|
267
|
-
console.log(`\n[Init] ⚠️ No files were read (${response.files_failed} failed).`);
|
|
268
|
-
}
|
|
269
272
|
console.log('\n╔═══════════════════════════════════════════════╗');
|
|
270
273
|
console.log('║ ✓ Project Initialization Complete ║');
|
|
271
274
|
console.log('╚═══════════════════════════════════════════════╝');
|
|
@@ -22,6 +22,9 @@ export class ToolsExecutor {
|
|
|
22
22
|
'get_repository_root': this.getRepositoryRoot.bind(this),
|
|
23
23
|
'get_project_tree': this.getProjectTree.bind(this),
|
|
24
24
|
'execute_command': this.executeCommand.bind(this),
|
|
25
|
+
'run_bash': this.runBash.bind(this),
|
|
26
|
+
'glob_search': this.globSearch.bind(this),
|
|
27
|
+
'grep_search': this.grepSearch.bind(this),
|
|
25
28
|
};
|
|
26
29
|
}
|
|
27
30
|
/**
|
|
@@ -285,6 +288,149 @@ export class ToolsExecutor {
|
|
|
285
288
|
message: `No .git folder found after searching ${maxDepth} levels up`
|
|
286
289
|
};
|
|
287
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Returns combined output matching tools.py's run_bash format.
|
|
293
|
+
*/
|
|
294
|
+
async runBash(params) {
|
|
295
|
+
const { command } = params;
|
|
296
|
+
if (!command) {
|
|
297
|
+
return { status: 'error', error: 'command is required' };
|
|
298
|
+
}
|
|
299
|
+
console.log(`[ToolsExecutor] run_bash: ${command}`);
|
|
300
|
+
try {
|
|
301
|
+
const { stdout, stderr } = await execAsync(command, {
|
|
302
|
+
cwd: this.workspaceRoot,
|
|
303
|
+
timeout: 30000,
|
|
304
|
+
maxBuffer: 1024 * 1024,
|
|
305
|
+
});
|
|
306
|
+
let output = stdout;
|
|
307
|
+
if (stderr)
|
|
308
|
+
output += `\n[STDERR]\n${stderr}`;
|
|
309
|
+
return {
|
|
310
|
+
status: 'success',
|
|
311
|
+
output: output.trim() || '[No output]',
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
let output = error.stdout || '';
|
|
316
|
+
if (error.stderr)
|
|
317
|
+
output += `\n[STDERR]\n${error.stderr}`;
|
|
318
|
+
output += `\n[EXIT CODE: ${error.code || 1}]`;
|
|
319
|
+
return {
|
|
320
|
+
status: 'error',
|
|
321
|
+
output: output.trim(),
|
|
322
|
+
error: error.message,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Find files matching a glob pattern
|
|
328
|
+
*/
|
|
329
|
+
async globSearch(params) {
|
|
330
|
+
const { pattern } = params;
|
|
331
|
+
if (!pattern) {
|
|
332
|
+
return { status: 'error', error: 'pattern is required' };
|
|
333
|
+
}
|
|
334
|
+
console.log(`[ToolsExecutor] Glob search: ${pattern}`);
|
|
335
|
+
let basePath = '.';
|
|
336
|
+
let namePattern = pattern;
|
|
337
|
+
const lastSlash = pattern.lastIndexOf('/');
|
|
338
|
+
if (lastSlash !== -1) {
|
|
339
|
+
basePath = pattern.substring(0, lastSlash).replace(/\/?\*\*$/, '') || '.';
|
|
340
|
+
namePattern = pattern.substring(lastSlash + 1);
|
|
341
|
+
}
|
|
342
|
+
const searchDir = path.join(this.workspaceRoot, basePath);
|
|
343
|
+
const excludes = [
|
|
344
|
+
'-not', '-path', '*/node_modules/*',
|
|
345
|
+
'-not', '-path', '*/.git/*',
|
|
346
|
+
'-not', '-path', '*/__pycache__/*',
|
|
347
|
+
'-not', '-path', '*/dist/*',
|
|
348
|
+
'-not', '-path', '*/build/*',
|
|
349
|
+
'-not', '-path', '*/.venv/*',
|
|
350
|
+
];
|
|
351
|
+
// Quote searchDir to handle workspace paths that contain spaces
|
|
352
|
+
const cmd = `find "${searchDir}" -name "${namePattern}" ${excludes.join(' ')}`;
|
|
353
|
+
try {
|
|
354
|
+
const { stdout } = await execAsync(cmd, { timeout: 30000 });
|
|
355
|
+
const matches = stdout
|
|
356
|
+
.trim()
|
|
357
|
+
.split('\n')
|
|
358
|
+
.filter(Boolean)
|
|
359
|
+
.map(f => path.relative(this.workspaceRoot, f))
|
|
360
|
+
.slice(0, 200);
|
|
361
|
+
return {
|
|
362
|
+
status: 'success',
|
|
363
|
+
pattern,
|
|
364
|
+
matches,
|
|
365
|
+
count: matches.length,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
return { status: 'error', error: error.message };
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Search file contents with a regex pattern
|
|
374
|
+
*/
|
|
375
|
+
async grepSearch(params) {
|
|
376
|
+
const { pattern, file_glob } = params;
|
|
377
|
+
if (!pattern) {
|
|
378
|
+
return { status: 'error', error: 'pattern is required' };
|
|
379
|
+
}
|
|
380
|
+
console.log(`[ToolsExecutor] Grep search: ${pattern}${file_glob ? ` (${file_glob})` : ''}`);
|
|
381
|
+
const baseArgs = [
|
|
382
|
+
'grep', '-rn', '-E',
|
|
383
|
+
'--exclude-dir=node_modules',
|
|
384
|
+
'--exclude-dir=.git',
|
|
385
|
+
'--exclude-dir=__pycache__',
|
|
386
|
+
'--exclude-dir=dist',
|
|
387
|
+
'--exclude-dir=build',
|
|
388
|
+
'--exclude-dir=.venv',
|
|
389
|
+
'--binary-files=without-match',
|
|
390
|
+
];
|
|
391
|
+
let searchPath = '.';
|
|
392
|
+
if (file_glob) {
|
|
393
|
+
if (file_glob.includes('/')) {
|
|
394
|
+
const match = file_glob.match(/^([^*]+?)\/(?:\*\*\/)?(.+)$/);
|
|
395
|
+
if (match) {
|
|
396
|
+
searchPath = match[1];
|
|
397
|
+
const namePat = match[2];
|
|
398
|
+
if (namePat && namePat !== '*')
|
|
399
|
+
baseArgs.push(`--include=${namePat}`);
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
baseArgs.push(`--include=${file_glob}`);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
baseArgs.push(`--include=${file_glob}`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const args = [...baseArgs, `'${pattern.replace(/'/g, `'\\''`)}'`, searchPath];
|
|
410
|
+
try {
|
|
411
|
+
const { stdout } = await execAsync(args.join(' '), {
|
|
412
|
+
cwd: this.workspaceRoot,
|
|
413
|
+
timeout: 30000,
|
|
414
|
+
maxBuffer: 1024 * 1024,
|
|
415
|
+
});
|
|
416
|
+
const lines = stdout.trim().split('\n').filter(Boolean);
|
|
417
|
+
const truncated = lines.length > 500;
|
|
418
|
+
const output = truncated ? lines.slice(0, 500).join('\n') + `\n\n... [truncated — ${lines.length} total matches, showing first 500]` : stdout.trim();
|
|
419
|
+
return {
|
|
420
|
+
status: 'success',
|
|
421
|
+
pattern,
|
|
422
|
+
file_glob: file_glob || null,
|
|
423
|
+
output,
|
|
424
|
+
match_count: lines.length,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
if (error.code === 1 && !error.stderr) {
|
|
429
|
+
return { status: 'success', pattern, file_glob: file_glob || null, output: '', match_count: 0 };
|
|
430
|
+
}
|
|
431
|
+
return { status: 'error', error: error.message };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
288
434
|
/**
|
|
289
435
|
* Get project tree — delegates to shared tree-scanner utility
|
|
290
436
|
*/
|
package/build/mcp-server.js
CHANGED
|
@@ -110,6 +110,24 @@ function formatDependencies(data) {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
|
+
const implicitDeps = data.implicit_dependencies || [];
|
|
114
|
+
if (implicitDeps.length > 0) {
|
|
115
|
+
lines.push('');
|
|
116
|
+
lines.push('## Implicit / external dependencies');
|
|
117
|
+
lines.push('');
|
|
118
|
+
lines.push(`Implicit dependencies (**${implicitDeps.length}**):`);
|
|
119
|
+
lines.push('');
|
|
120
|
+
const implicitEdges = data.implicit_edge_summaries || {};
|
|
121
|
+
for (const dep of implicitDeps) {
|
|
122
|
+
const summary = implicitEdges[dep];
|
|
123
|
+
if (summary) {
|
|
124
|
+
lines.push(`- **${dep}**: ${summary}`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
lines.push(`- **${dep}**`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
113
131
|
return lines.join('\n');
|
|
114
132
|
}
|
|
115
133
|
/** Format blast_radius response as readable markdown */
|