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.
- 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 +149 -45
- 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 +19 -9
- 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 +298 -127
- package/src/review/fixer.ts +10 -6
- package/src/review/multi-agent-runner.ts +115 -14
- 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 +11 -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 +1401 -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,115 @@
|
|
|
1
|
+
import type { WorkspaceTarget } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export interface WorkspaceTargetOption<TTarget extends WorkspaceTarget = WorkspaceTarget> {
|
|
4
|
+
target: TTarget;
|
|
5
|
+
changed: boolean;
|
|
6
|
+
label?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function tokenizeCliArgs(args?: string): string[] {
|
|
10
|
+
if (!args) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return (args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? [])
|
|
15
|
+
.map((token) => token.replace(/^['"]|['"]$/g, ""));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function stripCliArg(args: string | undefined, flag: string): string | undefined {
|
|
19
|
+
const tokens = tokenizeCliArgs(args);
|
|
20
|
+
const retained: string[] = [];
|
|
21
|
+
|
|
22
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
23
|
+
const token = tokens[index];
|
|
24
|
+
if (token === flag) {
|
|
25
|
+
index += 1;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (token.startsWith(`${flag}=`)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
retained.push(token);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return retained.length > 0 ? retained.join(" ") : undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function parseTargetArg(args?: string): string | null {
|
|
38
|
+
const tokens = tokenizeCliArgs(args);
|
|
39
|
+
|
|
40
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
41
|
+
const token = tokens[index];
|
|
42
|
+
if (token === "--target") {
|
|
43
|
+
return tokens[index + 1] ?? null;
|
|
44
|
+
}
|
|
45
|
+
if (token.startsWith("--target=")) {
|
|
46
|
+
return token.slice("--target=".length) || null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function resolveRequestedWorkspaceTarget<TTarget extends WorkspaceTarget>(
|
|
54
|
+
targets: TTarget[],
|
|
55
|
+
requestedTarget: string | null,
|
|
56
|
+
): TTarget | null {
|
|
57
|
+
if (!requestedTarget) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return targets.find((target) => target.id === requestedTarget || target.name === requestedTarget) ?? null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function sortWorkspaceTargetOptions<TTarget extends WorkspaceTarget>(
|
|
65
|
+
options: WorkspaceTargetOption<TTarget>[],
|
|
66
|
+
): WorkspaceTargetOption<TTarget>[] {
|
|
67
|
+
return [...options].sort((left, right) => {
|
|
68
|
+
if (left.changed !== right.changed) {
|
|
69
|
+
return left.changed ? -1 : 1;
|
|
70
|
+
}
|
|
71
|
+
return left.target.name.localeCompare(right.target.name);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function buildWorkspaceTargetOptionLabel(
|
|
76
|
+
option: WorkspaceTargetOption,
|
|
77
|
+
details: string[] = [],
|
|
78
|
+
): string {
|
|
79
|
+
return [
|
|
80
|
+
option.target.name,
|
|
81
|
+
option.target.relativeDir,
|
|
82
|
+
...details.map((detail) => detail.trim()).filter(Boolean),
|
|
83
|
+
].join(" — ");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function selectWorkspaceTarget<TTarget extends WorkspaceTarget>(
|
|
87
|
+
ctx: { ui: { select(title: string, options: string[], opts?: any): Promise<string | null> } },
|
|
88
|
+
options: WorkspaceTargetOption<TTarget>[],
|
|
89
|
+
requestedTarget: string | null,
|
|
90
|
+
selection: {
|
|
91
|
+
title: string;
|
|
92
|
+
helpText?: string;
|
|
93
|
+
autoSelectSingle?: boolean;
|
|
94
|
+
},
|
|
95
|
+
): Promise<TTarget | null> {
|
|
96
|
+
if (requestedTarget) {
|
|
97
|
+
return resolveRequestedWorkspaceTarget(options.map((option) => option.target), requestedTarget);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const autoSelectSingle = selection.autoSelectSingle ?? true;
|
|
101
|
+
if (autoSelectSingle && options.length === 1) {
|
|
102
|
+
return options[0]?.target ?? null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const labels = options.map((option) => option.label ?? buildWorkspaceTargetOptionLabel(option));
|
|
106
|
+
const choice = await ctx.ui.select(selection.title, labels, {
|
|
107
|
+
helpText: selection.helpText,
|
|
108
|
+
});
|
|
109
|
+
if (!choice) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const selectedIndex = labels.indexOf(choice);
|
|
114
|
+
return selectedIndex >= 0 ? options[selectedIndex]?.target ?? null : null;
|
|
115
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import type { PlatformPaths } from "../platform/types.js";
|
|
3
|
+
import type { WorkspaceTarget } from "../types.js";
|
|
4
|
+
import { projectSlugFromRepoRoot } from "./project-slug.js";
|
|
5
|
+
import { resolveRepoIdentityRootFromFs } from "./repo-root.js";
|
|
6
|
+
import { ROOT_WORKSPACE_RELATIVE_DIR, normalizeWorkspaceRelativePath } from "./targets.js";
|
|
7
|
+
|
|
8
|
+
const WORKSPACES_DIR = "workspaces";
|
|
9
|
+
const PROJECTS_DIR = "projects";
|
|
10
|
+
|
|
11
|
+
function splitWorkspacePath(relativeDir: string): string[] {
|
|
12
|
+
const normalized = normalizeWorkspaceRelativePath(relativeDir);
|
|
13
|
+
return normalized === ROOT_WORKSPACE_RELATIVE_DIR ? [] : normalized.split("/").filter(Boolean);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
17
|
+
// Team-shared (local) state: <cwd-or-repoRoot>/.omp/supipowers/<...>
|
|
18
|
+
//
|
|
19
|
+
// These paths are committed (or at least shareable) across a team clone. Used for
|
|
20
|
+
// config.json, model.json, review-agents/config.yml, mcpc manifests, etc.
|
|
21
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
export function getRootStateDir(paths: PlatformPaths, repoRoot: string): string {
|
|
24
|
+
return paths.project(repoRoot);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getWorkspaceStateDir(
|
|
28
|
+
paths: PlatformPaths,
|
|
29
|
+
repoRoot: string,
|
|
30
|
+
workspaceRelativeDir: string,
|
|
31
|
+
): string {
|
|
32
|
+
return path.join(getRootStateDir(paths, repoRoot), WORKSPACES_DIR, ...splitWorkspacePath(workspaceRelativeDir));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getTargetStateDir(paths: PlatformPaths, target: WorkspaceTarget): string {
|
|
36
|
+
return target.kind === "root"
|
|
37
|
+
? getRootStateDir(paths, target.repoRoot)
|
|
38
|
+
: getWorkspaceStateDir(paths, target.repoRoot, target.relativeDir);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getRootConfigPath(paths: PlatformPaths, repoRoot: string): string {
|
|
42
|
+
return path.join(getRootStateDir(paths, repoRoot), "config.json");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a team-shared config/data path inside `<cwd>/.omp/supipowers/...`.
|
|
47
|
+
* Use this for files that belong to the repo: project config, model config, review-agent
|
|
48
|
+
* definitions. Do not use this for per-invocation execution state — that goes through
|
|
49
|
+
* {@link getProjectStatePath}.
|
|
50
|
+
*/
|
|
51
|
+
export function getLocalStatePath(paths: PlatformPaths, cwd: string, ...segments: string[]): string {
|
|
52
|
+
return paths.project(cwd, ...segments);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Workspace-aware variant of {@link getLocalStatePath}. Root targets resolve under the repo
|
|
57
|
+
* root; workspace targets resolve under `<repo>/.omp/supipowers/workspaces/<rel>/...`.
|
|
58
|
+
*/
|
|
59
|
+
export function getLocalTargetStatePath(
|
|
60
|
+
paths: PlatformPaths,
|
|
61
|
+
target: WorkspaceTarget,
|
|
62
|
+
...segments: string[]
|
|
63
|
+
): string {
|
|
64
|
+
return path.join(getTargetStateDir(paths, target), ...segments);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
68
|
+
// Project-scoped (global) execution state: ~/.omp/supipowers/projects/<slug>/<...>
|
|
69
|
+
//
|
|
70
|
+
// These paths are keyed by a deterministic slug derived from the repo identity root, so
|
|
71
|
+
// multiple clones of the same repo do not collide. Used for plans, reviews, reports,
|
|
72
|
+
// debug traces, telemetry, visual/ui-design sessions, etc.
|
|
73
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Resolve the project-scoped global state directory for the repo containing `cwd`.
|
|
77
|
+
*/
|
|
78
|
+
export function getProjectStateDir(paths: PlatformPaths, cwd: string): string {
|
|
79
|
+
const identityRoot = resolveRepoIdentityRootFromFs(cwd);
|
|
80
|
+
const slug = projectSlugFromRepoRoot(identityRoot);
|
|
81
|
+
return paths.global(PROJECTS_DIR, slug);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Resolve a path inside the project-scoped global state directory for `cwd`.
|
|
86
|
+
* Use this for per-invocation execution artifacts (plans, reviews, reports, debug logs, etc.).
|
|
87
|
+
*/
|
|
88
|
+
export function getProjectStatePath(
|
|
89
|
+
paths: PlatformPaths,
|
|
90
|
+
cwd: string,
|
|
91
|
+
...segments: string[]
|
|
92
|
+
): string {
|
|
93
|
+
return path.join(getProjectStateDir(paths, cwd), ...segments);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Resolve the project-scoped global state directory for a specific workspace target.
|
|
98
|
+
* Root targets resolve under `<slug>/`; workspace targets resolve under
|
|
99
|
+
* `<slug>/workspaces/<rel>/`.
|
|
100
|
+
*/
|
|
101
|
+
export function getProjectTargetStateDir(paths: PlatformPaths, target: WorkspaceTarget): string {
|
|
102
|
+
const base = getProjectStateDir(paths, target.repoRoot);
|
|
103
|
+
if (target.kind === "root") {
|
|
104
|
+
return base;
|
|
105
|
+
}
|
|
106
|
+
return path.join(base, WORKSPACES_DIR, ...splitWorkspacePath(target.relativeDir));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Workspace-aware variant of {@link getProjectStatePath}.
|
|
111
|
+
*/
|
|
112
|
+
export function getProjectTargetStatePath(
|
|
113
|
+
paths: PlatformPaths,
|
|
114
|
+
target: WorkspaceTarget,
|
|
115
|
+
...segments: string[]
|
|
116
|
+
): string {
|
|
117
|
+
return path.join(getProjectTargetStateDir(paths, target), ...segments);
|
|
118
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import type { PackageManagerId, WorkspaceTarget, WorkspaceTargetKind } from "../types.js";
|
|
5
|
+
|
|
6
|
+
interface WorkspaceManifest {
|
|
7
|
+
name?: string;
|
|
8
|
+
version?: string;
|
|
9
|
+
private?: boolean;
|
|
10
|
+
workspaces?: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface WorkspaceObjectConfig {
|
|
14
|
+
packages?: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface PnpmWorkspaceConfig {
|
|
18
|
+
packages?: unknown;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const IGNORED_WORKSPACE_DIRS = new Set([".git", "node_modules"]);
|
|
22
|
+
export const ROOT_WORKSPACE_RELATIVE_DIR = ".";
|
|
23
|
+
|
|
24
|
+
export function normalizeWorkspaceRelativePath(value: string): string {
|
|
25
|
+
const normalized = value
|
|
26
|
+
.replace(/\\/g, "/")
|
|
27
|
+
.replace(/^\.\//, "")
|
|
28
|
+
.replace(/^\//, "")
|
|
29
|
+
.replace(/\/package\.json$/, "")
|
|
30
|
+
.replace(/\/+$/, "");
|
|
31
|
+
|
|
32
|
+
return normalized.length > 0 ? normalized : ROOT_WORKSPACE_RELATIVE_DIR;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function toWorkspaceRelativeDir(repoRoot: string, dir: string): string {
|
|
36
|
+
return normalizeWorkspaceRelativePath(path.relative(repoRoot, dir));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readManifest(manifestPath: string): WorkspaceManifest | null {
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(fs.readFileSync(manifestPath, "utf-8")) as WorkspaceManifest;
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isWorkspacePackagesConfig(value: unknown): value is string[] {
|
|
48
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function readPackageJsonWorkspacePatterns(manifest: WorkspaceManifest): string[] {
|
|
52
|
+
if (isWorkspacePackagesConfig(manifest.workspaces)) {
|
|
53
|
+
return manifest.workspaces;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (manifest.workspaces && typeof manifest.workspaces === "object" && !Array.isArray(manifest.workspaces)) {
|
|
57
|
+
const packages = (manifest.workspaces as WorkspaceObjectConfig).packages;
|
|
58
|
+
if (isWorkspacePackagesConfig(packages)) {
|
|
59
|
+
return packages;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readPnpmWorkspacePatterns(repoRoot: string): string[] {
|
|
67
|
+
const workspacePath = path.join(repoRoot, "pnpm-workspace.yaml");
|
|
68
|
+
if (!fs.existsSync(workspacePath)) {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const parsed = parseYaml(fs.readFileSync(workspacePath, "utf-8")) as PnpmWorkspaceConfig | null;
|
|
74
|
+
return isWorkspacePackagesConfig(parsed?.packages) ? parsed.packages : [];
|
|
75
|
+
} catch {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function expandBracePatterns(pattern: string): string[] {
|
|
81
|
+
const start = pattern.indexOf("{");
|
|
82
|
+
if (start === -1) {
|
|
83
|
+
return [pattern];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const end = pattern.indexOf("}", start + 1);
|
|
87
|
+
if (end === -1) {
|
|
88
|
+
return [pattern];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const prefix = pattern.slice(0, start);
|
|
92
|
+
const suffix = pattern.slice(end + 1);
|
|
93
|
+
return pattern
|
|
94
|
+
.slice(start + 1, end)
|
|
95
|
+
.split(",")
|
|
96
|
+
.flatMap((option) => expandBracePatterns(`${prefix}${option}${suffix}`));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function normalizeWorkspacePattern(pattern: string): string {
|
|
100
|
+
return normalizeWorkspaceRelativePath(pattern);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function listDirectories(dir: string): string[] {
|
|
104
|
+
try {
|
|
105
|
+
return fs
|
|
106
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
107
|
+
.filter((entry) => entry.isDirectory() && !IGNORED_WORKSPACE_DIRS.has(entry.name))
|
|
108
|
+
.map((entry) => entry.name)
|
|
109
|
+
.sort();
|
|
110
|
+
} catch {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function hasMagic(segment: string): boolean {
|
|
116
|
+
return segment.includes("*") || segment.includes("?");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function escapeRegExp(value: string): string {
|
|
120
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function matchesSegment(name: string, pattern: string): boolean {
|
|
124
|
+
const regex = new RegExp(
|
|
125
|
+
`^${escapeRegExp(pattern)
|
|
126
|
+
.replace(/\\\*/g, "[^/]*")
|
|
127
|
+
.replace(/\\\?/g, "[^/]")}$`,
|
|
128
|
+
);
|
|
129
|
+
return regex.test(name);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function expandWorkspacePattern(repoRoot: string, pattern: string): string[] {
|
|
133
|
+
const normalizedPattern = normalizeWorkspacePattern(pattern);
|
|
134
|
+
if (normalizedPattern === ROOT_WORKSPACE_RELATIVE_DIR) {
|
|
135
|
+
return fs.existsSync(path.join(repoRoot, "package.json")) ? [repoRoot] : [];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const segments = normalizedPattern.split("/").filter(Boolean);
|
|
139
|
+
const matches = new Set<string>();
|
|
140
|
+
|
|
141
|
+
const visit = (currentDir: string, index: number): void => {
|
|
142
|
+
if (index >= segments.length) {
|
|
143
|
+
if (fs.existsSync(path.join(currentDir, "package.json"))) {
|
|
144
|
+
matches.add(currentDir);
|
|
145
|
+
}
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const segment = segments[index];
|
|
150
|
+
if (segment === "**") {
|
|
151
|
+
visit(currentDir, index + 1);
|
|
152
|
+
for (const child of listDirectories(currentDir)) {
|
|
153
|
+
visit(path.join(currentDir, child), index);
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!hasMagic(segment)) {
|
|
159
|
+
const nextDir = path.join(currentDir, segment);
|
|
160
|
+
if (fs.existsSync(nextDir) && fs.statSync(nextDir).isDirectory()) {
|
|
161
|
+
visit(nextDir, index + 1);
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
for (const child of listDirectories(currentDir)) {
|
|
167
|
+
if (matchesSegment(child, segment)) {
|
|
168
|
+
visit(path.join(currentDir, child), index + 1);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
visit(repoRoot, 0);
|
|
174
|
+
return [...matches].sort();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function globPathToRegExp(pattern: string): RegExp {
|
|
178
|
+
const normalizedPattern = normalizeWorkspacePattern(pattern);
|
|
179
|
+
if (normalizedPattern === ROOT_WORKSPACE_RELATIVE_DIR) {
|
|
180
|
+
return /^\.$/;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let regex = "^";
|
|
184
|
+
for (let index = 0; index < normalizedPattern.length; index += 1) {
|
|
185
|
+
const char = normalizedPattern[index];
|
|
186
|
+
const nextChar = normalizedPattern[index + 1];
|
|
187
|
+
if (char === "*" && nextChar === "*") {
|
|
188
|
+
regex += ".*";
|
|
189
|
+
index += 1;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (char === "*") {
|
|
193
|
+
regex += "[^/]*";
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (char === "?") {
|
|
197
|
+
regex += "[^/]";
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
regex += escapeRegExp(char);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
regex += "$";
|
|
204
|
+
return new RegExp(regex);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function collectWorkspaceDirectories(repoRoot: string, patterns: string[]): string[] {
|
|
208
|
+
const expandedPatterns = patterns.flatMap((pattern) => expandBracePatterns(pattern));
|
|
209
|
+
const includePatterns = expandedPatterns.filter((pattern) => !pattern.startsWith("!"));
|
|
210
|
+
const excludeMatchers = expandedPatterns
|
|
211
|
+
.filter((pattern) => pattern.startsWith("!"))
|
|
212
|
+
.map((pattern) => globPathToRegExp(pattern.slice(1)));
|
|
213
|
+
|
|
214
|
+
const directories = new Set<string>();
|
|
215
|
+
for (const pattern of includePatterns) {
|
|
216
|
+
for (const dir of expandWorkspacePattern(repoRoot, pattern)) {
|
|
217
|
+
const relativeDir = toWorkspaceRelativeDir(repoRoot, dir);
|
|
218
|
+
if (excludeMatchers.some((matcher) => matcher.test(relativeDir))) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
directories.add(dir);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return [...directories].sort((left, right) => {
|
|
226
|
+
const leftRelative = toWorkspaceRelativeDir(repoRoot, left);
|
|
227
|
+
const rightRelative = toWorkspaceRelativeDir(repoRoot, right);
|
|
228
|
+
return leftRelative.localeCompare(rightRelative);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function isWorkspaceTargetManifest(manifest: WorkspaceManifest | null): manifest is WorkspaceManifest & { name: string; version?: string } {
|
|
233
|
+
return typeof manifest?.name === "string" && manifest.name.trim().length > 0;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function buildWorkspaceTarget(
|
|
237
|
+
repoRoot: string,
|
|
238
|
+
packageDir: string,
|
|
239
|
+
manifest: WorkspaceManifest & { name: string; version?: string },
|
|
240
|
+
packageManager: PackageManagerId,
|
|
241
|
+
kind: WorkspaceTargetKind,
|
|
242
|
+
): WorkspaceTarget {
|
|
243
|
+
return {
|
|
244
|
+
id: manifest.name,
|
|
245
|
+
name: manifest.name,
|
|
246
|
+
kind,
|
|
247
|
+
repoRoot,
|
|
248
|
+
packageDir,
|
|
249
|
+
manifestPath: path.join(packageDir, "package.json"),
|
|
250
|
+
relativeDir: toWorkspaceRelativeDir(repoRoot, packageDir),
|
|
251
|
+
version: typeof manifest.version === "string" && manifest.version.trim().length > 0
|
|
252
|
+
? manifest.version
|
|
253
|
+
: "0.0.0",
|
|
254
|
+
private: manifest.private === true,
|
|
255
|
+
packageManager,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function discoverWorkspaceTargets(repoRoot: string, packageManager: PackageManagerId): WorkspaceTarget[] {
|
|
260
|
+
const rootManifestPath = path.join(repoRoot, "package.json");
|
|
261
|
+
const rootManifest = readManifest(rootManifestPath);
|
|
262
|
+
const targetByManifestPath = new Map<string, WorkspaceTarget>();
|
|
263
|
+
|
|
264
|
+
if (isWorkspaceTargetManifest(rootManifest)) {
|
|
265
|
+
targetByManifestPath.set(
|
|
266
|
+
rootManifestPath,
|
|
267
|
+
buildWorkspaceTarget(repoRoot, repoRoot, rootManifest, packageManager, "root"),
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const workspacePatterns = [
|
|
272
|
+
...readPackageJsonWorkspacePatterns(rootManifest ?? {}),
|
|
273
|
+
...readPnpmWorkspacePatterns(repoRoot),
|
|
274
|
+
];
|
|
275
|
+
|
|
276
|
+
for (const workspaceDir of collectWorkspaceDirectories(repoRoot, workspacePatterns)) {
|
|
277
|
+
const manifestPath = path.join(workspaceDir, "package.json");
|
|
278
|
+
const manifest = readManifest(manifestPath);
|
|
279
|
+
if (!isWorkspaceTargetManifest(manifest)) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
targetByManifestPath.set(
|
|
284
|
+
manifestPath,
|
|
285
|
+
buildWorkspaceTarget(
|
|
286
|
+
repoRoot,
|
|
287
|
+
workspaceDir,
|
|
288
|
+
manifest,
|
|
289
|
+
packageManager,
|
|
290
|
+
workspaceDir === repoRoot ? "root" : "workspace",
|
|
291
|
+
),
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Synthesize a root target when the repo is a monorepo (workspacePatterns exist) but the
|
|
296
|
+
// root package.json has no name field (pure workspace aggregators often omit it). Without
|
|
297
|
+
// a root target, root-level files like bun.lock, .gitignore, and config files have no
|
|
298
|
+
// mapping and become unaddressable by workspace-aware commands.
|
|
299
|
+
if (!targetByManifestPath.has(rootManifestPath) && workspacePatterns.length > 0) {
|
|
300
|
+
const fallbackName = path.basename(repoRoot) || "root";
|
|
301
|
+
targetByManifestPath.set(
|
|
302
|
+
rootManifestPath,
|
|
303
|
+
buildWorkspaceTarget(repoRoot, repoRoot, { name: fallbackName }, packageManager, "root"),
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return [...targetByManifestPath.values()].sort((left, right) => {
|
|
308
|
+
if (left.kind !== right.kind) {
|
|
309
|
+
return left.kind === "root" ? -1 : 1;
|
|
310
|
+
}
|
|
311
|
+
return left.relativeDir.localeCompare(right.relativeDir);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Compares two JSONL comment snapshots, outputs only new/changed comments
|
|
3
|
-
# Usage: diff-comments.sh <prev_snapshot> <new_snapshot>
|
|
4
|
-
# Exit 0 if new comments found, exit 1 if identical
|
|
5
|
-
set -euo pipefail
|
|
6
|
-
|
|
7
|
-
PREV="$1"
|
|
8
|
-
NEW="$2"
|
|
9
|
-
|
|
10
|
-
# If no previous snapshot, all comments are new
|
|
11
|
-
if [[ ! -f "$PREV" ]]; then
|
|
12
|
-
cat "$NEW"
|
|
13
|
-
exit 0
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
# Build fingerprint: id + updatedAt for each comment
|
|
17
|
-
prev_fingerprints=$(jq -r '[.id, .updatedAt] | @tsv' "$PREV" 2>/dev/null | sort)
|
|
18
|
-
new_fingerprints=$(jq -r '[.id, .updatedAt] | @tsv' "$NEW" 2>/dev/null | sort)
|
|
19
|
-
|
|
20
|
-
# Find IDs that are new or changed
|
|
21
|
-
new_ids=$(comm -13 <(echo "$prev_fingerprints") <(echo "$new_fingerprints") | cut -f1)
|
|
22
|
-
|
|
23
|
-
if [[ -z "$new_ids" ]]; then
|
|
24
|
-
exit 1
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
# Output the full comment objects for new/changed IDs
|
|
28
|
-
while IFS= read -r id; do
|
|
29
|
-
[[ -z "$id" ]] && continue
|
|
30
|
-
jq -c "select(.id == $id)" "$NEW"
|
|
31
|
-
done <<< "$new_ids"
|
|
32
|
-
|
|
33
|
-
exit 0
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Fetches all review comments for a PR, outputs JSONL
|
|
3
|
-
# Usage: fetch-pr-comments.sh <owner/repo> <pr_number> <output_file>
|
|
4
|
-
set -euo pipefail
|
|
5
|
-
|
|
6
|
-
REPO="$1"
|
|
7
|
-
PR="$2"
|
|
8
|
-
OUTPUT="$3"
|
|
9
|
-
|
|
10
|
-
# Ensure output directory exists
|
|
11
|
-
mkdir -p "$(dirname "$OUTPUT")"
|
|
12
|
-
|
|
13
|
-
# Fetch inline review comments (code-level)
|
|
14
|
-
gh api --paginate "repos/${REPO}/pulls/${PR}/comments" \
|
|
15
|
-
--jq '.[] | {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"}' \
|
|
16
|
-
> "$OUTPUT" 2>/dev/null || true
|
|
17
|
-
|
|
18
|
-
# Fetch review-level comments (top-level reviews with body text)
|
|
19
|
-
gh api --paginate "repos/${REPO}/pulls/${PR}/reviews" \
|
|
20
|
-
--jq '.[] | 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}' \
|
|
21
|
-
>> "$OUTPUT" 2>/dev/null || true
|
|
22
|
-
|
|
23
|
-
# Output summary to stderr for caller
|
|
24
|
-
TOTAL=$(wc -l < "$OUTPUT" | tr -d ' ')
|
|
25
|
-
echo "{\"total\": ${TOTAL}}" >&2
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Triggers automated reviewer to re-review a PR
|
|
3
|
-
# Usage: trigger-review.sh <owner/repo> <pr_number> <reviewer_type> <trigger_method>
|
|
4
|
-
set -euo pipefail
|
|
5
|
-
|
|
6
|
-
REPO="$1"
|
|
7
|
-
PR="$2"
|
|
8
|
-
REVIEWER="$3"
|
|
9
|
-
METHOD="${4:-}"
|
|
10
|
-
|
|
11
|
-
case "$REVIEWER" in
|
|
12
|
-
coderabbit)
|
|
13
|
-
gh api "repos/${REPO}/issues/${PR}/comments" -f body="$METHOD" >/dev/null 2>&1
|
|
14
|
-
echo '{"triggered": true, "reviewer": "coderabbit"}'
|
|
15
|
-
;;
|
|
16
|
-
copilot)
|
|
17
|
-
if [[ -n "$METHOD" ]]; then
|
|
18
|
-
gh api "repos/${REPO}/issues/${PR}/comments" -f body="$METHOD" >/dev/null 2>&1
|
|
19
|
-
else
|
|
20
|
-
gh api "repos/${REPO}/pulls/${PR}/requested_reviewers" \
|
|
21
|
-
--method POST -f "reviewers[]=copilot" >/dev/null 2>&1 || true
|
|
22
|
-
fi
|
|
23
|
-
echo '{"triggered": true, "reviewer": "copilot"}'
|
|
24
|
-
;;
|
|
25
|
-
gemini)
|
|
26
|
-
gh api "repos/${REPO}/issues/${PR}/comments" -f body="$METHOD" >/dev/null 2>&1
|
|
27
|
-
echo '{"triggered": true, "reviewer": "gemini"}'
|
|
28
|
-
;;
|
|
29
|
-
none)
|
|
30
|
-
echo '{"triggered": false, "reviewer": "none"}'
|
|
31
|
-
;;
|
|
32
|
-
*)
|
|
33
|
-
echo '{"triggered": false, "error": "unknown reviewer type: '"$REVIEWER"'"}'
|
|
34
|
-
exit 1
|
|
35
|
-
;;
|
|
36
|
-
esac
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Waits for delay, fetches new PR comments, diffs against previous snapshot
|
|
3
|
-
# Usage: wait-and-check.sh <session_dir> <delay_seconds> <iteration> <owner/repo> <pr_number>
|
|
4
|
-
# Output: new comment lines + JSON summary on last line
|
|
5
|
-
set -euo pipefail
|
|
6
|
-
|
|
7
|
-
SESSION_DIR="$1"
|
|
8
|
-
DELAY="$2"
|
|
9
|
-
ITERATION="$3"
|
|
10
|
-
REPO="$4"
|
|
11
|
-
PR="$5"
|
|
12
|
-
|
|
13
|
-
SNAPSHOTS_DIR="${SESSION_DIR}/snapshots"
|
|
14
|
-
PREV_ITERATION=$((ITERATION - 1))
|
|
15
|
-
PREV_SNAPSHOT="${SNAPSHOTS_DIR}/comments-${PREV_ITERATION}.jsonl"
|
|
16
|
-
NEW_SNAPSHOT="${SNAPSHOTS_DIR}/comments-${ITERATION}.jsonl"
|
|
17
|
-
|
|
18
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
19
|
-
|
|
20
|
-
# Wait for reviewer to process
|
|
21
|
-
echo "Waiting ${DELAY}s for reviewer to process changes..." >&2
|
|
22
|
-
sleep "$DELAY"
|
|
23
|
-
|
|
24
|
-
# Fetch new comments
|
|
25
|
-
echo "Fetching PR comments (iteration ${ITERATION})..." >&2
|
|
26
|
-
bash "${SCRIPT_DIR}/fetch-pr-comments.sh" "$REPO" "$PR" "$NEW_SNAPSHOT"
|
|
27
|
-
|
|
28
|
-
# Diff against previous
|
|
29
|
-
DIFF_OUTPUT=$(bash "${SCRIPT_DIR}/diff-comments.sh" "$PREV_SNAPSHOT" "$NEW_SNAPSHOT" 2>/dev/null) || true
|
|
30
|
-
|
|
31
|
-
if [[ -n "$DIFF_OUTPUT" ]]; then
|
|
32
|
-
DIFF_COUNT=$(echo "$DIFF_OUTPUT" | wc -l | tr -d ' ')
|
|
33
|
-
echo "$DIFF_OUTPUT"
|
|
34
|
-
echo "{\"hasNewComments\": true, \"count\": ${DIFF_COUNT}, \"iteration\": ${ITERATION}}"
|
|
35
|
-
else
|
|
36
|
-
echo "{\"hasNewComments\": false, \"count\": 0, \"iteration\": ${ITERATION}}"
|
|
37
|
-
fi
|