supipowers 1.5.3 → 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.
- package/README.md +14 -8
- package/bin/install.mjs +20 -5
- package/bin/install.ts +95 -0
- package/package.json +8 -4
- package/skills/context-mode/SKILL.md +17 -10
- package/skills/harness/SKILL.md +94 -0
- package/skills/ui-design/SKILL.md +63 -0
- package/skills/ui-design/sub-agent-templates/component-builder.md +29 -0
- package/skills/ui-design/sub-agent-templates/design-critic.md +46 -0
- package/skills/ui-design/sub-agent-templates/pencil/component-builder.md +29 -0
- package/skills/ui-design/sub-agent-templates/pencil/design-critic.md +42 -0
- package/skills/ui-design/sub-agent-templates/pencil/section-assembler.md +27 -0
- package/skills/ui-design/sub-agent-templates/section-assembler.md +27 -0
- package/skills/ultraplan-discover/SKILL.md +96 -0
- package/skills/ultraplan-intake/SKILL.md +89 -0
- package/skills/ultraplan-research/SKILL.md +129 -0
- package/skills/ultraplan-review/SKILL.md +86 -0
- package/skills/ultraplan-review-scope/SKILL.md +111 -0
- package/skills/ultraplan-review-structure/SKILL.md +120 -0
- package/skills/ultraplan-review-tdd/SKILL.md +142 -0
- package/skills/ultraplan-scout/SKILL.md +110 -0
- package/skills/ultraplan-synthesize/SKILL.md +124 -0
- package/src/{quality/ai-session.ts → ai/final-message.ts} +27 -0
- package/src/ai/schema-text.ts +129 -0
- package/src/ai/structured-output.ts +274 -0
- package/src/ai/template.ts +27 -0
- package/src/bootstrap.ts +63 -28
- package/src/commands/agents.ts +131 -42
- package/src/commands/ai-review.ts +251 -30
- package/src/commands/clear.ts +434 -0
- package/src/commands/commit.ts +1 -0
- package/src/commands/config.ts +242 -44
- package/src/commands/context.ts +55 -28
- package/src/commands/doctor.ts +234 -6
- package/src/commands/fix-pr.ts +306 -131
- package/src/commands/generate.ts +111 -21
- package/src/commands/memory.ts +192 -0
- package/src/commands/model-picker.ts +28 -21
- package/src/commands/model.ts +18 -8
- package/src/commands/optimize-context.ts +408 -29
- package/src/commands/plan.ts +2 -0
- package/src/commands/qa.ts +312 -137
- package/src/commands/release.ts +259 -76
- package/src/commands/review.ts +293 -59
- package/src/commands/status.ts +200 -13
- package/src/commands/supi.ts +3 -35
- package/src/commands/ui-design.ts +394 -0
- package/src/commands/ultraplan.ts +1518 -0
- package/src/commands/update.ts +86 -0
- package/src/config/defaults.ts +62 -0
- package/src/config/loader.ts +448 -60
- package/src/config/schema.ts +108 -2
- package/src/context/optimizer.ts +25 -33
- package/src/context/rule-renderer.ts +223 -0
- package/src/context/savings.ts +258 -0
- package/src/context/startup-check.ts +380 -0
- package/src/context/startup-optimizer.ts +355 -0
- package/src/context/tokenignore.ts +146 -0
- package/src/context-mode/cache-handle.ts +49 -0
- package/src/context-mode/cache-preview.ts +71 -0
- package/src/context-mode/cache-store.ts +738 -0
- package/src/context-mode/compressor.ts +131 -26
- package/src/context-mode/dedup.ts +108 -0
- package/src/context-mode/detector.ts +35 -4
- package/src/context-mode/event-extractor.ts +14 -12
- package/src/context-mode/event-store.ts +91 -36
- package/src/context-mode/hooks.ts +798 -56
- package/src/context-mode/knowledge/store.ts +255 -11
- package/src/context-mode/memory-store.ts +325 -0
- package/src/context-mode/metrics-recorder.ts +158 -0
- package/src/context-mode/metrics-store.ts +765 -0
- package/src/context-mode/model.ts +24 -0
- package/src/context-mode/processor-keys.ts +29 -0
- package/src/context-mode/processors/build.ts +66 -0
- package/src/context-mode/processors/docker.ts +57 -0
- package/src/context-mode/processors/git.ts +111 -0
- package/src/context-mode/processors/json.ts +112 -0
- package/src/context-mode/processors/k8s.ts +67 -0
- package/src/context-mode/processors/lint.ts +67 -0
- package/src/context-mode/processors/log.ts +86 -0
- package/src/context-mode/processors/registry.ts +116 -0
- package/src/context-mode/processors/test-runner.ts +102 -0
- package/src/context-mode/processors/types.ts +20 -0
- package/src/context-mode/repomap.ts +400 -0
- package/src/context-mode/routing.ts +97 -24
- package/src/context-mode/sandbox/runners.ts +5 -1
- package/src/context-mode/snapshot-builder.ts +106 -11
- package/src/context-mode/source-hash.ts +173 -0
- package/src/context-mode/tool-name.ts +11 -0
- package/src/context-mode/tools.ts +654 -22
- package/src/context-mode/web/fetcher.ts +31 -12
- package/src/debug/logger.ts +2 -1
- package/src/deps/registry.ts +1 -1
- package/src/discipline/failure-summarizer.ts +170 -0
- package/src/discipline/failure-taxonomy.ts +131 -0
- package/src/discipline/workflow-invariants.ts +125 -0
- package/src/discovery/index.ts +31 -0
- package/src/discovery/lsp.ts +87 -0
- package/src/discovery/rank.ts +144 -0
- package/src/discovery/sources.ts +89 -0
- package/src/discovery/workflow.ts +87 -0
- package/src/docs/contracts.ts +39 -0
- package/src/docs/drift.ts +117 -87
- package/src/fix-pr/assessment.ts +200 -0
- package/src/fix-pr/contracts.ts +47 -0
- package/src/fix-pr/fetch-comments.ts +80 -0
- package/src/fix-pr/prompt-builder.ts +58 -40
- package/src/fix-pr/scripts/exec.ts +34 -0
- package/src/fix-pr/scripts/trigger-review.ts +106 -0
- package/src/fix-pr/scripts/wait-and-check.ts +108 -0
- package/src/fix-pr/types.ts +4 -0
- package/src/git/branch-finish.ts +5 -0
- package/src/git/commit-contract.ts +83 -0
- package/src/git/commit.ts +121 -184
- package/src/git/status.ts +62 -8
- package/src/harness/anti_slop/architecture-parser.ts +210 -0
- package/src/harness/anti_slop/backend-factory.ts +30 -0
- package/src/harness/anti_slop/backend.ts +140 -0
- package/src/harness/anti_slop/desloppify-adapter.ts +319 -0
- package/src/harness/anti_slop/fallow-adapter.ts +305 -0
- package/src/harness/anti_slop/installer.ts +227 -0
- package/src/harness/anti_slop/queue.ts +216 -0
- package/src/harness/anti_slop/recommend.ts +84 -0
- package/src/harness/anti_slop/score.ts +180 -0
- package/src/harness/anti_slop/synthetic-edit-test.ts +128 -0
- package/src/harness/artifacts/agents-md.ts +88 -0
- package/src/harness/artifacts/checks-wiring.ts +57 -0
- package/src/harness/artifacts/docs-tree.ts +79 -0
- package/src/harness/artifacts/lint-configs.ts +136 -0
- package/src/harness/artifacts/review-agents.ts +67 -0
- package/src/harness/bare-entry.ts +108 -0
- package/src/harness/command.ts +1010 -0
- package/src/harness/default-agents/design.md +23 -0
- package/src/harness/default-agents/discover.md +18 -0
- package/src/harness/default-agents/implement.md +24 -0
- package/src/harness/default-agents/plan.md +19 -0
- package/src/harness/default-agents/research.md +21 -0
- package/src/harness/default-agents/validate.md +22 -0
- package/src/harness/gc/reporter.ts +28 -0
- package/src/harness/gc/runner.ts +136 -0
- package/src/harness/hooks/layer-context-inject.ts +155 -0
- package/src/harness/hooks/post-session-sweep.ts +130 -0
- package/src/harness/hooks/pre-edit-dupe-probe.ts +224 -0
- package/src/harness/hooks/register.ts +118 -0
- package/src/harness/model.ts +117 -0
- package/src/harness/pipeline.ts +348 -0
- package/src/harness/project-paths.ts +235 -0
- package/src/harness/stage-runner.ts +107 -0
- package/src/harness/stages/design.ts +386 -0
- package/src/harness/stages/discover.ts +454 -0
- package/src/harness/stages/implement.ts +162 -0
- package/src/harness/stages/plan.ts +335 -0
- package/src/harness/stages/research.ts +263 -0
- package/src/harness/stages/validate.ts +684 -0
- package/src/harness/storage.ts +467 -0
- package/src/harness/tools.ts +426 -0
- package/src/lsp/bridge.ts +56 -95
- package/src/lsp/capabilities.ts +108 -0
- package/src/lsp/contracts.ts +35 -0
- package/src/lsp/detector.ts +8 -12
- package/src/markdown-frontmatter.ts +68 -0
- package/src/mempalace/bridge.ts +129 -0
- package/src/mempalace/config.ts +75 -0
- package/src/mempalace/format.ts +163 -0
- package/src/mempalace/hooks.ts +370 -0
- package/src/mempalace/installer-helper.ts +194 -0
- package/src/mempalace/python/mempalace_bridge.py +440 -0
- package/src/mempalace/runtime.ts +565 -0
- package/src/mempalace/schema.ts +264 -0
- package/src/mempalace/session-summary.ts +198 -0
- package/src/mempalace/tool.ts +186 -0
- package/src/mempalace/uv.ts +256 -0
- package/src/migrate/runner.ts +354 -0
- package/src/planning/approval-flow.ts +206 -9
- package/src/planning/plan-writer-prompt.ts +4 -3
- package/src/planning/planning-ask-tool.ts +39 -0
- package/src/planning/render-markdown.ts +74 -0
- package/src/planning/spec.ts +42 -0
- package/src/planning/system-prompt.ts +11 -8
- package/src/planning/validate.ts +84 -0
- package/src/platform/omp.ts +15 -2
- package/src/platform/system-prompt.ts +37 -0
- package/src/platform/test-utils.ts +3 -0
- package/src/platform/types.ts +6 -1
- package/src/qa/config.ts +12 -6
- package/src/qa/detect-app-type.ts +13 -6
- package/src/qa/matrix.ts +12 -6
- package/src/qa/prompt-builder.ts +28 -30
- package/src/qa/scripts/dev-server-utils.ts +72 -0
- package/src/qa/scripts/run-e2e-tests.ts +226 -0
- package/src/qa/scripts/start-dev-server.ts +138 -0
- package/src/qa/scripts/stop-dev-server.ts +77 -0
- package/src/qa/session.ts +13 -7
- package/src/quality/ai-setup.ts +27 -25
- package/src/quality/contracts.ts +34 -0
- package/src/quality/gates/ai-review.ts +20 -58
- package/src/quality/gates/command.ts +249 -46
- package/src/quality/review-gates.ts +18 -2
- package/src/quality/runner.ts +63 -22
- package/src/quality/schemas.ts +37 -2
- package/src/quality/setup.ts +96 -16
- package/src/release/changelog.ts +1 -1
- package/src/release/channels/custom.ts +13 -3
- package/src/release/channels/types.ts +5 -0
- package/src/release/contracts.ts +90 -0
- package/src/release/executor.ts +122 -45
- package/src/release/prompt.ts +18 -2
- package/src/release/targets.ts +86 -0
- package/src/release/version.ts +96 -71
- package/src/review/agent-loader.ts +221 -109
- package/src/review/fixer.ts +10 -6
- package/src/review/multi-agent-runner.ts +114 -13
- package/src/review/output.ts +12 -139
- package/src/review/runner.ts +12 -6
- package/src/review/scope.ts +144 -24
- package/src/review/types.ts +1 -20
- package/src/review/validator.ts +12 -6
- package/src/storage/fix-pr-sessions.ts +21 -14
- package/src/storage/plans.ts +14 -5
- package/src/storage/qa-sessions.ts +25 -19
- package/src/storage/reliability-metrics.ts +180 -0
- package/src/storage/reports.ts +8 -7
- package/src/storage/review-sessions.ts +55 -20
- package/src/tool-catalog/active-tool-controller.ts +164 -0
- package/src/tool-catalog/active-tool-planner.ts +212 -0
- package/src/tool-catalog/tool-groups.ts +102 -0
- package/src/types.ts +1399 -5
- package/src/ui-design/backend-adapter.ts +78 -0
- package/src/ui-design/backends/local-html.ts +82 -0
- package/src/ui-design/backends/pencil-mcp.ts +111 -0
- package/src/ui-design/components-scanner.ts +124 -0
- package/src/ui-design/config.ts +55 -0
- package/src/ui-design/pen-scanner.ts +95 -0
- package/src/ui-design/pen-selector.ts +72 -0
- package/src/ui-design/prompt-builder.ts +73 -0
- package/src/ui-design/scanner.ts +136 -0
- package/src/ui-design/session.ts +974 -0
- package/src/ui-design/system-prompt.ts +312 -0
- package/src/ui-design/tokens-scanner.ts +181 -0
- package/src/ui-design/types.ts +96 -0
- package/src/ultraplan/agent-catalog.ts +522 -0
- package/src/ultraplan/authoring/agent-catalog.ts +310 -0
- package/src/ultraplan/authoring/authoring-tools.ts +552 -0
- package/src/ultraplan/authoring/command-handlers.ts +339 -0
- package/src/ultraplan/authoring/markdown.ts +510 -0
- package/src/ultraplan/authoring/model.ts +162 -0
- package/src/ultraplan/authoring/pipeline.ts +319 -0
- package/src/ultraplan/authoring/stage-runner.ts +141 -0
- package/src/ultraplan/authoring/stages/approve.ts +249 -0
- package/src/ultraplan/authoring/stages/discover.ts +289 -0
- package/src/ultraplan/authoring/stages/intake.ts +203 -0
- package/src/ultraplan/authoring/stages/research.ts +399 -0
- package/src/ultraplan/authoring/stages/review.ts +333 -0
- package/src/ultraplan/authoring/stages/scout.ts +188 -0
- package/src/ultraplan/authoring/stages/synthesize.ts +348 -0
- package/src/ultraplan/authoring/storage.ts +594 -0
- package/src/ultraplan/authoring/synth-gate.ts +165 -0
- package/src/ultraplan/authoring-draft.ts +653 -0
- package/src/ultraplan/authoring-persist.ts +180 -0
- package/src/ultraplan/authoring-tool.ts +608 -0
- package/src/ultraplan/authoring-wizard.ts +587 -0
- package/src/ultraplan/batch/merge.ts +98 -0
- package/src/ultraplan/batch/planner.ts +150 -0
- package/src/ultraplan/batch/presenter.ts +97 -0
- package/src/ultraplan/batch/storage.ts +420 -0
- package/src/ultraplan/batch/supervisor.ts +317 -0
- package/src/ultraplan/batch/worker.ts +26 -0
- package/src/ultraplan/batch/worktree.ts +110 -0
- package/src/ultraplan/contracts.ts +1593 -0
- package/src/ultraplan/default-agents/authoring/discoverer.md +12 -0
- package/src/ultraplan/default-agents/authoring/intake.md +12 -0
- package/src/ultraplan/default-agents/authoring/planner.md +12 -0
- package/src/ultraplan/default-agents/authoring/researcher.md +12 -0
- package/src/ultraplan/default-agents/authoring/scope-checker.md +12 -0
- package/src/ultraplan/default-agents/authoring/scout.md +12 -0
- package/src/ultraplan/default-agents/authoring/structure-checker.md +12 -0
- package/src/ultraplan/default-agents/authoring/tdd-checker.md +12 -0
- package/src/ultraplan/default-agents/backend-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/backend-executor.md +10 -0
- package/src/ultraplan/default-agents/backend-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/backend-tester.md +10 -0
- package/src/ultraplan/default-agents/frontend-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/frontend-executor.md +10 -0
- package/src/ultraplan/default-agents/frontend-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/frontend-tester.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-executor.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-tester.md +10 -0
- package/src/ultraplan/execution/contract.ts +71 -0
- package/src/ultraplan/execution/policy.ts +217 -0
- package/src/ultraplan/execution/runtime-tools.ts +107 -0
- package/src/ultraplan/execution/session-runner.ts +281 -0
- package/src/ultraplan/next-router.ts +85 -0
- package/src/ultraplan/presenter.ts +359 -0
- package/src/ultraplan/project-paths.ts +342 -0
- package/src/ultraplan/runtime/active-execution.ts +72 -0
- package/src/ultraplan/runtime/apply-mutation.ts +416 -0
- package/src/ultraplan/runtime/blockers.ts +243 -0
- package/src/ultraplan/runtime/hook-bridge.ts +486 -0
- package/src/ultraplan/runtime/launch-context.ts +207 -0
- package/src/ultraplan/runtime/migration.ts +524 -0
- package/src/ultraplan/runtime/normalize.ts +281 -0
- package/src/ultraplan/runtime/proof.ts +260 -0
- package/src/ultraplan/runtime/reducer.ts +416 -0
- package/src/ultraplan/runtime/repair.ts +251 -0
- package/src/ultraplan/runtime/tracker-storage.ts +368 -0
- package/src/ultraplan/session-selection.ts +291 -0
- package/src/ultraplan/storage.ts +374 -0
- package/src/utils/editor.ts +38 -0
- package/src/utils/executable.ts +80 -0
- package/src/utils/paths.ts +1 -20
- package/src/utils/shell.ts +31 -0
- package/src/visual/companion.ts +2 -1
- package/src/visual/scripts/frame-template.html +60 -0
- package/src/visual/scripts/index.js +59 -13
- package/src/visual/scripts/package.json +3 -0
- package/src/visual/start-server.ts +2 -1
- package/src/workspace/git-scope.ts +64 -0
- package/src/workspace/locks.ts +23 -0
- package/src/workspace/package-manager.ts +117 -0
- package/src/workspace/path-mapping.ts +75 -0
- package/src/workspace/project-slug.ts +92 -0
- package/src/workspace/repo-root.ts +137 -0
- package/src/workspace/selector.ts +115 -0
- package/src/workspace/state-paths.ts +118 -0
- package/src/workspace/targets.ts +313 -0
- package/src/fix-pr/scripts/diff-comments.sh +0 -33
- package/src/fix-pr/scripts/fetch-pr-comments.sh +0 -25
- package/src/fix-pr/scripts/trigger-review.sh +0 -36
- package/src/fix-pr/scripts/wait-and-check.sh +0 -37
- package/src/qa/scripts/detect-app-type.sh +0 -68
- package/src/qa/scripts/discover-routes.sh +0 -143
- package/src/qa/scripts/run-e2e-tests.sh +0 -131
- package/src/qa/scripts/start-dev-server.sh +0 -46
- package/src/qa/scripts/stop-dev-server.sh +0 -36
- package/src/review/prompts/fix-output-schema.md +0 -18
- package/src/review/prompts/review-output-schema.md +0 -38
- package/src/review/template.ts +0 -15
- /package/src/{review → ai}/prompts/invalid-output-retry.md +0 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// src/fix-pr/assessment.ts
|
|
2
|
+
//
|
|
3
|
+
// Runs the structured per-comment assessment for fix-pr. One schema-backed
|
|
4
|
+
// AI call per cluster; the validated FixPrAssessmentBatch is the single
|
|
5
|
+
// source of truth for downstream work batches.
|
|
6
|
+
|
|
7
|
+
import type { Platform, PlatformPaths } from "../platform/types.js";
|
|
8
|
+
import {
|
|
9
|
+
parseStructuredOutput,
|
|
10
|
+
runWithOutputValidation,
|
|
11
|
+
type StructuredOutputResult,
|
|
12
|
+
} from "../ai/structured-output.js";
|
|
13
|
+
import { renderSchemaText } from "../ai/schema-text.js";
|
|
14
|
+
import {
|
|
15
|
+
FixPrAssessmentBatchSchema,
|
|
16
|
+
type FixPrAssessmentBatch,
|
|
17
|
+
type FixPrWorkBatch,
|
|
18
|
+
} from "./contracts.js";
|
|
19
|
+
import type { PrComment } from "./types.js";
|
|
20
|
+
|
|
21
|
+
export interface RunFixPrAssessmentInput {
|
|
22
|
+
createAgentSession: Platform["createAgentSession"];
|
|
23
|
+
paths?: PlatformPaths;
|
|
24
|
+
cwd: string;
|
|
25
|
+
comments: readonly PrComment[];
|
|
26
|
+
repo: string;
|
|
27
|
+
prNumber: number;
|
|
28
|
+
selectedTargetLabel: string;
|
|
29
|
+
model?: string;
|
|
30
|
+
thinkingLevel?: string | null;
|
|
31
|
+
maxAttempts?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface BuildAssessmentPromptArgs {
|
|
35
|
+
schemaText: string;
|
|
36
|
+
comments: readonly PrComment[];
|
|
37
|
+
repo: string;
|
|
38
|
+
prNumber: number;
|
|
39
|
+
selectedTargetLabel: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function buildAssessmentPrompt(args: BuildAssessmentPromptArgs): string {
|
|
43
|
+
const commentsJsonl = args.comments.map((c) => JSON.stringify(c)).join("\n");
|
|
44
|
+
return [
|
|
45
|
+
"# Fix-PR Assessment",
|
|
46
|
+
"",
|
|
47
|
+
`You are assessing PR review comments on \`${args.repo}\` PR #${args.prNumber} for target: ${args.selectedTargetLabel}.`,
|
|
48
|
+
"",
|
|
49
|
+
"For each comment, emit one assessment object with:",
|
|
50
|
+
`- verdict: "apply" (reviewer is right, fix it), "reject" (reviewer is wrong, explain), "investigate" (needs more info before deciding)`,
|
|
51
|
+
"- rationale: 1-3 sentences of technical reasoning grounded in the actual code",
|
|
52
|
+
`- affectedFiles: files that would be edited if verdict is "apply"; empty array otherwise`,
|
|
53
|
+
"- rippleEffects: downstream impacts (callers, tests, docs); empty array if none",
|
|
54
|
+
"- verificationPlan: how to confirm the fix is correct (which tests to run, behaviour to check)",
|
|
55
|
+
"",
|
|
56
|
+
"Rules:",
|
|
57
|
+
"- Read the referenced code before assigning a verdict.",
|
|
58
|
+
"- Do not perform any code edits. This is a pure assessment pass.",
|
|
59
|
+
"- One assessment per comment. `commentId` ties back to the PR comment id.",
|
|
60
|
+
"",
|
|
61
|
+
"Comments (JSONL, one per line):",
|
|
62
|
+
"```jsonl",
|
|
63
|
+
commentsJsonl,
|
|
64
|
+
"```",
|
|
65
|
+
"",
|
|
66
|
+
"Respond with a JSON object that matches this TypeScript shape exactly:",
|
|
67
|
+
"",
|
|
68
|
+
"```ts",
|
|
69
|
+
args.schemaText,
|
|
70
|
+
"```",
|
|
71
|
+
"",
|
|
72
|
+
"Respond with only the JSON object. You may wrap it in a ```json fence.",
|
|
73
|
+
].join("\n");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Run a schema-backed assessment over a cluster of PR comments.
|
|
78
|
+
*
|
|
79
|
+
* Empty clusters short-circuit with `{ assessments: [] }` without calling
|
|
80
|
+
* the AI, so a no-op target can be persisted and grouped without cost.
|
|
81
|
+
*/
|
|
82
|
+
export async function runFixPrAssessment(
|
|
83
|
+
input: RunFixPrAssessmentInput,
|
|
84
|
+
): Promise<StructuredOutputResult<FixPrAssessmentBatch>> {
|
|
85
|
+
if (input.comments.length === 0) {
|
|
86
|
+
return {
|
|
87
|
+
status: "ok",
|
|
88
|
+
output: { assessments: [] },
|
|
89
|
+
rawOutput: "",
|
|
90
|
+
attempts: 0,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const schemaText = renderSchemaText(FixPrAssessmentBatchSchema);
|
|
95
|
+
const prompt = buildAssessmentPrompt({
|
|
96
|
+
schemaText,
|
|
97
|
+
comments: input.comments,
|
|
98
|
+
repo: input.repo,
|
|
99
|
+
prNumber: input.prNumber,
|
|
100
|
+
selectedTargetLabel: input.selectedTargetLabel,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return runWithOutputValidation<FixPrAssessmentBatch>(
|
|
104
|
+
input.createAgentSession as any,
|
|
105
|
+
{
|
|
106
|
+
cwd: input.cwd,
|
|
107
|
+
prompt,
|
|
108
|
+
schema: schemaText,
|
|
109
|
+
parse: (raw) =>
|
|
110
|
+
parseStructuredOutput<FixPrAssessmentBatch>(raw, FixPrAssessmentBatchSchema),
|
|
111
|
+
model: input.model,
|
|
112
|
+
thinkingLevel: input.thinkingLevel ?? null,
|
|
113
|
+
maxAttempts: input.maxAttempts,
|
|
114
|
+
reliability: input.paths
|
|
115
|
+
? { paths: input.paths, cwd: input.cwd, command: "fix-pr", operation: "assessment" }
|
|
116
|
+
: undefined,
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Deterministic grouping rule:
|
|
123
|
+
*
|
|
124
|
+
* 1. Only `apply` verdicts produce work batches; `reject` and `investigate`
|
|
125
|
+
* are preserved in the artifact but excluded here.
|
|
126
|
+
* 2. Two `apply` assessments go in the same batch when their `affectedFiles`
|
|
127
|
+
* sets share at least one path (connected components over the file graph).
|
|
128
|
+
* 3. An `apply` assessment with empty `affectedFiles` is its own singleton
|
|
129
|
+
* batch — nothing to merge against.
|
|
130
|
+
* 4. Within a batch, commentIds are sorted ascending; batches are ordered by
|
|
131
|
+
* their smallest commentId. Batch ids are `batch-<minCommentId>`.
|
|
132
|
+
*
|
|
133
|
+
* The rule depends only on the validated artifact, so batching is a pure
|
|
134
|
+
* function of `FixPrAssessmentBatch`.
|
|
135
|
+
*/
|
|
136
|
+
export function groupAssessmentsIntoBatches(
|
|
137
|
+
batch: FixPrAssessmentBatch,
|
|
138
|
+
): FixPrWorkBatch[] {
|
|
139
|
+
const applies = batch.assessments.filter((a) => a.verdict === "apply");
|
|
140
|
+
if (applies.length === 0) return [];
|
|
141
|
+
|
|
142
|
+
const parent = applies.map((_, i) => i);
|
|
143
|
+
const find = (i: number): number => {
|
|
144
|
+
let root = i;
|
|
145
|
+
while (parent[root] !== root) root = parent[root];
|
|
146
|
+
// path compression
|
|
147
|
+
let cur = i;
|
|
148
|
+
while (parent[cur] !== root) {
|
|
149
|
+
const next = parent[cur];
|
|
150
|
+
parent[cur] = root;
|
|
151
|
+
cur = next;
|
|
152
|
+
}
|
|
153
|
+
return root;
|
|
154
|
+
};
|
|
155
|
+
const union = (a: number, b: number) => {
|
|
156
|
+
const ra = find(a);
|
|
157
|
+
const rb = find(b);
|
|
158
|
+
if (ra !== rb) parent[ra] = rb;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const fileOwner = new Map<string, number>();
|
|
162
|
+
for (let i = 0; i < applies.length; i += 1) {
|
|
163
|
+
for (const file of applies[i].affectedFiles) {
|
|
164
|
+
const existing = fileOwner.get(file);
|
|
165
|
+
if (existing === undefined) {
|
|
166
|
+
fileOwner.set(file, i);
|
|
167
|
+
} else {
|
|
168
|
+
union(existing, i);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const groups = new Map<number, number[]>();
|
|
174
|
+
for (let i = 0; i < applies.length; i += 1) {
|
|
175
|
+
const root = find(i);
|
|
176
|
+
const list = groups.get(root);
|
|
177
|
+
if (list) list.push(i);
|
|
178
|
+
else groups.set(root, [i]);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const result: FixPrWorkBatch[] = [];
|
|
182
|
+
for (const members of groups.values()) {
|
|
183
|
+
const commentIds = members
|
|
184
|
+
.map((i) => applies[i].commentId)
|
|
185
|
+
.sort((a, b) => a - b);
|
|
186
|
+
const fileSet = new Set<string>();
|
|
187
|
+
for (const i of members) {
|
|
188
|
+
for (const f of applies[i].affectedFiles) fileSet.add(f);
|
|
189
|
+
}
|
|
190
|
+
const affectedFiles = [...fileSet].sort();
|
|
191
|
+
result.push({
|
|
192
|
+
id: `batch-${commentIds[0]}`,
|
|
193
|
+
commentIds,
|
|
194
|
+
affectedFiles,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
result.sort((a, b) => a.commentIds[0] - b.commentIds[0]);
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// src/fix-pr/contracts.ts
|
|
2
|
+
//
|
|
3
|
+
// Schema-backed contract for the per-comment assessment artifact produced
|
|
4
|
+
// before any code edits begin. Every fix-pr run must emit JSON that parses
|
|
5
|
+
// against FixPrAssessmentBatchSchema; downstream work batches are derived
|
|
6
|
+
// from this validated artifact, not from ad-hoc orchestration prose.
|
|
7
|
+
|
|
8
|
+
import { Type, type Static } from "@sinclair/typebox";
|
|
9
|
+
|
|
10
|
+
export const FIX_PR_ASSESSMENT_VERDICTS = ["apply", "reject", "investigate"] as const;
|
|
11
|
+
export type FixPrAssessmentVerdict = (typeof FIX_PR_ASSESSMENT_VERDICTS)[number];
|
|
12
|
+
|
|
13
|
+
export const FixPrCommentAssessmentSchema = Type.Object(
|
|
14
|
+
{
|
|
15
|
+
commentId: Type.Integer(),
|
|
16
|
+
verdict: Type.Union(
|
|
17
|
+
FIX_PR_ASSESSMENT_VERDICTS.map((value) => Type.Literal(value)),
|
|
18
|
+
),
|
|
19
|
+
rationale: Type.String({ minLength: 1 }),
|
|
20
|
+
affectedFiles: Type.Array(Type.String({ minLength: 1 })),
|
|
21
|
+
rippleEffects: Type.Array(Type.String({ minLength: 1 })),
|
|
22
|
+
verificationPlan: Type.String({ minLength: 1 }),
|
|
23
|
+
},
|
|
24
|
+
{ additionalProperties: false },
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
export const FixPrAssessmentBatchSchema = Type.Object(
|
|
28
|
+
{
|
|
29
|
+
assessments: Type.Array(FixPrCommentAssessmentSchema),
|
|
30
|
+
summary: Type.Optional(Type.String()),
|
|
31
|
+
},
|
|
32
|
+
{ additionalProperties: false },
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export type FixPrCommentAssessment = Static<typeof FixPrCommentAssessmentSchema>;
|
|
36
|
+
export type FixPrAssessmentBatch = Static<typeof FixPrAssessmentBatchSchema>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A deterministic execution unit derived from a validated FixPrAssessmentBatch.
|
|
40
|
+
* Only `apply` verdicts produce work batches; reject/investigate are tracked in
|
|
41
|
+
* the assessment artifact but excluded from execution.
|
|
42
|
+
*/
|
|
43
|
+
export interface FixPrWorkBatch {
|
|
44
|
+
id: string;
|
|
45
|
+
commentIds: number[];
|
|
46
|
+
affectedFiles: string[];
|
|
47
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { Platform } from "../platform/types.js";
|
|
4
|
+
import type { WorkspaceTarget } from "../types.js";
|
|
5
|
+
import { findWorkspaceTargetForPath } from "../workspace/path-mapping.js";
|
|
6
|
+
import type { PrComment } from "./types.js";
|
|
4
7
|
|
|
5
8
|
const INLINE_COMMENTS_JQ =
|
|
6
9
|
'.[] | {id, path, line: .line, body, user: .user.login, userType: .user.type, createdAt: .created_at, updatedAt: .updated_at, inReplyToId: .in_reply_to_id, diffHunk: .diff_hunk, state: "COMMENTED"}';
|
|
@@ -8,6 +11,83 @@ const INLINE_COMMENTS_JQ =
|
|
|
8
11
|
const REVIEW_COMMENTS_JQ =
|
|
9
12
|
'.[] | select(.body != null and .body != "") | {id, path: null, line: null, body, user: .user.login, userType: .user.type, createdAt: .submitted_at, updatedAt: .submitted_at, inReplyToId: null, diffHunk: null, state}';
|
|
10
13
|
|
|
14
|
+
export interface ClusteredPrComments<TTarget extends WorkspaceTarget = WorkspaceTarget> {
|
|
15
|
+
allComments: PrComment[];
|
|
16
|
+
commentsByTargetId: Map<string, PrComment[]>;
|
|
17
|
+
unscopedComments: PrComment[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function appendComment(commentsByTargetId: Map<string, PrComment[]>, targetId: string, comment: PrComment): void {
|
|
21
|
+
const existing = commentsByTargetId.get(targetId);
|
|
22
|
+
if (existing) {
|
|
23
|
+
existing.push(comment);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
commentsByTargetId.set(targetId, [comment]);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function parsePrCommentsJsonl(content: string): PrComment[] {
|
|
31
|
+
return content
|
|
32
|
+
.split(/\r?\n/)
|
|
33
|
+
.map((line) => line.trim())
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.flatMap((line) => {
|
|
36
|
+
try {
|
|
37
|
+
return [JSON.parse(line) as PrComment];
|
|
38
|
+
} catch {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function stringifyPrCommentsJsonl(comments: readonly PrComment[]): string {
|
|
45
|
+
if (comments.length === 0) {
|
|
46
|
+
return "";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return `${comments.map((comment) => JSON.stringify(comment)).join("\n")}\n`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function clusterPrCommentsByTarget<TTarget extends WorkspaceTarget>(
|
|
53
|
+
targets: readonly TTarget[],
|
|
54
|
+
comments: readonly PrComment[],
|
|
55
|
+
): ClusteredPrComments<TTarget> {
|
|
56
|
+
const commentsByTargetId = new Map<string, PrComment[]>();
|
|
57
|
+
const rootTarget = targets.find((target) => target.kind === "root") ?? null;
|
|
58
|
+
const unscopedComments: PrComment[] = [];
|
|
59
|
+
|
|
60
|
+
for (const target of targets) {
|
|
61
|
+
commentsByTargetId.set(target.id, []);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const comment of comments) {
|
|
65
|
+
const commentPath = comment.path?.trim();
|
|
66
|
+
if (!commentPath) {
|
|
67
|
+
if (targets.length === 1 && rootTarget) {
|
|
68
|
+
appendComment(commentsByTargetId, rootTarget.id, comment);
|
|
69
|
+
} else {
|
|
70
|
+
unscopedComments.push(comment);
|
|
71
|
+
}
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const owner = findWorkspaceTargetForPath([...targets], commentPath);
|
|
76
|
+
if (!owner) {
|
|
77
|
+
unscopedComments.push(comment);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
appendComment(commentsByTargetId, owner.id, comment);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
allComments: [...comments],
|
|
86
|
+
commentsByTargetId,
|
|
87
|
+
unscopedComments,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
11
91
|
/**
|
|
12
92
|
* Fetch all review comments for a PR and write them as JSONL to outputPath.
|
|
13
93
|
*
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { FixPrConfig } from "./types.js";
|
|
2
|
+
import type { FixPrAssessmentBatch, FixPrWorkBatch } from "./contracts.js";
|
|
3
|
+
import { FixPrAssessmentBatchSchema } from "./contracts.js";
|
|
4
|
+
import { renderSchemaText } from "../ai/schema-text.js";
|
|
2
5
|
import { buildReceivingReviewInstructions } from "../discipline/receiving-review.js";
|
|
3
6
|
|
|
4
7
|
export interface FixPrPromptOptions {
|
|
@@ -10,8 +13,11 @@ export interface FixPrPromptOptions {
|
|
|
10
13
|
config: FixPrConfig;
|
|
11
14
|
iteration: number;
|
|
12
15
|
skillContent: string;
|
|
13
|
-
/** Resolved model ID for sub-agent tasks (planner, fixer roles). */
|
|
14
16
|
taskModel: string;
|
|
17
|
+
selectedTargetLabel: string;
|
|
18
|
+
deferredCommentsSummary: string | null;
|
|
19
|
+
assessment: FixPrAssessmentBatch;
|
|
20
|
+
workBatches: FixPrWorkBatch[];
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
function buildReplyInstructions(config: FixPrConfig): string {
|
|
@@ -49,7 +55,21 @@ function buildReplyInstructions(config: FixPrConfig): string {
|
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
export function buildFixPrOrchestratorPrompt(options: FixPrPromptOptions): string {
|
|
52
|
-
const {
|
|
58
|
+
const {
|
|
59
|
+
prNumber,
|
|
60
|
+
repo,
|
|
61
|
+
comments,
|
|
62
|
+
sessionDir,
|
|
63
|
+
scriptsDir,
|
|
64
|
+
config,
|
|
65
|
+
iteration,
|
|
66
|
+
skillContent,
|
|
67
|
+
taskModel,
|
|
68
|
+
selectedTargetLabel,
|
|
69
|
+
deferredCommentsSummary,
|
|
70
|
+
assessment,
|
|
71
|
+
workBatches,
|
|
72
|
+
} = options;
|
|
53
73
|
const { loop, reviewer } = config;
|
|
54
74
|
const maxIter = loop.maxIterations;
|
|
55
75
|
const delay = loop.delaySeconds;
|
|
@@ -63,8 +83,20 @@ export function buildFixPrOrchestratorPrompt(options: FixPrPromptOptions): strin
|
|
|
63
83
|
"",
|
|
64
84
|
`- Session dir: \`${sessionDir}\``,
|
|
65
85
|
`- Iteration: ${iteration} of ${maxIter}`,
|
|
86
|
+
`- Selected target: ${selectedTargetLabel}`,
|
|
66
87
|
`- Comment reply policy: ${config.commentPolicy}`,
|
|
67
88
|
`- Reviewer: ${reviewer.type}${reviewer.triggerMethod ? ` (trigger: ${reviewer.triggerMethod})` : ""}`,
|
|
89
|
+
deferredCommentsSummary
|
|
90
|
+
? `- Deferred comments outside this target: ${deferredCommentsSummary}`
|
|
91
|
+
: "- Deferred comments outside this target: none",
|
|
92
|
+
"",
|
|
93
|
+
"## Review Scope Rules",
|
|
94
|
+
"",
|
|
95
|
+
"- Process only the comments listed below for the selected target.",
|
|
96
|
+
deferredCommentsSummary
|
|
97
|
+
? "- Comments outside the selected target were intentionally excluded for a separate run. Do not remediate them here."
|
|
98
|
+
: "- There are no deferred comments outside this target in this snapshot.",
|
|
99
|
+
"- After each wait-and-check cycle, keep enforcing the same target boundary. If new root or sibling-package comments appear, surface them explicitly and leave them for a separate run.",
|
|
68
100
|
"",
|
|
69
101
|
"## Review Comments to Process",
|
|
70
102
|
"",
|
|
@@ -76,7 +108,6 @@ export function buildFixPrOrchestratorPrompt(options: FixPrPromptOptions): strin
|
|
|
76
108
|
"",
|
|
77
109
|
];
|
|
78
110
|
|
|
79
|
-
// Embedded skill
|
|
80
111
|
if (skillContent) {
|
|
81
112
|
sections.push(
|
|
82
113
|
"## Assessment Methodology",
|
|
@@ -86,7 +117,6 @@ export function buildFixPrOrchestratorPrompt(options: FixPrPromptOptions): strin
|
|
|
86
117
|
);
|
|
87
118
|
}
|
|
88
119
|
|
|
89
|
-
// Receiving review discipline
|
|
90
120
|
sections.push(
|
|
91
121
|
"## Review Discipline",
|
|
92
122
|
"",
|
|
@@ -94,39 +124,34 @@ export function buildFixPrOrchestratorPrompt(options: FixPrPromptOptions): strin
|
|
|
94
124
|
"",
|
|
95
125
|
);
|
|
96
126
|
|
|
97
|
-
// Step 1: Assess
|
|
98
127
|
sections.push(
|
|
99
|
-
"## Step 1:
|
|
128
|
+
"## Step 1: Validated Assessment Artifact",
|
|
100
129
|
"",
|
|
101
|
-
"
|
|
102
|
-
"1. Read the actual code at the file and line referenced",
|
|
103
|
-
"2. Determine the verdict: **ACCEPT** / **REJECT** / **INVESTIGATE**",
|
|
104
|
-
"3. Check ripple effects — who calls this, what tests cover it",
|
|
105
|
-
"4. YAGNI check — does the reviewer's suggestion address a real problem?",
|
|
130
|
+
"The per-comment assessment for this run has already been validated against the `FixPrAssessmentBatchSchema` contract. Treat it as the source of truth: do not re-assess, do not change verdicts. Each entry has a verdict (`apply` / `reject` / `investigate`), rationale, affectedFiles, rippleEffects (downstream callers/tests/docs), and a verificationPlan.",
|
|
106
131
|
"",
|
|
107
|
-
"
|
|
132
|
+
"Schema:",
|
|
133
|
+
"```ts",
|
|
134
|
+
renderSchemaText(FixPrAssessmentBatchSchema),
|
|
108
135
|
"```",
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
|
|
113
|
-
"Group: [group-id]",
|
|
136
|
+
"",
|
|
137
|
+
"Validated artifact:",
|
|
138
|
+
"```json",
|
|
139
|
+
JSON.stringify(assessment, null, 2),
|
|
114
140
|
"```",
|
|
115
141
|
"",
|
|
116
142
|
);
|
|
117
143
|
|
|
118
|
-
// Step 2: Group
|
|
119
144
|
sections.push(
|
|
120
|
-
"## Step 2:
|
|
145
|
+
"## Step 2: Work Batches (Parallel Execution Groups)",
|
|
146
|
+
"",
|
|
147
|
+
"Batches were derived deterministically from the artifact above by grouping `apply` assessments whose `affectedFiles` overlap. Independent batches may run in parallel; a batch's commentIds share at least one file and must execute together. `reject` and `investigate` verdicts produce no batch — handle those per the reply policy below.",
|
|
121
148
|
"",
|
|
122
|
-
"
|
|
123
|
-
|
|
124
|
-
"
|
|
125
|
-
"- Cosmetic vs functional → separate groups",
|
|
149
|
+
"```json",
|
|
150
|
+
JSON.stringify(workBatches, null, 2),
|
|
151
|
+
"```",
|
|
126
152
|
"",
|
|
127
153
|
);
|
|
128
154
|
|
|
129
|
-
// Step 3: Plan
|
|
130
155
|
sections.push(
|
|
131
156
|
"## Step 3: Plan Each Group",
|
|
132
157
|
"",
|
|
@@ -138,7 +163,6 @@ export function buildFixPrOrchestratorPrompt(options: FixPrPromptOptions): strin
|
|
|
138
163
|
"",
|
|
139
164
|
);
|
|
140
165
|
|
|
141
|
-
// Step 4: Execute
|
|
142
166
|
sections.push(
|
|
143
167
|
"## Step 4: Execute Fixes",
|
|
144
168
|
"",
|
|
@@ -149,27 +173,25 @@ export function buildFixPrOrchestratorPrompt(options: FixPrPromptOptions): strin
|
|
|
149
173
|
"",
|
|
150
174
|
);
|
|
151
175
|
|
|
152
|
-
// Step 5: Reply
|
|
153
176
|
sections.push(buildReplyInstructions(config), "");
|
|
154
177
|
|
|
155
|
-
// Step 6: Push and loop
|
|
156
178
|
sections.push(
|
|
157
179
|
"## Step 6: Push and Check for New Comments",
|
|
158
180
|
"",
|
|
159
|
-
|
|
181
|
+
`1. Stage and commit: \`git add -A && git commit -m \"fix: address PR review comments (iteration ${iteration})\"\``,
|
|
160
182
|
"2. Push: `git push`",
|
|
161
183
|
);
|
|
162
184
|
|
|
163
185
|
if (reviewer.type !== "none" && reviewer.triggerMethod) {
|
|
164
186
|
sections.push(
|
|
165
|
-
`3. Trigger re-review: \`
|
|
187
|
+
`3. Trigger re-review: \`bun \"${scriptsDir}/trigger-review.ts\" \"${repo}\" ${prNumber} \"${reviewer.type}\" \"${reviewer.triggerMethod}\"\``,
|
|
166
188
|
);
|
|
167
189
|
}
|
|
168
190
|
|
|
169
191
|
sections.push(
|
|
170
|
-
`${reviewer.type !== "none" ? "4" : "3"}. Run the check
|
|
171
|
-
"```
|
|
172
|
-
`
|
|
192
|
+
`${reviewer.type !== "none" ? "4" : "3"}. Run the wait-and-check runner:`,
|
|
193
|
+
"```text",
|
|
194
|
+
`bun \"${scriptsDir}/wait-and-check.ts\" \"${sessionDir}\" ${delay} ${iteration + 1} \"${repo}\" ${prNumber}`,
|
|
173
195
|
"```",
|
|
174
196
|
`${reviewer.type !== "none" ? "5" : "4"}. Read the last line of output:`,
|
|
175
197
|
` - If \`hasNewComments: true\` and iteration < ${maxIter}: process the new comments (go back to Step 1)`,
|
|
@@ -177,22 +199,18 @@ export function buildFixPrOrchestratorPrompt(options: FixPrPromptOptions): strin
|
|
|
177
199
|
"",
|
|
178
200
|
);
|
|
179
201
|
|
|
180
|
-
// Script paths reference
|
|
181
202
|
sections.push(
|
|
182
|
-
"##
|
|
203
|
+
"## Runner Paths",
|
|
183
204
|
"",
|
|
184
|
-
`-
|
|
185
|
-
`-
|
|
186
|
-
`- trigger-review.sh: \`${scriptsDir}/trigger-review.sh\``,
|
|
187
|
-
`- wait-and-check.sh: \`${scriptsDir}/wait-and-check.sh\``,
|
|
205
|
+
`- trigger-review.ts: \`${scriptsDir}/trigger-review.ts\``,
|
|
206
|
+
`- wait-and-check.ts: \`${scriptsDir}/wait-and-check.ts\``,
|
|
188
207
|
"",
|
|
189
208
|
);
|
|
190
209
|
|
|
191
|
-
// Model guidance
|
|
192
210
|
sections.push(
|
|
193
211
|
"## Model Guidance",
|
|
194
212
|
"",
|
|
195
|
-
|
|
213
|
+
"- **Orchestrator** (this session): handles assessment & grouping",
|
|
196
214
|
`- **Planner & Fixer** (sub-agents): use model \`${taskModel}\``,
|
|
197
215
|
"",
|
|
198
216
|
"Sub-agents inherit the task model for planning and code changes.",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import type { ExecOptions, ExecResult, Platform } from "../../platform/types.js";
|
|
3
|
+
import { findExecutable } from "../../utils/executable.js";
|
|
4
|
+
|
|
5
|
+
export function runCliCommand(
|
|
6
|
+
command: string,
|
|
7
|
+
args: string[],
|
|
8
|
+
options?: ExecOptions,
|
|
9
|
+
): ExecResult {
|
|
10
|
+
const env = options?.env ? { ...process.env, ...options.env } : process.env;
|
|
11
|
+
const resolvedCommand = findExecutable(command, {
|
|
12
|
+
searchPath: env.PATH,
|
|
13
|
+
pathext: env.PATHEXT,
|
|
14
|
+
}) ?? command;
|
|
15
|
+
|
|
16
|
+
const result = spawnSync(resolvedCommand, args, {
|
|
17
|
+
cwd: options?.cwd,
|
|
18
|
+
env,
|
|
19
|
+
encoding: "utf8",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
stdout: result.stdout ?? "",
|
|
24
|
+
stderr: result.stderr || (result.error instanceof Error ? result.error.message : ""),
|
|
25
|
+
code: result.status ?? (result.error ? 1 : 0),
|
|
26
|
+
killed: result.signal != null,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function createCliPlatformExec(): Pick<Platform, "exec"> {
|
|
31
|
+
return {
|
|
32
|
+
exec: async (command, args, options) => runCliCommand(command, args, options),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { runCliCommand } from "./exec.js";
|
|
4
|
+
|
|
5
|
+
export interface TriggerReviewResult {
|
|
6
|
+
triggered: boolean;
|
|
7
|
+
reviewer: string;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function buildResult(result: TriggerReviewResult): string {
|
|
12
|
+
return JSON.stringify(result);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function postIssueComment(repo: string, prNumber: number, body: string): TriggerReviewResult {
|
|
16
|
+
const result = runCliCommand("gh", [
|
|
17
|
+
"api",
|
|
18
|
+
`repos/${repo}/issues/${prNumber}/comments`,
|
|
19
|
+
"-f",
|
|
20
|
+
`body=${body}`,
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
if (result.code !== 0) {
|
|
24
|
+
throw new Error(result.stderr || result.stdout || "gh api comment request failed");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return { triggered: true, reviewer: "comment" };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function triggerReview(
|
|
31
|
+
repo: string,
|
|
32
|
+
prNumber: number,
|
|
33
|
+
reviewer: string,
|
|
34
|
+
triggerMethod = "",
|
|
35
|
+
): { exitCode: number; output: string } {
|
|
36
|
+
try {
|
|
37
|
+
switch (reviewer) {
|
|
38
|
+
case "coderabbit": {
|
|
39
|
+
postIssueComment(repo, prNumber, triggerMethod);
|
|
40
|
+
return { exitCode: 0, output: buildResult({ triggered: true, reviewer: "coderabbit" }) };
|
|
41
|
+
}
|
|
42
|
+
case "copilot": {
|
|
43
|
+
if (triggerMethod) {
|
|
44
|
+
postIssueComment(repo, prNumber, triggerMethod);
|
|
45
|
+
} else {
|
|
46
|
+
runCliCommand("gh", [
|
|
47
|
+
"api",
|
|
48
|
+
`repos/${repo}/pulls/${prNumber}/requested_reviewers`,
|
|
49
|
+
"--method",
|
|
50
|
+
"POST",
|
|
51
|
+
"-f",
|
|
52
|
+
"reviewers[]=copilot",
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
55
|
+
return { exitCode: 0, output: buildResult({ triggered: true, reviewer: "copilot" }) };
|
|
56
|
+
}
|
|
57
|
+
case "gemini": {
|
|
58
|
+
postIssueComment(repo, prNumber, triggerMethod);
|
|
59
|
+
return { exitCode: 0, output: buildResult({ triggered: true, reviewer: "gemini" }) };
|
|
60
|
+
}
|
|
61
|
+
case "none":
|
|
62
|
+
return { exitCode: 0, output: buildResult({ triggered: false, reviewer: "none" }) };
|
|
63
|
+
default:
|
|
64
|
+
return {
|
|
65
|
+
exitCode: 1,
|
|
66
|
+
output: buildResult({
|
|
67
|
+
triggered: false,
|
|
68
|
+
reviewer,
|
|
69
|
+
error: `unknown reviewer type: ${reviewer}`,
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
return {
|
|
75
|
+
exitCode: 1,
|
|
76
|
+
output: buildResult({
|
|
77
|
+
triggered: false,
|
|
78
|
+
reviewer,
|
|
79
|
+
error: error instanceof Error ? error.message : String(error),
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function main(): void {
|
|
86
|
+
const [repo, prNumberArg, reviewer, triggerMethod = ""] = process.argv.slice(2);
|
|
87
|
+
const prNumber = Number.parseInt(prNumberArg ?? "", 10);
|
|
88
|
+
|
|
89
|
+
if (!repo || !Number.isInteger(prNumber) || !reviewer) {
|
|
90
|
+
console.log(buildResult({
|
|
91
|
+
triggered: false,
|
|
92
|
+
reviewer: reviewer ?? "unknown",
|
|
93
|
+
error: "Usage: trigger-review.ts <owner/repo> <pr_number> <reviewer_type> <trigger_method>",
|
|
94
|
+
}));
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const result = triggerReview(repo, prNumber, reviewer, triggerMethod);
|
|
99
|
+
console.log(result.output);
|
|
100
|
+
process.exit(result.exitCode);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
104
|
+
if (isMain) {
|
|
105
|
+
main();
|
|
106
|
+
}
|