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.
Files changed (264) hide show
  1. package/.claude-plugin/marketplace.json +21 -14
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/AGENTS.md +2 -1
  4. package/CHANGELOG.md +24 -1
  5. package/README.md +7 -6
  6. package/adapters/codex/skills/agnix/SKILL.md +0 -1
  7. package/adapters/codex/skills/audit-project/SKILL.md +0 -1
  8. package/adapters/codex/skills/audit-project-agents/SKILL.md +0 -1
  9. package/adapters/codex/skills/audit-project-github/SKILL.md +0 -1
  10. package/adapters/codex/skills/consult/SKILL.md +133 -59
  11. package/adapters/codex/skills/debate/SKILL.md +214 -0
  12. package/adapters/codex/skills/delivery-approval/SKILL.md +0 -1
  13. package/adapters/codex/skills/deslop/SKILL.md +0 -1
  14. package/adapters/codex/skills/drift-detect/SKILL.md +0 -1
  15. package/adapters/codex/skills/enhance/SKILL.md +0 -1
  16. package/adapters/codex/skills/learn/SKILL.md +0 -1
  17. package/adapters/codex/skills/next-task/SKILL.md +0 -1
  18. package/adapters/codex/skills/perf/SKILL.md +0 -1
  19. package/adapters/codex/skills/repo-map/SKILL.md +0 -1
  20. package/adapters/codex/skills/ship/SKILL.md +0 -1
  21. package/adapters/codex/skills/ship-ci-review-loop/SKILL.md +0 -1
  22. package/adapters/codex/skills/ship-deployment/SKILL.md +0 -1
  23. package/adapters/codex/skills/ship-error-handling/SKILL.md +0 -1
  24. package/adapters/codex/skills/sync-docs/SKILL.md +0 -1
  25. package/adapters/opencode/agents/agent-enhancer.md +0 -1
  26. package/adapters/opencode/agents/agnix-agent.md +0 -1
  27. package/adapters/opencode/agents/ci-fixer.md +0 -1
  28. package/adapters/opencode/agents/ci-monitor.md +0 -1
  29. package/adapters/opencode/agents/claudemd-enhancer.md +0 -1
  30. package/adapters/opencode/agents/consult-agent.md +123 -31
  31. package/adapters/opencode/agents/cross-file-enhancer.md +0 -1
  32. package/adapters/opencode/agents/debate-orchestrator.md +169 -0
  33. package/adapters/opencode/agents/delivery-validator.md +0 -1
  34. package/adapters/opencode/agents/deslop-agent.md +0 -1
  35. package/adapters/opencode/agents/docs-enhancer.md +0 -1
  36. package/adapters/opencode/agents/exploration-agent.md +0 -1
  37. package/adapters/opencode/agents/hooks-enhancer.md +0 -1
  38. package/adapters/opencode/agents/implementation-agent.md +0 -1
  39. package/adapters/opencode/agents/learn-agent.md +0 -1
  40. package/adapters/opencode/agents/map-validator.md +0 -1
  41. package/adapters/opencode/agents/perf-analyzer.md +0 -1
  42. package/adapters/opencode/agents/perf-code-paths.md +0 -1
  43. package/adapters/opencode/agents/perf-investigation-logger.md +0 -1
  44. package/adapters/opencode/agents/perf-orchestrator.md +0 -1
  45. package/adapters/opencode/agents/perf-theory-gatherer.md +0 -1
  46. package/adapters/opencode/agents/perf-theory-tester.md +0 -1
  47. package/adapters/opencode/agents/plan-synthesizer.md +0 -1
  48. package/adapters/opencode/agents/planning-agent.md +0 -1
  49. package/adapters/opencode/agents/plugin-enhancer.md +0 -1
  50. package/adapters/opencode/agents/prompt-enhancer.md +0 -1
  51. package/adapters/opencode/agents/simple-fixer.md +0 -1
  52. package/adapters/opencode/agents/skills-enhancer.md +0 -1
  53. package/adapters/opencode/agents/sync-docs-agent.md +0 -1
  54. package/adapters/opencode/agents/task-discoverer.md +0 -1
  55. package/adapters/opencode/agents/test-coverage-checker.md +0 -1
  56. package/adapters/opencode/agents/worktree-manager.md +0 -1
  57. package/adapters/opencode/commands/agnix.md +0 -1
  58. package/adapters/opencode/commands/audit-project-agents.md +0 -1
  59. package/adapters/opencode/commands/audit-project-github.md +0 -1
  60. package/adapters/opencode/commands/audit-project.md +0 -1
  61. package/adapters/opencode/commands/consult.md +134 -59
  62. package/adapters/opencode/commands/debate.md +224 -0
  63. package/adapters/opencode/commands/delivery-approval.md +0 -1
  64. package/adapters/opencode/commands/deslop.md +0 -1
  65. package/adapters/opencode/commands/drift-detect.md +0 -1
  66. package/adapters/opencode/commands/enhance.md +0 -1
  67. package/adapters/opencode/commands/learn.md +0 -1
  68. package/adapters/opencode/commands/next-task.md +0 -1
  69. package/adapters/opencode/commands/perf.md +0 -1
  70. package/adapters/opencode/commands/repo-map.md +0 -1
  71. package/adapters/opencode/commands/ship-ci-review-loop.md +0 -1
  72. package/adapters/opencode/commands/ship-deployment.md +0 -1
  73. package/adapters/opencode/commands/ship-error-handling.md +0 -1
  74. package/adapters/opencode/commands/ship.md +0 -1
  75. package/adapters/opencode/commands/sync-docs.md +0 -1
  76. package/adapters/opencode/skills/agnix/SKILL.md +1 -2
  77. package/adapters/opencode/skills/consult/SKILL.md +41 -27
  78. package/adapters/opencode/skills/debate/SKILL.md +245 -0
  79. package/adapters/opencode/skills/deslop/SKILL.md +1 -2
  80. package/adapters/opencode/skills/discover-tasks/SKILL.md +1 -2
  81. package/adapters/opencode/skills/drift-analysis/SKILL.md +1 -2
  82. package/adapters/opencode/skills/enhance-agent-prompts/SKILL.md +1 -2
  83. package/adapters/opencode/skills/enhance-claude-memory/SKILL.md +1 -2
  84. package/adapters/opencode/skills/enhance-cross-file/SKILL.md +1 -2
  85. package/adapters/opencode/skills/enhance-docs/SKILL.md +1 -2
  86. package/adapters/opencode/skills/enhance-hooks/SKILL.md +1 -2
  87. package/adapters/opencode/skills/enhance-orchestrator/SKILL.md +1 -2
  88. package/adapters/opencode/skills/enhance-plugins/SKILL.md +1 -2
  89. package/adapters/opencode/skills/enhance-prompts/SKILL.md +1 -2
  90. package/adapters/opencode/skills/enhance-skills/SKILL.md +1 -2
  91. package/adapters/opencode/skills/learn/SKILL.md +1 -2
  92. package/adapters/opencode/skills/orchestrate-review/SKILL.md +0 -1
  93. package/adapters/opencode/skills/perf-analyzer/SKILL.md +1 -2
  94. package/adapters/opencode/skills/perf-baseline-manager/SKILL.md +1 -2
  95. package/adapters/opencode/skills/perf-benchmarker/SKILL.md +1 -2
  96. package/adapters/opencode/skills/perf-code-paths/SKILL.md +1 -2
  97. package/adapters/opencode/skills/perf-investigation-logger/SKILL.md +1 -2
  98. package/adapters/opencode/skills/perf-profiler/SKILL.md +1 -2
  99. package/adapters/opencode/skills/perf-theory-gatherer/SKILL.md +1 -2
  100. package/adapters/opencode/skills/perf-theory-tester/SKILL.md +1 -2
  101. package/adapters/opencode/skills/repo-mapping/SKILL.md +1 -2
  102. package/adapters/opencode/skills/sync-docs/SKILL.md +1 -2
  103. package/adapters/opencode/skills/validate-delivery/SKILL.md +1 -2
  104. package/lib/adapter-transforms.js +24 -4
  105. package/package.json +1 -1
  106. package/plugins/agnix/.claude-plugin/plugin.json +1 -1
  107. package/plugins/agnix/skills/agnix/SKILL.md +1 -1
  108. package/plugins/audit-project/.claude-plugin/plugin.json +1 -1
  109. package/plugins/audit-project/lib/adapter-transforms.js +24 -4
  110. package/plugins/consult/.claude-plugin/plugin.json +1 -1
  111. package/plugins/consult/agents/consult-agent.md +123 -30
  112. package/plugins/consult/commands/consult.md +136 -60
  113. package/plugins/consult/skills/consult/SKILL.md +39 -24
  114. package/plugins/debate/.claude-plugin/plugin.json +21 -0
  115. package/plugins/debate/agents/debate-orchestrator.md +175 -0
  116. package/plugins/debate/commands/debate.md +221 -0
  117. package/plugins/debate/lib/adapter-transforms.js +298 -0
  118. package/plugins/debate/lib/collectors/codebase.js +392 -0
  119. package/plugins/debate/lib/collectors/docs-patterns.js +713 -0
  120. package/plugins/debate/lib/collectors/documentation.js +219 -0
  121. package/plugins/debate/lib/collectors/github.js +330 -0
  122. package/plugins/debate/lib/collectors/index.js +126 -0
  123. package/plugins/debate/lib/config/index.js +14 -0
  124. package/plugins/debate/lib/cross-platform/index.js +539 -0
  125. package/plugins/debate/lib/discovery/index.js +352 -0
  126. package/plugins/debate/lib/drift-detect/collectors.js +37 -0
  127. package/plugins/debate/lib/enhance/agent-analyzer.js +421 -0
  128. package/plugins/debate/lib/enhance/agent-patterns.js +571 -0
  129. package/plugins/debate/lib/enhance/auto-suppression.js +622 -0
  130. package/plugins/debate/lib/enhance/benchmark.js +417 -0
  131. package/plugins/debate/lib/enhance/cross-file-analyzer.js +930 -0
  132. package/plugins/debate/lib/enhance/cross-file-patterns.js +370 -0
  133. package/plugins/debate/lib/enhance/docs-analyzer.js +325 -0
  134. package/plugins/debate/lib/enhance/docs-patterns.js +671 -0
  135. package/plugins/debate/lib/enhance/fixer.js +721 -0
  136. package/plugins/debate/lib/enhance/hook-analyzer.js +135 -0
  137. package/plugins/debate/lib/enhance/hook-patterns.js +40 -0
  138. package/plugins/debate/lib/enhance/index.js +127 -0
  139. package/plugins/debate/lib/enhance/plugin-analyzer.js +402 -0
  140. package/plugins/debate/lib/enhance/plugin-patterns.js +326 -0
  141. package/plugins/debate/lib/enhance/projectmemory-analyzer.js +551 -0
  142. package/plugins/debate/lib/enhance/projectmemory-patterns.js +617 -0
  143. package/plugins/debate/lib/enhance/prompt-analyzer.js +457 -0
  144. package/plugins/debate/lib/enhance/prompt-patterns.js +1484 -0
  145. package/plugins/debate/lib/enhance/reporter.js +1348 -0
  146. package/plugins/debate/lib/enhance/security-patterns.js +284 -0
  147. package/plugins/debate/lib/enhance/skill-analyzer.js +182 -0
  148. package/plugins/debate/lib/enhance/skill-patterns.js +147 -0
  149. package/plugins/debate/lib/enhance/suppression.js +352 -0
  150. package/plugins/debate/lib/enhance/tool-patterns.js +373 -0
  151. package/plugins/debate/lib/index.js +270 -0
  152. package/plugins/debate/lib/patterns/cli-enhancers.js +611 -0
  153. package/plugins/debate/lib/patterns/pipeline.js +948 -0
  154. package/plugins/debate/lib/patterns/review-patterns.js +558 -0
  155. package/plugins/debate/lib/patterns/slop-analyzers.js +2305 -0
  156. package/plugins/debate/lib/patterns/slop-patterns.js +1187 -0
  157. package/plugins/debate/lib/perf/analyzer/index.js +22 -0
  158. package/plugins/debate/lib/perf/argument-parser.js +105 -0
  159. package/plugins/debate/lib/perf/baseline-comparator.js +50 -0
  160. package/plugins/debate/lib/perf/baseline-store.js +127 -0
  161. package/plugins/debate/lib/perf/benchmark-runner.js +404 -0
  162. package/plugins/debate/lib/perf/breaking-point-finder.js +52 -0
  163. package/plugins/debate/lib/perf/breaking-point-runner.js +60 -0
  164. package/plugins/debate/lib/perf/checkpoint.js +123 -0
  165. package/plugins/debate/lib/perf/code-paths.js +86 -0
  166. package/plugins/debate/lib/perf/consolidation.js +37 -0
  167. package/plugins/debate/lib/perf/constraint-runner.js +71 -0
  168. package/plugins/debate/lib/perf/experiment-runner.js +32 -0
  169. package/plugins/debate/lib/perf/index.js +41 -0
  170. package/plugins/debate/lib/perf/investigation-state.js +874 -0
  171. package/plugins/debate/lib/perf/optimization-runner.js +79 -0
  172. package/plugins/debate/lib/perf/profilers/go.js +22 -0
  173. package/plugins/debate/lib/perf/profilers/index.js +46 -0
  174. package/plugins/debate/lib/perf/profilers/java.js +23 -0
  175. package/plugins/debate/lib/perf/profilers/node.js +27 -0
  176. package/plugins/debate/lib/perf/profilers/python.js +23 -0
  177. package/plugins/debate/lib/perf/profilers/rust.js +23 -0
  178. package/plugins/debate/lib/perf/profiling-runner.js +75 -0
  179. package/plugins/debate/lib/perf/schemas.js +140 -0
  180. package/plugins/debate/lib/platform/detect-platform.js +413 -0
  181. package/plugins/debate/lib/platform/detection-configs.js +93 -0
  182. package/plugins/debate/lib/platform/state-dir.js +132 -0
  183. package/plugins/debate/lib/platform/verify-tools.js +182 -0
  184. package/plugins/debate/lib/repo-map/cache.js +152 -0
  185. package/plugins/debate/lib/repo-map/concurrency.js +29 -0
  186. package/plugins/debate/lib/repo-map/index.js +222 -0
  187. package/plugins/debate/lib/repo-map/installer.js +212 -0
  188. package/plugins/debate/lib/repo-map/queries/go.js +27 -0
  189. package/plugins/debate/lib/repo-map/queries/index.js +100 -0
  190. package/plugins/debate/lib/repo-map/queries/java.js +38 -0
  191. package/plugins/debate/lib/repo-map/queries/javascript.js +55 -0
  192. package/plugins/debate/lib/repo-map/queries/python.js +24 -0
  193. package/plugins/debate/lib/repo-map/queries/rust.js +73 -0
  194. package/plugins/debate/lib/repo-map/queries/typescript.js +38 -0
  195. package/plugins/debate/lib/repo-map/runner.js +1364 -0
  196. package/plugins/debate/lib/repo-map/updater.js +562 -0
  197. package/plugins/debate/lib/repo-map/usage-analyzer.js +407 -0
  198. package/plugins/debate/lib/schemas/plugin-manifest.schema.json +57 -0
  199. package/plugins/debate/lib/schemas/validator.js +247 -0
  200. package/plugins/debate/lib/sources/custom-handler.js +199 -0
  201. package/plugins/debate/lib/sources/policy-questions.js +246 -0
  202. package/plugins/debate/lib/sources/source-cache.js +165 -0
  203. package/plugins/debate/lib/state/workflow-state.js +576 -0
  204. package/plugins/debate/lib/types/agent-frontmatter.d.ts +134 -0
  205. package/plugins/debate/lib/types/command-frontmatter.d.ts +107 -0
  206. package/plugins/debate/lib/types/hook-frontmatter.d.ts +115 -0
  207. package/plugins/debate/lib/types/index.d.ts +84 -0
  208. package/plugins/debate/lib/types/plugin-manifest.d.ts +102 -0
  209. package/plugins/debate/lib/types/skill-frontmatter.d.ts +89 -0
  210. package/plugins/debate/lib/utils/atomic-write.js +94 -0
  211. package/plugins/debate/lib/utils/cache-manager.js +159 -0
  212. package/plugins/debate/lib/utils/command-parser.js +0 -0
  213. package/plugins/debate/lib/utils/context-optimizer.js +300 -0
  214. package/plugins/debate/lib/utils/deprecation.js +37 -0
  215. package/plugins/debate/lib/utils/shell-escape.js +88 -0
  216. package/plugins/debate/lib/utils/state-helpers.js +61 -0
  217. package/plugins/debate/skills/debate/SKILL.md +264 -0
  218. package/plugins/deslop/.claude-plugin/plugin.json +1 -1
  219. package/plugins/deslop/lib/adapter-transforms.js +24 -4
  220. package/plugins/deslop/skills/deslop/SKILL.md +1 -1
  221. package/plugins/drift-detect/.claude-plugin/plugin.json +1 -1
  222. package/plugins/drift-detect/lib/adapter-transforms.js +24 -4
  223. package/plugins/drift-detect/skills/drift-analysis/SKILL.md +1 -1
  224. package/plugins/enhance/.claude-plugin/plugin.json +1 -1
  225. package/plugins/enhance/lib/adapter-transforms.js +24 -4
  226. package/plugins/enhance/skills/enhance-agent-prompts/SKILL.md +1 -1
  227. package/plugins/enhance/skills/enhance-claude-memory/SKILL.md +1 -1
  228. package/plugins/enhance/skills/enhance-cross-file/SKILL.md +1 -1
  229. package/plugins/enhance/skills/enhance-docs/SKILL.md +1 -1
  230. package/plugins/enhance/skills/enhance-hooks/SKILL.md +1 -1
  231. package/plugins/enhance/skills/enhance-orchestrator/SKILL.md +1 -1
  232. package/plugins/enhance/skills/enhance-plugins/SKILL.md +1 -1
  233. package/plugins/enhance/skills/enhance-prompts/SKILL.md +1 -1
  234. package/plugins/enhance/skills/enhance-skills/SKILL.md +1 -1
  235. package/plugins/learn/.claude-plugin/plugin.json +1 -1
  236. package/plugins/learn/agents/learn-agent.md +1 -1
  237. package/plugins/learn/lib/adapter-transforms.js +24 -4
  238. package/plugins/learn/skills/learn/SKILL.md +1 -1
  239. package/plugins/next-task/.claude-plugin/plugin.json +1 -1
  240. package/plugins/next-task/agents/exploration-agent.md +1 -1
  241. package/plugins/next-task/lib/adapter-transforms.js +24 -4
  242. package/plugins/next-task/skills/discover-tasks/SKILL.md +1 -1
  243. package/plugins/next-task/skills/validate-delivery/SKILL.md +1 -1
  244. package/plugins/perf/.claude-plugin/plugin.json +1 -1
  245. package/plugins/perf/lib/adapter-transforms.js +24 -4
  246. package/plugins/perf/skills/perf-analyzer/SKILL.md +1 -1
  247. package/plugins/perf/skills/perf-baseline-manager/SKILL.md +1 -1
  248. package/plugins/perf/skills/perf-benchmarker/SKILL.md +1 -1
  249. package/plugins/perf/skills/perf-code-paths/SKILL.md +1 -1
  250. package/plugins/perf/skills/perf-investigation-logger/SKILL.md +1 -1
  251. package/plugins/perf/skills/perf-profiler/SKILL.md +1 -1
  252. package/plugins/perf/skills/perf-theory-gatherer/SKILL.md +1 -1
  253. package/plugins/perf/skills/perf-theory-tester/SKILL.md +1 -1
  254. package/plugins/repo-map/.claude-plugin/plugin.json +1 -1
  255. package/plugins/repo-map/lib/adapter-transforms.js +24 -4
  256. package/plugins/ship/.claude-plugin/plugin.json +1 -1
  257. package/plugins/ship/lib/adapter-transforms.js +24 -4
  258. package/plugins/sync-docs/.claude-plugin/plugin.json +1 -1
  259. package/plugins/sync-docs/lib/adapter-transforms.js +24 -4
  260. package/plugins/sync-docs/skills/sync-docs/SKILL.md +1 -1
  261. package/scripts/gen-adapters.js +6 -7
  262. package/scripts/generate-docs.js +4 -2
  263. package/scripts/plugins.txt +1 -0
  264. package/site/content.json +6 -6
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Optimization runner for /perf experiments.
3
+ *
4
+ * @module lib/perf/optimization-runner
5
+ */
6
+
7
+ const { runBenchmark, runBenchmarkSeries, DEFAULT_MIN_DURATION } = require('./benchmark-runner');
8
+ const { compareBaselines } = require('./baseline-comparator');
9
+ const { isWorkingTreeClean } = require('./checkpoint');
10
+
11
+ /**
12
+ * Run a single optimization experiment with two benchmark runs.
13
+ * NOTE: This helper does not modify code; it assumes the change was applied externally.
14
+ *
15
+ * @param {object} options
16
+ * @param {string} options.command
17
+ * @param {string} options.changeSummary
18
+ * @param {number} [options.duration]
19
+ * @param {number} [options.minDuration]
20
+ * @param {number} [options.runs]
21
+ * @param {string} [options.aggregate]
22
+ * @param {object} [options.env]
23
+ * @returns {{ baseline: object, experiment: object, delta: object, verdict: string, change: string }}
24
+ */
25
+ function runOptimizationExperiment(options) {
26
+ const { command, changeSummary, duration, minDuration, runs, aggregate, env } = options || {};
27
+
28
+ if (!command || typeof command !== 'string') {
29
+ throw new Error('command must be a non-empty string');
30
+ }
31
+ if (!changeSummary || typeof changeSummary !== 'string') {
32
+ throw new Error('changeSummary must be a non-empty string');
33
+ }
34
+
35
+ const shouldCheckClean = options?.requireClean !== false && process.env.PERF_ALLOW_DIRTY !== '1';
36
+ if (shouldCheckClean && !isWorkingTreeClean()) {
37
+ throw new Error('working tree is dirty before experiment');
38
+ }
39
+
40
+ const baselineEnv = { ...env, PERF_EXPERIMENT: '0' };
41
+ const experimentEnv = { ...env, PERF_EXPERIMENT: '1' };
42
+ const seriesOptions = {
43
+ duration: duration ?? DEFAULT_MIN_DURATION,
44
+ minDuration,
45
+ runs,
46
+ aggregate
47
+ };
48
+
49
+ const baselineRun = runBenchmarkSeries(command, { ...seriesOptions, env: baselineEnv });
50
+
51
+ // NOTE: Caller is responsible for applying the experiment change here.
52
+ // Warm up the system (caches/JIT) before capturing experiment metrics.
53
+ const runMode = runs && runs > 1 ? 'oneshot' : 'duration';
54
+ runBenchmark(command, {
55
+ duration: duration ?? DEFAULT_MIN_DURATION,
56
+ minDuration,
57
+ env: experimentEnv,
58
+ runMode,
59
+ setDurationEnv: runMode !== 'oneshot'
60
+ });
61
+ const experimentRun = runBenchmarkSeries(command, { ...seriesOptions, env: experimentEnv });
62
+
63
+ const delta = compareBaselines(
64
+ { metrics: baselineRun.metrics },
65
+ { metrics: experimentRun.metrics }
66
+ );
67
+
68
+ return {
69
+ change: changeSummary,
70
+ baseline: { metrics: baselineRun.metrics },
71
+ experiment: { metrics: experimentRun.metrics },
72
+ delta,
73
+ verdict: 'inconclusive'
74
+ };
75
+ }
76
+
77
+ module.exports = {
78
+ runOptimizationExperiment
79
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Go pprof helper.
3
+ *
4
+ * @module lib/perf/profilers/go
5
+ */
6
+
7
+ module.exports = {
8
+ id: 'pprof',
9
+ tool: 'pprof',
10
+ buildCommand(options = {}) {
11
+ const command = options.command || 'go test';
12
+ const output = options.output || 'cpu.pprof';
13
+ return `${command} -cpuprofile=${output}`;
14
+ },
15
+ parseOutput() {
16
+ return {
17
+ tool: 'pprof',
18
+ hotspots: [],
19
+ artifacts: []
20
+ };
21
+ }
22
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Profilers registry for /perf.
3
+ *
4
+ * @module lib/perf/profilers
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const cliEnhancers = require('../../patterns/cli-enhancers');
10
+ const nodeProfiler = require('./node');
11
+ const pythonProfiler = require('./python');
12
+ const goProfiler = require('./go');
13
+ const rustProfiler = require('./rust');
14
+ const javaProfiler = require('./java');
15
+
16
+ function hasJavaIndicators(repoPath) {
17
+ const indicators = ['pom.xml', 'build.gradle', 'build.gradle.kts'];
18
+ return indicators.some((file) => fs.existsSync(path.join(repoPath, file)));
19
+ }
20
+
21
+ function selectProfiler(repoPath = process.cwd()) {
22
+ const languages = cliEnhancers.detectProjectLanguages(repoPath);
23
+
24
+ if (hasJavaIndicators(repoPath)) return javaProfiler;
25
+ if (languages.includes('typescript') || languages.includes('javascript')) return nodeProfiler;
26
+ if (languages.includes('go')) return goProfiler;
27
+ if (languages.includes('python')) return pythonProfiler;
28
+ if (languages.includes('rust')) return rustProfiler;
29
+
30
+ return nodeProfiler;
31
+ }
32
+
33
+ function listAvailable() {
34
+ return [
35
+ nodeProfiler.id,
36
+ javaProfiler.id,
37
+ pythonProfiler.id,
38
+ goProfiler.id,
39
+ rustProfiler.id
40
+ ];
41
+ }
42
+
43
+ module.exports = {
44
+ listAvailable,
45
+ selectProfiler
46
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Java JFR profiler helper.
3
+ *
4
+ * @module lib/perf/profilers/java
5
+ */
6
+
7
+ module.exports = {
8
+ id: 'jfr',
9
+ tool: 'jfr',
10
+ buildCommand(options = {}) {
11
+ const command = options.command || 'java';
12
+ const output = options.output || 'profile.jfr';
13
+ const duration = options.duration || '60s';
14
+ return `${command} -XX:StartFlightRecording=duration=${duration},filename=${output}`;
15
+ },
16
+ parseOutput() {
17
+ return {
18
+ tool: 'jfr',
19
+ hotspots: [],
20
+ artifacts: []
21
+ };
22
+ }
23
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Node.js profiler helper.
3
+ *
4
+ * @module lib/perf/profilers/node
5
+ */
6
+
7
+ module.exports = {
8
+ id: 'node',
9
+ tool: '--cpu-prof',
10
+ buildCommand(options = {}) {
11
+ const command = options.command || 'node';
12
+ const output = options.output || 'node.cpuprofile';
13
+ const trimmed = command.trim();
14
+ if (trimmed.startsWith('node ')) {
15
+ const rest = trimmed.slice(5);
16
+ return `node --cpu-prof --cpu-prof-name=${output} ${rest}`.trim();
17
+ }
18
+ return `${command} --cpu-prof --cpu-prof-name=${output}`.trim();
19
+ },
20
+ parseOutput() {
21
+ return {
22
+ tool: 'node',
23
+ hotspots: [],
24
+ artifacts: []
25
+ };
26
+ }
27
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Python cProfile helper.
3
+ *
4
+ * @module lib/perf/profilers/python
5
+ */
6
+
7
+ module.exports = {
8
+ id: 'cprofile',
9
+ tool: 'cProfile',
10
+ buildCommand(options = {}) {
11
+ const command = options.command || 'python';
12
+ const target = options.target || '-m';
13
+ const output = options.output || 'profile.prof';
14
+ return `${command} -m cProfile -o ${output} ${target}`;
15
+ },
16
+ parseOutput() {
17
+ return {
18
+ tool: 'cprofile',
19
+ hotspots: [],
20
+ artifacts: []
21
+ };
22
+ }
23
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Rust perf helper (Linux).
3
+ *
4
+ * @module lib/perf/profilers/rust
5
+ */
6
+
7
+ module.exports = {
8
+ id: 'perf',
9
+ tool: 'perf',
10
+ buildCommand(options = {}) {
11
+ const command = options.command || 'perf record';
12
+ const output = options.output || 'perf.data';
13
+ const target = options.target || './target/release/app';
14
+ return `${command} -o ${output} ${target}`;
15
+ },
16
+ parseOutput() {
17
+ return {
18
+ tool: 'perf',
19
+ hotspots: [],
20
+ artifacts: []
21
+ };
22
+ }
23
+ };
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Profiling execution helper.
3
+ *
4
+ * @module lib/perf/profiling-runner
5
+ */
6
+
7
+ const { execFileSync } = require('child_process');
8
+ const profilers = require('./profilers');
9
+ const { parseCommand, resolveExecutableForPlatform } = require('../utils/command-parser');
10
+
11
+ /**
12
+ * Run a profiling command and return artifacts/hotspots metadata.
13
+ * @param {object} options
14
+ * @param {string} [options.repoPath]
15
+ * @param {object} [options.profileOptions]
16
+ * @returns {{ ok: boolean, result?: object, error?: string }}
17
+ */
18
+ function runProfiling(options = {}) {
19
+ const repoPath = options.repoPath || process.cwd();
20
+ const timeoutMs = Number.isFinite(options.timeoutMs)
21
+ ? Math.max(1, Math.floor(options.timeoutMs))
22
+ : null;
23
+ const profiler = profilers.selectProfiler(repoPath);
24
+
25
+ if (!profiler || typeof profiler.buildCommand !== 'function') {
26
+ return { ok: false, error: 'No profiler available' };
27
+ }
28
+
29
+ const command = profiler.buildCommand({
30
+ command: options.command,
31
+ output: options.output,
32
+ ...(options.profileOptions || {})
33
+ });
34
+ const parsedCommand = parseCommand(command, 'Profiling command');
35
+ const executable = resolveExecutableForPlatform(parsedCommand.executable);
36
+ const env = {
37
+ ...process.env,
38
+ ...(options.env || {})
39
+ };
40
+ try {
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);
52
+ } catch (error) {
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}` };
57
+ }
58
+
59
+ const parsed = typeof profiler.parseOutput === 'function'
60
+ ? profiler.parseOutput()
61
+ : { tool: profiler.id, hotspots: [], artifacts: [] };
62
+
63
+ const result = {
64
+ tool: profiler.id,
65
+ command: parsedCommand.display,
66
+ hotspots: parsed.hotspots || [],
67
+ artifacts: parsed.artifacts || []
68
+ };
69
+
70
+ return { ok: true, result };
71
+ }
72
+
73
+ module.exports = {
74
+ runProfiling
75
+ };
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Schema validation helpers for /perf.
3
+ *
4
+ * @module lib/perf/schemas
5
+ */
6
+
7
+ const REQUIRED_INVESTIGATION_FIELDS = ['schemaVersion', 'id', 'status', 'phase', 'scenario'];
8
+ const REQUIRED_BASELINE_FIELDS = ['version', 'recordedAt', 'metrics', 'command'];
9
+
10
+ function isObject(value) {
11
+ return value != null && typeof value === 'object' && !Array.isArray(value);
12
+ }
13
+
14
+ function validateInvestigationState(state) {
15
+ const errors = [];
16
+
17
+ if (!isObject(state)) {
18
+ return { ok: false, errors: ['state must be an object'] };
19
+ }
20
+
21
+ for (const field of REQUIRED_INVESTIGATION_FIELDS) {
22
+ if (!Object.prototype.hasOwnProperty.call(state, field)) {
23
+ errors.push(`missing ${field}`);
24
+ }
25
+ }
26
+
27
+ if (typeof state.id !== 'string' || state.id.trim().length === 0) {
28
+ errors.push('id must be a non-empty string');
29
+ }
30
+
31
+ if (typeof state.phase !== 'string' || state.phase.trim().length === 0) {
32
+ errors.push('phase must be a non-empty string');
33
+ }
34
+
35
+ if (!isObject(state.scenario)) {
36
+ errors.push('scenario must be an object');
37
+ } else {
38
+ if (typeof state.scenario.description !== 'string') {
39
+ errors.push('scenario.description must be a string');
40
+ }
41
+ if (!Array.isArray(state.scenario.metrics)) {
42
+ errors.push('scenario.metrics must be an array');
43
+ }
44
+ if (typeof state.scenario.successCriteria !== 'string') {
45
+ errors.push('scenario.successCriteria must be a string');
46
+ }
47
+ if (state.scenario.scenarios != null) {
48
+ if (!Array.isArray(state.scenario.scenarios)) {
49
+ errors.push('scenario.scenarios must be an array when provided');
50
+ } else {
51
+ state.scenario.scenarios.forEach((scenario, index) => {
52
+ if (!isObject(scenario)) {
53
+ errors.push(`scenario.scenarios[${index}] must be an object`);
54
+ return;
55
+ }
56
+ if (typeof scenario.name !== 'string' || scenario.name.trim().length === 0) {
57
+ errors.push(`scenario.scenarios[${index}].name must be a non-empty string`);
58
+ }
59
+ if (scenario.params != null && !isObject(scenario.params)) {
60
+ errors.push(`scenario.scenarios[${index}].params must be an object when provided`);
61
+ }
62
+ });
63
+ }
64
+ }
65
+ }
66
+
67
+ return { ok: errors.length === 0, errors };
68
+ }
69
+
70
+ function validateBaseline(baseline) {
71
+ const errors = [];
72
+
73
+ if (!isObject(baseline)) {
74
+ return { ok: false, errors: ['baseline must be an object'] };
75
+ }
76
+
77
+ for (const field of REQUIRED_BASELINE_FIELDS) {
78
+ if (!Object.prototype.hasOwnProperty.call(baseline, field)) {
79
+ errors.push(`missing ${field}`);
80
+ }
81
+ }
82
+
83
+ if (typeof baseline.version !== 'string' || baseline.version.trim().length === 0) {
84
+ errors.push('version must be a non-empty string');
85
+ }
86
+
87
+ if (typeof baseline.recordedAt !== 'string' || baseline.recordedAt.trim().length === 0) {
88
+ errors.push('recordedAt must be an ISO8601 string');
89
+ }
90
+
91
+ if (typeof baseline.command !== 'string' || baseline.command.trim().length === 0) {
92
+ errors.push('command must be a non-empty string');
93
+ }
94
+
95
+ if (!isObject(baseline.metrics)) {
96
+ errors.push('metrics must be an object');
97
+ } else {
98
+ if (baseline.metrics.scenarios != null) {
99
+ if (!isObject(baseline.metrics.scenarios)) {
100
+ errors.push('metrics.scenarios must be an object when provided');
101
+ } else {
102
+ for (const [scenarioName, scenarioMetrics] of Object.entries(baseline.metrics.scenarios)) {
103
+ if (!isObject(scenarioMetrics)) {
104
+ errors.push(`metrics.scenarios.${scenarioName} must be an object`);
105
+ continue;
106
+ }
107
+ for (const [key, value] of Object.entries(scenarioMetrics)) {
108
+ if (typeof value !== 'number' || Number.isNaN(value)) {
109
+ errors.push(`metric ${scenarioName}.${key} must be a number`);
110
+ }
111
+ }
112
+ }
113
+ }
114
+ } else {
115
+ for (const [key, value] of Object.entries(baseline.metrics)) {
116
+ if (typeof value !== 'number' || Number.isNaN(value)) {
117
+ errors.push(`metric ${key} must be a number`);
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ if (baseline.env && !isObject(baseline.env)) {
124
+ errors.push('env must be an object when provided');
125
+ }
126
+
127
+ return { ok: errors.length === 0, errors };
128
+ }
129
+
130
+ function assertValid(result, message) {
131
+ if (!result.ok) {
132
+ throw new Error(`${message}: ${result.errors.join(', ')}`);
133
+ }
134
+ }
135
+
136
+ module.exports = {
137
+ validateInvestigationState,
138
+ validateBaseline,
139
+ assertValid
140
+ };