agentsys 5.5.0 → 5.7.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 +6 -17
- package/.claude-plugin/plugin.json +1 -1
- package/.kiro/agents/exploration-agent.json +1 -1
- package/.kiro/agents/implementation-agent.json +1 -1
- package/.kiro/agents/map-validator.json +2 -2
- package/.kiro/agents/perf-orchestrator.json +1 -1
- package/.kiro/agents/planning-agent.json +1 -1
- package/.kiro/skills/perf-code-paths/SKILL.md +1 -1
- package/.kiro/skills/perf-theory-gatherer/SKILL.md +1 -1
- package/.kiro/skills/repo-intel/SKILL.md +63 -0
- package/AGENTS.md +3 -3
- package/CHANGELOG.md +31 -0
- package/README.md +95 -68
- package/lib/binary/version.js +1 -1
- package/lib/repo-map/converter.js +130 -0
- package/lib/repo-map/index.js +117 -74
- package/lib/repo-map/installer.js +38 -172
- package/lib/repo-map/updater.js +16 -474
- package/meta/skills/maintain-cross-platform/SKILL.md +5 -5
- package/package.json +3 -3
- package/scripts/fix-graduated-repos.js +2 -2
- package/scripts/generate-docs.js +39 -20
- package/scripts/graduate-plugin.js +1 -1
- package/scripts/preflight.js +4 -4
- package/scripts/validate-counts.js +1 -1
- package/scripts/validate-cross-platform-docs.js +2 -2
- package/site/content.json +76 -58
- package/site/index.html +44 -12
- package/site/ux-spec.md +1 -1
- package/.kiro/skills/repo-mapping/SKILL.md +0 -83
- package/lib/repo-map/concurrency.js +0 -29
- package/lib/repo-map/queries/go.js +0 -27
- package/lib/repo-map/queries/index.js +0 -100
- package/lib/repo-map/queries/java.js +0 -38
- package/lib/repo-map/queries/javascript.js +0 -55
- package/lib/repo-map/queries/python.js +0 -24
- package/lib/repo-map/queries/rust.js +0 -73
- package/lib/repo-map/queries/typescript.js +0 -38
- package/lib/repo-map/runner.js +0 -1364
- package/lib/repo-map/usage-analyzer.js +0 -407
package/lib/repo-map/index.js
CHANGED
|
@@ -1,74 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
* Repo Map -
|
|
4
|
+
* Repo Map - repository symbol mapping via agent-analyzer
|
|
3
5
|
*
|
|
4
|
-
* Uses
|
|
5
|
-
*
|
|
6
|
+
* Uses agent-analyzer for symbol extraction. The binary is auto-downloaded
|
|
7
|
+
* on first use - no external tool installation required.
|
|
6
8
|
*
|
|
7
9
|
* @module lib/repo-map
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const { execFileSync } = require('child_process');
|
|
11
15
|
|
|
12
16
|
const installer = require('./installer');
|
|
13
|
-
const runner = require('./runner');
|
|
14
17
|
const cache = require('./cache');
|
|
15
18
|
const updater = require('./updater');
|
|
16
|
-
const
|
|
19
|
+
const converter = require('./converter');
|
|
20
|
+
const binary = require('../binary');
|
|
21
|
+
const { getStateDirPath } = require('../platform/state-dir');
|
|
22
|
+
const { writeJsonAtomic } = require('../utils/atomic-write');
|
|
23
|
+
|
|
24
|
+
const REPO_INTEL_FILENAME = 'repo-intel.json';
|
|
25
|
+
|
|
26
|
+
function getIntelMapPath(basePath) {
|
|
27
|
+
return path.join(getStateDirPath(basePath), REPO_INTEL_FILENAME);
|
|
28
|
+
}
|
|
17
29
|
|
|
18
30
|
/**
|
|
19
31
|
* Initialize a new repo map (full scan)
|
|
20
32
|
* @param {string} basePath - Repository root path
|
|
21
33
|
* @param {Object} options - Options
|
|
22
34
|
* @param {boolean} options.force - Force rebuild even if map exists
|
|
23
|
-
* @param {string[]} options.languages - Languages to scan (auto-detect if not specified)
|
|
24
35
|
* @returns {Promise<{success: boolean, map?: Object, error?: string}>}
|
|
25
36
|
*/
|
|
26
37
|
async function init(basePath, options = {}) {
|
|
27
|
-
// Check if ast-grep is installed
|
|
28
38
|
const installed = await installer.checkInstalled();
|
|
29
39
|
if (!installed.found) {
|
|
30
40
|
return {
|
|
31
41
|
success: false,
|
|
32
|
-
error: '
|
|
42
|
+
error: 'agent-analyzer binary unavailable: ' + (installed.error || 'unknown error'),
|
|
33
43
|
installSuggestion: installer.getInstallInstructions()
|
|
34
44
|
};
|
|
35
45
|
}
|
|
36
46
|
|
|
37
|
-
|
|
47
|
+
const existing = cache.load(basePath);
|
|
48
|
+
if (existing && !options.force) {
|
|
38
49
|
return {
|
|
39
50
|
success: false,
|
|
40
|
-
error:
|
|
41
|
-
|
|
51
|
+
error: 'Repo map already exists. Use --force to rebuild or /repo-map update to refresh.',
|
|
52
|
+
existing: cache.getStatus(basePath)
|
|
42
53
|
};
|
|
43
54
|
}
|
|
44
55
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
const startTime = Date.now();
|
|
57
|
+
|
|
58
|
+
let intelJson;
|
|
59
|
+
try {
|
|
60
|
+
intelJson = await binary.runAnalyzerAsync(['repo-intel', 'init', basePath]);
|
|
61
|
+
} catch (e) {
|
|
48
62
|
return {
|
|
49
63
|
success: false,
|
|
50
|
-
error: '
|
|
51
|
-
existing: cache.getStatus(basePath)
|
|
64
|
+
error: 'agent-analyzer repo-intel init failed: ' + e.message
|
|
52
65
|
};
|
|
53
66
|
}
|
|
54
67
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
let intel;
|
|
69
|
+
try {
|
|
70
|
+
intel = JSON.parse(intelJson);
|
|
71
|
+
} catch (e) {
|
|
58
72
|
return {
|
|
59
73
|
success: false,
|
|
60
|
-
error: '
|
|
74
|
+
error: 'Failed to parse repo-intel output: ' + e.message
|
|
61
75
|
};
|
|
62
76
|
}
|
|
63
77
|
|
|
64
|
-
//
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
78
|
+
// Persist repo-intel.json for future incremental updates
|
|
79
|
+
const intelPath = getIntelMapPath(basePath);
|
|
80
|
+
try {
|
|
81
|
+
writeJsonAtomic(intelPath, intel);
|
|
82
|
+
} catch {
|
|
83
|
+
// Non-fatal: update() will fall back to full init
|
|
84
|
+
}
|
|
70
85
|
|
|
71
|
-
|
|
86
|
+
const map = converter.convertIntelToRepoMap(intel);
|
|
87
|
+
map.stats.scanDurationMs = Date.now() - startTime;
|
|
72
88
|
cache.save(basePath, map);
|
|
73
89
|
|
|
74
90
|
return {
|
|
@@ -84,53 +100,83 @@ async function init(basePath, options = {}) {
|
|
|
84
100
|
}
|
|
85
101
|
|
|
86
102
|
/**
|
|
87
|
-
* Update an existing repo map (incremental)
|
|
103
|
+
* Update an existing repo map (incremental via agent-analyzer)
|
|
88
104
|
* @param {string} basePath - Repository root path
|
|
89
105
|
* @param {Object} options - Options
|
|
90
106
|
* @param {boolean} options.full - Force full rebuild instead of incremental
|
|
91
|
-
* @returns {Promise<{success: boolean,
|
|
107
|
+
* @returns {Promise<{success: boolean, summary?: Object, error?: string}>}
|
|
92
108
|
*/
|
|
93
109
|
async function update(basePath, options = {}) {
|
|
94
|
-
// Check if ast-grep is installed
|
|
95
110
|
const installed = await installer.checkInstalled();
|
|
96
111
|
if (!installed.found) {
|
|
97
112
|
return {
|
|
98
113
|
success: false,
|
|
99
|
-
error: '
|
|
114
|
+
error: 'agent-analyzer binary unavailable: ' + (installed.error || 'unknown error'),
|
|
100
115
|
installSuggestion: installer.getInstallInstructions()
|
|
101
116
|
};
|
|
102
117
|
}
|
|
103
118
|
|
|
104
|
-
if (!
|
|
119
|
+
if (!cache.exists(basePath)) {
|
|
105
120
|
return {
|
|
106
121
|
success: false,
|
|
107
|
-
error:
|
|
108
|
-
installSuggestion: installer.getInstallInstructions()
|
|
122
|
+
error: 'No repo map found. Run /repo-map init first.'
|
|
109
123
|
};
|
|
110
124
|
}
|
|
111
125
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
126
|
+
if (options.full) {
|
|
127
|
+
return init(basePath, { force: true });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const intelPath = getIntelMapPath(basePath);
|
|
131
|
+
if (!fs.existsSync(intelPath)) {
|
|
132
|
+
return init(basePath, { force: true });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const startTime = Date.now();
|
|
136
|
+
|
|
137
|
+
let intelJson;
|
|
138
|
+
try {
|
|
139
|
+
intelJson = await binary.runAnalyzerAsync([
|
|
140
|
+
'repo-intel', 'update',
|
|
141
|
+
'--map-file', intelPath,
|
|
142
|
+
basePath
|
|
143
|
+
]);
|
|
144
|
+
} catch (e) {
|
|
115
145
|
return {
|
|
116
146
|
success: false,
|
|
117
|
-
error: '
|
|
147
|
+
error: 'agent-analyzer repo-intel update failed: ' + e.message
|
|
118
148
|
};
|
|
119
149
|
}
|
|
120
150
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
151
|
+
let intel;
|
|
152
|
+
try {
|
|
153
|
+
intel = JSON.parse(intelJson);
|
|
154
|
+
} catch (e) {
|
|
155
|
+
return {
|
|
156
|
+
success: false,
|
|
157
|
+
error: 'Failed to parse repo-intel update output: ' + e.message
|
|
158
|
+
};
|
|
124
159
|
}
|
|
125
160
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
cache.save(basePath, result.map);
|
|
161
|
+
try {
|
|
162
|
+
writeJsonAtomic(intelPath, intel);
|
|
163
|
+
} catch {
|
|
164
|
+
// Non-fatal
|
|
131
165
|
}
|
|
132
166
|
|
|
133
|
-
|
|
167
|
+
const map = converter.convertIntelToRepoMap(intel);
|
|
168
|
+
map.stats.scanDurationMs = Date.now() - startTime;
|
|
169
|
+
cache.save(basePath, map);
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
success: true,
|
|
173
|
+
map,
|
|
174
|
+
summary: {
|
|
175
|
+
files: Object.keys(map.files).length,
|
|
176
|
+
symbols: map.stats.totalSymbols,
|
|
177
|
+
duration: map.stats.scanDurationMs
|
|
178
|
+
}
|
|
179
|
+
};
|
|
134
180
|
}
|
|
135
181
|
|
|
136
182
|
/**
|
|
@@ -146,13 +192,20 @@ function status(basePath) {
|
|
|
146
192
|
|
|
147
193
|
const staleness = updater.checkStaleness(basePath, map);
|
|
148
194
|
|
|
195
|
+
let branch;
|
|
196
|
+
try {
|
|
197
|
+
branch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: basePath, encoding: 'utf8' }).trim();
|
|
198
|
+
} catch {
|
|
199
|
+
// Non-fatal
|
|
200
|
+
}
|
|
201
|
+
|
|
149
202
|
return {
|
|
150
203
|
exists: true,
|
|
151
204
|
status: {
|
|
152
205
|
generated: map.generated,
|
|
153
206
|
updated: map.updated,
|
|
154
207
|
commit: map.git?.commit,
|
|
155
|
-
branch
|
|
208
|
+
branch,
|
|
156
209
|
files: Object.keys(map.files).length,
|
|
157
210
|
symbols: map.stats?.totalSymbols || 0,
|
|
158
211
|
languages: map.project?.languages || [],
|
|
@@ -164,37 +217,38 @@ function status(basePath) {
|
|
|
164
217
|
/**
|
|
165
218
|
* Load repo map (if exists)
|
|
166
219
|
* @param {string} basePath - Repository root path
|
|
167
|
-
* @returns {Object|null}
|
|
220
|
+
* @returns {Object|null}
|
|
168
221
|
*/
|
|
169
222
|
function load(basePath) {
|
|
170
223
|
return cache.load(basePath);
|
|
171
224
|
}
|
|
172
225
|
|
|
173
226
|
/**
|
|
174
|
-
* Check if
|
|
175
|
-
* @
|
|
227
|
+
* Check if repo map exists
|
|
228
|
+
* @param {string} basePath - Repository root path
|
|
229
|
+
* @returns {boolean}
|
|
230
|
+
*/
|
|
231
|
+
function exists(basePath) {
|
|
232
|
+
return cache.exists(basePath);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Check if agent-analyzer is available (compat alias for checkInstalled).
|
|
237
|
+
* Previously checked ast-grep; now checks agent-analyzer.
|
|
238
|
+
* @returns {Promise<{found: boolean, version?: string, tool: string}>}
|
|
176
239
|
*/
|
|
177
240
|
async function checkAstGrepInstalled() {
|
|
178
241
|
return installer.checkInstalled();
|
|
179
242
|
}
|
|
180
243
|
|
|
181
244
|
/**
|
|
182
|
-
* Get install instructions
|
|
245
|
+
* Get install instructions (compat alias).
|
|
183
246
|
* @returns {string}
|
|
184
247
|
*/
|
|
185
248
|
function getInstallInstructions() {
|
|
186
249
|
return installer.getInstallInstructions();
|
|
187
250
|
}
|
|
188
251
|
|
|
189
|
-
/**
|
|
190
|
-
* Check if repo map exists
|
|
191
|
-
* @param {string} basePath - Repository root path
|
|
192
|
-
* @returns {boolean}
|
|
193
|
-
*/
|
|
194
|
-
function exists(basePath) {
|
|
195
|
-
return cache.exists(basePath);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
252
|
module.exports = {
|
|
199
253
|
init,
|
|
200
254
|
update,
|
|
@@ -204,19 +258,8 @@ module.exports = {
|
|
|
204
258
|
checkAstGrepInstalled,
|
|
205
259
|
getInstallInstructions,
|
|
206
260
|
|
|
207
|
-
// Usage analysis functions
|
|
208
|
-
buildUsageIndex: usageAnalyzer.buildUsageIndex,
|
|
209
|
-
findUsages: usageAnalyzer.findUsages,
|
|
210
|
-
findDependents: usageAnalyzer.findDependents,
|
|
211
|
-
findUnusedExports: usageAnalyzer.findUnusedExports,
|
|
212
|
-
findOrphanedInfrastructure: usageAnalyzer.findOrphanedInfrastructure,
|
|
213
|
-
getDependencyGraph: usageAnalyzer.getDependencyGraph,
|
|
214
|
-
findCircularDependencies: usageAnalyzer.findCircularDependencies,
|
|
215
|
-
|
|
216
261
|
// Re-export submodules for advanced usage
|
|
217
262
|
installer,
|
|
218
|
-
runner,
|
|
219
263
|
cache,
|
|
220
|
-
updater
|
|
221
|
-
usageAnalyzer
|
|
264
|
+
updater
|
|
222
265
|
};
|
|
@@ -1,212 +1,78 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
4
|
+
* agent-analyzer binary availability check.
|
|
5
|
+
*
|
|
6
|
+
* Replaces the former ast-grep installer. The agent-analyzer binary is
|
|
7
|
+
* auto-downloaded by agent-core on first use - no manual install required.
|
|
3
8
|
*
|
|
4
9
|
* @module lib/repo-map/installer
|
|
5
10
|
*/
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const { execSync, execFileSync, execFile } = require('child_process');
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
const { promisify } = require('util');
|
|
13
|
-
|
|
14
|
-
const execFileAsync = promisify(execFile);
|
|
15
|
-
|
|
16
|
-
// Commands to try (sg is the common alias)
|
|
17
|
-
const AST_GREP_COMMANDS = ['sg', 'ast-grep'];
|
|
18
|
-
|
|
19
|
-
function pickCommandPath(pathOutput) {
|
|
20
|
-
if (!pathOutput) return null;
|
|
21
|
-
const candidates = pathOutput.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
|
22
|
-
if (candidates.length === 0) return null;
|
|
23
|
-
|
|
24
|
-
if (process.platform !== 'win32') {
|
|
25
|
-
return candidates[0];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const exe = candidates.find(candidate => candidate.toLowerCase().endsWith('.exe'));
|
|
29
|
-
if (exe) return exe;
|
|
30
|
-
|
|
31
|
-
const primary = candidates[0];
|
|
32
|
-
if (!primary) return null;
|
|
33
|
-
|
|
34
|
-
const baseDir = path.dirname(primary);
|
|
35
|
-
const npmExe = path.join(baseDir, 'node_modules', '@ast-grep', 'cli', 'sg.exe');
|
|
36
|
-
if (fs.existsSync(npmExe)) return npmExe;
|
|
37
|
-
|
|
38
|
-
return primary;
|
|
39
|
-
}
|
|
12
|
+
const binary = require('../binary');
|
|
40
13
|
|
|
41
14
|
/**
|
|
42
|
-
* Check if
|
|
43
|
-
* @returns {Promise<{found: boolean, version?: string,
|
|
15
|
+
* Check if agent-analyzer is available (async). Downloads if missing.
|
|
16
|
+
* @returns {Promise<{found: boolean, version?: string, tool: string}>}
|
|
44
17
|
*/
|
|
45
18
|
async function checkInstalled() {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const version = stdout.trim().replace(/^ast-grep\s*/i, '');
|
|
56
|
-
|
|
57
|
-
// Try to get path
|
|
58
|
-
let cmdPath = null;
|
|
59
|
-
try {
|
|
60
|
-
const whereCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
61
|
-
// Use execFileAsync with args array (no shell interpolation)
|
|
62
|
-
const { stdout: pathOut } = await execFileAsync(whereCmd, [cmd], {
|
|
63
|
-
timeout: 5000,
|
|
64
|
-
windowsHide: true,
|
|
65
|
-
shell: process.platform === 'win32'
|
|
66
|
-
});
|
|
67
|
-
cmdPath = pickCommandPath(pathOut);
|
|
68
|
-
} catch {
|
|
69
|
-
// Path lookup failed, but command works
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
found: true,
|
|
74
|
-
version,
|
|
75
|
-
path: cmdPath,
|
|
76
|
-
command: cmdPath || cmd
|
|
77
|
-
};
|
|
78
|
-
} catch {
|
|
79
|
-
// This command not found, try next
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
19
|
+
if (binary.isAvailable()) {
|
|
20
|
+
return { found: true, version: binary.getVersion(), tool: 'agent-analyzer' };
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
await binary.ensureBinary();
|
|
24
|
+
return { found: true, version: binary.getVersion(), tool: 'agent-analyzer' };
|
|
25
|
+
} catch (e) {
|
|
26
|
+
return { found: false, error: e.message, tool: 'agent-analyzer' };
|
|
82
27
|
}
|
|
83
|
-
|
|
84
|
-
return { found: false };
|
|
85
28
|
}
|
|
86
29
|
|
|
87
30
|
/**
|
|
88
|
-
* Check if
|
|
89
|
-
* @returns {{found: boolean, version?: string,
|
|
31
|
+
* Check if agent-analyzer is available (sync). Downloads if missing.
|
|
32
|
+
* @returns {{found: boolean, version?: string, tool: string}}
|
|
90
33
|
*/
|
|
91
34
|
function checkInstalledSync() {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
shell: process.platform === 'win32' // Windows needs shell for PATH lookup
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
const version = stdout.trim().replace(/^ast-grep\s*/i, '');
|
|
104
|
-
let cmdPath = null;
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
const whereCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
108
|
-
// Use execFileSync with args array (no shell interpolation)
|
|
109
|
-
const pathOut = execFileSync(whereCmd, [cmd], {
|
|
110
|
-
timeout: 5000,
|
|
111
|
-
windowsHide: true,
|
|
112
|
-
encoding: 'utf8',
|
|
113
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
114
|
-
shell: process.platform === 'win32'
|
|
115
|
-
});
|
|
116
|
-
cmdPath = pickCommandPath(pathOut);
|
|
117
|
-
} catch {
|
|
118
|
-
// Path lookup failed, but command works
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return { found: true, version, command: cmdPath || cmd, path: cmdPath };
|
|
122
|
-
} catch {
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
35
|
+
if (binary.isAvailable()) {
|
|
36
|
+
return { found: true, version: binary.getVersion(), tool: 'agent-analyzer' };
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
binary.ensureBinarySync();
|
|
40
|
+
return { found: true, version: binary.getVersion(), tool: 'agent-analyzer' };
|
|
41
|
+
} catch (e) {
|
|
42
|
+
return { found: false, error: e.message, tool: 'agent-analyzer' };
|
|
125
43
|
}
|
|
126
|
-
|
|
127
|
-
return { found: false };
|
|
128
44
|
}
|
|
129
45
|
|
|
130
46
|
/**
|
|
131
|
-
*
|
|
132
|
-
* @returns {
|
|
47
|
+
* Version check is handled by the binary module - always true when found.
|
|
48
|
+
* @returns {boolean}
|
|
133
49
|
*/
|
|
134
|
-
function
|
|
135
|
-
|
|
136
|
-
return result.found ? result.command : null;
|
|
50
|
+
function meetsMinimumVersion() {
|
|
51
|
+
return true;
|
|
137
52
|
}
|
|
138
53
|
|
|
139
54
|
/**
|
|
140
|
-
* Get
|
|
55
|
+
* Get install instructions (binary is auto-downloaded, but here for compat).
|
|
141
56
|
* @returns {string}
|
|
142
57
|
*/
|
|
143
58
|
function getInstallInstructions() {
|
|
144
|
-
return
|
|
145
|
-
|
|
146
|
-
Install using one of these methods:
|
|
147
|
-
|
|
148
|
-
npm: npm install -g @ast-grep/cli
|
|
149
|
-
pip: pip install ast-grep-cli
|
|
150
|
-
brew: brew install ast-grep
|
|
151
|
-
cargo: cargo install ast-grep --locked
|
|
152
|
-
scoop: scoop install main/ast-grep
|
|
153
|
-
|
|
154
|
-
After installation, verify with: sg --version
|
|
155
|
-
|
|
156
|
-
Documentation: https://ast-grep.github.io/`;
|
|
59
|
+
return 'agent-analyzer is downloaded automatically on first use from https://github.com/agent-sh/agent-analyzer/releases';
|
|
157
60
|
}
|
|
158
61
|
|
|
159
62
|
/**
|
|
160
|
-
* Get
|
|
161
|
-
* @returns {string}
|
|
162
|
-
*/
|
|
163
|
-
function getShortInstallSuggestion() {
|
|
164
|
-
if (process.platform === 'win32') {
|
|
165
|
-
return 'Install ast-grep: npm i -g @ast-grep/cli (or scoop install ast-grep)';
|
|
166
|
-
} else if (process.platform === 'darwin') {
|
|
167
|
-
return 'Install ast-grep: brew install ast-grep (or npm i -g @ast-grep/cli)';
|
|
168
|
-
} else {
|
|
169
|
-
return 'Install ast-grep: npm i -g @ast-grep/cli (or pip install ast-grep-cli)';
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Get minimum required version
|
|
63
|
+
* Get minimum version string.
|
|
175
64
|
* @returns {string}
|
|
176
65
|
*/
|
|
177
66
|
function getMinimumVersion() {
|
|
178
|
-
return '0.
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Check if installed version meets minimum requirements
|
|
183
|
-
* @param {string} version - Installed version
|
|
184
|
-
* @returns {boolean}
|
|
185
|
-
*/
|
|
186
|
-
function meetsMinimumVersion(version) {
|
|
187
|
-
if (!version) return false;
|
|
188
|
-
|
|
189
|
-
// Parse version (e.g., "0.25.0" or "0.25.0-beta.1")
|
|
190
|
-
const match = version.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
191
|
-
if (!match) return false;
|
|
192
|
-
|
|
193
|
-
const [, major, minor, patch] = match.map(Number);
|
|
194
|
-
const [reqMajor, reqMinor, reqPatch] = getMinimumVersion().split('.').map(Number);
|
|
195
|
-
|
|
196
|
-
if (major > reqMajor) return true;
|
|
197
|
-
if (major < reqMajor) return false;
|
|
198
|
-
if (minor > reqMinor) return true;
|
|
199
|
-
if (minor < reqMinor) return false;
|
|
200
|
-
return patch >= reqPatch;
|
|
67
|
+
return '0.3.0';
|
|
201
68
|
}
|
|
202
69
|
|
|
203
70
|
module.exports = {
|
|
204
71
|
checkInstalled,
|
|
205
72
|
checkInstalledSync,
|
|
206
|
-
|
|
73
|
+
meetsMinimumVersion,
|
|
207
74
|
getInstallInstructions,
|
|
208
|
-
getShortInstallSuggestion,
|
|
209
75
|
getMinimumVersion,
|
|
210
|
-
|
|
211
|
-
|
|
76
|
+
// Stub: runner.js references this but is no longer the scan path
|
|
77
|
+
getCommand: () => null
|
|
212
78
|
};
|