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
package/src/commands/fix-pr.ts
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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 {
|
|
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
|
|
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
|
|
46
|
-
if (fs.existsSync(
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
96
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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,
|