supipowers 1.5.2 → 2.0.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 (340) hide show
  1. package/README.md +14 -8
  2. package/bin/install.mjs +20 -5
  3. package/bin/install.ts +95 -0
  4. package/package.json +8 -4
  5. package/skills/context-mode/SKILL.md +17 -10
  6. package/skills/harness/SKILL.md +94 -0
  7. package/skills/ui-design/SKILL.md +63 -0
  8. package/skills/ui-design/sub-agent-templates/component-builder.md +29 -0
  9. package/skills/ui-design/sub-agent-templates/design-critic.md +46 -0
  10. package/skills/ui-design/sub-agent-templates/pencil/component-builder.md +29 -0
  11. package/skills/ui-design/sub-agent-templates/pencil/design-critic.md +42 -0
  12. package/skills/ui-design/sub-agent-templates/pencil/section-assembler.md +27 -0
  13. package/skills/ui-design/sub-agent-templates/section-assembler.md +27 -0
  14. package/skills/ultraplan-discover/SKILL.md +96 -0
  15. package/skills/ultraplan-intake/SKILL.md +89 -0
  16. package/skills/ultraplan-research/SKILL.md +129 -0
  17. package/skills/ultraplan-review/SKILL.md +86 -0
  18. package/skills/ultraplan-review-scope/SKILL.md +111 -0
  19. package/skills/ultraplan-review-structure/SKILL.md +120 -0
  20. package/skills/ultraplan-review-tdd/SKILL.md +142 -0
  21. package/skills/ultraplan-scout/SKILL.md +110 -0
  22. package/skills/ultraplan-synthesize/SKILL.md +124 -0
  23. package/src/{quality/ai-session.ts → ai/final-message.ts} +27 -0
  24. package/src/ai/schema-text.ts +129 -0
  25. package/src/ai/structured-output.ts +274 -0
  26. package/src/ai/template.ts +27 -0
  27. package/src/bootstrap.ts +63 -28
  28. package/src/commands/agents.ts +149 -45
  29. package/src/commands/ai-review.ts +251 -30
  30. package/src/commands/clear.ts +434 -0
  31. package/src/commands/commit.ts +1 -0
  32. package/src/commands/config.ts +242 -44
  33. package/src/commands/context.ts +55 -28
  34. package/src/commands/doctor.ts +234 -6
  35. package/src/commands/fix-pr.ts +306 -131
  36. package/src/commands/generate.ts +111 -21
  37. package/src/commands/memory.ts +192 -0
  38. package/src/commands/model-picker.ts +28 -21
  39. package/src/commands/model.ts +19 -9
  40. package/src/commands/optimize-context.ts +408 -29
  41. package/src/commands/plan.ts +2 -0
  42. package/src/commands/qa.ts +312 -137
  43. package/src/commands/release.ts +259 -76
  44. package/src/commands/review.ts +293 -59
  45. package/src/commands/status.ts +200 -13
  46. package/src/commands/supi.ts +3 -35
  47. package/src/commands/ui-design.ts +394 -0
  48. package/src/commands/ultraplan.ts +1518 -0
  49. package/src/commands/update.ts +86 -0
  50. package/src/config/defaults.ts +62 -0
  51. package/src/config/loader.ts +448 -60
  52. package/src/config/schema.ts +108 -2
  53. package/src/context/optimizer.ts +25 -33
  54. package/src/context/rule-renderer.ts +223 -0
  55. package/src/context/savings.ts +258 -0
  56. package/src/context/startup-check.ts +380 -0
  57. package/src/context/startup-optimizer.ts +355 -0
  58. package/src/context/tokenignore.ts +146 -0
  59. package/src/context-mode/cache-handle.ts +49 -0
  60. package/src/context-mode/cache-preview.ts +71 -0
  61. package/src/context-mode/cache-store.ts +738 -0
  62. package/src/context-mode/compressor.ts +131 -26
  63. package/src/context-mode/dedup.ts +108 -0
  64. package/src/context-mode/detector.ts +35 -4
  65. package/src/context-mode/event-extractor.ts +14 -12
  66. package/src/context-mode/event-store.ts +91 -36
  67. package/src/context-mode/hooks.ts +798 -56
  68. package/src/context-mode/knowledge/store.ts +255 -11
  69. package/src/context-mode/memory-store.ts +325 -0
  70. package/src/context-mode/metrics-recorder.ts +158 -0
  71. package/src/context-mode/metrics-store.ts +765 -0
  72. package/src/context-mode/model.ts +24 -0
  73. package/src/context-mode/processor-keys.ts +29 -0
  74. package/src/context-mode/processors/build.ts +66 -0
  75. package/src/context-mode/processors/docker.ts +57 -0
  76. package/src/context-mode/processors/git.ts +111 -0
  77. package/src/context-mode/processors/json.ts +112 -0
  78. package/src/context-mode/processors/k8s.ts +67 -0
  79. package/src/context-mode/processors/lint.ts +67 -0
  80. package/src/context-mode/processors/log.ts +86 -0
  81. package/src/context-mode/processors/registry.ts +116 -0
  82. package/src/context-mode/processors/test-runner.ts +102 -0
  83. package/src/context-mode/processors/types.ts +20 -0
  84. package/src/context-mode/repomap.ts +400 -0
  85. package/src/context-mode/routing.ts +97 -24
  86. package/src/context-mode/sandbox/runners.ts +5 -1
  87. package/src/context-mode/snapshot-builder.ts +106 -11
  88. package/src/context-mode/source-hash.ts +173 -0
  89. package/src/context-mode/tool-name.ts +11 -0
  90. package/src/context-mode/tools.ts +654 -22
  91. package/src/context-mode/web/fetcher.ts +31 -12
  92. package/src/debug/logger.ts +2 -1
  93. package/src/deps/registry.ts +1 -1
  94. package/src/discipline/failure-summarizer.ts +170 -0
  95. package/src/discipline/failure-taxonomy.ts +131 -0
  96. package/src/discipline/workflow-invariants.ts +125 -0
  97. package/src/discovery/index.ts +31 -0
  98. package/src/discovery/lsp.ts +87 -0
  99. package/src/discovery/rank.ts +144 -0
  100. package/src/discovery/sources.ts +89 -0
  101. package/src/discovery/workflow.ts +87 -0
  102. package/src/docs/contracts.ts +39 -0
  103. package/src/docs/drift.ts +117 -87
  104. package/src/fix-pr/assessment.ts +200 -0
  105. package/src/fix-pr/contracts.ts +47 -0
  106. package/src/fix-pr/fetch-comments.ts +80 -0
  107. package/src/fix-pr/prompt-builder.ts +58 -40
  108. package/src/fix-pr/scripts/exec.ts +34 -0
  109. package/src/fix-pr/scripts/trigger-review.ts +106 -0
  110. package/src/fix-pr/scripts/wait-and-check.ts +108 -0
  111. package/src/fix-pr/types.ts +4 -0
  112. package/src/git/branch-finish.ts +5 -0
  113. package/src/git/commit-contract.ts +83 -0
  114. package/src/git/commit.ts +121 -184
  115. package/src/git/status.ts +62 -8
  116. package/src/harness/anti_slop/architecture-parser.ts +210 -0
  117. package/src/harness/anti_slop/backend-factory.ts +30 -0
  118. package/src/harness/anti_slop/backend.ts +140 -0
  119. package/src/harness/anti_slop/desloppify-adapter.ts +319 -0
  120. package/src/harness/anti_slop/fallow-adapter.ts +305 -0
  121. package/src/harness/anti_slop/installer.ts +227 -0
  122. package/src/harness/anti_slop/queue.ts +216 -0
  123. package/src/harness/anti_slop/recommend.ts +84 -0
  124. package/src/harness/anti_slop/score.ts +180 -0
  125. package/src/harness/anti_slop/synthetic-edit-test.ts +128 -0
  126. package/src/harness/artifacts/agents-md.ts +88 -0
  127. package/src/harness/artifacts/checks-wiring.ts +57 -0
  128. package/src/harness/artifacts/docs-tree.ts +79 -0
  129. package/src/harness/artifacts/lint-configs.ts +136 -0
  130. package/src/harness/artifacts/review-agents.ts +67 -0
  131. package/src/harness/bare-entry.ts +108 -0
  132. package/src/harness/command.ts +1010 -0
  133. package/src/harness/default-agents/design.md +23 -0
  134. package/src/harness/default-agents/discover.md +18 -0
  135. package/src/harness/default-agents/implement.md +24 -0
  136. package/src/harness/default-agents/plan.md +19 -0
  137. package/src/harness/default-agents/research.md +21 -0
  138. package/src/harness/default-agents/validate.md +22 -0
  139. package/src/harness/gc/reporter.ts +28 -0
  140. package/src/harness/gc/runner.ts +136 -0
  141. package/src/harness/hooks/layer-context-inject.ts +155 -0
  142. package/src/harness/hooks/post-session-sweep.ts +130 -0
  143. package/src/harness/hooks/pre-edit-dupe-probe.ts +224 -0
  144. package/src/harness/hooks/register.ts +118 -0
  145. package/src/harness/model.ts +117 -0
  146. package/src/harness/pipeline.ts +348 -0
  147. package/src/harness/project-paths.ts +235 -0
  148. package/src/harness/stage-runner.ts +107 -0
  149. package/src/harness/stages/design.ts +386 -0
  150. package/src/harness/stages/discover.ts +454 -0
  151. package/src/harness/stages/implement.ts +162 -0
  152. package/src/harness/stages/plan.ts +335 -0
  153. package/src/harness/stages/research.ts +263 -0
  154. package/src/harness/stages/validate.ts +684 -0
  155. package/src/harness/storage.ts +467 -0
  156. package/src/harness/tools.ts +426 -0
  157. package/src/lsp/bridge.ts +56 -95
  158. package/src/lsp/capabilities.ts +108 -0
  159. package/src/lsp/contracts.ts +35 -0
  160. package/src/lsp/detector.ts +8 -12
  161. package/src/markdown-frontmatter.ts +68 -0
  162. package/src/mempalace/bridge.ts +129 -0
  163. package/src/mempalace/config.ts +75 -0
  164. package/src/mempalace/format.ts +163 -0
  165. package/src/mempalace/hooks.ts +370 -0
  166. package/src/mempalace/installer-helper.ts +194 -0
  167. package/src/mempalace/python/mempalace_bridge.py +440 -0
  168. package/src/mempalace/runtime.ts +565 -0
  169. package/src/mempalace/schema.ts +264 -0
  170. package/src/mempalace/session-summary.ts +198 -0
  171. package/src/mempalace/tool.ts +186 -0
  172. package/src/mempalace/uv.ts +256 -0
  173. package/src/migrate/runner.ts +354 -0
  174. package/src/planning/approval-flow.ts +206 -9
  175. package/src/planning/plan-writer-prompt.ts +4 -3
  176. package/src/planning/planning-ask-tool.ts +39 -0
  177. package/src/planning/render-markdown.ts +74 -0
  178. package/src/planning/spec.ts +42 -0
  179. package/src/planning/system-prompt.ts +11 -8
  180. package/src/planning/validate.ts +84 -0
  181. package/src/platform/omp.ts +15 -2
  182. package/src/platform/system-prompt.ts +37 -0
  183. package/src/platform/test-utils.ts +3 -0
  184. package/src/platform/types.ts +6 -1
  185. package/src/qa/config.ts +12 -6
  186. package/src/qa/detect-app-type.ts +13 -6
  187. package/src/qa/matrix.ts +12 -6
  188. package/src/qa/prompt-builder.ts +28 -30
  189. package/src/qa/scripts/dev-server-utils.ts +72 -0
  190. package/src/qa/scripts/run-e2e-tests.ts +226 -0
  191. package/src/qa/scripts/start-dev-server.ts +138 -0
  192. package/src/qa/scripts/stop-dev-server.ts +77 -0
  193. package/src/qa/session.ts +13 -7
  194. package/src/quality/ai-setup.ts +27 -25
  195. package/src/quality/contracts.ts +34 -0
  196. package/src/quality/gates/ai-review.ts +20 -58
  197. package/src/quality/gates/command.ts +249 -46
  198. package/src/quality/review-gates.ts +18 -2
  199. package/src/quality/runner.ts +63 -22
  200. package/src/quality/schemas.ts +37 -2
  201. package/src/quality/setup.ts +96 -16
  202. package/src/release/changelog.ts +1 -1
  203. package/src/release/channels/custom.ts +13 -3
  204. package/src/release/channels/types.ts +5 -0
  205. package/src/release/contracts.ts +90 -0
  206. package/src/release/executor.ts +122 -45
  207. package/src/release/prompt.ts +18 -2
  208. package/src/release/targets.ts +86 -0
  209. package/src/release/version.ts +96 -71
  210. package/src/review/agent-loader.ts +298 -127
  211. package/src/review/fixer.ts +10 -6
  212. package/src/review/multi-agent-runner.ts +115 -14
  213. package/src/review/output.ts +12 -139
  214. package/src/review/runner.ts +12 -6
  215. package/src/review/scope.ts +144 -24
  216. package/src/review/types.ts +11 -20
  217. package/src/review/validator.ts +12 -6
  218. package/src/storage/fix-pr-sessions.ts +21 -14
  219. package/src/storage/plans.ts +14 -5
  220. package/src/storage/qa-sessions.ts +25 -19
  221. package/src/storage/reliability-metrics.ts +180 -0
  222. package/src/storage/reports.ts +8 -7
  223. package/src/storage/review-sessions.ts +55 -20
  224. package/src/tool-catalog/active-tool-controller.ts +164 -0
  225. package/src/tool-catalog/active-tool-planner.ts +212 -0
  226. package/src/tool-catalog/tool-groups.ts +102 -0
  227. package/src/types.ts +1401 -5
  228. package/src/ui-design/backend-adapter.ts +78 -0
  229. package/src/ui-design/backends/local-html.ts +82 -0
  230. package/src/ui-design/backends/pencil-mcp.ts +111 -0
  231. package/src/ui-design/components-scanner.ts +124 -0
  232. package/src/ui-design/config.ts +55 -0
  233. package/src/ui-design/pen-scanner.ts +95 -0
  234. package/src/ui-design/pen-selector.ts +72 -0
  235. package/src/ui-design/prompt-builder.ts +73 -0
  236. package/src/ui-design/scanner.ts +136 -0
  237. package/src/ui-design/session.ts +974 -0
  238. package/src/ui-design/system-prompt.ts +312 -0
  239. package/src/ui-design/tokens-scanner.ts +181 -0
  240. package/src/ui-design/types.ts +96 -0
  241. package/src/ultraplan/agent-catalog.ts +522 -0
  242. package/src/ultraplan/authoring/agent-catalog.ts +310 -0
  243. package/src/ultraplan/authoring/authoring-tools.ts +552 -0
  244. package/src/ultraplan/authoring/command-handlers.ts +339 -0
  245. package/src/ultraplan/authoring/markdown.ts +510 -0
  246. package/src/ultraplan/authoring/model.ts +162 -0
  247. package/src/ultraplan/authoring/pipeline.ts +319 -0
  248. package/src/ultraplan/authoring/stage-runner.ts +141 -0
  249. package/src/ultraplan/authoring/stages/approve.ts +249 -0
  250. package/src/ultraplan/authoring/stages/discover.ts +289 -0
  251. package/src/ultraplan/authoring/stages/intake.ts +203 -0
  252. package/src/ultraplan/authoring/stages/research.ts +399 -0
  253. package/src/ultraplan/authoring/stages/review.ts +333 -0
  254. package/src/ultraplan/authoring/stages/scout.ts +188 -0
  255. package/src/ultraplan/authoring/stages/synthesize.ts +348 -0
  256. package/src/ultraplan/authoring/storage.ts +594 -0
  257. package/src/ultraplan/authoring/synth-gate.ts +165 -0
  258. package/src/ultraplan/authoring-draft.ts +653 -0
  259. package/src/ultraplan/authoring-persist.ts +180 -0
  260. package/src/ultraplan/authoring-tool.ts +608 -0
  261. package/src/ultraplan/authoring-wizard.ts +587 -0
  262. package/src/ultraplan/batch/merge.ts +98 -0
  263. package/src/ultraplan/batch/planner.ts +150 -0
  264. package/src/ultraplan/batch/presenter.ts +97 -0
  265. package/src/ultraplan/batch/storage.ts +420 -0
  266. package/src/ultraplan/batch/supervisor.ts +317 -0
  267. package/src/ultraplan/batch/worker.ts +26 -0
  268. package/src/ultraplan/batch/worktree.ts +110 -0
  269. package/src/ultraplan/contracts.ts +1593 -0
  270. package/src/ultraplan/default-agents/authoring/discoverer.md +12 -0
  271. package/src/ultraplan/default-agents/authoring/intake.md +12 -0
  272. package/src/ultraplan/default-agents/authoring/planner.md +12 -0
  273. package/src/ultraplan/default-agents/authoring/researcher.md +12 -0
  274. package/src/ultraplan/default-agents/authoring/scope-checker.md +12 -0
  275. package/src/ultraplan/default-agents/authoring/scout.md +12 -0
  276. package/src/ultraplan/default-agents/authoring/structure-checker.md +12 -0
  277. package/src/ultraplan/default-agents/authoring/tdd-checker.md +12 -0
  278. package/src/ultraplan/default-agents/backend-domain-reviewer.md +10 -0
  279. package/src/ultraplan/default-agents/backend-executor.md +10 -0
  280. package/src/ultraplan/default-agents/backend-stack-reviewer.md +10 -0
  281. package/src/ultraplan/default-agents/backend-tester.md +10 -0
  282. package/src/ultraplan/default-agents/frontend-domain-reviewer.md +10 -0
  283. package/src/ultraplan/default-agents/frontend-executor.md +10 -0
  284. package/src/ultraplan/default-agents/frontend-stack-reviewer.md +10 -0
  285. package/src/ultraplan/default-agents/frontend-tester.md +10 -0
  286. package/src/ultraplan/default-agents/infrastructure-domain-reviewer.md +10 -0
  287. package/src/ultraplan/default-agents/infrastructure-executor.md +10 -0
  288. package/src/ultraplan/default-agents/infrastructure-stack-reviewer.md +10 -0
  289. package/src/ultraplan/default-agents/infrastructure-tester.md +10 -0
  290. package/src/ultraplan/execution/contract.ts +71 -0
  291. package/src/ultraplan/execution/policy.ts +217 -0
  292. package/src/ultraplan/execution/runtime-tools.ts +107 -0
  293. package/src/ultraplan/execution/session-runner.ts +281 -0
  294. package/src/ultraplan/next-router.ts +85 -0
  295. package/src/ultraplan/presenter.ts +359 -0
  296. package/src/ultraplan/project-paths.ts +342 -0
  297. package/src/ultraplan/runtime/active-execution.ts +72 -0
  298. package/src/ultraplan/runtime/apply-mutation.ts +416 -0
  299. package/src/ultraplan/runtime/blockers.ts +243 -0
  300. package/src/ultraplan/runtime/hook-bridge.ts +486 -0
  301. package/src/ultraplan/runtime/launch-context.ts +207 -0
  302. package/src/ultraplan/runtime/migration.ts +524 -0
  303. package/src/ultraplan/runtime/normalize.ts +281 -0
  304. package/src/ultraplan/runtime/proof.ts +260 -0
  305. package/src/ultraplan/runtime/reducer.ts +416 -0
  306. package/src/ultraplan/runtime/repair.ts +251 -0
  307. package/src/ultraplan/runtime/tracker-storage.ts +368 -0
  308. package/src/ultraplan/session-selection.ts +291 -0
  309. package/src/ultraplan/storage.ts +374 -0
  310. package/src/utils/editor.ts +38 -0
  311. package/src/utils/executable.ts +80 -0
  312. package/src/utils/paths.ts +1 -20
  313. package/src/utils/shell.ts +31 -0
  314. package/src/visual/companion.ts +2 -1
  315. package/src/visual/scripts/frame-template.html +60 -0
  316. package/src/visual/scripts/index.js +59 -13
  317. package/src/visual/scripts/package.json +3 -0
  318. package/src/visual/start-server.ts +2 -1
  319. package/src/workspace/git-scope.ts +64 -0
  320. package/src/workspace/locks.ts +23 -0
  321. package/src/workspace/package-manager.ts +117 -0
  322. package/src/workspace/path-mapping.ts +75 -0
  323. package/src/workspace/project-slug.ts +92 -0
  324. package/src/workspace/repo-root.ts +137 -0
  325. package/src/workspace/selector.ts +115 -0
  326. package/src/workspace/state-paths.ts +118 -0
  327. package/src/workspace/targets.ts +313 -0
  328. package/src/fix-pr/scripts/diff-comments.sh +0 -33
  329. package/src/fix-pr/scripts/fetch-pr-comments.sh +0 -25
  330. package/src/fix-pr/scripts/trigger-review.sh +0 -36
  331. package/src/fix-pr/scripts/wait-and-check.sh +0 -37
  332. package/src/qa/scripts/detect-app-type.sh +0 -68
  333. package/src/qa/scripts/discover-routes.sh +0 -143
  334. package/src/qa/scripts/run-e2e-tests.sh +0 -131
  335. package/src/qa/scripts/start-dev-server.sh +0 -46
  336. package/src/qa/scripts/stop-dev-server.sh +0 -36
  337. package/src/review/prompts/fix-output-schema.md +0 -18
  338. package/src/review/prompts/review-output-schema.md +0 -38
  339. package/src/review/template.ts +0 -15
  340. /package/src/{review → ai}/prompts/invalid-output-retry.md +0 -0
