agentsys 5.0.1 → 5.0.3
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 +10 -0
- package/README.md +1 -0
- package/adapters/codex/skills/consult/SKILL.md +2 -2
- package/adapters/opencode/agents/consult-agent.md +1 -1
- package/adapters/opencode/commands/consult.md +2 -2
- package/adapters/opencode/skills/agnix/SKILL.md +1 -1
- package/adapters/opencode/skills/consult/SKILL.md +22 -6
- package/adapters/opencode/skills/deslop/SKILL.md +1 -1
- package/adapters/opencode/skills/discover-tasks/SKILL.md +1 -1
- 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/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 +1 -1
- package/bin/cli.js +25 -6
- 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 +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/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 +2 -2
- package/plugins/consult/skills/consult/SKILL.md +22 -6
- 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/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 +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/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/site/content.json +1 -1
package/lib/collectors/github.js
CHANGED
|
@@ -14,6 +14,7 @@ const { execFileSync } = require('child_process');
|
|
|
14
14
|
const DEFAULT_OPTIONS = {
|
|
15
15
|
issueLimit: 100,
|
|
16
16
|
prLimit: 50,
|
|
17
|
+
milestoneLimit: 100,
|
|
17
18
|
timeout: 10000,
|
|
18
19
|
cwd: process.cwd()
|
|
19
20
|
};
|
|
@@ -25,16 +26,41 @@ const DEFAULT_OPTIONS = {
|
|
|
25
26
|
* @returns {Object|null} Parsed JSON result or null
|
|
26
27
|
*/
|
|
27
28
|
function execGh(args, options = {}) {
|
|
29
|
+
const result = execGhWithResult(args, options);
|
|
30
|
+
return result.ok ? result.data : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function execGhWithResult(args, options = {}) {
|
|
28
34
|
try {
|
|
29
|
-
const
|
|
35
|
+
const output = execFileSync('gh', args, {
|
|
30
36
|
encoding: 'utf8',
|
|
31
37
|
stdio: 'pipe',
|
|
32
38
|
timeout: options.timeout || DEFAULT_OPTIONS.timeout,
|
|
33
39
|
cwd: options.cwd || DEFAULT_OPTIONS.cwd
|
|
34
40
|
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
return { ok: true, data: JSON.parse(output) };
|
|
44
|
+
} catch (error) {
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
error: {
|
|
48
|
+
type: 'parse',
|
|
49
|
+
message: `Failed to parse gh output as JSON: ${error.message}`,
|
|
50
|
+
raw: output.slice(0, 500)
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return {
|
|
56
|
+
ok: false,
|
|
57
|
+
error: {
|
|
58
|
+
type: error.killed ? 'timeout' : 'process',
|
|
59
|
+
message: error.message,
|
|
60
|
+
exitCode: error.status ?? null,
|
|
61
|
+
stderr: error.stderr ? String(error.stderr).trim() : ''
|
|
62
|
+
}
|
|
63
|
+
};
|
|
38
64
|
}
|
|
39
65
|
}
|
|
40
66
|
|
|
@@ -192,10 +218,18 @@ function scanGitHubState(options = {}) {
|
|
|
192
218
|
|
|
193
219
|
const result = {
|
|
194
220
|
available: false,
|
|
221
|
+
partial: false,
|
|
222
|
+
errors: [],
|
|
195
223
|
summary: { issueCount: 0, prCount: 0, milestoneCount: 0 },
|
|
196
224
|
issues: [],
|
|
197
225
|
prs: [],
|
|
198
226
|
milestones: [],
|
|
227
|
+
overdueMilestones: [],
|
|
228
|
+
pagination: {
|
|
229
|
+
issues: { requestedLimit: opts.issueLimit, fetchedCount: 0, hasMore: false },
|
|
230
|
+
prs: { requestedLimit: opts.prLimit, fetchedCount: 0, hasMore: false },
|
|
231
|
+
milestones: { requestedLimit: opts.milestoneLimit, fetchedCount: 0, hasMore: false }
|
|
232
|
+
},
|
|
199
233
|
categorized: { bugs: [], features: [], security: [], enhancements: [], other: [] },
|
|
200
234
|
stale: [],
|
|
201
235
|
themes: []
|
|
@@ -209,44 +243,74 @@ function scanGitHubState(options = {}) {
|
|
|
209
243
|
result.available = true;
|
|
210
244
|
|
|
211
245
|
// Fetch open issues
|
|
212
|
-
const
|
|
246
|
+
const issuesResult = execGhWithResult([
|
|
213
247
|
'issue', 'list',
|
|
214
248
|
'--state', 'open',
|
|
215
249
|
'--json', 'number,title,labels,milestone,createdAt,updatedAt,body',
|
|
216
250
|
'--limit', String(opts.issueLimit)
|
|
217
251
|
], opts);
|
|
218
252
|
|
|
219
|
-
if (
|
|
253
|
+
if (issuesResult.ok && Array.isArray(issuesResult.data)) {
|
|
254
|
+
const issues = issuesResult.data;
|
|
220
255
|
result.issues = issues.map(summarizeIssue);
|
|
221
256
|
result.summary.issueCount = issues.length;
|
|
257
|
+
result.pagination.issues.fetchedCount = issues.length;
|
|
258
|
+
result.pagination.issues.hasMore = opts.issueLimit > 0 && issues.length >= opts.issueLimit;
|
|
222
259
|
categorizeIssues(result, issues);
|
|
223
260
|
findStaleItems(result, issues, 90);
|
|
224
261
|
extractThemes(result, issues);
|
|
262
|
+
} else if (!issuesResult.ok) {
|
|
263
|
+
result.errors.push({ source: 'issues', ...issuesResult.error });
|
|
225
264
|
}
|
|
226
265
|
|
|
227
266
|
// Fetch open PRs with files changed
|
|
228
|
-
const
|
|
267
|
+
const prsResult = execGhWithResult([
|
|
229
268
|
'pr', 'list',
|
|
230
269
|
'--state', 'open',
|
|
231
270
|
'--json', 'number,title,labels,isDraft,createdAt,updatedAt,body,files',
|
|
232
271
|
'--limit', String(opts.prLimit)
|
|
233
272
|
], opts);
|
|
234
273
|
|
|
235
|
-
if (
|
|
274
|
+
if (prsResult.ok && Array.isArray(prsResult.data)) {
|
|
275
|
+
const prs = prsResult.data;
|
|
236
276
|
result.prs = prs.map(summarizePR);
|
|
237
277
|
result.summary.prCount = prs.length;
|
|
278
|
+
result.pagination.prs.fetchedCount = prs.length;
|
|
279
|
+
result.pagination.prs.hasMore = opts.prLimit > 0 && prs.length >= opts.prLimit;
|
|
280
|
+
} else if (!prsResult.ok) {
|
|
281
|
+
result.errors.push({ source: 'prs', ...prsResult.error });
|
|
238
282
|
}
|
|
239
283
|
|
|
240
284
|
// Fetch milestones
|
|
241
|
-
const
|
|
285
|
+
const milestonesResult = execGhWithResult([
|
|
242
286
|
'api', 'repos/{owner}/{repo}/milestones',
|
|
243
|
-
'--
|
|
287
|
+
'--paginate',
|
|
288
|
+
'--slurp'
|
|
244
289
|
], opts);
|
|
245
290
|
|
|
246
|
-
if (
|
|
247
|
-
|
|
291
|
+
if (milestonesResult.ok && Array.isArray(milestonesResult.data)) {
|
|
292
|
+
const pages = milestonesResult.data;
|
|
293
|
+
const allMilestones = pages.flatMap(page => Array.isArray(page) ? page : []);
|
|
294
|
+
const mappedMilestones = allMilestones.map((milestone) => ({
|
|
295
|
+
title: milestone.title,
|
|
296
|
+
state: milestone.state,
|
|
297
|
+
due_on: milestone.due_on,
|
|
298
|
+
open_issues: milestone.open_issues,
|
|
299
|
+
closed_issues: milestone.closed_issues
|
|
300
|
+
}));
|
|
301
|
+
|
|
302
|
+
result.pagination.milestones.fetchedCount = mappedMilestones.length;
|
|
303
|
+
result.pagination.milestones.hasMore = opts.milestoneLimit > 0 && mappedMilestones.length > opts.milestoneLimit;
|
|
304
|
+
result.milestones = mappedMilestones.slice(0, opts.milestoneLimit);
|
|
248
305
|
result.summary.milestoneCount = result.milestones.length;
|
|
249
306
|
findOverdueMilestones(result);
|
|
307
|
+
} else if (!milestonesResult.ok) {
|
|
308
|
+
result.errors.push({ source: 'milestones', ...milestonesResult.error });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
result.partial = result.errors.length > 0;
|
|
312
|
+
if (result.partial && !result.error) {
|
|
313
|
+
result.error = 'Partial GitHub data collected';
|
|
250
314
|
}
|
|
251
315
|
|
|
252
316
|
return result;
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* @module lib/perf/benchmark-runner
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const {
|
|
7
|
+
const { execFileSync } = require('child_process');
|
|
8
8
|
const { validateBaseline } = require('./schemas');
|
|
9
|
+
const { parseCommand, resolveExecutableForPlatform } = require('../utils/command-parser');
|
|
9
10
|
|
|
10
11
|
const DEFAULT_MIN_DURATION = 60;
|
|
11
12
|
const BINARY_SEARCH_MIN_DURATION = 30;
|
|
@@ -55,6 +56,8 @@ function runBenchmark(command, options = {}) {
|
|
|
55
56
|
throw new Error('Benchmark command must be a non-empty string');
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
const parsedCommand = parseCommand(command, 'Benchmark command');
|
|
60
|
+
const executable = resolveExecutableForPlatform(parsedCommand.executable);
|
|
58
61
|
const normalized = normalizeBenchmarkOptions(options);
|
|
59
62
|
const setDurationEnv = options.setDurationEnv !== false;
|
|
60
63
|
const env = {
|
|
@@ -71,18 +74,20 @@ function runBenchmark(command, options = {}) {
|
|
|
71
74
|
const start = Date.now();
|
|
72
75
|
let output;
|
|
73
76
|
try {
|
|
74
|
-
output =
|
|
77
|
+
output = execFileSync(executable, parsedCommand.args, {
|
|
75
78
|
stdio: 'pipe',
|
|
76
79
|
encoding: 'utf8',
|
|
77
|
-
env
|
|
80
|
+
env,
|
|
81
|
+
windowsHide: true,
|
|
82
|
+
cwd: options.cwd || process.cwd()
|
|
78
83
|
});
|
|
79
84
|
} catch (error) {
|
|
80
|
-
const stderr = error.stderr ? error.stderr
|
|
81
|
-
const stdout = error.stdout ? error.stdout
|
|
85
|
+
const stderr = error.stderr ? String(error.stderr).trim() : '';
|
|
86
|
+
const stdout = error.stdout ? String(error.stdout).trim() : '';
|
|
82
87
|
const exitCode = error.status ?? 'unknown';
|
|
83
88
|
const details = stderr || stdout || error.message || 'No error details available';
|
|
84
89
|
throw new Error(
|
|
85
|
-
`Benchmark command failed (exit code ${exitCode}): ${
|
|
90
|
+
`Benchmark command failed (exit code ${exitCode}): ${parsedCommand.display}\n` +
|
|
86
91
|
`Details: ${details}`
|
|
87
92
|
);
|
|
88
93
|
}
|
|
@@ -14,12 +14,12 @@ const crypto = require('crypto');
|
|
|
14
14
|
const { getStateDir } = require('../platform/state-dir');
|
|
15
15
|
const { validateInvestigationState, assertValid } = require('./schemas');
|
|
16
16
|
const { writeJsonAtomic, writeFileAtomic } = require('../utils/atomic-write');
|
|
17
|
+
const { isPlainObject, updatesApplied, sleepForRetry } = require('../utils/state-helpers');
|
|
17
18
|
|
|
18
19
|
const SCHEMA_VERSION = 1;
|
|
19
20
|
const INVESTIGATION_FILE = 'investigation.json';
|
|
20
21
|
const LOG_DIR = 'investigations';
|
|
21
22
|
const BASELINE_DIR = 'baselines';
|
|
22
|
-
|
|
23
23
|
const PHASES = [
|
|
24
24
|
'setup',
|
|
25
25
|
'baseline',
|
|
@@ -196,10 +196,12 @@ function writeInvestigation(state, basePath = process.cwd()) {
|
|
|
196
196
|
* @returns {object|null}
|
|
197
197
|
*/
|
|
198
198
|
function updateInvestigation(updates, basePath = process.cwd()) {
|
|
199
|
-
const MAX_RETRIES =
|
|
199
|
+
const MAX_RETRIES = 5;
|
|
200
|
+
let fallbackState = null;
|
|
200
201
|
|
|
201
202
|
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
202
203
|
const current = readInvestigation(basePath) || {};
|
|
204
|
+
fallbackState = current;
|
|
203
205
|
const initialVersion = current._version || 0;
|
|
204
206
|
const nextState = { ...current };
|
|
205
207
|
|
|
@@ -209,10 +211,7 @@ function updateInvestigation(updates, basePath = process.cwd()) {
|
|
|
209
211
|
|
|
210
212
|
if (value === null) {
|
|
211
213
|
nextState[key] = null;
|
|
212
|
-
} else if (
|
|
213
|
-
value && typeof value === 'object' && !Array.isArray(value) &&
|
|
214
|
-
nextState[key] && typeof nextState[key] === 'object' && !Array.isArray(nextState[key])
|
|
215
|
-
) {
|
|
214
|
+
} else if (isPlainObject(value) && isPlainObject(nextState[key])) {
|
|
216
215
|
nextState[key] = { ...nextState[key], ...value };
|
|
217
216
|
} else {
|
|
218
217
|
nextState[key] = value;
|
|
@@ -226,23 +225,23 @@ function updateInvestigation(updates, basePath = process.cwd()) {
|
|
|
226
225
|
|
|
227
226
|
// Re-read to verify our write succeeded
|
|
228
227
|
const afterWrite = readInvestigation(basePath);
|
|
229
|
-
if (afterWrite
|
|
228
|
+
if (afterWrite) {
|
|
229
|
+
fallbackState = afterWrite;
|
|
230
|
+
}
|
|
231
|
+
if (afterWrite && afterWrite._version >= initialVersion + 1 && updatesApplied(afterWrite, updates)) {
|
|
230
232
|
return afterWrite; // Success
|
|
231
233
|
}
|
|
232
234
|
|
|
233
235
|
// Version conflict - retry after brief delay
|
|
234
236
|
if (attempt < MAX_RETRIES - 1) {
|
|
235
237
|
const delay = Math.floor(Math.random() * 50) + 10;
|
|
236
|
-
|
|
237
|
-
while (Date.now() - start < delay) {
|
|
238
|
-
// Busy wait (synchronous delay)
|
|
239
|
-
}
|
|
238
|
+
sleepForRetry(delay);
|
|
240
239
|
}
|
|
241
240
|
}
|
|
242
241
|
|
|
243
242
|
// All retries exhausted
|
|
244
|
-
console.error('[
|
|
245
|
-
return readInvestigation(basePath);
|
|
243
|
+
console.error('[ERROR] updateInvestigation: failed to apply updates after max retries');
|
|
244
|
+
return readInvestigation(basePath) || fallbackState || { ...updates };
|
|
246
245
|
}
|
|
247
246
|
|
|
248
247
|
/**
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* @module lib/perf/profiling-runner
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const {
|
|
7
|
+
const { execFileSync } = require('child_process');
|
|
8
8
|
const profilers = require('./profilers');
|
|
9
|
+
const { parseCommand, resolveExecutableForPlatform } = require('../utils/command-parser');
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Run a profiling command and return artifacts/hotspots metadata.
|
|
@@ -16,6 +17,9 @@ const profilers = require('./profilers');
|
|
|
16
17
|
*/
|
|
17
18
|
function runProfiling(options = {}) {
|
|
18
19
|
const repoPath = options.repoPath || process.cwd();
|
|
20
|
+
const timeoutMs = Number.isFinite(options.timeoutMs)
|
|
21
|
+
? Math.max(1, Math.floor(options.timeoutMs))
|
|
22
|
+
: null;
|
|
19
23
|
const profiler = profilers.selectProfiler(repoPath);
|
|
20
24
|
|
|
21
25
|
if (!profiler || typeof profiler.buildCommand !== 'function') {
|
|
@@ -27,14 +31,29 @@ function runProfiling(options = {}) {
|
|
|
27
31
|
output: options.output,
|
|
28
32
|
...(options.profileOptions || {})
|
|
29
33
|
});
|
|
34
|
+
const parsedCommand = parseCommand(command, 'Profiling command');
|
|
35
|
+
const executable = resolveExecutableForPlatform(parsedCommand.executable);
|
|
30
36
|
const env = {
|
|
31
37
|
...process.env,
|
|
32
38
|
...(options.env || {})
|
|
33
39
|
};
|
|
34
40
|
try {
|
|
35
|
-
|
|
41
|
+
const execOptions = {
|
|
42
|
+
stdio: 'pipe',
|
|
43
|
+
env,
|
|
44
|
+
cwd: repoPath,
|
|
45
|
+
windowsHide: true
|
|
46
|
+
};
|
|
47
|
+
if (timeoutMs !== null) {
|
|
48
|
+
execOptions.timeout = timeoutMs;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
execFileSync(executable, parsedCommand.args, execOptions);
|
|
36
52
|
} catch (error) {
|
|
37
|
-
|
|
53
|
+
const stderr = error.stderr ? String(error.stderr).trim() : '';
|
|
54
|
+
const stdout = error.stdout ? String(error.stdout).trim() : '';
|
|
55
|
+
const details = stderr || stdout || error.message;
|
|
56
|
+
return { ok: false, error: `Profiling command failed: ${details}` };
|
|
38
57
|
}
|
|
39
58
|
|
|
40
59
|
const parsed = typeof profiler.parseOutput === 'function'
|
|
@@ -43,7 +62,7 @@ function runProfiling(options = {}) {
|
|
|
43
62
|
|
|
44
63
|
const result = {
|
|
45
64
|
tool: profiler.id,
|
|
46
|
-
command,
|
|
65
|
+
command: parsedCommand.display,
|
|
47
66
|
hotspots: parsed.hotspots || [],
|
|
48
67
|
artifacts: parsed.artifacts || []
|
|
49
68
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
async function runWithConcurrency(items, limit, worker) {
|
|
4
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
5
|
+
return [];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const maxConcurrency = Math.max(1, Math.min(items.length, Math.floor(limit) || 1));
|
|
9
|
+
const results = new Array(items.length);
|
|
10
|
+
let cursor = 0;
|
|
11
|
+
|
|
12
|
+
async function runWorker() {
|
|
13
|
+
while (true) {
|
|
14
|
+
const index = cursor;
|
|
15
|
+
cursor += 1;
|
|
16
|
+
if (index >= items.length) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
results[index] = await worker(items[index], index);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
await Promise.all(Array.from({ length: maxConcurrency }, () => runWorker()));
|
|
24
|
+
return results;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
runWithConcurrency
|
|
29
|
+
};
|