agentsys 5.0.2 → 5.1.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/.claude-plugin/marketplace.json +21 -14
- package/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +2 -1
- package/CHANGELOG.md +24 -1
- package/README.md +7 -6
- package/adapters/codex/skills/agnix/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project-agents/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project-github/SKILL.md +0 -1
- package/adapters/codex/skills/consult/SKILL.md +133 -59
- package/adapters/codex/skills/debate/SKILL.md +214 -0
- package/adapters/codex/skills/delivery-approval/SKILL.md +0 -1
- package/adapters/codex/skills/deslop/SKILL.md +0 -1
- package/adapters/codex/skills/drift-detect/SKILL.md +0 -1
- package/adapters/codex/skills/enhance/SKILL.md +0 -1
- package/adapters/codex/skills/learn/SKILL.md +0 -1
- package/adapters/codex/skills/next-task/SKILL.md +0 -1
- package/adapters/codex/skills/perf/SKILL.md +0 -1
- package/adapters/codex/skills/repo-map/SKILL.md +0 -1
- package/adapters/codex/skills/ship/SKILL.md +0 -1
- package/adapters/codex/skills/ship-ci-review-loop/SKILL.md +0 -1
- package/adapters/codex/skills/ship-deployment/SKILL.md +0 -1
- package/adapters/codex/skills/ship-error-handling/SKILL.md +0 -1
- package/adapters/codex/skills/sync-docs/SKILL.md +0 -1
- package/adapters/opencode/agents/agent-enhancer.md +0 -1
- package/adapters/opencode/agents/agnix-agent.md +0 -1
- package/adapters/opencode/agents/ci-fixer.md +0 -1
- package/adapters/opencode/agents/ci-monitor.md +0 -1
- package/adapters/opencode/agents/claudemd-enhancer.md +0 -1
- package/adapters/opencode/agents/consult-agent.md +123 -31
- package/adapters/opencode/agents/cross-file-enhancer.md +0 -1
- package/adapters/opencode/agents/debate-orchestrator.md +169 -0
- package/adapters/opencode/agents/delivery-validator.md +0 -1
- package/adapters/opencode/agents/deslop-agent.md +0 -1
- package/adapters/opencode/agents/docs-enhancer.md +0 -1
- package/adapters/opencode/agents/exploration-agent.md +0 -1
- package/adapters/opencode/agents/hooks-enhancer.md +0 -1
- package/adapters/opencode/agents/implementation-agent.md +0 -1
- package/adapters/opencode/agents/learn-agent.md +0 -1
- package/adapters/opencode/agents/map-validator.md +0 -1
- package/adapters/opencode/agents/perf-analyzer.md +0 -1
- package/adapters/opencode/agents/perf-code-paths.md +0 -1
- package/adapters/opencode/agents/perf-investigation-logger.md +0 -1
- package/adapters/opencode/agents/perf-orchestrator.md +0 -1
- package/adapters/opencode/agents/perf-theory-gatherer.md +0 -1
- package/adapters/opencode/agents/perf-theory-tester.md +0 -1
- package/adapters/opencode/agents/plan-synthesizer.md +0 -1
- package/adapters/opencode/agents/planning-agent.md +0 -1
- package/adapters/opencode/agents/plugin-enhancer.md +0 -1
- package/adapters/opencode/agents/prompt-enhancer.md +0 -1
- package/adapters/opencode/agents/simple-fixer.md +0 -1
- package/adapters/opencode/agents/skills-enhancer.md +0 -1
- package/adapters/opencode/agents/sync-docs-agent.md +0 -1
- package/adapters/opencode/agents/task-discoverer.md +0 -1
- package/adapters/opencode/agents/test-coverage-checker.md +0 -1
- package/adapters/opencode/agents/worktree-manager.md +0 -1
- package/adapters/opencode/commands/agnix.md +0 -1
- package/adapters/opencode/commands/audit-project-agents.md +0 -1
- package/adapters/opencode/commands/audit-project-github.md +0 -1
- package/adapters/opencode/commands/audit-project.md +0 -1
- package/adapters/opencode/commands/consult.md +134 -59
- package/adapters/opencode/commands/debate.md +224 -0
- package/adapters/opencode/commands/delivery-approval.md +0 -1
- package/adapters/opencode/commands/deslop.md +0 -1
- package/adapters/opencode/commands/drift-detect.md +0 -1
- package/adapters/opencode/commands/enhance.md +0 -1
- package/adapters/opencode/commands/learn.md +0 -1
- package/adapters/opencode/commands/next-task.md +0 -1
- package/adapters/opencode/commands/perf.md +0 -1
- package/adapters/opencode/commands/repo-map.md +0 -1
- package/adapters/opencode/commands/ship-ci-review-loop.md +0 -1
- package/adapters/opencode/commands/ship-deployment.md +0 -1
- package/adapters/opencode/commands/ship-error-handling.md +0 -1
- package/adapters/opencode/commands/ship.md +0 -1
- package/adapters/opencode/commands/sync-docs.md +0 -1
- package/adapters/opencode/skills/agnix/SKILL.md +1 -2
- package/adapters/opencode/skills/consult/SKILL.md +41 -27
- package/adapters/opencode/skills/debate/SKILL.md +245 -0
- package/adapters/opencode/skills/deslop/SKILL.md +1 -2
- package/adapters/opencode/skills/discover-tasks/SKILL.md +1 -2
- package/adapters/opencode/skills/drift-analysis/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-agent-prompts/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-claude-memory/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-cross-file/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-docs/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-hooks/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-orchestrator/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-plugins/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-prompts/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-skills/SKILL.md +1 -2
- package/adapters/opencode/skills/learn/SKILL.md +1 -2
- package/adapters/opencode/skills/orchestrate-review/SKILL.md +0 -1
- package/adapters/opencode/skills/perf-analyzer/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-baseline-manager/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-benchmarker/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-code-paths/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-investigation-logger/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-profiler/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-theory-gatherer/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-theory-tester/SKILL.md +1 -2
- package/adapters/opencode/skills/repo-mapping/SKILL.md +1 -2
- package/adapters/opencode/skills/sync-docs/SKILL.md +1 -2
- package/adapters/opencode/skills/validate-delivery/SKILL.md +1 -2
- package/lib/adapter-transforms.js +24 -4
- package/package.json +1 -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/adapter-transforms.js +24 -4
- package/plugins/consult/.claude-plugin/plugin.json +1 -1
- package/plugins/consult/agents/consult-agent.md +123 -30
- package/plugins/consult/commands/consult.md +136 -60
- package/plugins/consult/skills/consult/SKILL.md +39 -24
- package/plugins/debate/.claude-plugin/plugin.json +21 -0
- package/plugins/debate/agents/debate-orchestrator.md +175 -0
- package/plugins/debate/commands/debate.md +221 -0
- package/plugins/debate/lib/adapter-transforms.js +298 -0
- package/plugins/debate/lib/collectors/codebase.js +392 -0
- package/plugins/debate/lib/collectors/docs-patterns.js +713 -0
- package/plugins/debate/lib/collectors/documentation.js +219 -0
- package/plugins/debate/lib/collectors/github.js +330 -0
- package/plugins/debate/lib/collectors/index.js +126 -0
- package/plugins/debate/lib/config/index.js +14 -0
- package/plugins/debate/lib/cross-platform/index.js +539 -0
- package/plugins/debate/lib/discovery/index.js +352 -0
- package/plugins/debate/lib/drift-detect/collectors.js +37 -0
- package/plugins/debate/lib/enhance/agent-analyzer.js +421 -0
- package/plugins/debate/lib/enhance/agent-patterns.js +571 -0
- package/plugins/debate/lib/enhance/auto-suppression.js +622 -0
- package/plugins/debate/lib/enhance/benchmark.js +417 -0
- package/plugins/debate/lib/enhance/cross-file-analyzer.js +930 -0
- package/plugins/debate/lib/enhance/cross-file-patterns.js +370 -0
- package/plugins/debate/lib/enhance/docs-analyzer.js +325 -0
- package/plugins/debate/lib/enhance/docs-patterns.js +671 -0
- package/plugins/debate/lib/enhance/fixer.js +721 -0
- package/plugins/debate/lib/enhance/hook-analyzer.js +135 -0
- package/plugins/debate/lib/enhance/hook-patterns.js +40 -0
- package/plugins/debate/lib/enhance/index.js +127 -0
- package/plugins/debate/lib/enhance/plugin-analyzer.js +402 -0
- package/plugins/debate/lib/enhance/plugin-patterns.js +326 -0
- package/plugins/debate/lib/enhance/projectmemory-analyzer.js +551 -0
- package/plugins/debate/lib/enhance/projectmemory-patterns.js +617 -0
- package/plugins/debate/lib/enhance/prompt-analyzer.js +457 -0
- package/plugins/debate/lib/enhance/prompt-patterns.js +1484 -0
- package/plugins/debate/lib/enhance/reporter.js +1348 -0
- package/plugins/debate/lib/enhance/security-patterns.js +284 -0
- package/plugins/debate/lib/enhance/skill-analyzer.js +182 -0
- package/plugins/debate/lib/enhance/skill-patterns.js +147 -0
- package/plugins/debate/lib/enhance/suppression.js +352 -0
- package/plugins/debate/lib/enhance/tool-patterns.js +373 -0
- package/plugins/debate/lib/index.js +270 -0
- package/plugins/debate/lib/patterns/cli-enhancers.js +611 -0
- package/plugins/debate/lib/patterns/pipeline.js +948 -0
- package/plugins/debate/lib/patterns/review-patterns.js +558 -0
- package/plugins/debate/lib/patterns/slop-analyzers.js +2305 -0
- package/plugins/debate/lib/patterns/slop-patterns.js +1187 -0
- package/plugins/debate/lib/perf/analyzer/index.js +22 -0
- package/plugins/debate/lib/perf/argument-parser.js +105 -0
- package/plugins/debate/lib/perf/baseline-comparator.js +50 -0
- package/plugins/debate/lib/perf/baseline-store.js +127 -0
- package/plugins/debate/lib/perf/benchmark-runner.js +404 -0
- package/plugins/debate/lib/perf/breaking-point-finder.js +52 -0
- package/plugins/debate/lib/perf/breaking-point-runner.js +60 -0
- package/plugins/debate/lib/perf/checkpoint.js +123 -0
- package/plugins/debate/lib/perf/code-paths.js +86 -0
- package/plugins/debate/lib/perf/consolidation.js +37 -0
- package/plugins/debate/lib/perf/constraint-runner.js +71 -0
- package/plugins/debate/lib/perf/experiment-runner.js +32 -0
- package/plugins/debate/lib/perf/index.js +41 -0
- package/plugins/debate/lib/perf/investigation-state.js +874 -0
- package/plugins/debate/lib/perf/optimization-runner.js +79 -0
- package/plugins/debate/lib/perf/profilers/go.js +22 -0
- package/plugins/debate/lib/perf/profilers/index.js +46 -0
- package/plugins/debate/lib/perf/profilers/java.js +23 -0
- package/plugins/debate/lib/perf/profilers/node.js +27 -0
- package/plugins/debate/lib/perf/profilers/python.js +23 -0
- package/plugins/debate/lib/perf/profilers/rust.js +23 -0
- package/plugins/debate/lib/perf/profiling-runner.js +75 -0
- package/plugins/debate/lib/perf/schemas.js +140 -0
- package/plugins/debate/lib/platform/detect-platform.js +413 -0
- package/plugins/debate/lib/platform/detection-configs.js +93 -0
- package/plugins/debate/lib/platform/state-dir.js +132 -0
- package/plugins/debate/lib/platform/verify-tools.js +182 -0
- package/plugins/debate/lib/repo-map/cache.js +152 -0
- package/plugins/debate/lib/repo-map/concurrency.js +29 -0
- package/plugins/debate/lib/repo-map/index.js +222 -0
- package/plugins/debate/lib/repo-map/installer.js +212 -0
- package/plugins/debate/lib/repo-map/queries/go.js +27 -0
- package/plugins/debate/lib/repo-map/queries/index.js +100 -0
- package/plugins/debate/lib/repo-map/queries/java.js +38 -0
- package/plugins/debate/lib/repo-map/queries/javascript.js +55 -0
- package/plugins/debate/lib/repo-map/queries/python.js +24 -0
- package/plugins/debate/lib/repo-map/queries/rust.js +73 -0
- package/plugins/debate/lib/repo-map/queries/typescript.js +38 -0
- package/plugins/debate/lib/repo-map/runner.js +1364 -0
- package/plugins/debate/lib/repo-map/updater.js +562 -0
- package/plugins/debate/lib/repo-map/usage-analyzer.js +407 -0
- package/plugins/debate/lib/schemas/plugin-manifest.schema.json +57 -0
- package/plugins/debate/lib/schemas/validator.js +247 -0
- package/plugins/debate/lib/sources/custom-handler.js +199 -0
- package/plugins/debate/lib/sources/policy-questions.js +246 -0
- package/plugins/debate/lib/sources/source-cache.js +165 -0
- package/plugins/debate/lib/state/workflow-state.js +576 -0
- package/plugins/debate/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/debate/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/debate/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/debate/lib/types/index.d.ts +84 -0
- package/plugins/debate/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/debate/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/debate/lib/utils/atomic-write.js +94 -0
- package/plugins/debate/lib/utils/cache-manager.js +159 -0
- package/plugins/debate/lib/utils/command-parser.js +0 -0
- package/plugins/debate/lib/utils/context-optimizer.js +300 -0
- package/plugins/debate/lib/utils/deprecation.js +37 -0
- package/plugins/debate/lib/utils/shell-escape.js +88 -0
- package/plugins/debate/lib/utils/state-helpers.js +61 -0
- package/plugins/debate/skills/debate/SKILL.md +264 -0
- package/plugins/deslop/.claude-plugin/plugin.json +1 -1
- package/plugins/deslop/lib/adapter-transforms.js +24 -4
- package/plugins/deslop/skills/deslop/SKILL.md +1 -1
- package/plugins/drift-detect/.claude-plugin/plugin.json +1 -1
- package/plugins/drift-detect/lib/adapter-transforms.js +24 -4
- package/plugins/drift-detect/skills/drift-analysis/SKILL.md +1 -1
- package/plugins/enhance/.claude-plugin/plugin.json +1 -1
- package/plugins/enhance/lib/adapter-transforms.js +24 -4
- 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/agents/learn-agent.md +1 -1
- package/plugins/learn/lib/adapter-transforms.js +24 -4
- package/plugins/learn/skills/learn/SKILL.md +1 -1
- package/plugins/next-task/.claude-plugin/plugin.json +1 -1
- package/plugins/next-task/agents/exploration-agent.md +1 -1
- package/plugins/next-task/lib/adapter-transforms.js +24 -4
- package/plugins/next-task/skills/discover-tasks/SKILL.md +1 -1
- package/plugins/next-task/skills/validate-delivery/SKILL.md +1 -1
- package/plugins/perf/.claude-plugin/plugin.json +1 -1
- package/plugins/perf/lib/adapter-transforms.js +24 -4
- 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/adapter-transforms.js +24 -4
- package/plugins/ship/.claude-plugin/plugin.json +1 -1
- package/plugins/ship/lib/adapter-transforms.js +24 -4
- package/plugins/sync-docs/.claude-plugin/plugin.json +1 -1
- package/plugins/sync-docs/lib/adapter-transforms.js +24 -4
- package/plugins/sync-docs/skills/sync-docs/SKILL.md +1 -1
- package/scripts/gen-adapters.js +6 -7
- package/scripts/generate-docs.js +4 -2
- package/scripts/plugins.txt +1 -0
- package/site/content.json +6 -6
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repo map incremental updater
|
|
3
|
+
*
|
|
4
|
+
* @module lib/repo-map/updater
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const fsPromises = require('fs').promises;
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { execFileSync } = require('child_process');
|
|
12
|
+
|
|
13
|
+
const runner = require('./runner');
|
|
14
|
+
const cache = require('./cache');
|
|
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
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Perform incremental update based on git diff
|
|
28
|
+
* @param {string} basePath - Repository root
|
|
29
|
+
* @param {Object} map - Existing repo map
|
|
30
|
+
* @returns {Promise<{success: boolean, map?: Object, changes?: Object, error?: string, needsFullRebuild?: boolean}>}
|
|
31
|
+
*/
|
|
32
|
+
async function incrementalUpdate(basePath, map) {
|
|
33
|
+
// Validate ast-grep
|
|
34
|
+
const installed = installer.checkInstalledSync();
|
|
35
|
+
if (!installed.found) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: 'ast-grep not found',
|
|
39
|
+
installSuggestion: installer.getInstallInstructions()
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!installer.meetsMinimumVersion(installed.version)) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
error: `ast-grep version ${installed.version || 'unknown'} is too old. Minimum required: ${installer.getMinimumVersion()}`,
|
|
47
|
+
installSuggestion: installer.getInstallInstructions()
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!map || !map.files) {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
error: 'Invalid repo map',
|
|
55
|
+
needsFullRebuild: true
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
map.stats = map.stats || {};
|
|
59
|
+
if (!Array.isArray(map.stats.errors)) {
|
|
60
|
+
map.stats.errors = [];
|
|
61
|
+
}
|
|
62
|
+
if (map.docs) {
|
|
63
|
+
delete map.docs;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Try git-based update first
|
|
67
|
+
const gitInfo = runner.getGitInfo(basePath);
|
|
68
|
+
if (!gitInfo || !map.git?.commit) {
|
|
69
|
+
return updateWithoutGit(basePath, map, installed.command);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Check if base commit exists
|
|
73
|
+
if (!commitExists(basePath, map.git.commit)) {
|
|
74
|
+
return {
|
|
75
|
+
success: false,
|
|
76
|
+
error: 'Base commit not found (history rewritten). Full rebuild required.',
|
|
77
|
+
needsFullRebuild: true
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const diff = getGitDiff(basePath, map.git.commit);
|
|
82
|
+
if (diff === null) {
|
|
83
|
+
return updateWithoutGit(basePath, map, installed.command);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const changes = parseDiff(diff);
|
|
87
|
+
|
|
88
|
+
// No changes - just update metadata
|
|
89
|
+
if (changes.total === 0) {
|
|
90
|
+
map.git = gitInfo;
|
|
91
|
+
map.updated = new Date().toISOString();
|
|
92
|
+
return {
|
|
93
|
+
success: true,
|
|
94
|
+
map,
|
|
95
|
+
changes: { total: 0, updated: 0, added: 0, deleted: 0, renamed: 0 }
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Apply deletions
|
|
100
|
+
for (const file of changes.deleted) {
|
|
101
|
+
delete map.files[file];
|
|
102
|
+
delete map.dependencies[file];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Apply renames
|
|
106
|
+
for (const { from, to } of changes.renamed) {
|
|
107
|
+
if (map.files[from]) {
|
|
108
|
+
map.files[to] = map.files[from];
|
|
109
|
+
delete map.files[from];
|
|
110
|
+
}
|
|
111
|
+
if (map.dependencies[from]) {
|
|
112
|
+
map.dependencies[to] = map.dependencies[from];
|
|
113
|
+
delete map.dependencies[from];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Apply added/modified - batch file existence checks
|
|
118
|
+
const updatedFiles = [...changes.added, ...changes.modified];
|
|
119
|
+
const fullPaths = updatedFiles.map(file => ({ file, fullPath: path.join(basePath, file) }));
|
|
120
|
+
|
|
121
|
+
// Batch check file existence
|
|
122
|
+
const existenceChecks = await Promise.all(
|
|
123
|
+
fullPaths.map(async ({ file, fullPath }) => {
|
|
124
|
+
try {
|
|
125
|
+
await fsPromises.access(fullPath);
|
|
126
|
+
return { file, fullPath, exists: true };
|
|
127
|
+
} catch {
|
|
128
|
+
return { file, fullPath, exists: false };
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
|
|
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
|
+
});
|
|
142
|
+
|
|
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);
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
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
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Recalculate stats
|
|
176
|
+
recalculateStats(map);
|
|
177
|
+
|
|
178
|
+
// Update git metadata
|
|
179
|
+
map.git = gitInfo;
|
|
180
|
+
map.updated = new Date().toISOString();
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
success: true,
|
|
184
|
+
map,
|
|
185
|
+
changes: {
|
|
186
|
+
total: changes.total,
|
|
187
|
+
updated: updatedFiles.length,
|
|
188
|
+
added: changes.added.length,
|
|
189
|
+
deleted: changes.deleted.length,
|
|
190
|
+
renamed: changes.renamed.length
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Update without git (hash comparison)
|
|
197
|
+
* @param {string} basePath - Repository root
|
|
198
|
+
* @param {Object} map - Existing repo map
|
|
199
|
+
* @param {string} cmd - ast-grep command
|
|
200
|
+
* @returns {Promise<{success: boolean, map?: Object, changes?: Object}>}
|
|
201
|
+
*/
|
|
202
|
+
async function updateWithoutGit(basePath, map, cmd) {
|
|
203
|
+
const currentFiles = new Set();
|
|
204
|
+
const languages = map.project?.languages || [];
|
|
205
|
+
map.stats = map.stats || {};
|
|
206
|
+
if (!Array.isArray(map.stats.errors)) {
|
|
207
|
+
map.stats.errors = [];
|
|
208
|
+
}
|
|
209
|
+
if (map.docs) {
|
|
210
|
+
delete map.docs;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
for (const lang of languages) {
|
|
214
|
+
const files = runner.findFilesForLanguage(basePath, lang);
|
|
215
|
+
for (const file of files) {
|
|
216
|
+
currentFiles.add(path.relative(basePath, file).replace(/\\/g, '/'));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const changes = {
|
|
221
|
+
added: [],
|
|
222
|
+
modified: [],
|
|
223
|
+
deleted: [],
|
|
224
|
+
renamed: [],
|
|
225
|
+
total: 0
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// Collect existing files to check
|
|
229
|
+
const existingFiles = Object.keys(map.files);
|
|
230
|
+
const filesToCheck = [];
|
|
231
|
+
const filesToDelete = [];
|
|
232
|
+
|
|
233
|
+
for (const file of existingFiles) {
|
|
234
|
+
if (!currentFiles.has(file)) {
|
|
235
|
+
filesToDelete.push(file);
|
|
236
|
+
} else {
|
|
237
|
+
filesToCheck.push(file);
|
|
238
|
+
currentFiles.delete(file);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Process deletions
|
|
243
|
+
for (const file of filesToDelete) {
|
|
244
|
+
changes.deleted.push(file);
|
|
245
|
+
delete map.files[file];
|
|
246
|
+
delete map.dependencies[file];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Process existing files for modifications (async file reads)
|
|
250
|
+
const checkResults = await runWithConcurrency(filesToCheck, SCAN_CONCURRENCY, async (file) => {
|
|
251
|
+
const fullPath = path.join(basePath, file);
|
|
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)));
|
|
276
|
+
} else {
|
|
277
|
+
delete map.dependencies[result.file];
|
|
278
|
+
}
|
|
279
|
+
changes.modified.push(result.file);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Process new files (async file reads)
|
|
284
|
+
const addedFiles = Array.from(currentFiles);
|
|
285
|
+
const addResults = await runWithConcurrency(addedFiles, SCAN_CONCURRENCY, async (file) => {
|
|
286
|
+
const fullPath = path.join(basePath, file);
|
|
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);
|
|
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
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
changes.total = changes.added.length + changes.modified.length + changes.deleted.length;
|
|
323
|
+
|
|
324
|
+
recalculateStats(map);
|
|
325
|
+
map.updated = new Date().toISOString();
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
success: true,
|
|
329
|
+
map,
|
|
330
|
+
changes: {
|
|
331
|
+
total: changes.total,
|
|
332
|
+
updated: changes.modified.length,
|
|
333
|
+
added: changes.added.length,
|
|
334
|
+
deleted: changes.deleted.length,
|
|
335
|
+
renamed: changes.renamed.length
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Check if repo-map is stale
|
|
342
|
+
* @param {string} basePath - Repository root
|
|
343
|
+
* @param {Object} map - Repo map
|
|
344
|
+
* @returns {Object} Staleness info
|
|
345
|
+
*/
|
|
346
|
+
function checkStaleness(basePath, map) {
|
|
347
|
+
const result = {
|
|
348
|
+
isStale: false,
|
|
349
|
+
reason: null,
|
|
350
|
+
commitsBehind: 0,
|
|
351
|
+
suggestFullRebuild: false
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
if (!map?.git?.commit) {
|
|
355
|
+
result.isStale = true;
|
|
356
|
+
result.reason = 'Missing base commit in repo-map';
|
|
357
|
+
result.suggestFullRebuild = true;
|
|
358
|
+
return result;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (cache.isMarkedStale(basePath)) {
|
|
362
|
+
result.isStale = true;
|
|
363
|
+
result.reason = 'Marked stale by hook';
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (!commitExists(basePath, map.git.commit)) {
|
|
367
|
+
result.isStale = true;
|
|
368
|
+
result.reason = 'Base commit no longer exists (rebased?)';
|
|
369
|
+
result.suggestFullRebuild = true;
|
|
370
|
+
return result;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const currentBranch = getCurrentBranch(basePath);
|
|
374
|
+
if (currentBranch && map.git.branch && currentBranch !== map.git.branch) {
|
|
375
|
+
result.isStale = true;
|
|
376
|
+
result.reason = `Branch changed from ${map.git.branch} to ${currentBranch}`;
|
|
377
|
+
result.suggestFullRebuild = true;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const commitsBehind = getCommitsBehind(basePath, map.git.commit);
|
|
381
|
+
if (commitsBehind > 0) {
|
|
382
|
+
result.isStale = true;
|
|
383
|
+
result.commitsBehind = commitsBehind;
|
|
384
|
+
if (!result.reason) {
|
|
385
|
+
result.reason = `${commitsBehind} commits behind HEAD`;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return result;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Parse git diff output
|
|
394
|
+
* @param {string} diff - Git diff output
|
|
395
|
+
* @returns {Object}
|
|
396
|
+
*/
|
|
397
|
+
function parseDiff(diff) {
|
|
398
|
+
const changes = {
|
|
399
|
+
added: [],
|
|
400
|
+
modified: [],
|
|
401
|
+
deleted: [],
|
|
402
|
+
renamed: [],
|
|
403
|
+
total: 0
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const lines = diff.split('\n').filter(Boolean);
|
|
407
|
+
for (const line of lines) {
|
|
408
|
+
const parts = line.split('\t');
|
|
409
|
+
const status = parts[0];
|
|
410
|
+
|
|
411
|
+
if (status.startsWith('R')) {
|
|
412
|
+
const from = normalizePath(parts[1]);
|
|
413
|
+
const to = normalizePath(parts[2]);
|
|
414
|
+
changes.renamed.push({ from, to });
|
|
415
|
+
const renameScore = Number(status.slice(1));
|
|
416
|
+
if (!Number.isNaN(renameScore) && renameScore < 100 && to) {
|
|
417
|
+
changes.modified.push(to);
|
|
418
|
+
}
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const file = normalizePath(parts[1]);
|
|
423
|
+
if (!file) continue;
|
|
424
|
+
|
|
425
|
+
if (status === 'A') changes.added.push(file);
|
|
426
|
+
else if (status === 'M') changes.modified.push(file);
|
|
427
|
+
else if (status === 'D') changes.deleted.push(file);
|
|
428
|
+
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
changes.total = changes.added.length + changes.modified.length + changes.deleted.length + changes.renamed.length;
|
|
432
|
+
return changes;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Validate git commit hash format
|
|
437
|
+
* @param {string} commit - Commit hash to validate
|
|
438
|
+
* @returns {boolean} True if valid hex commit hash
|
|
439
|
+
*/
|
|
440
|
+
function isValidCommitHash(commit) {
|
|
441
|
+
// Git commit hashes are 4-40 hex characters (short to full SHA)
|
|
442
|
+
return typeof commit === 'string' && /^[0-9a-fA-F]{4,40}$/.test(commit);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Get git diff name-status
|
|
447
|
+
* @param {string} basePath - Repository root
|
|
448
|
+
* @param {string} sinceCommit - Base commit
|
|
449
|
+
* @returns {string|null}
|
|
450
|
+
*/
|
|
451
|
+
function getGitDiff(basePath, sinceCommit) {
|
|
452
|
+
// Validate commit hash to prevent command injection
|
|
453
|
+
if (!isValidCommitHash(sinceCommit)) {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
try {
|
|
457
|
+
// Use execFileSync with arg array to prevent command injection
|
|
458
|
+
return execFileSync('git', ['diff', '--name-status', '-M', sinceCommit, 'HEAD'], {
|
|
459
|
+
cwd: basePath,
|
|
460
|
+
encoding: 'utf8',
|
|
461
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
462
|
+
}).trim();
|
|
463
|
+
} catch {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Check if commit exists
|
|
470
|
+
* @param {string} basePath - Repository root
|
|
471
|
+
* @param {string} commit - Commit hash
|
|
472
|
+
* @returns {boolean}
|
|
473
|
+
*/
|
|
474
|
+
function commitExists(basePath, commit) {
|
|
475
|
+
// Validate commit hash to prevent command injection
|
|
476
|
+
if (!isValidCommitHash(commit)) {
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
try {
|
|
480
|
+
// Use execFileSync with arg array to prevent command injection
|
|
481
|
+
execFileSync('git', ['cat-file', '-e', commit], {
|
|
482
|
+
cwd: basePath,
|
|
483
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
484
|
+
});
|
|
485
|
+
return true;
|
|
486
|
+
} catch {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Get current branch name
|
|
493
|
+
* @param {string} basePath - Repository root
|
|
494
|
+
* @returns {string|null}
|
|
495
|
+
*/
|
|
496
|
+
function getCurrentBranch(basePath) {
|
|
497
|
+
try {
|
|
498
|
+
// Use execFileSync with arg array for consistency
|
|
499
|
+
return execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
500
|
+
cwd: basePath,
|
|
501
|
+
encoding: 'utf8',
|
|
502
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
503
|
+
}).trim();
|
|
504
|
+
} catch {
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Get number of commits behind HEAD
|
|
511
|
+
* @param {string} basePath - Repository root
|
|
512
|
+
* @param {string} commit - Base commit
|
|
513
|
+
* @returns {number}
|
|
514
|
+
*/
|
|
515
|
+
function getCommitsBehind(basePath, commit) {
|
|
516
|
+
// Validate commit hash to prevent command injection
|
|
517
|
+
if (!isValidCommitHash(commit)) {
|
|
518
|
+
return 0;
|
|
519
|
+
}
|
|
520
|
+
try {
|
|
521
|
+
// Use execFileSync with arg array to prevent command injection
|
|
522
|
+
const out = execFileSync('git', ['rev-list', `${commit}..HEAD`, '--count'], {
|
|
523
|
+
cwd: basePath,
|
|
524
|
+
encoding: 'utf8',
|
|
525
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
526
|
+
}).trim();
|
|
527
|
+
return Number(out) || 0;
|
|
528
|
+
} catch {
|
|
529
|
+
return 0;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Normalize file path to forward slashes
|
|
535
|
+
* @param {string} filePath - Path to normalize
|
|
536
|
+
* @returns {string}
|
|
537
|
+
*/
|
|
538
|
+
function normalizePath(filePath) {
|
|
539
|
+
return filePath ? filePath.replace(/\\/g, '/') : filePath;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Recalculate map stats
|
|
544
|
+
* @param {Object} map - Repo map
|
|
545
|
+
*/
|
|
546
|
+
function recalculateStats(map) {
|
|
547
|
+
const files = Object.values(map.files || {});
|
|
548
|
+
map.stats.totalFiles = files.length;
|
|
549
|
+
map.stats.totalSymbols = files.reduce((sum, file) => {
|
|
550
|
+
return sum +
|
|
551
|
+
(file.symbols?.functions?.length || 0) +
|
|
552
|
+
(file.symbols?.classes?.length || 0) +
|
|
553
|
+
(file.symbols?.types?.length || 0) +
|
|
554
|
+
(file.symbols?.constants?.length || 0);
|
|
555
|
+
}, 0);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
module.exports = {
|
|
559
|
+
incrementalUpdate,
|
|
560
|
+
updateWithoutGit,
|
|
561
|
+
checkStaleness
|
|
562
|
+
};
|