agentsys 5.6.4 → 5.8.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 +30 -19
- 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 +10 -8
- package/CHANGELOG.md +37 -0
- package/README.md +152 -98
- 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 +7 -6
- package/package.json +3 -3
- package/scripts/fix-graduated-repos.js +2 -2
- package/scripts/generate-docs.js +22 -16
- package/scripts/graduate-plugin.js +1 -1
- package/scripts/plugins.txt +7 -1
- package/scripts/preflight.js +4 -4
- package/scripts/validate-cross-platform-docs.js +2 -2
- package/site/content.json +40 -23
- package/site/index.html +44 -12
- package/site/ux-spec.md +6 -6
- 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/updater.js
CHANGED
|
@@ -1,347 +1,23 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
* Repo map
|
|
4
|
+
* Repo map staleness checker.
|
|
5
|
+
*
|
|
6
|
+
* Determines whether a cached repo-map is stale relative to the current git HEAD.
|
|
7
|
+
* The incremental update path is handled by agent-analyzer (repo-intel update).
|
|
3
8
|
*
|
|
4
9
|
* @module lib/repo-map/updater
|
|
5
10
|
*/
|
|
6
11
|
|
|
7
|
-
'use strict';
|
|
8
|
-
|
|
9
|
-
const fsPromises = require('fs').promises;
|
|
10
|
-
const path = require('path');
|
|
11
12
|
const { execFileSync } = require('child_process');
|
|
12
13
|
|
|
13
|
-
const runner = require('./runner');
|
|
14
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
15
|
|
|
195
16
|
/**
|
|
196
|
-
*
|
|
197
|
-
* @param {string} basePath - Repository root
|
|
198
|
-
* @param {Object} map -
|
|
199
|
-
* @
|
|
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
|
|
17
|
+
* Check whether the cached map is stale
|
|
18
|
+
* @param {string} basePath - Repository root path
|
|
19
|
+
* @param {Object} map - Cached repo map
|
|
20
|
+
* @returns {{isStale: boolean, reason: string|null, commitsBehind: number, suggestFullRebuild: boolean}}
|
|
345
21
|
*/
|
|
346
22
|
function checkStaleness(basePath, map) {
|
|
347
23
|
const result = {
|
|
@@ -389,140 +65,35 @@ function checkStaleness(basePath, map) {
|
|
|
389
65
|
return result;
|
|
390
66
|
}
|
|
391
67
|
|
|
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
68
|
function isValidCommitHash(commit) {
|
|
441
|
-
// Git commit hashes are 4-40 hex characters (short to full SHA)
|
|
442
69
|
return typeof commit === 'string' && /^[0-9a-fA-F]{4,40}$/.test(commit);
|
|
443
70
|
}
|
|
444
71
|
|
|
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
72
|
function commitExists(basePath, commit) {
|
|
475
|
-
|
|
476
|
-
if (!isValidCommitHash(commit)) {
|
|
477
|
-
return false;
|
|
478
|
-
}
|
|
73
|
+
if (!isValidCommitHash(commit)) return false;
|
|
479
74
|
try {
|
|
480
|
-
|
|
481
|
-
execFileSync('git', ['cat-file', '-e', commit], {
|
|
482
|
-
cwd: basePath,
|
|
483
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
484
|
-
});
|
|
75
|
+
execFileSync('git', ['cat-file', '-e', commit], { cwd: basePath, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
485
76
|
return true;
|
|
486
77
|
} catch {
|
|
487
78
|
return false;
|
|
488
79
|
}
|
|
489
80
|
}
|
|
490
81
|
|
|
491
|
-
/**
|
|
492
|
-
* Get current branch name
|
|
493
|
-
* @param {string} basePath - Repository root
|
|
494
|
-
* @returns {string|null}
|
|
495
|
-
*/
|
|
496
82
|
function getCurrentBranch(basePath) {
|
|
497
83
|
try {
|
|
498
|
-
// Use execFileSync with arg array for consistency
|
|
499
84
|
return execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
500
|
-
cwd: basePath,
|
|
501
|
-
encoding: 'utf8',
|
|
502
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
85
|
+
cwd: basePath, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
|
|
503
86
|
}).trim();
|
|
504
87
|
} catch {
|
|
505
88
|
return null;
|
|
506
89
|
}
|
|
507
90
|
}
|
|
508
91
|
|
|
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
92
|
function getCommitsBehind(basePath, commit) {
|
|
516
|
-
|
|
517
|
-
if (!isValidCommitHash(commit)) {
|
|
518
|
-
return 0;
|
|
519
|
-
}
|
|
93
|
+
if (!isValidCommitHash(commit)) return 0;
|
|
520
94
|
try {
|
|
521
|
-
// Use execFileSync with arg array to prevent command injection
|
|
522
95
|
const out = execFileSync('git', ['rev-list', `${commit}..HEAD`, '--count'], {
|
|
523
|
-
cwd: basePath,
|
|
524
|
-
encoding: 'utf8',
|
|
525
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
96
|
+
cwd: basePath, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
|
|
526
97
|
}).trim();
|
|
527
98
|
return Number(out) || 0;
|
|
528
99
|
} catch {
|
|
@@ -530,33 +101,4 @@ function getCommitsBehind(basePath, commit) {
|
|
|
530
101
|
}
|
|
531
102
|
}
|
|
532
103
|
|
|
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
|
-
};
|
|
104
|
+
module.exports = { checkStaleness };
|
|
@@ -493,7 +493,7 @@ If pushing version tag (v*):
|
|
|
493
493
|
**installForClaude()** - Line 116
|
|
494
494
|
- Adds marketplace: `claude plugin marketplace add agent-sh/agentsys`
|
|
495
495
|
- Installs 9 plugins: `claude plugin install {plugin}@agentsys`
|
|
496
|
-
- Commands: /next-task, /ship, /deslop, /audit-project, /drift-detect, /enhance, /perf, /sync-docs, /repo-
|
|
496
|
+
- Commands: /next-task, /ship, /deslop, /audit-project, /drift-detect, /enhance, /perf, /sync-docs, /repo-intel
|
|
497
497
|
|
|
498
498
|
**installForOpenCode(installDir, options)** - Line 165
|
|
499
499
|
- Creates dirs: `~/.config/opencode/commands/`, `~/.config/opencode/plugins/agentsys.ts`
|
|
@@ -520,7 +520,7 @@ const commandMappings = [
|
|
|
520
520
|
['sync-docs.md', 'sync-docs', 'sync-docs.md'],
|
|
521
521
|
['audit-project.md', 'audit-project', 'audit-project.md'],
|
|
522
522
|
['drift-detect.md', 'drift-detect', 'drift-detect.md'],
|
|
523
|
-
['repo-
|
|
523
|
+
['repo-intel.md', 'repo-intel', 'repo-intel.md'],
|
|
524
524
|
['perf.md', 'perf', 'perf.md'],
|
|
525
525
|
['ship.md', 'ship', 'ship.md']
|
|
526
526
|
];
|
|
@@ -933,7 +933,7 @@ If you find any of these while working:
|
|
|
933
933
|
5. audit-project
|
|
934
934
|
6. deslop
|
|
935
935
|
7. drift-detect
|
|
936
|
-
8. repo-
|
|
936
|
+
8. repo-intel
|
|
937
937
|
9. sync-docs
|
|
938
938
|
|
|
939
939
|
**Agents:** 39 total = 29 file-based + 10 role-based
|
|
@@ -943,7 +943,7 @@ If you find any of these while working:
|
|
|
943
943
|
- enhance: 9
|
|
944
944
|
- perf: 6
|
|
945
945
|
- drift-detect: 1
|
|
946
|
-
- repo-
|
|
946
|
+
- repo-intel: 1
|
|
947
947
|
|
|
948
948
|
**Role-based (10):** Defined inline in audit-project command
|
|
949
949
|
- code-quality-reviewer, security-expert, performance-engineer, test-quality-guardian
|
|
@@ -951,11 +951,12 @@ If you find any of these while working:
|
|
|
951
951
|
- frontend-specialist, backend-specialist, devops-reviewer
|
|
952
952
|
|
|
953
953
|
**Skills:** 23 - Count SKILL.md in `plugins/*/skills/*/SKILL.md`
|
|
954
|
-
- next-task:
|
|
954
|
+
- next-task: 1 (discover-tasks)
|
|
955
|
+
- prepare-delivery: 4 (prepare-delivery, check-test-coverage, orchestrate-review, validate-delivery)
|
|
955
956
|
- enhance: 10 (orchestrator, reporter, agent-prompts, claude-memory, docs, plugins, prompts, hooks, skills)
|
|
956
957
|
- perf: 8 (analyzer, baseline, benchmark, code-paths, investigation-logger, profile, theory, theory-tester)
|
|
957
958
|
- drift-detect: 1 (drift-analysis)
|
|
958
|
-
- repo-
|
|
959
|
+
- repo-intel: 1 (repo-intel)
|
|
959
960
|
|
|
960
961
|
**Version Fields:** 11 files
|
|
961
962
|
- 1x package.json
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentsys",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.0",
|
|
4
4
|
"description": "A modular runtime and orchestration system for AI agents - works with Claude Code, OpenCode, and Codex CLI",
|
|
5
5
|
"main": "lib/platform/detect-platform.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"cli",
|
|
65
65
|
"pr-automation",
|
|
66
66
|
"productivity",
|
|
67
|
-
"repo-
|
|
68
|
-
"
|
|
67
|
+
"repo-intel",
|
|
68
|
+
"static-analysis",
|
|
69
69
|
"drift-detect"
|
|
70
70
|
],
|
|
71
71
|
"author": {
|
|
@@ -26,14 +26,14 @@ const WORK_DIR = '/tmp/fix-repos';
|
|
|
26
26
|
// 12 graduated plugins (agnix excluded — it stays in monorepo)
|
|
27
27
|
const PLUGINS = [
|
|
28
28
|
'next-task', 'ship', 'enhance', 'deslop', 'learn', 'consult',
|
|
29
|
-
'debate', 'drift-detect', 'repo-
|
|
29
|
+
'debate', 'drift-detect', 'repo-intel', 'sync-docs', 'audit-project',
|
|
30
30
|
'perf'
|
|
31
31
|
];
|
|
32
32
|
|
|
33
33
|
// All 13 plugin dir names that may exist in the repos
|
|
34
34
|
const ALL_PLUGIN_DIRS = [
|
|
35
35
|
'next-task', 'ship', 'enhance', 'deslop', 'learn', 'consult',
|
|
36
|
-
'debate', 'drift-detect', 'repo-
|
|
36
|
+
'debate', 'drift-detect', 'repo-intel', 'sync-docs', 'audit-project',
|
|
37
37
|
'perf', 'agnix'
|
|
38
38
|
];
|
|
39
39
|
|