agentsys 5.0.3 → 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 +18 -0
  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 +132 -57
  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 +122 -30
  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 +133 -57
  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 +33 -23
  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 +122 -29
  112. package/plugins/consult/commands/consult.md +135 -58
  113. package/plugins/consult/skills/consult/SKILL.md +31 -20
  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,52 @@
1
+ /**
2
+ * Binary search helper for breaking point discovery.
3
+ *
4
+ * @module lib/perf/breaking-point-finder
5
+ */
6
+
7
+ /**
8
+ * Find breaking point using binary search.
9
+ * The runner should return { ok: boolean, data?: any }.
10
+ *
11
+ * @param {object} options
12
+ * @param {number} options.min
13
+ * @param {number} options.max
14
+ * @param {(value:number)=>Promise<{ok:boolean,data?:any}>} options.runner
15
+ * @returns {Promise<{breakingPoint:number|null, attempts:number, history:Array}>}
16
+ */
17
+ async function findBreakingPoint({ min, max, runner }) {
18
+ if (typeof min !== 'number' || typeof max !== 'number') {
19
+ throw new Error('min and max must be numbers');
20
+ }
21
+ if (typeof runner !== 'function') {
22
+ throw new Error('runner must be a function');
23
+ }
24
+
25
+ let low = min;
26
+ let high = max;
27
+ let breakingPoint = null;
28
+ const history = [];
29
+
30
+ while (low <= high) {
31
+ const mid = Math.floor((low + high) / 2);
32
+ const result = await runner(mid);
33
+ history.push({ value: mid, ok: result.ok });
34
+
35
+ if (result.ok) {
36
+ low = mid + 1;
37
+ } else {
38
+ breakingPoint = mid;
39
+ high = mid - 1;
40
+ }
41
+ }
42
+
43
+ return {
44
+ breakingPoint,
45
+ attempts: history.length,
46
+ history
47
+ };
48
+ }
49
+
50
+ module.exports = {
51
+ findBreakingPoint
52
+ };
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Breaking point runner wrapper for /perf.
3
+ *
4
+ * @module lib/perf/breaking-point-runner
5
+ */
6
+
7
+ const { runBenchmark, parseMetrics, BINARY_SEARCH_MIN_DURATION } = require('./benchmark-runner');
8
+ const { findBreakingPoint } = require('./breaking-point-finder');
9
+
10
+ /**
11
+ * Run a binary search to find the breaking point for a numeric parameter.
12
+ * The benchmark command should accept the value via an env var.
13
+ *
14
+ * @param {object} options
15
+ * @param {string} options.command
16
+ * @param {string} options.paramEnv
17
+ * @param {number} options.min
18
+ * @param {number} options.max
19
+ * @returns {Promise<{breakingPoint:number|null, attempts:number, history:Array}>}
20
+ */
21
+ async function runBreakingPointSearch(options) {
22
+ const { command, paramEnv, min, max } = options || {};
23
+
24
+ if (!command || typeof command !== 'string') {
25
+ throw new Error('command must be a non-empty string');
26
+ }
27
+ if (!paramEnv || typeof paramEnv !== 'string') {
28
+ throw new Error('paramEnv must be a non-empty string');
29
+ }
30
+ if (typeof min !== 'number' || typeof max !== 'number') {
31
+ throw new Error('min and max must be numbers');
32
+ }
33
+
34
+ const runner = async (value) => {
35
+ try {
36
+ const result = runBenchmark(command, {
37
+ mode: 'binary-search',
38
+ duration: BINARY_SEARCH_MIN_DURATION,
39
+ env: {
40
+ [paramEnv]: String(value)
41
+ }
42
+ });
43
+
44
+ const parsed = parseMetrics(result.output);
45
+ if (!parsed.ok) {
46
+ return { ok: false, data: { error: parsed.error } };
47
+ }
48
+
49
+ return { ok: true, data: { metrics: parsed.metrics } };
50
+ } catch (error) {
51
+ return { ok: false, data: { error: error.message } };
52
+ }
53
+ };
54
+
55
+ return findBreakingPoint({ min, max, runner });
56
+ }
57
+
58
+ module.exports = {
59
+ runBreakingPointSearch
60
+ };
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Git checkpoint helper for /perf phases.
3
+ *
4
+ * @module lib/perf/checkpoint
5
+ */
6
+
7
+ const { execFileSync } = require('child_process');
8
+ const path = require('path');
9
+ const { getStateDir } = require('../platform/state-dir');
10
+
11
+ /**
12
+ * Check if git repo is clean.
13
+ * @returns {boolean}
14
+ */
15
+ function isWorkingTreeClean() {
16
+ const output = execFileSync('git', ['status', '--porcelain'], { encoding: 'utf8' }).trim();
17
+ return output.length === 0;
18
+ }
19
+
20
+ /**
21
+ * Build checkpoint commit message.
22
+ * @param {object} input
23
+ * @param {string} input.phase
24
+ * @param {string} input.id
25
+ * @param {string} [input.baselineVersion]
26
+ * @param {string} [input.deltaSummary]
27
+ * @returns {string}
28
+ */
29
+ function buildCheckpointMessage(input) {
30
+ if (!input || typeof input !== 'object') {
31
+ throw new Error('Checkpoint input must be an object');
32
+ }
33
+ const { phase, id, baselineVersion, deltaSummary } = input;
34
+
35
+ if (!phase || typeof phase !== 'string') {
36
+ throw new Error('phase is required');
37
+ }
38
+ if (!id || typeof id !== 'string') {
39
+ throw new Error('id is required');
40
+ }
41
+
42
+ const baseline = baselineVersion || 'n/a';
43
+ const delta = deltaSummary || 'n/a';
44
+ return `perf: phase ${phase} [${id}] baseline=${baseline} delta=${delta}`;
45
+ }
46
+
47
+ /**
48
+ * Get the most recent git commit message.
49
+ * @returns {string|null}
50
+ */
51
+ function getLastCommitMessage() {
52
+ try {
53
+ return execFileSync('git', ['log', '-1', '--pretty=%B'], { encoding: 'utf8' }).trim();
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Get recent git commit summaries.
61
+ * @param {number} [limit=5]
62
+ * @returns {string[]}
63
+ */
64
+ function getRecentCommits(limit = 5) {
65
+ try {
66
+ const count = Number.isFinite(limit) ? Math.max(1, Math.floor(limit)) : 5;
67
+ const output = execFileSync('git', ['log', `-${count}`, '--pretty=format:%h %s'], { encoding: 'utf8' }).trim();
68
+ if (!output) return [];
69
+ return output.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
70
+ } catch {
71
+ return [];
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Check if the next checkpoint would duplicate the last commit.
77
+ * @param {string} message
78
+ * @returns {boolean}
79
+ */
80
+ function isDuplicateCheckpoint(message) {
81
+ const last = getLastCommitMessage();
82
+ if (!last) return false;
83
+ return last.trim() === String(message || '').trim();
84
+ }
85
+
86
+ /**
87
+ * Commit a checkpoint for a perf phase.
88
+ * @param {object} input
89
+ * @returns {{ ok: boolean, message?: string, reason?: string }}
90
+ */
91
+ function commitCheckpoint(input) {
92
+ try {
93
+ execFileSync('git', ['rev-parse', '--is-inside-work-tree'], { stdio: 'ignore' });
94
+ } catch {
95
+ return { ok: false, reason: 'not a git repo' };
96
+ }
97
+
98
+ if (isWorkingTreeClean()) {
99
+ return { ok: false, reason: 'nothing to commit' };
100
+ }
101
+
102
+ const message = buildCheckpointMessage(input);
103
+ if (isDuplicateCheckpoint(message)) {
104
+ return { ok: false, reason: 'duplicate checkpoint' };
105
+ }
106
+ const perfDir = path.join(getStateDir(), 'perf');
107
+ try {
108
+ execFileSync('git', ['add', '-A', '--', perfDir], { stdio: 'ignore' });
109
+ } catch {
110
+ execFileSync('git', ['add', '-A'], { stdio: 'ignore' });
111
+ }
112
+ execFileSync('git', ['commit', '-m', message], { stdio: 'ignore' });
113
+ return { ok: true, message };
114
+ }
115
+
116
+ module.exports = {
117
+ isWorkingTreeClean,
118
+ buildCheckpointMessage,
119
+ getLastCommitMessage,
120
+ getRecentCommits,
121
+ isDuplicateCheckpoint,
122
+ commitCheckpoint
123
+ };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Code-path discovery helpers for /perf.
3
+ *
4
+ * @module lib/perf/code-paths
5
+ */
6
+
7
+ const DEFAULT_STOPWORDS = new Set([
8
+ 'the', 'and', 'for', 'with', 'from', 'that', 'this', 'these', 'those',
9
+ 'into', 'over', 'under', 'than', 'then', 'when', 'where', 'what', 'which',
10
+ 'your', 'you', 'our', 'their', 'there', 'have', 'has', 'had', 'will',
11
+ 'would', 'should', 'could', 'about', 'across', 'after', 'before', 'while',
12
+ 'perf', 'performance', 'investigation', 'baseline', 'benchmark', 'scenario'
13
+ ]);
14
+
15
+ function normalizeKeywords(text) {
16
+ if (!text || typeof text !== 'string') return [];
17
+ const tokens = text
18
+ .toLowerCase()
19
+ .split(/[^a-z0-9]+/g)
20
+ .filter(Boolean)
21
+ .filter(token => token.length > 2)
22
+ .filter(token => !DEFAULT_STOPWORDS.has(token));
23
+
24
+ return Array.from(new Set(tokens));
25
+ }
26
+
27
+ function scoreEntry(entry, keywords) {
28
+ let score = 0;
29
+ if (!entry || keywords.length === 0) return score;
30
+
31
+ const haystack = [
32
+ entry.file || '',
33
+ ...(entry.symbols || [])
34
+ ].join(' ').toLowerCase();
35
+
36
+ for (const keyword of keywords) {
37
+ if (haystack.includes(keyword)) score += 1;
38
+ }
39
+
40
+ return score;
41
+ }
42
+
43
+ function extractSymbols(fileData) {
44
+ if (!fileData || !fileData.symbols) return [];
45
+ const symbols = [];
46
+ for (const group of Object.values(fileData.symbols)) {
47
+ if (!Array.isArray(group)) continue;
48
+ for (const symbol of group) {
49
+ if (symbol && symbol.name) symbols.push(symbol.name);
50
+ }
51
+ }
52
+ return symbols;
53
+ }
54
+
55
+ function collectCodePaths(repoMap, scenario, limit = 12) {
56
+ if (!repoMap || !repoMap.files) {
57
+ return { keywords: normalizeKeywords(scenario), paths: [] };
58
+ }
59
+
60
+ const keywords = normalizeKeywords(scenario);
61
+ const candidates = [];
62
+
63
+ for (const [file, data] of Object.entries(repoMap.files)) {
64
+ const symbols = extractSymbols(data);
65
+ const entry = { file, symbols };
66
+ const score = scoreEntry(entry, keywords);
67
+ if (score <= 0) continue;
68
+ candidates.push({ ...entry, score });
69
+ }
70
+
71
+ candidates.sort((a, b) => b.score - a.score || a.file.localeCompare(b.file));
72
+
73
+ return {
74
+ keywords,
75
+ paths: candidates.slice(0, limit).map(item => ({
76
+ file: item.file,
77
+ score: item.score,
78
+ symbols: item.symbols.slice(0, 8)
79
+ }))
80
+ };
81
+ }
82
+
83
+ module.exports = {
84
+ normalizeKeywords,
85
+ collectCodePaths
86
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Baseline consolidation helper.
3
+ *
4
+ * @module lib/perf/consolidation
5
+ */
6
+
7
+ const baselineStore = require('./baseline-store');
8
+
9
+ /**
10
+ * Consolidate a baseline for a version (overwrite existing).
11
+ * @param {object} input
12
+ * @param {string} input.version
13
+ * @param {object} input.baseline
14
+ * @param {string} [basePath]
15
+ * @returns {{ version: string, path: string }}
16
+ */
17
+ function consolidateBaseline(input, basePath = process.cwd()) {
18
+ if (!input || typeof input !== 'object') {
19
+ throw new Error('consolidateBaseline requires an input object');
20
+ }
21
+ const { version, baseline } = input;
22
+
23
+ if (!version || typeof version !== 'string') {
24
+ throw new Error('version is required');
25
+ }
26
+ if (!baseline || typeof baseline !== 'object') {
27
+ throw new Error('baseline is required');
28
+ }
29
+
30
+ baselineStore.writeBaseline(version, baseline, basePath);
31
+ const path = baselineStore.getBaselinePath(version, basePath);
32
+ return { version, path };
33
+ }
34
+
35
+ module.exports = {
36
+ consolidateBaseline
37
+ };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Constraint testing runner for /perf.
3
+ *
4
+ * @module lib/perf/constraint-runner
5
+ */
6
+
7
+ const { runBenchmarkSeries, DEFAULT_MIN_DURATION } = require('./benchmark-runner');
8
+ const { compareBaselines } = require('./baseline-comparator');
9
+
10
+ /**
11
+ * Run baseline and constrained benchmarks sequentially.
12
+ * Constraints are provided via env vars to keep it cross-platform.
13
+ *
14
+ * @param {object} options
15
+ * @param {string} options.command
16
+ * @param {object} options.constraints
17
+ * @param {number} [options.duration]
18
+ * @param {number} [options.minDuration]
19
+ * @param {number} [options.runs]
20
+ * @param {string} [options.aggregate]
21
+ * @param {object} [options.env]
22
+ * @returns {{ constraints: object, baseline: object, constrained: object, delta: object }}
23
+ */
24
+ function runConstraintTest(options) {
25
+ const { command, constraints, duration, minDuration, runs, aggregate, env } = options || {};
26
+
27
+ if (!command || typeof command !== 'string') {
28
+ throw new Error('command must be a non-empty string');
29
+ }
30
+ if (!constraints || typeof constraints !== 'object' || Array.isArray(constraints)) {
31
+ throw new Error('constraints must be an object');
32
+ }
33
+
34
+ const baselineResult = runBenchmarkSeries(command, {
35
+ duration: duration ?? DEFAULT_MIN_DURATION,
36
+ minDuration,
37
+ runs,
38
+ aggregate,
39
+ env: {
40
+ ...env
41
+ }
42
+ });
43
+
44
+ const constrainedResult = runBenchmarkSeries(command, {
45
+ duration: duration ?? DEFAULT_MIN_DURATION,
46
+ minDuration,
47
+ runs,
48
+ aggregate,
49
+ env: {
50
+ ...env,
51
+ PERF_CPU_LIMIT: constraints.cpu,
52
+ PERF_MEMORY_LIMIT: constraints.memory
53
+ }
54
+ });
55
+
56
+ const delta = compareBaselines(
57
+ { metrics: baselineResult.metrics },
58
+ { metrics: constrainedResult.metrics }
59
+ );
60
+
61
+ return {
62
+ constraints,
63
+ baseline: { metrics: baselineResult.metrics },
64
+ constrained: { metrics: constrainedResult.metrics },
65
+ delta
66
+ };
67
+ }
68
+
69
+ module.exports = {
70
+ runConstraintTest
71
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Experiment runner utilities.
3
+ *
4
+ * @module lib/perf/experiment-runner
5
+ */
6
+
7
+ /**
8
+ * Run experiments sequentially (never parallel).
9
+ * @param {Array<object>} experiments
10
+ * @param {(experiment:object)=>Promise<object>} runner
11
+ * @returns {Promise<{results:Array<object>}>}
12
+ */
13
+ async function runExperiments(experiments, runner) {
14
+ if (!Array.isArray(experiments)) {
15
+ throw new Error('experiments must be an array');
16
+ }
17
+ if (typeof runner !== 'function') {
18
+ throw new Error('runner must be a function');
19
+ }
20
+
21
+ const results = [];
22
+ for (const experiment of experiments) {
23
+ const result = await runner(experiment);
24
+ results.push(result);
25
+ }
26
+
27
+ return { results };
28
+ }
29
+
30
+ module.exports = {
31
+ runExperiments
32
+ };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Performance investigation utilities
3
+ *
4
+ * @module lib/perf
5
+ */
6
+
7
+ const investigationState = require('./investigation-state');
8
+ const baselineStore = require('./baseline-store');
9
+ const baselineComparator = require('./baseline-comparator');
10
+ const benchmarkRunner = require('./benchmark-runner');
11
+ const breakingPointFinder = require('./breaking-point-finder');
12
+ const breakingPointRunner = require('./breaking-point-runner');
13
+ const experimentRunner = require('./experiment-runner');
14
+ const constraintRunner = require('./constraint-runner');
15
+ const checkpoint = require('./checkpoint');
16
+ const profilingRunner = require('./profiling-runner');
17
+ const optimizationRunner = require('./optimization-runner');
18
+ const consolidation = require('./consolidation');
19
+ const profilers = require('./profilers');
20
+ const analyzer = require('./analyzer');
21
+ const argumentParser = require('./argument-parser');
22
+ const codePaths = require('./code-paths');
23
+
24
+ module.exports = {
25
+ investigationState,
26
+ baselineStore,
27
+ baselineComparator,
28
+ benchmarkRunner,
29
+ breakingPointFinder,
30
+ breakingPointRunner,
31
+ experimentRunner,
32
+ constraintRunner,
33
+ checkpoint,
34
+ profilingRunner,
35
+ optimizationRunner,
36
+ consolidation,
37
+ profilers,
38
+ analyzer,
39
+ argumentParser,
40
+ codePaths
41
+ };