agentsys 5.0.0 → 5.0.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.
- package/.claude-plugin/marketplace.json +13 -13
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +19 -2
- package/README.md +1 -0
- package/adapters/README.md +1 -1
- package/adapters/codex/skills/consult/SKILL.md +3 -2
- package/adapters/codex/skills/next-task/SKILL.md +8 -8
- package/adapters/opencode/agents/consult-agent.md +1 -1
- package/adapters/opencode/agents/delivery-validator.md +1 -1
- package/adapters/opencode/agents/implementation-agent.md +1 -1
- package/adapters/opencode/agents/worktree-manager.md +3 -3
- package/adapters/opencode/commands/consult.md +3 -2
- package/adapters/opencode/commands/next-task.md +7 -7
- package/adapters/opencode/skills/agnix/SKILL.md +1 -1
- package/adapters/opencode/skills/consult/SKILL.md +16 -4
- package/adapters/opencode/skills/deslop/SKILL.md +1 -1
- package/adapters/opencode/skills/discover-tasks/SKILL.md +2 -2
- package/adapters/opencode/skills/drift-analysis/SKILL.md +1 -1
- package/adapters/opencode/skills/enhance-agent-prompts/SKILL.md +1 -1
- package/adapters/opencode/skills/enhance-claude-memory/SKILL.md +1 -1
- package/adapters/opencode/skills/enhance-cross-file/SKILL.md +1 -1
- package/adapters/opencode/skills/enhance-docs/SKILL.md +1 -1
- package/adapters/opencode/skills/enhance-hooks/SKILL.md +1 -1
- package/adapters/opencode/skills/enhance-orchestrator/SKILL.md +1 -1
- package/adapters/opencode/skills/enhance-plugins/SKILL.md +1 -1
- package/adapters/opencode/skills/enhance-prompts/SKILL.md +1 -1
- package/adapters/opencode/skills/enhance-skills/SKILL.md +1 -1
- package/adapters/opencode/skills/learn/SKILL.md +1 -1
- package/adapters/opencode/skills/orchestrate-review/SKILL.md +1 -1
- package/adapters/opencode/skills/perf-analyzer/SKILL.md +1 -1
- package/adapters/opencode/skills/perf-baseline-manager/SKILL.md +1 -1
- package/adapters/opencode/skills/perf-benchmarker/SKILL.md +1 -1
- package/adapters/opencode/skills/perf-code-paths/SKILL.md +1 -1
- package/adapters/opencode/skills/perf-investigation-logger/SKILL.md +1 -1
- package/adapters/opencode/skills/perf-profiler/SKILL.md +1 -1
- package/adapters/opencode/skills/perf-theory-gatherer/SKILL.md +1 -1
- package/adapters/opencode/skills/perf-theory-tester/SKILL.md +1 -1
- package/adapters/opencode/skills/sync-docs/SKILL.md +1 -1
- package/adapters/opencode/skills/validate-delivery/SKILL.md +2 -2
- package/bin/cli.js +42 -8
- package/bin/dev-cli.js +16 -6
- package/lib/collectors/github.js +76 -12
- package/lib/perf/benchmark-runner.js +11 -6
- package/lib/perf/investigation-state.js +12 -13
- package/lib/perf/profiling-runner.js +23 -4
- package/lib/repo-map/concurrency.js +29 -0
- package/lib/repo-map/runner.js +218 -19
- package/lib/repo-map/updater.js +115 -27
- package/lib/state/workflow-state.js +31 -30
- package/lib/utils/command-parser.js +0 -0
- package/lib/utils/state-helpers.js +61 -0
- package/package.json +2 -1
- package/plugins/agnix/.claude-plugin/plugin.json +1 -1
- package/plugins/agnix/skills/agnix/SKILL.md +1 -1
- package/plugins/audit-project/.claude-plugin/plugin.json +1 -1
- package/plugins/audit-project/lib/collectors/github.js +76 -12
- package/plugins/audit-project/lib/perf/benchmark-runner.js +11 -6
- package/plugins/audit-project/lib/perf/investigation-state.js +12 -13
- package/plugins/audit-project/lib/perf/profiling-runner.js +23 -4
- package/plugins/audit-project/lib/repo-map/concurrency.js +29 -0
- package/plugins/audit-project/lib/repo-map/runner.js +218 -19
- package/plugins/audit-project/lib/repo-map/updater.js +115 -27
- package/plugins/audit-project/lib/state/workflow-state.js +31 -30
- package/plugins/audit-project/lib/utils/command-parser.js +0 -0
- package/plugins/audit-project/lib/utils/state-helpers.js +61 -0
- package/plugins/consult/.claude-plugin/plugin.json +1 -1
- package/plugins/consult/agents/consult-agent.md +1 -1
- package/plugins/consult/commands/consult.md +3 -2
- package/plugins/consult/skills/consult/SKILL.md +16 -4
- package/plugins/deslop/.claude-plugin/plugin.json +1 -1
- package/plugins/deslop/lib/collectors/github.js +76 -12
- package/plugins/deslop/lib/perf/benchmark-runner.js +11 -6
- package/plugins/deslop/lib/perf/investigation-state.js +12 -13
- package/plugins/deslop/lib/perf/profiling-runner.js +23 -4
- package/plugins/deslop/lib/repo-map/concurrency.js +29 -0
- package/plugins/deslop/lib/repo-map/runner.js +218 -19
- package/plugins/deslop/lib/repo-map/updater.js +115 -27
- package/plugins/deslop/lib/state/workflow-state.js +31 -30
- package/plugins/deslop/lib/utils/command-parser.js +0 -0
- package/plugins/deslop/lib/utils/state-helpers.js +61 -0
- package/plugins/deslop/skills/deslop/SKILL.md +1 -1
- package/plugins/drift-detect/.claude-plugin/plugin.json +1 -1
- package/plugins/drift-detect/lib/collectors/github.js +76 -12
- package/plugins/drift-detect/lib/perf/benchmark-runner.js +11 -6
- package/plugins/drift-detect/lib/perf/investigation-state.js +12 -13
- package/plugins/drift-detect/lib/perf/profiling-runner.js +23 -4
- package/plugins/drift-detect/lib/repo-map/concurrency.js +29 -0
- package/plugins/drift-detect/lib/repo-map/runner.js +218 -19
- package/plugins/drift-detect/lib/repo-map/updater.js +115 -27
- package/plugins/drift-detect/lib/state/workflow-state.js +31 -30
- package/plugins/drift-detect/lib/utils/command-parser.js +0 -0
- package/plugins/drift-detect/lib/utils/state-helpers.js +61 -0
- package/plugins/drift-detect/skills/drift-analysis/SKILL.md +1 -1
- package/plugins/enhance/.claude-plugin/plugin.json +1 -1
- package/plugins/enhance/lib/collectors/github.js +76 -12
- package/plugins/enhance/lib/perf/benchmark-runner.js +11 -6
- package/plugins/enhance/lib/perf/investigation-state.js +12 -13
- package/plugins/enhance/lib/perf/profiling-runner.js +23 -4
- package/plugins/enhance/lib/repo-map/concurrency.js +29 -0
- package/plugins/enhance/lib/repo-map/runner.js +218 -19
- package/plugins/enhance/lib/repo-map/updater.js +115 -27
- package/plugins/enhance/lib/state/workflow-state.js +31 -30
- package/plugins/enhance/lib/utils/command-parser.js +0 -0
- package/plugins/enhance/lib/utils/state-helpers.js +61 -0
- package/plugins/enhance/skills/enhance-agent-prompts/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-claude-memory/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-cross-file/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-docs/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-hooks/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-orchestrator/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-plugins/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-prompts/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-skills/SKILL.md +1 -1
- package/plugins/learn/.claude-plugin/plugin.json +1 -1
- package/plugins/learn/lib/collectors/github.js +76 -12
- package/plugins/learn/lib/perf/benchmark-runner.js +11 -6
- package/plugins/learn/lib/perf/investigation-state.js +12 -13
- package/plugins/learn/lib/perf/profiling-runner.js +23 -4
- package/plugins/learn/lib/repo-map/concurrency.js +29 -0
- package/plugins/learn/lib/repo-map/runner.js +218 -19
- package/plugins/learn/lib/repo-map/updater.js +115 -27
- package/plugins/learn/lib/state/workflow-state.js +31 -30
- package/plugins/learn/lib/utils/command-parser.js +0 -0
- package/plugins/learn/lib/utils/state-helpers.js +61 -0
- package/plugins/learn/skills/learn/SKILL.md +1 -1
- package/plugins/next-task/.claude-plugin/plugin.json +1 -1
- package/plugins/next-task/agents/delivery-validator.md +1 -1
- package/plugins/next-task/agents/implementation-agent.md +2 -2
- package/plugins/next-task/agents/worktree-manager.md +3 -3
- package/plugins/next-task/commands/next-task.md +8 -8
- package/plugins/next-task/hooks/hooks.json +1 -1
- package/plugins/next-task/lib/collectors/github.js +76 -12
- package/plugins/next-task/lib/perf/benchmark-runner.js +11 -6
- package/plugins/next-task/lib/perf/investigation-state.js +12 -13
- package/plugins/next-task/lib/perf/profiling-runner.js +23 -4
- package/plugins/next-task/lib/repo-map/concurrency.js +29 -0
- package/plugins/next-task/lib/repo-map/runner.js +218 -19
- package/plugins/next-task/lib/repo-map/updater.js +115 -27
- package/plugins/next-task/lib/state/workflow-state.js +31 -30
- package/plugins/next-task/lib/utils/command-parser.js +0 -0
- package/plugins/next-task/lib/utils/state-helpers.js +61 -0
- package/plugins/next-task/skills/discover-tasks/SKILL.md +2 -2
- package/plugins/next-task/skills/orchestrate-review/SKILL.md +1 -1
- package/plugins/next-task/skills/validate-delivery/SKILL.md +2 -2
- package/plugins/perf/.claude-plugin/plugin.json +1 -1
- package/plugins/perf/lib/collectors/github.js +76 -12
- package/plugins/perf/lib/perf/benchmark-runner.js +11 -6
- package/plugins/perf/lib/perf/investigation-state.js +12 -13
- package/plugins/perf/lib/perf/profiling-runner.js +23 -4
- package/plugins/perf/lib/repo-map/concurrency.js +29 -0
- package/plugins/perf/lib/repo-map/runner.js +218 -19
- package/plugins/perf/lib/repo-map/updater.js +115 -27
- package/plugins/perf/lib/state/workflow-state.js +31 -30
- package/plugins/perf/lib/utils/command-parser.js +0 -0
- package/plugins/perf/lib/utils/state-helpers.js +61 -0
- package/plugins/perf/skills/perf-analyzer/SKILL.md +1 -1
- package/plugins/perf/skills/perf-baseline-manager/SKILL.md +1 -1
- package/plugins/perf/skills/perf-benchmarker/SKILL.md +1 -1
- package/plugins/perf/skills/perf-code-paths/SKILL.md +1 -1
- package/plugins/perf/skills/perf-investigation-logger/SKILL.md +1 -1
- package/plugins/perf/skills/perf-profiler/SKILL.md +1 -1
- package/plugins/perf/skills/perf-theory-gatherer/SKILL.md +1 -1
- package/plugins/perf/skills/perf-theory-tester/SKILL.md +1 -1
- package/plugins/repo-map/.claude-plugin/plugin.json +1 -1
- package/plugins/repo-map/lib/collectors/github.js +76 -12
- package/plugins/repo-map/lib/perf/benchmark-runner.js +11 -6
- package/plugins/repo-map/lib/perf/investigation-state.js +12 -13
- package/plugins/repo-map/lib/perf/profiling-runner.js +23 -4
- package/plugins/repo-map/lib/repo-map/concurrency.js +29 -0
- package/plugins/repo-map/lib/repo-map/runner.js +218 -19
- package/plugins/repo-map/lib/repo-map/updater.js +115 -27
- package/plugins/repo-map/lib/state/workflow-state.js +31 -30
- package/plugins/repo-map/lib/utils/command-parser.js +0 -0
- package/plugins/repo-map/lib/utils/state-helpers.js +61 -0
- package/plugins/ship/.claude-plugin/plugin.json +1 -1
- package/plugins/ship/lib/collectors/github.js +76 -12
- package/plugins/ship/lib/perf/benchmark-runner.js +11 -6
- package/plugins/ship/lib/perf/investigation-state.js +12 -13
- package/plugins/ship/lib/perf/profiling-runner.js +23 -4
- package/plugins/ship/lib/repo-map/concurrency.js +29 -0
- package/plugins/ship/lib/repo-map/runner.js +218 -19
- package/plugins/ship/lib/repo-map/updater.js +115 -27
- package/plugins/ship/lib/state/workflow-state.js +31 -30
- package/plugins/ship/lib/utils/command-parser.js +0 -0
- package/plugins/ship/lib/utils/state-helpers.js +61 -0
- package/plugins/sync-docs/.claude-plugin/plugin.json +1 -1
- package/plugins/sync-docs/lib/collectors/github.js +76 -12
- package/plugins/sync-docs/lib/perf/benchmark-runner.js +11 -6
- package/plugins/sync-docs/lib/perf/investigation-state.js +12 -13
- package/plugins/sync-docs/lib/perf/profiling-runner.js +23 -4
- package/plugins/sync-docs/lib/repo-map/concurrency.js +29 -0
- package/plugins/sync-docs/lib/repo-map/runner.js +218 -19
- package/plugins/sync-docs/lib/repo-map/updater.js +115 -27
- package/plugins/sync-docs/lib/state/workflow-state.js +31 -30
- package/plugins/sync-docs/lib/utils/command-parser.js +0 -0
- package/plugins/sync-docs/lib/utils/state-helpers.js +61 -0
- package/plugins/sync-docs/skills/sync-docs/SKILL.md +1 -1
- package/scripts/bump-version.js +4 -1
- package/scripts/dev-install.js +9 -0
- package/scripts/validate-opencode-install.js +17 -4
- package/site/content.json +1 -1
- package/site/index.html +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
-
const { execFileSync, spawnSync } = require('child_process');
|
|
9
|
+
const { execFileSync, spawnSync, spawn } = require('child_process');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const fsPromises = require('fs').promises;
|
|
@@ -15,6 +15,7 @@ const crypto = require('crypto');
|
|
|
15
15
|
const installer = require('./installer');
|
|
16
16
|
const queries = require('./queries');
|
|
17
17
|
const slopAnalyzers = require('../patterns/slop-analyzers');
|
|
18
|
+
const { runWithConcurrency } = require('./concurrency');
|
|
18
19
|
|
|
19
20
|
// Language file extensions mapping
|
|
20
21
|
const LANGUAGE_EXTENSIONS = {
|
|
@@ -33,6 +34,7 @@ const EXCLUDE_DIRS = Array.from(new Set([
|
|
|
33
34
|
]));
|
|
34
35
|
|
|
35
36
|
const AST_GREP_BATCH_SIZE = 100;
|
|
37
|
+
const AST_GREP_CONCURRENCY = 4;
|
|
36
38
|
const LANGUAGE_EXTENSION_SCAN_LIMIT = 500;
|
|
37
39
|
const FILE_READ_BATCH_SIZE = 50; // Concurrent file reads
|
|
38
40
|
|
|
@@ -253,8 +255,12 @@ async function fullScan(basePath, languages, options = {}) {
|
|
|
253
255
|
const pattern = typeof patternDef === 'string' ? patternDef : patternDef.pattern;
|
|
254
256
|
if (!pattern) continue;
|
|
255
257
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
+
const matchesByChunk = await runAstGrepPatternBatches(cmd, pattern, sgLang, basePath, chunks, {
|
|
259
|
+
onError: (error) => map.stats.errors.push(error),
|
|
260
|
+
concurrency: options.astGrepConcurrency
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
for (const matches of matchesByChunk) {
|
|
258
264
|
for (const match of matches) {
|
|
259
265
|
const matchedPath = normalizeMatchPath(match.file, basePath);
|
|
260
266
|
if (!matchedPath) continue;
|
|
@@ -495,6 +501,127 @@ function chunkArray(items, size) {
|
|
|
495
501
|
return chunks;
|
|
496
502
|
}
|
|
497
503
|
|
|
504
|
+
function truncatePattern(pattern, max = 120) {
|
|
505
|
+
if (typeof pattern !== 'string') return '';
|
|
506
|
+
if (pattern.length <= max) return pattern;
|
|
507
|
+
return `${pattern.slice(0, max - 3)}...`;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function buildAstGrepError({ reason, pattern, lang, filePaths, basePath, stderr }) {
|
|
511
|
+
const batchLabel = Array.isArray(filePaths) && filePaths.length === 1
|
|
512
|
+
? normalizeMatchPath(filePaths[0], basePath)
|
|
513
|
+
: '[batch]';
|
|
514
|
+
|
|
515
|
+
const details = stderr && String(stderr).trim()
|
|
516
|
+
? ` (${String(stderr).trim()})`
|
|
517
|
+
: '';
|
|
518
|
+
|
|
519
|
+
return {
|
|
520
|
+
file: batchLabel,
|
|
521
|
+
error: `ast-grep ${reason} for ${lang}${details}`,
|
|
522
|
+
pattern: truncatePattern(pattern)
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function runAstGrepPatternAsync(cmd, pattern, lang, basePath, filePaths, options = {}) {
|
|
527
|
+
if (!pattern || !filePaths || filePaths.length === 0) {
|
|
528
|
+
return Promise.resolve([]);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return new Promise((resolve) => {
|
|
532
|
+
const child = spawn(cmd, [
|
|
533
|
+
'run',
|
|
534
|
+
'--pattern', pattern,
|
|
535
|
+
'--lang', lang,
|
|
536
|
+
'--json=stream',
|
|
537
|
+
...filePaths
|
|
538
|
+
], {
|
|
539
|
+
cwd: basePath,
|
|
540
|
+
windowsHide: true,
|
|
541
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
let stdout = '';
|
|
545
|
+
let stderr = '';
|
|
546
|
+
let settled = false;
|
|
547
|
+
|
|
548
|
+
const timeoutHandle = setTimeout(() => {
|
|
549
|
+
if (settled) return;
|
|
550
|
+
settled = true;
|
|
551
|
+
child.kill();
|
|
552
|
+
if (typeof options.onError === 'function') {
|
|
553
|
+
options.onError(buildAstGrepError({
|
|
554
|
+
reason: 'timed out after 300000ms',
|
|
555
|
+
pattern,
|
|
556
|
+
lang,
|
|
557
|
+
filePaths,
|
|
558
|
+
basePath,
|
|
559
|
+
stderr
|
|
560
|
+
}));
|
|
561
|
+
}
|
|
562
|
+
resolve([]);
|
|
563
|
+
}, 300000);
|
|
564
|
+
|
|
565
|
+
child.stdout.on('data', (chunk) => {
|
|
566
|
+
stdout += chunk.toString();
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
child.stderr.on('data', (chunk) => {
|
|
570
|
+
stderr += chunk.toString();
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
child.on('error', (error) => {
|
|
574
|
+
if (settled) return;
|
|
575
|
+
settled = true;
|
|
576
|
+
clearTimeout(timeoutHandle);
|
|
577
|
+
if (typeof options.onError === 'function') {
|
|
578
|
+
options.onError(buildAstGrepError({
|
|
579
|
+
reason: 'execution failed',
|
|
580
|
+
pattern,
|
|
581
|
+
lang,
|
|
582
|
+
filePaths,
|
|
583
|
+
basePath,
|
|
584
|
+
stderr: error.message
|
|
585
|
+
}));
|
|
586
|
+
}
|
|
587
|
+
resolve([]);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
child.on('close', (code) => {
|
|
591
|
+
if (settled) return;
|
|
592
|
+
settled = true;
|
|
593
|
+
clearTimeout(timeoutHandle);
|
|
594
|
+
|
|
595
|
+
if (typeof code === 'number' && code > 1) {
|
|
596
|
+
if (typeof options.onError === 'function') {
|
|
597
|
+
options.onError(buildAstGrepError({
|
|
598
|
+
reason: `returned exit code ${code}`,
|
|
599
|
+
pattern,
|
|
600
|
+
lang,
|
|
601
|
+
filePaths,
|
|
602
|
+
basePath,
|
|
603
|
+
stderr
|
|
604
|
+
}));
|
|
605
|
+
}
|
|
606
|
+
resolve([]);
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
resolve(parseNdjson(stdout));
|
|
611
|
+
});
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
async function runAstGrepPatternBatches(cmd, pattern, lang, basePath, chunks, options = {}) {
|
|
616
|
+
const concurrency = Number.isFinite(options.concurrency)
|
|
617
|
+
? Math.max(1, Math.floor(options.concurrency))
|
|
618
|
+
: AST_GREP_CONCURRENCY;
|
|
619
|
+
|
|
620
|
+
return runWithConcurrency(chunks, concurrency, async (chunk) => {
|
|
621
|
+
return runAstGrepPatternAsync(cmd, pattern, lang, basePath, chunk, options);
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
|
|
498
625
|
function normalizeMatchPath(matchFile, basePath) {
|
|
499
626
|
if (!matchFile) return null;
|
|
500
627
|
const absolutePath = path.isAbsolute(matchFile) ? matchFile : path.join(basePath, matchFile);
|
|
@@ -513,7 +640,7 @@ function addSymbolToMap(map, name, match, kind, extra = {}) {
|
|
|
513
640
|
}
|
|
514
641
|
}
|
|
515
642
|
|
|
516
|
-
function runAstGrepPattern(cmd, pattern, lang, basePath, filePaths) {
|
|
643
|
+
function runAstGrepPattern(cmd, pattern, lang, basePath, filePaths, options = {}) {
|
|
517
644
|
if (!pattern || !filePaths || filePaths.length === 0) return [];
|
|
518
645
|
|
|
519
646
|
try {
|
|
@@ -532,15 +659,45 @@ function runAstGrepPattern(cmd, pattern, lang, basePath, filePaths) {
|
|
|
532
659
|
});
|
|
533
660
|
|
|
534
661
|
if (result.error) {
|
|
662
|
+
if (typeof options.onError === 'function') {
|
|
663
|
+
options.onError(buildAstGrepError({
|
|
664
|
+
reason: 'execution failed',
|
|
665
|
+
pattern,
|
|
666
|
+
lang,
|
|
667
|
+
filePaths,
|
|
668
|
+
basePath,
|
|
669
|
+
stderr: result.error.message
|
|
670
|
+
}));
|
|
671
|
+
}
|
|
535
672
|
return [];
|
|
536
673
|
}
|
|
537
674
|
|
|
538
675
|
if (typeof result.status === 'number' && result.status > 1) {
|
|
676
|
+
if (typeof options.onError === 'function') {
|
|
677
|
+
options.onError(buildAstGrepError({
|
|
678
|
+
reason: `returned exit code ${result.status}`,
|
|
679
|
+
pattern,
|
|
680
|
+
lang,
|
|
681
|
+
filePaths,
|
|
682
|
+
basePath,
|
|
683
|
+
stderr: result.stderr
|
|
684
|
+
}));
|
|
685
|
+
}
|
|
539
686
|
return [];
|
|
540
687
|
}
|
|
541
688
|
|
|
542
689
|
return parseNdjson(result.stdout);
|
|
543
|
-
} catch {
|
|
690
|
+
} catch (error) {
|
|
691
|
+
if (typeof options.onError === 'function') {
|
|
692
|
+
options.onError(buildAstGrepError({
|
|
693
|
+
reason: 'threw an exception',
|
|
694
|
+
pattern,
|
|
695
|
+
lang,
|
|
696
|
+
filePaths,
|
|
697
|
+
basePath,
|
|
698
|
+
stderr: error.message
|
|
699
|
+
}));
|
|
700
|
+
}
|
|
544
701
|
return [];
|
|
545
702
|
}
|
|
546
703
|
}
|
|
@@ -555,7 +712,7 @@ function runAstGrepPattern(cmd, pattern, lang, basePath, filePaths) {
|
|
|
555
712
|
* @param {string} content - File content
|
|
556
713
|
* @returns {Object} - Extracted symbols
|
|
557
714
|
*/
|
|
558
|
-
function extractSymbols(cmd, file, language, langQueries, basePath, content) {
|
|
715
|
+
function extractSymbols(cmd, file, language, langQueries, basePath, content, options = {}) {
|
|
559
716
|
const symbols = {
|
|
560
717
|
exports: [],
|
|
561
718
|
functions: [],
|
|
@@ -588,7 +745,7 @@ function extractSymbols(cmd, file, language, langQueries, basePath, content) {
|
|
|
588
745
|
if (!patterns) return;
|
|
589
746
|
for (const patternDef of patterns) {
|
|
590
747
|
const pattern = patternDef.pattern || patternDef;
|
|
591
|
-
const results = runAstGrep(cmd, file, pattern, sgLang, basePath);
|
|
748
|
+
const results = runAstGrep(cmd, file, pattern, sgLang, basePath, options);
|
|
592
749
|
for (const match of results) {
|
|
593
750
|
const names = extractNamesFromMatch(match, patternDef);
|
|
594
751
|
for (const name of names) {
|
|
@@ -640,7 +797,7 @@ function extractSymbols(cmd, file, language, langQueries, basePath, content) {
|
|
|
640
797
|
* @param {string} basePath - Repository root (for cwd)
|
|
641
798
|
* @returns {Array} - Extracted imports
|
|
642
799
|
*/
|
|
643
|
-
function extractImports(cmd, file, language, langQueries, basePath) {
|
|
800
|
+
function extractImports(cmd, file, language, langQueries, basePath, options = {}) {
|
|
644
801
|
const imports = [];
|
|
645
802
|
|
|
646
803
|
if (!langQueries.imports) return imports;
|
|
@@ -650,7 +807,7 @@ function extractImports(cmd, file, language, langQueries, basePath) {
|
|
|
650
807
|
|
|
651
808
|
for (const patternDef of langQueries.imports) {
|
|
652
809
|
const pattern = patternDef.pattern || patternDef;
|
|
653
|
-
const results = runAstGrep(cmd, file, pattern, sgLang, basePath);
|
|
810
|
+
const results = runAstGrep(cmd, file, pattern, sgLang, basePath, options);
|
|
654
811
|
for (const match of results) {
|
|
655
812
|
const sourceResult = extractSourceFromMatch(match, patternDef);
|
|
656
813
|
const sources = Array.isArray(sourceResult) ? sourceResult : [sourceResult];
|
|
@@ -680,7 +837,7 @@ function extractImports(cmd, file, language, langQueries, basePath) {
|
|
|
680
837
|
* @param {string} basePath - Working directory
|
|
681
838
|
* @returns {Array} - Match results
|
|
682
839
|
*/
|
|
683
|
-
function runAstGrep(cmd, file, pattern, lang, basePath) {
|
|
840
|
+
function runAstGrep(cmd, file, pattern, lang, basePath, options = {}) {
|
|
684
841
|
try {
|
|
685
842
|
const result = spawnSync(cmd, [
|
|
686
843
|
'run',
|
|
@@ -697,16 +854,46 @@ function runAstGrep(cmd, file, pattern, lang, basePath) {
|
|
|
697
854
|
});
|
|
698
855
|
|
|
699
856
|
if (result.error) {
|
|
857
|
+
if (typeof options.onError === 'function') {
|
|
858
|
+
options.onError(buildAstGrepError({
|
|
859
|
+
reason: 'execution failed',
|
|
860
|
+
pattern,
|
|
861
|
+
lang,
|
|
862
|
+
filePaths: [file],
|
|
863
|
+
basePath,
|
|
864
|
+
stderr: result.error.message
|
|
865
|
+
}));
|
|
866
|
+
}
|
|
700
867
|
return [];
|
|
701
868
|
}
|
|
702
869
|
|
|
703
870
|
// ast-grep exits with 1 when no matches
|
|
704
871
|
if (typeof result.status === 'number' && result.status > 1) {
|
|
872
|
+
if (typeof options.onError === 'function') {
|
|
873
|
+
options.onError(buildAstGrepError({
|
|
874
|
+
reason: `returned exit code ${result.status}`,
|
|
875
|
+
pattern,
|
|
876
|
+
lang,
|
|
877
|
+
filePaths: [file],
|
|
878
|
+
basePath,
|
|
879
|
+
stderr: result.stderr
|
|
880
|
+
}));
|
|
881
|
+
}
|
|
705
882
|
return [];
|
|
706
883
|
}
|
|
707
884
|
|
|
708
885
|
return parseNdjson(result.stdout);
|
|
709
|
-
} catch {
|
|
886
|
+
} catch (error) {
|
|
887
|
+
if (typeof options.onError === 'function') {
|
|
888
|
+
options.onError(buildAstGrepError({
|
|
889
|
+
reason: 'threw an exception',
|
|
890
|
+
pattern,
|
|
891
|
+
lang,
|
|
892
|
+
filePaths: [file],
|
|
893
|
+
basePath,
|
|
894
|
+
stderr: error.message
|
|
895
|
+
}));
|
|
896
|
+
}
|
|
710
897
|
return [];
|
|
711
898
|
}
|
|
712
899
|
}
|
|
@@ -1070,7 +1257,7 @@ function detectProjectType(languages) {
|
|
|
1070
1257
|
* @param {string} basePath - Repository root
|
|
1071
1258
|
* @returns {Object|null} - File data or null if failed
|
|
1072
1259
|
*/
|
|
1073
|
-
function scanSingleFile(cmd, file, basePath) {
|
|
1260
|
+
function scanSingleFile(cmd, file, basePath, options = {}) {
|
|
1074
1261
|
const ext = path.extname(file).toLowerCase();
|
|
1075
1262
|
|
|
1076
1263
|
// Find language for this extension
|
|
@@ -1091,8 +1278,8 @@ function scanSingleFile(cmd, file, basePath) {
|
|
|
1091
1278
|
const content = fs.readFileSync(file, 'utf8');
|
|
1092
1279
|
const hash = crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
1093
1280
|
|
|
1094
|
-
const symbols = extractSymbols(cmd, file, language, langQueries, basePath, content);
|
|
1095
|
-
const imports = extractImports(cmd, file, language, langQueries, basePath);
|
|
1281
|
+
const symbols = extractSymbols(cmd, file, language, langQueries, basePath, content, options);
|
|
1282
|
+
const imports = extractImports(cmd, file, language, langQueries, basePath, options);
|
|
1096
1283
|
|
|
1097
1284
|
return {
|
|
1098
1285
|
hash,
|
|
@@ -1101,7 +1288,13 @@ function scanSingleFile(cmd, file, basePath) {
|
|
|
1101
1288
|
symbols,
|
|
1102
1289
|
imports
|
|
1103
1290
|
};
|
|
1104
|
-
} catch {
|
|
1291
|
+
} catch (error) {
|
|
1292
|
+
if (typeof options.onError === 'function') {
|
|
1293
|
+
options.onError({
|
|
1294
|
+
file: normalizeMatchPath(file, basePath) || file,
|
|
1295
|
+
error: `Failed to scan file: ${error.message}`
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1105
1298
|
return null;
|
|
1106
1299
|
}
|
|
1107
1300
|
}
|
|
@@ -1114,7 +1307,7 @@ function scanSingleFile(cmd, file, basePath) {
|
|
|
1114
1307
|
* @param {string} basePath - Repository root
|
|
1115
1308
|
* @returns {Promise<Object|null>} - File data or null if failed
|
|
1116
1309
|
*/
|
|
1117
|
-
async function scanSingleFileAsync(cmd, file, basePath) {
|
|
1310
|
+
async function scanSingleFileAsync(cmd, file, basePath, options = {}) {
|
|
1118
1311
|
const ext = path.extname(file).toLowerCase();
|
|
1119
1312
|
|
|
1120
1313
|
// Find language for this extension
|
|
@@ -1135,8 +1328,8 @@ async function scanSingleFileAsync(cmd, file, basePath) {
|
|
|
1135
1328
|
const content = await fsPromises.readFile(file, 'utf8');
|
|
1136
1329
|
const hash = crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
1137
1330
|
|
|
1138
|
-
const symbols = extractSymbols(cmd, file, language, langQueries, basePath, content);
|
|
1139
|
-
const imports = extractImports(cmd, file, language, langQueries, basePath);
|
|
1331
|
+
const symbols = extractSymbols(cmd, file, language, langQueries, basePath, content, options);
|
|
1332
|
+
const imports = extractImports(cmd, file, language, langQueries, basePath, options);
|
|
1140
1333
|
|
|
1141
1334
|
return {
|
|
1142
1335
|
hash,
|
|
@@ -1145,7 +1338,13 @@ async function scanSingleFileAsync(cmd, file, basePath) {
|
|
|
1145
1338
|
symbols,
|
|
1146
1339
|
imports
|
|
1147
1340
|
};
|
|
1148
|
-
} catch {
|
|
1341
|
+
} catch (error) {
|
|
1342
|
+
if (typeof options.onError === 'function') {
|
|
1343
|
+
options.onError({
|
|
1344
|
+
file: normalizeMatchPath(file, basePath) || file,
|
|
1345
|
+
error: `Failed to scan file: ${error.message}`
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1149
1348
|
return null;
|
|
1150
1349
|
}
|
|
1151
1350
|
}
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
-
const fs = require('fs');
|
|
10
9
|
const fsPromises = require('fs').promises;
|
|
11
10
|
const path = require('path');
|
|
12
11
|
const { execFileSync } = require('child_process');
|
|
@@ -14,6 +13,15 @@ const { execFileSync } = require('child_process');
|
|
|
14
13
|
const runner = require('./runner');
|
|
15
14
|
const cache = require('./cache');
|
|
16
15
|
const installer = require('./installer');
|
|
16
|
+
const { runWithConcurrency } = require('./concurrency');
|
|
17
|
+
|
|
18
|
+
const SCAN_CONCURRENCY = 8;
|
|
19
|
+
const SCANNABLE_EXTENSIONS = new Set(Object.values(runner.LANGUAGE_EXTENSIONS).flat());
|
|
20
|
+
|
|
21
|
+
function isScannableFile(filePath) {
|
|
22
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
23
|
+
return SCANNABLE_EXTENSIONS.has(ext);
|
|
24
|
+
}
|
|
17
25
|
|
|
18
26
|
/**
|
|
19
27
|
* Perform incremental update based on git diff
|
|
@@ -47,6 +55,10 @@ async function incrementalUpdate(basePath, map) {
|
|
|
47
55
|
needsFullRebuild: true
|
|
48
56
|
};
|
|
49
57
|
}
|
|
58
|
+
map.stats = map.stats || {};
|
|
59
|
+
if (!Array.isArray(map.stats.errors)) {
|
|
60
|
+
map.stats.errors = [];
|
|
61
|
+
}
|
|
50
62
|
if (map.docs) {
|
|
51
63
|
delete map.docs;
|
|
52
64
|
}
|
|
@@ -118,19 +130,46 @@ async function incrementalUpdate(basePath, map) {
|
|
|
118
130
|
})
|
|
119
131
|
);
|
|
120
132
|
|
|
121
|
-
// Process files that exist
|
|
122
|
-
|
|
123
|
-
|
|
133
|
+
// Process files that exist with bounded concurrency
|
|
134
|
+
const scanTargets = existenceChecks.filter(({ file, exists }) => exists && isScannableFile(file));
|
|
135
|
+
const scanResults = await runWithConcurrency(scanTargets, SCAN_CONCURRENCY, async ({ file, fullPath }) => {
|
|
136
|
+
const astErrors = [];
|
|
137
|
+
const fileData = await runner.scanSingleFileAsync(installed.command, fullPath, basePath, {
|
|
138
|
+
onError: (error) => astErrors.push(error)
|
|
139
|
+
});
|
|
140
|
+
return { file, fileData, astErrors };
|
|
141
|
+
});
|
|
124
142
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
143
|
+
const scanFailures = [];
|
|
144
|
+
for (const result of scanResults) {
|
|
145
|
+
if (!result) continue;
|
|
146
|
+
|
|
147
|
+
if (result.astErrors.length > 0) {
|
|
148
|
+
map.stats.errors.push(...result.astErrors);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!result.fileData) {
|
|
152
|
+
if (result.astErrors.length > 0) {
|
|
153
|
+
scanFailures.push(result.file);
|
|
132
154
|
}
|
|
155
|
+
continue;
|
|
133
156
|
}
|
|
157
|
+
|
|
158
|
+
map.files[result.file] = result.fileData;
|
|
159
|
+
if (result.fileData.imports && result.fileData.imports.length > 0) {
|
|
160
|
+
map.dependencies[result.file] = Array.from(new Set(result.fileData.imports.map(imp => imp.source)));
|
|
161
|
+
} else {
|
|
162
|
+
delete map.dependencies[result.file];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (scanFailures.length > 0) {
|
|
167
|
+
return {
|
|
168
|
+
success: false,
|
|
169
|
+
error: `Failed to rescan ${scanFailures.length} file(s) during incremental update`,
|
|
170
|
+
needsFullRebuild: true,
|
|
171
|
+
failedFiles: scanFailures
|
|
172
|
+
};
|
|
134
173
|
}
|
|
135
174
|
|
|
136
175
|
// Recalculate stats
|
|
@@ -163,6 +202,10 @@ async function incrementalUpdate(basePath, map) {
|
|
|
163
202
|
async function updateWithoutGit(basePath, map, cmd) {
|
|
164
203
|
const currentFiles = new Set();
|
|
165
204
|
const languages = map.project?.languages || [];
|
|
205
|
+
map.stats = map.stats || {};
|
|
206
|
+
if (!Array.isArray(map.stats.errors)) {
|
|
207
|
+
map.stats.errors = [];
|
|
208
|
+
}
|
|
166
209
|
if (map.docs) {
|
|
167
210
|
delete map.docs;
|
|
168
211
|
}
|
|
@@ -204,31 +247,76 @@ async function updateWithoutGit(basePath, map, cmd) {
|
|
|
204
247
|
}
|
|
205
248
|
|
|
206
249
|
// Process existing files for modifications (async file reads)
|
|
207
|
-
|
|
250
|
+
const checkResults = await runWithConcurrency(filesToCheck, SCAN_CONCURRENCY, async (file) => {
|
|
208
251
|
const fullPath = path.join(basePath, file);
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
252
|
+
const astErrors = [];
|
|
253
|
+
const fileData = await runner.scanSingleFileAsync(cmd, fullPath, basePath, {
|
|
254
|
+
onError: (error) => astErrors.push(error)
|
|
255
|
+
});
|
|
256
|
+
return { file, fileData, astErrors };
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const scanFailures = [];
|
|
260
|
+
for (const result of checkResults) {
|
|
261
|
+
if (!result) continue;
|
|
262
|
+
|
|
263
|
+
if (result.astErrors.length > 0) {
|
|
264
|
+
map.stats.errors.push(...result.astErrors);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (!result.fileData) {
|
|
268
|
+
scanFailures.push(result.file);
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (result.fileData.hash !== map.files[result.file].hash) {
|
|
273
|
+
map.files[result.file] = result.fileData;
|
|
274
|
+
if (result.fileData.imports && result.fileData.imports.length > 0) {
|
|
275
|
+
map.dependencies[result.file] = Array.from(new Set(result.fileData.imports.map(imp => imp.source)));
|
|
214
276
|
} else {
|
|
215
|
-
delete map.dependencies[file];
|
|
277
|
+
delete map.dependencies[result.file];
|
|
216
278
|
}
|
|
217
|
-
changes.modified.push(file);
|
|
279
|
+
changes.modified.push(result.file);
|
|
218
280
|
}
|
|
219
281
|
}
|
|
220
282
|
|
|
221
283
|
// Process new files (async file reads)
|
|
222
|
-
|
|
284
|
+
const addedFiles = Array.from(currentFiles);
|
|
285
|
+
const addResults = await runWithConcurrency(addedFiles, SCAN_CONCURRENCY, async (file) => {
|
|
223
286
|
const fullPath = path.join(basePath, file);
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
287
|
+
const astErrors = [];
|
|
288
|
+
const fileData = await runner.scanSingleFileAsync(cmd, fullPath, basePath, {
|
|
289
|
+
onError: (error) => astErrors.push(error)
|
|
290
|
+
});
|
|
291
|
+
return { file, fileData, astErrors };
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
for (const result of addResults) {
|
|
295
|
+
if (!result) continue;
|
|
296
|
+
|
|
297
|
+
if (result.astErrors.length > 0) {
|
|
298
|
+
map.stats.errors.push(...result.astErrors);
|
|
231
299
|
}
|
|
300
|
+
|
|
301
|
+
if (!result.fileData) {
|
|
302
|
+
scanFailures.push(result.file);
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
map.files[result.file] = result.fileData;
|
|
307
|
+
if (result.fileData.imports && result.fileData.imports.length > 0) {
|
|
308
|
+
map.dependencies[result.file] = Array.from(new Set(result.fileData.imports.map(imp => imp.source)));
|
|
309
|
+
}
|
|
310
|
+
changes.added.push(result.file);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (scanFailures.length > 0) {
|
|
314
|
+
return {
|
|
315
|
+
success: false,
|
|
316
|
+
error: `Failed to rescan ${scanFailures.length} file(s) during non-git update`,
|
|
317
|
+
needsFullRebuild: true,
|
|
318
|
+
failedFiles: scanFailures
|
|
319
|
+
};
|
|
232
320
|
}
|
|
233
321
|
|
|
234
322
|
changes.total = changes.added.length + changes.modified.length + changes.deleted.length;
|