@@ -1,10 +1,23 @@
1
- import type { Platform } from "../platform/types.js";
2
1
  import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
3
  import * as path from "node:path";
4
- import { moduleDir, toBashPath } from "../utils/paths.js";
4
+ import type { Platform } from "../platform/types.js";
5
+ import type { WorkspaceTarget } from "../types.js";
6
+ import { buildWorkspaceTargetOptionLabel, parseTargetArg, selectWorkspaceTarget, stripCliArg, tokenizeCliArgs } from "../workspace/selector.js";
7
+ import { resolvePackageManager } from "../workspace/package-manager.js";
8
+ import { resolveRepoRoot } from "../workspace/repo-root.js";
9
+ import { discoverWorkspaceTargets } from "../workspace/targets.js";
10
+ import { moduleDir } from "../utils/paths.js";
5
11
  import { loadFixPrConfig, saveFixPrConfig } from "../fix-pr/config.js";
6
12
  import { buildFixPrOrchestratorPrompt } from "../fix-pr/prompt-builder.js";
7
- import type { FixPrConfig, CommentReplyPolicy } from "../fix-pr/types.js";
13
+ import type { FixPrAssessmentBatch } from "../fix-pr/contracts.js";
14
+ import type { FixPrConfig, CommentReplyPolicy, PrComment } from "../fix-pr/types.js";
15
+ import {
16
+ clusterPrCommentsByTarget,
17
+ fetchPrComments,
18
+ parsePrCommentsJsonl,
19
+ stringifyPrCommentsJsonl,
20
+ } from "../fix-pr/fetch-comments.js";
8
21
  import {
9
22
  generateFixPrSessionId,
10
23
  createFixPrSession,
@@ -16,7 +29,8 @@ import { modelRegistry } from "../config/model-registry-instance.js";
16
29
  import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
17
30
  import { loadModelConfig } from "../config/model-config.js";
18
31
  import { detectBotReviewers } from "../fix-pr/bot-detector.js";
19
- import { fetchPrComments } from "../fix-pr/fetch-comments.js";
32
+ import { runFixPrAssessment, groupAssessmentsIntoBatches } from "../fix-pr/assessment.js";
33
+ import { updateFixPrSession } from "../storage/fix-pr-sessions.js";
20
34
 
21
35
  modelRegistry.register({
22
36
  id: "fix-pr",
@@ -34,7 +48,7 @@ modelRegistry.register({
34
48
  });
35
49
 
36
50
  function getScriptsDir(): string {
37
- return toBashPath(path.join(moduleDir(import.meta.url), "..", "fix-pr", "scripts"));
51
+ return path.join(moduleDir(import.meta.url), "..", "fix-pr", "scripts");
38
52
  }
39
53
 
40
54
  function findSkillPath(skillName: string): string | null {
@@ -42,49 +56,128 @@ function findSkillPath(skillName: string): string | null {
42
56
  path.join(process.cwd(), "skills", skillName, "SKILL.md"),
43
57
  path.join(moduleDir(import.meta.url), "..", "..", "skills", skillName, "SKILL.md"),
44
58
  ];
45
- for (const p of candidates) {
46
- if (fs.existsSync(p)) return p;
59
+ for (const candidate of candidates) {
60
+ if (fs.existsSync(candidate)) return candidate;
61
+ }
62
+ return null;
63
+ }
64
+
65
+
66
+ function parsePrNumberArg(args?: string): number | null {
67
+ const tokens = tokenizeCliArgs(stripCliArg(args, "--target"));
68
+
69
+ for (const token of tokens) {
70
+ const normalized = token.replace(/^#/, "");
71
+ if (/^\d+$/.test(normalized)) {
72
+ return parseInt(normalized, 10);
73
+ }
47
74
  }
75
+
48
76
  return null;
49
77
  }
50
78
 
79
+
80
+ function describeTarget(target: WorkspaceTarget): string {
81
+ return target.kind === "root"
82
+ ? `root (${target.relativeDir})`
83
+ : `${target.name} (${target.relativeDir})`;
84
+ }
85
+
86
+ function formatCommentCount(count: number): string {
87
+ return `${count} comment${count === 1 ? "" : "s"}`;
88
+ }
89
+
90
+ function formatUnscopedCommentCount(count: number): string {
91
+ return `${formatCommentCount(count)} without file path`;
92
+ }
93
+
94
+ function buildCommentTargetOptions(
95
+ targets: readonly WorkspaceTarget[],
96
+ commentsByTargetId: ReadonlyMap<string, readonly PrComment[]>,
97
+ ) {
98
+ return targets.flatMap((target) => {
99
+ const count = commentsByTargetId.get(target.id)?.length ?? 0;
100
+ if (count === 0) {
101
+ return [];
102
+ }
103
+
104
+ return [{
105
+ target,
106
+ changed: true,
107
+ label: buildWorkspaceTargetOptionLabel({ target, changed: true }, [formatCommentCount(count)]),
108
+ }];
109
+ });
110
+ }
111
+
112
+ function countUnresolvedAssessments(assessment: FixPrAssessmentBatch): number {
113
+ return assessment.assessments.filter((item: FixPrAssessmentBatch["assessments"][number]) => item.verdict !== "apply").length;
114
+ }
115
+
116
+
117
+ function buildDeferredCommentsSummary(
118
+ options: ReadonlyArray<{ target: WorkspaceTarget }>,
119
+ commentsByTargetId: ReadonlyMap<string, readonly PrComment[]>,
120
+ selectedTarget: WorkspaceTarget,
121
+ unscopedCommentCount: number,
122
+ ): string | null {
123
+ const deferred = options
124
+ .filter((option) => option.target.id !== selectedTarget.id)
125
+ .map((option) => `${describeTarget(option.target)}: ${formatCommentCount(commentsByTargetId.get(option.target.id)?.length ?? 0)}`);
126
+
127
+ if (unscopedCommentCount > 0) {
128
+ deferred.push(`unscoped review comments: ${formatUnscopedCommentCount(unscopedCommentCount)}`);
129
+ }
130
+
131
+ return deferred.length > 0 ? deferred.join("; ") : null;
132
+ }
133
+
134
+ function buildAvailableTargetDetail(
135
+ options: ReadonlyArray<{ target: WorkspaceTarget }>,
136
+ unscopedCommentCount: number,
137
+ ): string {
138
+ const availableTargets = options.length > 0
139
+ ? `Available targets: ${options.map((option) => option.target.id).join(", ")}`
140
+ : "No package or root targets have actionable comments in this PR snapshot.";
141
+
142
+ if (unscopedCommentCount === 0) {
143
+ return availableTargets;
144
+ }
145
+
146
+ return `${availableTargets} Deferred unscoped review comments: ${formatUnscopedCommentCount(unscopedCommentCount)}.`;
147
+ }
148
+
51
149
  export function registerFixPrCommand(platform: Platform): void {
52
150
  platform.registerCommand("supi:fix-pr", {
53
151
  description: "Fix PR review comments with token-optimized agent orchestration",
54
152
  async handler(args: string | undefined, ctx: any): Promise<void> {
55
- // Resolve and apply model override early — before any logic that might fail
56
153
  const modelConfig = loadModelConfig(platform.paths, ctx.cwd);
57
154
  const bridge = createModelBridge(platform);
58
155
  const resolved = resolveModelForAction("fix-pr", modelRegistry, modelConfig, bridge);
59
156
  await applyModelOverride(platform, ctx, "fix-pr", resolved);
60
157
 
61
- // ── Step 1: Detect PR ──────────────────────────────────────────
62
- let prNumber: number | null = null;
158
+ let prNumber = parsePrNumberArg(args);
63
159
  let repo: string | null = null;
160
+ const requestedTarget = parseTargetArg(args);
64
161
 
65
- // Try to parse from args
66
- const argTrimmed = args?.trim().replace("#", "") || "";
67
- if (/^\d+$/.test(argTrimmed)) {
68
- prNumber = parseInt(argTrimmed, 10);
69
- }
70
-
71
- // Detect repo
72
162
  try {
73
163
  const repoResult = await platform.exec("gh", ["repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"], { cwd: ctx.cwd });
74
164
  if (repoResult.code === 0) repo = repoResult.stdout.trim();
75
- } catch { /* ignore */ }
165
+ } catch {
166
+ // ignore
167
+ }
76
168
 
77
169
  if (!repo) {
78
170
  notifyError(ctx, "Could not detect repository", "Run from a git repo with gh CLI configured");
79
171
  return;
80
172
  }
81
173
 
82
- // Detect PR number from current branch if not provided
83
174
  if (!prNumber) {
84
175
  try {
85
176
  const prResult = await platform.exec("gh", ["pr", "view", "--json", "number", "-q", ".number"], { cwd: ctx.cwd });
86
177
  if (prResult.code === 0) prNumber = parseInt(prResult.stdout.trim(), 10);
87
- } catch { /* ignore */ }
178
+ } catch {
179
+ // ignore
180
+ }
88
181
  }
89
182
 
90
183
  if (!prNumber) {
@@ -92,12 +185,23 @@ export function registerFixPrCommand(platform: Platform): void {
92
185
  return;
93
186
  }
94
187
 
95
- // ── Step 2: Load or create config ──────────────────────────────
96
- let config = loadFixPrConfig(platform.paths, ctx.cwd);
188
+ const repoRoot = await resolveRepoRoot(platform, ctx.cwd);
189
+ if (!repoRoot) {
190
+ notifyError(ctx, "Could not detect repository root", "Run from inside a git worktree");
191
+ return;
192
+ }
97
193
 
194
+ const packageManager = resolvePackageManager(repoRoot);
195
+ const workspaceTargets = discoverWorkspaceTargets(repoRoot, packageManager.id);
196
+ if (workspaceTargets.length === 0) {
197
+ notifyError(ctx, "No workspace targets found", `Could not discover package targets from ${repoRoot}`);
198
+ return;
199
+ }
200
+
201
+ let config = loadFixPrConfig(platform.paths, ctx.cwd);
98
202
  if (!config && ctx.hasUI) {
99
203
  config = await runSetupWizard(ctx);
100
- if (!config) return; // user cancelled
204
+ if (!config) return;
101
205
  saveFixPrConfig(platform.paths, ctx.cwd, config);
102
206
  ctx.ui.notify(`Fix-PR config saved to ${platform.paths.dotDirDisplay}/supipowers/fix-pr.json`, "info");
103
207
  }
@@ -107,121 +211,196 @@ export function registerFixPrCommand(platform: Platform): void {
107
211
  return;
108
212
  }
109
213
 
110
- // ── Step 3: Session handling ───────────────────────────────────
111
- let activeSession = findActiveFixPrSession(platform.paths, ctx.cwd);
112
-
113
- if (activeSession && ctx.hasUI) {
114
- const choice = await ctx.ui.select(
115
- "Fix-PR Session",
116
- [
117
- `Resume ${activeSession.id} (iteration ${activeSession.iteration}, PR #${activeSession.prNumber})`,
118
- "Start new session",
119
- ],
120
- { helpText: "Select session · Esc to cancel" },
121
- );
122
- if (!choice) return;
123
- if (choice.startsWith("Start new")) activeSession = null;
124
- }
125
-
126
- const ledger = activeSession ?? {
127
- id: generateFixPrSessionId(),
128
- createdAt: new Date().toISOString(),
129
- updatedAt: new Date().toISOString(),
130
- prNumber,
131
- repo,
132
- status: "running" as const,
133
- iteration: 0,
134
- config,
135
- commentsProcessed: [],
136
- };
137
-
138
- if (!activeSession) {
139
- createFixPrSession(platform.paths, ctx.cwd, ledger);
140
- }
141
-
142
- // ── Step 4: Fetch initial comments ─────────────────────────────
143
- const sessionDir = toBashPath(getSessionDir(platform.paths, ctx.cwd, ledger.id));
144
- const scriptsDir = getScriptsDir();
145
- const snapshotPath = path.join(sessionDir, "snapshots", `comments-${ledger.iteration}.jsonl`);
146
-
147
- const fetchError = await fetchPrComments(platform, repo, prNumber, snapshotPath, ctx.cwd);
148
-
149
- if (fetchError) {
150
- notifyError(ctx, "Failed to fetch PR comments", fetchError);
151
- return;
152
- }
153
-
154
- // Read the snapshot
155
- let comments = "";
214
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "supi-fix-pr-"));
156
215
  try {
157
- comments = fs.readFileSync(snapshotPath, "utf-8").trim();
158
- } catch {
159
- notifyWarning(ctx, "No comments found", "PR has no review comments to process");
160
- return;
161
- }
216
+ const fetchedCommentsPath = path.join(tempDir, "comments.jsonl");
217
+ const fetchError = await fetchPrComments(platform, repo, prNumber, fetchedCommentsPath, repoRoot);
218
+ if (fetchError) {
219
+ notifyError(ctx, "Failed to fetch PR comments", fetchError);
220
+ return;
221
+ }
222
+
223
+ const fetchedComments = fs.readFileSync(fetchedCommentsPath, "utf-8").trim();
224
+ if (!fetchedComments) {
225
+ notifyInfo(ctx, "No comments to process", "PR has no review comments");
226
+ return;
227
+ }
228
+
229
+ const parsedComments = parsePrCommentsJsonl(fetchedComments);
230
+ if (parsedComments.length === 0) {
231
+ notifyWarning(ctx, "No comments found", "PR comments could not be parsed from the fetched snapshot");
232
+ return;
233
+ }
234
+
235
+ const clusteredComments = clusterPrCommentsByTarget(workspaceTargets, parsedComments);
236
+ const targetOptions = buildCommentTargetOptions(
237
+ workspaceTargets,
238
+ clusteredComments.commentsByTargetId,
239
+ );
162
240
 
163
- if (!comments) {
164
- notifyInfo(ctx, "No comments to process", "PR has no review comments");
165
- return;
166
- }
241
+ if (targetOptions.length === 0) {
242
+ const detail = clusteredComments.unscopedComments.length > 0
243
+ ? `PR comments were fetched but only ${formatUnscopedCommentCount(clusteredComments.unscopedComments.length)} could not be assigned to a workspace target`
244
+ : "PR comments were fetched but could not be assigned to a package or root target";
245
+ notifyWarning(ctx, "No actionable comments found", detail);
246
+ return;
247
+ }
248
+
249
+ if (!requestedTarget && !ctx.hasUI && targetOptions.length > 1) {
250
+ notifyError(ctx, "Multiple comment targets found", buildAvailableTargetDetail(targetOptions, clusteredComments.unscopedComments.length));
251
+ return;
252
+ }
253
+
254
+ const selectedTarget = await selectWorkspaceTarget(ctx, targetOptions, requestedTarget, {
255
+ title: "Fix-PR target",
256
+ helpText: "Select one target to process for this run",
257
+ });
258
+ if (!selectedTarget) {
259
+ if (requestedTarget) {
260
+ notifyError(ctx, "Target has no review comments", buildAvailableTargetDetail(targetOptions, clusteredComments.unscopedComments.length));
261
+ }
262
+ return;
263
+ }
264
+
265
+ const selectedComments = clusteredComments.commentsByTargetId.get(selectedTarget.id) ?? [];
266
+ if (selectedComments.length === 0) {
267
+ notifyInfo(ctx, "No comments for selected target", `${selectedTarget.id} has no actionable comments in this PR snapshot`);
268
+ return;
269
+ }
270
+
271
+ let activeSession = findActiveFixPrSession(platform.paths, selectedTarget, repo, prNumber);
272
+ if (activeSession && ctx.hasUI) {
273
+ const choice = await ctx.ui.select(
274
+ "Fix-PR Session",
275
+ [
276
+ `Resume ${activeSession.id} (iteration ${activeSession.iteration}, PR #${activeSession.prNumber})`,
277
+ "Start new session",
278
+ ],
279
+ { helpText: "Select session · Esc to cancel" },
280
+ );
281
+ if (!choice) return;
282
+ if (choice.startsWith("Start new")) activeSession = null;
283
+ }
284
+
285
+ const ledger = activeSession ?? {
286
+ id: generateFixPrSessionId(),
287
+ createdAt: new Date().toISOString(),
288
+ updatedAt: new Date().toISOString(),
289
+ prNumber,
290
+ repo,
291
+ status: "running" as const,
292
+ iteration: 0,
293
+ config,
294
+ commentsProcessed: [],
295
+ };
167
296
 
168
- const commentCount = comments.split("\n").length;
297
+ if (!activeSession) {
298
+ createFixPrSession(platform.paths, selectedTarget, ledger);
299
+ }
300
+
301
+ const sessionDir = getSessionDir(platform.paths, selectedTarget, ledger.id);
302
+ const scriptsDir = getScriptsDir();
303
+ const snapshotPath = path.join(sessionDir, "snapshots", `comments-${ledger.iteration}.jsonl`);
304
+ const selectedCommentsJsonl = stringifyPrCommentsJsonl(selectedComments);
305
+ fs.mkdirSync(path.dirname(snapshotPath), { recursive: true });
306
+ fs.writeFileSync(snapshotPath, selectedCommentsJsonl);
307
+
308
+ const detectedBots = detectBotReviewers(selectedCommentsJsonl);
309
+ if (detectedBots.length > 0) {
310
+ config = {
311
+ ...config,
312
+ reviewer: {
313
+ type: detectedBots[0].type,
314
+ triggerMethod: detectedBots[0].triggerMethod,
315
+ },
316
+ };
317
+ }
318
+
319
+ let skillContent = "";
320
+ const skillPath = findSkillPath("fix-pr");
321
+ if (skillPath) {
322
+ try {
323
+ skillContent = fs.readFileSync(skillPath, "utf-8");
324
+ } catch {
325
+ // proceed without skill content
326
+ }
327
+ }
328
+
329
+ const taskResolved = resolveModelForAction("task", modelRegistry, modelConfig, bridge);
330
+ const taskModel = taskResolved.model ?? resolved.model ?? "claude-sonnet-4-6";
331
+ const deferredCommentsSummary = buildDeferredCommentsSummary(
332
+ targetOptions,
333
+ clusteredComments.commentsByTargetId,
334
+ selectedTarget,
335
+ clusteredComments.unscopedComments.length,
336
+ );
169
337
 
170
- // Auto-detect bot reviewers from comment data
171
- const detectedBots = detectBotReviewers(comments);
172
- if (detectedBots.length > 0) {
173
- config = {
174
- ...config,
175
- reviewer: {
176
- type: detectedBots[0].type,
177
- triggerMethod: detectedBots[0].triggerMethod,
338
+ const assessmentResult = await runFixPrAssessment({
339
+ createAgentSession: platform.createAgentSession,
340
+ paths: platform.paths,
341
+ cwd: ctx.cwd,
342
+ comments: selectedComments,
343
+ repo,
344
+ prNumber,
345
+ selectedTargetLabel: describeTarget(selectedTarget),
346
+ model: resolved.model,
347
+ thinkingLevel: resolved.thinkingLevel,
348
+ });
349
+ if (assessmentResult.status === "blocked") {
350
+ notifyError(ctx, "Fix-PR assessment failed", assessmentResult.error);
351
+ return;
352
+ }
353
+ const assessment = assessmentResult.output;
354
+ const unresolvedAssessmentCount = countUnresolvedAssessments(assessment);
355
+ const workBatches = groupAssessmentsIntoBatches(assessment);
356
+ ledger.assessment = assessment;
357
+ updateFixPrSession(platform.paths, selectedTarget, ledger);
358
+
359
+ if (unresolvedAssessmentCount > 0) {
360
+ notifyWarning(
361
+ ctx,
362
+ "Unresolved comments remain",
363
+ `${formatCommentCount(unresolvedAssessmentCount)} for ${describeTarget(selectedTarget)} still need rejection or investigation handling before this run can be considered complete.`,
364
+ );
365
+ }
366
+
367
+ const prompt = buildFixPrOrchestratorPrompt({
368
+ prNumber,
369
+ repo,
370
+ comments: selectedCommentsJsonl.trim(),
371
+ sessionDir,
372
+ scriptsDir,
373
+ config,
374
+ iteration: ledger.iteration,
375
+ skillContent,
376
+ taskModel,
377
+ selectedTargetLabel: describeTarget(selectedTarget),
378
+ deferredCommentsSummary,
379
+ assessment,
380
+ workBatches,
381
+ });
382
+
383
+ platform.sendMessage(
384
+ {
385
+ customType: "supi-fix-pr",
386
+ content: [{ type: "text", text: prompt }],
387
+ display: "none",
178
388
  },
179
- };
180
- }
389
+ { deliverAs: "steer", triggerTurn: true },
390
+ );
181
391
 
182
- // ── Step 5: Load skill ─────────────────────────────────────────
183
- let skillContent = "";
184
- const skillPath = findSkillPath("fix-pr");
185
- if (skillPath) {
186
- try {
187
- skillContent = fs.readFileSync(skillPath, "utf-8");
188
- } catch { /* proceed without */ }
392
+ const detailParts = [`${formatCommentCount(selectedComments.length)} for ${describeTarget(selectedTarget)}`];
393
+ if (deferredCommentsSummary) {
394
+ detailParts.push(`deferred: ${deferredCommentsSummary}`);
395
+ }
396
+ notifyInfo(ctx, `Fix-PR started: PR #${prNumber}`, `${detailParts.join(" | ")} | session ${ledger.id}`);
397
+ } finally {
398
+ fs.rmSync(tempDir, { recursive: true, force: true });
189
399
  }
190
-
191
- // ── Step 6: Build and send prompt ──────────────────────────────
192
- // Resolve task model (sub-agents: planner, fixer). Falls back to fix-pr model.
193
- const taskResolved = resolveModelForAction("task", modelRegistry, modelConfig, bridge);
194
- const taskModel = taskResolved.model ?? resolved.model ?? "claude-sonnet-4-6";
195
- const prompt = buildFixPrOrchestratorPrompt({
196
- prNumber,
197
- repo,
198
- comments,
199
- sessionDir,
200
- scriptsDir,
201
- config,
202
- iteration: ledger.iteration,
203
- skillContent,
204
- taskModel,
205
- });
206
-
207
-
208
- platform.sendMessage(
209
- {
210
- customType: "supi-fix-pr",
211
- content: [{ type: "text", text: prompt }],
212
- display: "none",
213
- },
214
- { deliverAs: "steer", triggerTurn: true },
215
- );
216
-
217
- notifyInfo(ctx, `Fix-PR started: PR #${prNumber}`, `${commentCount} comments to assess | session ${ledger.id}`);
218
400
  },
219
401
  });
220
402
  }
221
403
 
222
- // ── Setup Wizard ───────────────────────────────────────────────────────
223
-
224
-
225
404
  const POLICY_OPTIONS = [
226
405
  "Answer all comments",
227
406
  "Only answer wrong/unnecessary ones (recommended)",
@@ -242,10 +421,7 @@ const ITERATION_OPTIONS = [
242
421
  "5",
243
422
  ];
244
423
 
245
-
246
424
  async function runSetupWizard(ctx: any): Promise<FixPrConfig | null> {
247
-
248
- // 2. Comment reply policy
249
425
  const policyChoice = await ctx.ui.select(
250
426
  "Comment reply policy",
251
427
  POLICY_OPTIONS,
@@ -257,7 +433,6 @@ async function runSetupWizard(ctx: any): Promise<FixPrConfig | null> {
257
433
  if (policyChoice.startsWith("Answer all")) commentPolicy = "answer-all";
258
434
  else if (policyChoice.startsWith("Don't")) commentPolicy = "no-answer";
259
435
 
260
- // 3. Loop timing
261
436
  const delayChoice = await ctx.ui.select(
262
437
  "Delay between review checks",
263
438
  DELAY_OPTIONS,