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
package/src/commands/release.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import type { Platform } from "../platform/types.js";
|
|
2
3
|
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
3
4
|
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
4
5
|
import { loadModelConfig } from "../config/model-config.js";
|
|
5
|
-
import type { ReleaseChannel, BumpType, ReviewReport, ResolvedModel } from "../types.js";
|
|
6
|
+
import type { ReleaseChannel, ReleaseTarget, BumpType, ReviewReport, ResolvedModel } from "../types.js";
|
|
6
7
|
import { loadConfig, updateConfig } from "../config/loader.js";
|
|
7
8
|
import { detectChannels } from "../release/detector.js";
|
|
8
9
|
import type { ChannelStatus } from "../release/channels/types.js";
|
|
10
|
+
import { resolvePackageManager } from "../workspace/package-manager.js";
|
|
11
|
+
import { resolveRepoRoot } from "../workspace/repo-root.js";
|
|
12
|
+
import { parseTargetArg, resolveRequestedWorkspaceTarget, sortWorkspaceTargetOptions, selectWorkspaceTarget, tokenizeCliArgs, type WorkspaceTargetOption } from "../workspace/selector.js";
|
|
13
|
+
import {
|
|
14
|
+
isWorkspaceTargetLocked,
|
|
15
|
+
releaseWorkspaceTargetLock,
|
|
16
|
+
tryAcquireWorkspaceTargetLock,
|
|
17
|
+
} from "../workspace/locks.js";
|
|
18
|
+
import { discoverReleaseTargets, getPublishableReleaseTargets } from "../release/targets.js";
|
|
9
19
|
import {
|
|
10
20
|
parseConventionalCommits,
|
|
11
21
|
buildChangelogMarkdown,
|
|
@@ -15,6 +25,8 @@ import {
|
|
|
15
25
|
import {
|
|
16
26
|
getCurrentVersion,
|
|
17
27
|
getPublishedPackagePaths,
|
|
28
|
+
getLatestReleaseTag,
|
|
29
|
+
getReleaseTagFormat,
|
|
18
30
|
suggestBump,
|
|
19
31
|
bumpVersion,
|
|
20
32
|
isVersionReleased,
|
|
@@ -27,7 +39,15 @@ import { buildPolishPrompt } from "../release/prompt.js";
|
|
|
27
39
|
import { notifyInfo, notifySuccess, notifyError } from "../notifications/renderer.js";
|
|
28
40
|
import { analyzeAndCommit } from "../git/commit.js";
|
|
29
41
|
import { getWorkingTreeStatus } from "../git/status.js";
|
|
30
|
-
import {
|
|
42
|
+
import { runWithOutputValidation, parseStructuredOutput } from "../ai/structured-output.js";
|
|
43
|
+
import { renderSchemaText } from "../ai/schema-text.js";
|
|
44
|
+
import {
|
|
45
|
+
ReleaseNotePolishOutputSchema,
|
|
46
|
+
ReleaseDocFixOutputSchema,
|
|
47
|
+
renderPolishedChangelog,
|
|
48
|
+
type ReleaseNotePolishOutput,
|
|
49
|
+
type ReleaseDocFixOutput,
|
|
50
|
+
} from "../release/contracts.js";
|
|
31
51
|
import {
|
|
32
52
|
checkDocDrift,
|
|
33
53
|
buildFixPrompt,
|
|
@@ -50,6 +70,71 @@ const BUMP_OPTIONS = [
|
|
|
50
70
|
"major — breaking changes",
|
|
51
71
|
];
|
|
52
72
|
|
|
73
|
+
interface ParsedReleaseArgs {
|
|
74
|
+
skipPolish: boolean;
|
|
75
|
+
isDryRun: boolean;
|
|
76
|
+
requestedTarget: string | null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface ReleaseTargetOption extends WorkspaceTargetOption<ReleaseTarget> {
|
|
80
|
+
lastTag: string | null;
|
|
81
|
+
summary: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
export function parseReleaseArgs(args?: string): ParsedReleaseArgs {
|
|
86
|
+
const tokens = tokenizeCliArgs(args);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
skipPolish: tokens.includes("--raw"),
|
|
90
|
+
isDryRun: tokens.includes("--dry-run"),
|
|
91
|
+
requestedTarget: parseTargetArg(args),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function createReleaseProgressKeys(runId: string): { statusKey: string; widgetKey: string } {
|
|
96
|
+
return {
|
|
97
|
+
statusKey: `supi-release:${runId}`,
|
|
98
|
+
widgetKey: `supi-release:${runId}`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function resolveRequestedReleaseTarget(
|
|
103
|
+
targets: ReleaseTarget[],
|
|
104
|
+
requestedTarget: string | null,
|
|
105
|
+
): ReleaseTarget | null {
|
|
106
|
+
return resolveRequestedWorkspaceTarget(targets, requestedTarget);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function sortReleaseTargetOptions(options: ReleaseTargetOption[]): ReleaseTargetOption[] {
|
|
110
|
+
return sortWorkspaceTargetOptions(options) as ReleaseTargetOption[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function buildReleaseTargetOptionLabel(option: ReleaseTargetOption): string {
|
|
114
|
+
const releaseState = option.lastTag ?? "unreleased";
|
|
115
|
+
const changeState = option.changed
|
|
116
|
+
? `changed — ${option.summary}`
|
|
117
|
+
: "unchanged — no publishable changes";
|
|
118
|
+
|
|
119
|
+
return `${option.target.name} — ${option.target.relativeDir} — ${releaseState} — ${changeState}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function createReleaseRunId(): string {
|
|
123
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function tryAcquireReleaseTargetLock(targetId: string): boolean {
|
|
127
|
+
return tryAcquireWorkspaceTargetLock("release", targetId);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function releaseReleaseTargetLock(targetId: string): void {
|
|
131
|
+
releaseWorkspaceTargetLock("release", targetId);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function isReleaseTargetLocked(targetId: string): boolean {
|
|
135
|
+
return isWorkspaceTargetLocked("release", targetId);
|
|
136
|
+
}
|
|
137
|
+
|
|
53
138
|
/**
|
|
54
139
|
* Returns true when re-running supi:release should skip the confirmation
|
|
55
140
|
* dialog and proceed directly to execution.
|
|
@@ -211,12 +296,13 @@ export const RELEASE_STEPS = [
|
|
|
211
296
|
|
|
212
297
|
type ReleaseStepKey = (typeof RELEASE_STEPS)[number]["key"];
|
|
213
298
|
|
|
214
|
-
function createReleaseProgress(ctx: any) {
|
|
299
|
+
function createReleaseProgress(ctx: any, runId: string) {
|
|
300
|
+
const progressKeys = createReleaseProgressKeys(runId);
|
|
215
301
|
const progress = createWorkflowProgress(ctx.ui, {
|
|
216
302
|
title: "supi:release",
|
|
217
|
-
statusKey:
|
|
303
|
+
statusKey: progressKeys.statusKey,
|
|
218
304
|
statusLabel: "Releasing...",
|
|
219
|
-
widgetKey:
|
|
305
|
+
widgetKey: progressKeys.widgetKey,
|
|
220
306
|
clearStatusKeys: ["supi-model"],
|
|
221
307
|
steps: [...RELEASE_STEPS],
|
|
222
308
|
});
|
|
@@ -254,6 +340,36 @@ function createReleaseProgress(ctx: any) {
|
|
|
254
340
|
};
|
|
255
341
|
}
|
|
256
342
|
|
|
343
|
+
function buildPendingReleaseTargetOptions(targets: ReleaseTarget[]): ReleaseTargetOption[] {
|
|
344
|
+
return sortReleaseTargetOptions(
|
|
345
|
+
targets.map((target) => ({
|
|
346
|
+
target,
|
|
347
|
+
changed: false,
|
|
348
|
+
lastTag: null,
|
|
349
|
+
summary: "change analysis pending",
|
|
350
|
+
})),
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export async function selectReleaseTarget(
|
|
355
|
+
ctx: any,
|
|
356
|
+
options: ReleaseTargetOption[],
|
|
357
|
+
requestedTarget: string | null,
|
|
358
|
+
): Promise<ReleaseTarget | null> {
|
|
359
|
+
return selectWorkspaceTarget(
|
|
360
|
+
ctx,
|
|
361
|
+
options.map((option) => ({
|
|
362
|
+
...option,
|
|
363
|
+
label: buildReleaseTargetOptionLabel(option),
|
|
364
|
+
})),
|
|
365
|
+
requestedTarget,
|
|
366
|
+
{
|
|
367
|
+
title: "Release target",
|
|
368
|
+
helpText: "Pick a publishable package to release. Target choice is runtime-only and is not persisted in config.",
|
|
369
|
+
},
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
257
373
|
/**
|
|
258
374
|
* Register the command for autocomplete and /help listing.
|
|
259
375
|
* Actual execution goes through handleRelease via the TUI dispatch.
|
|
@@ -283,19 +399,54 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
283
399
|
return;
|
|
284
400
|
}
|
|
285
401
|
|
|
286
|
-
const progress = createReleaseProgress(ctx);
|
|
287
|
-
|
|
288
402
|
void (async () => {
|
|
403
|
+
let progress: ReturnType<typeof createReleaseProgress> | null = null;
|
|
404
|
+
let lockedTargetId: string | null = null;
|
|
289
405
|
try {
|
|
290
|
-
const skipPolish = args
|
|
291
|
-
const
|
|
292
|
-
const
|
|
406
|
+
const { skipPolish, isDryRun, requestedTarget } = parseReleaseArgs(args);
|
|
407
|
+
const repoRoot = await resolveRepoRoot(platform, ctx.cwd);
|
|
408
|
+
const packageManager = resolvePackageManager(repoRoot);
|
|
409
|
+
const publishableTargets = getPublishableReleaseTargets(
|
|
410
|
+
discoverReleaseTargets(repoRoot, packageManager.id),
|
|
411
|
+
);
|
|
412
|
+
if (publishableTargets.length === 0) {
|
|
413
|
+
notifyError(ctx, "No publishable release targets found", "Create a non-private package.json with name and version before running /supi:release.");
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const selectedTarget = await selectReleaseTarget(
|
|
418
|
+
ctx,
|
|
419
|
+
buildPendingReleaseTargetOptions(publishableTargets),
|
|
420
|
+
requestedTarget,
|
|
421
|
+
);
|
|
422
|
+
if (requestedTarget && !selectedTarget) {
|
|
423
|
+
notifyError(ctx, "Release target not found", requestedTarget);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (!selectedTarget) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (!tryAcquireReleaseTargetLock(selectedTarget.id)) {
|
|
430
|
+
notifyError(
|
|
431
|
+
ctx,
|
|
432
|
+
"Release already in progress",
|
|
433
|
+
`${selectedTarget.name} is already being released in this session. Wait for that run to finish before retrying.`,
|
|
434
|
+
);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
lockedTargetId = selectedTarget.id;
|
|
438
|
+
|
|
439
|
+
progress = createReleaseProgress(ctx, createReleaseRunId());
|
|
440
|
+
progress.detail(`Target: ${selectedTarget.name} (${selectedTarget.relativeDir})`);
|
|
441
|
+
|
|
442
|
+
const config = loadConfig(platform.paths, repoRoot);
|
|
293
443
|
const tagFormat = config.release.tagFormat;
|
|
444
|
+
const effectiveTagFormat = getReleaseTagFormat(selectedTarget, tagFormat);
|
|
294
445
|
let didStash = false;
|
|
295
446
|
|
|
296
447
|
// ── 1. Quality checks (headless) ────────────────────────────────────
|
|
297
448
|
progress.activate("checks", "Running quality gates");
|
|
298
|
-
const checksReport = await runHeadlessChecks(platform,
|
|
449
|
+
const checksReport = await runHeadlessChecks(platform, repoRoot, config, resolved, selectedTarget);
|
|
299
450
|
if (checksReport) {
|
|
300
451
|
const { summary, overallStatus } = checksReport;
|
|
301
452
|
const detail = `${summary.passed} passed, ${summary.failed} failed, ${summary.blocked} blocked`;
|
|
@@ -325,7 +476,7 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
325
476
|
|
|
326
477
|
// ── 2. Doc-drift pre-check ──────────────────────────────────────────
|
|
327
478
|
progress.activate("doc-drift", "Checking documentation drift");
|
|
328
|
-
const driftResult = await checkDocDrift(platform,
|
|
479
|
+
const driftResult = await checkDocDrift(platform, repoRoot);
|
|
329
480
|
if (driftResult?.drifted) {
|
|
330
481
|
progress.complete("doc-drift", "Drift detected");
|
|
331
482
|
const driftAction = await ctx.ui.select(
|
|
@@ -342,20 +493,30 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
342
493
|
notifyInfo(ctx, "Updating documentation", driftResult.summary);
|
|
343
494
|
const fixPrompt = buildFixPrompt(driftResult.findings);
|
|
344
495
|
const { loadState: loadDriftState, saveState: saveDriftState, getHeadCommit } = await import("../docs/drift.js");
|
|
345
|
-
const driftHead = await getHeadCommit(platform,
|
|
346
|
-
const driftState = loadDriftState(platform.paths,
|
|
347
|
-
saveDriftState(platform.paths,
|
|
496
|
+
const driftHead = await getHeadCommit(platform, repoRoot);
|
|
497
|
+
const driftState = loadDriftState(platform.paths, repoRoot);
|
|
498
|
+
saveDriftState(platform.paths, repoRoot, { ...driftState, lastCommit: driftHead, lastRunAt: new Date().toISOString() });
|
|
348
499
|
|
|
349
|
-
const fixResult = await
|
|
500
|
+
const fixResult = await runWithOutputValidation<ReleaseDocFixOutput>(
|
|
350
501
|
platform.createAgentSession.bind(platform),
|
|
351
|
-
{
|
|
502
|
+
{
|
|
503
|
+
cwd: repoRoot,
|
|
504
|
+
prompt: fixPrompt,
|
|
505
|
+
schema: renderSchemaText(ReleaseDocFixOutputSchema),
|
|
506
|
+
parse: (raw) => parseStructuredOutput<ReleaseDocFixOutput>(raw, ReleaseDocFixOutputSchema),
|
|
507
|
+
reliability: { paths: platform.paths, cwd: repoRoot, command: "release", operation: "doc-fix" },
|
|
508
|
+
},
|
|
352
509
|
);
|
|
353
|
-
if (fixResult.status === "ok") {
|
|
354
|
-
progress.complete("doc-drift",
|
|
355
|
-
notifySuccess(ctx, "Documentation updated");
|
|
510
|
+
if (fixResult.status === "ok" && fixResult.output.status === "ok") {
|
|
511
|
+
progress.complete("doc-drift", `Fixed — ${fixResult.output.edits.length} file(s)`);
|
|
512
|
+
notifySuccess(ctx, "Documentation updated", fixResult.output.summary);
|
|
356
513
|
} else {
|
|
357
|
-
|
|
358
|
-
|
|
514
|
+
const detail =
|
|
515
|
+
fixResult.status === "blocked"
|
|
516
|
+
? fixResult.error
|
|
517
|
+
: `Doc fixer blocked: ${fixResult.output.summary}`;
|
|
518
|
+
progress.fail("doc-drift", detail);
|
|
519
|
+
notifyError(ctx, "Doc update failed", detail);
|
|
359
520
|
progress.dispose();
|
|
360
521
|
return;
|
|
361
522
|
}
|
|
@@ -368,7 +529,7 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
368
529
|
|
|
369
530
|
// ── 3. Check for uncommitted changes after preflight side effects ─────
|
|
370
531
|
progress.activate("working-tree", "Checking working tree");
|
|
371
|
-
const treeStatus = await getWorkingTreeStatus(platform.exec.bind(platform),
|
|
532
|
+
const treeStatus = await getWorkingTreeStatus(platform.exec.bind(platform), repoRoot);
|
|
372
533
|
if (treeStatus.dirty) {
|
|
373
534
|
progress.complete("working-tree", `${treeStatus.files.length} files changed`);
|
|
374
535
|
const filePreview = treeStatus.files.slice(0, 8).join(", ");
|
|
@@ -396,7 +557,7 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
396
557
|
notifyError(ctx, "Commit failed or cancelled", "Aborting release.");
|
|
397
558
|
return;
|
|
398
559
|
}
|
|
399
|
-
const afterStatus = await getWorkingTreeStatus(platform.exec.bind(platform),
|
|
560
|
+
const afterStatus = await getWorkingTreeStatus(platform.exec.bind(platform), repoRoot);
|
|
400
561
|
if (afterStatus.dirty) {
|
|
401
562
|
notifyError(ctx, "Still uncommitted changes", "Not all changes were committed. Aborting release.");
|
|
402
563
|
return;
|
|
@@ -404,7 +565,7 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
404
565
|
progress.complete("working-tree", "Committed");
|
|
405
566
|
} else if (action.startsWith("stash")) {
|
|
406
567
|
progress.activate("working-tree", "Stashing changes");
|
|
407
|
-
const stashResult = await platform.exec("git", ["stash", "push", "-m", "supi:release auto-stash"], { cwd:
|
|
568
|
+
const stashResult = await platform.exec("git", ["stash", "push", "-m", "supi:release auto-stash"], { cwd: repoRoot });
|
|
408
569
|
if (stashResult.code !== 0) {
|
|
409
570
|
progress.fail("working-tree", stashResult.stderr || "stash failed");
|
|
410
571
|
progress.dispose();
|
|
@@ -421,7 +582,7 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
421
582
|
// ── 4. Ensure channels are configured (or detect + ask) ─────────────
|
|
422
583
|
progress.activate("channels", "Detecting channels");
|
|
423
584
|
const customChannels = config.release.customChannels ?? {};
|
|
424
|
-
const detectedChannels = await detectChannels(platform.exec.bind(platform),
|
|
585
|
+
const detectedChannels = await detectChannels(platform.exec.bind(platform), repoRoot, customChannels);
|
|
425
586
|
let channels = config.release.channels;
|
|
426
587
|
// Track whether channels were already set in config before any interactive
|
|
427
588
|
// setup. This distinguishes "user already decided" from "just configured now",
|
|
@@ -441,7 +602,7 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
441
602
|
|
|
442
603
|
if (channels.length === 0) {
|
|
443
604
|
progress.complete("channels", "Awaiting selection");
|
|
444
|
-
channels = await setupChannels(platform, ctx, detectedChannels);
|
|
605
|
+
channels = await setupChannels(platform, ctx, repoRoot, detectedChannels);
|
|
445
606
|
if (channels.length === 0) {
|
|
446
607
|
progress.dispose();
|
|
447
608
|
return;
|
|
@@ -451,38 +612,38 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
451
612
|
|
|
452
613
|
// ── 5. Get last tag + current version + check if bump is needed ─────
|
|
453
614
|
progress.activate("commits", "Parsing git history");
|
|
454
|
-
const lastTag = await
|
|
455
|
-
|
|
615
|
+
const lastTag = await getLatestReleaseTag(
|
|
616
|
+
platform.exec.bind(platform),
|
|
617
|
+
selectedTarget,
|
|
618
|
+
tagFormat,
|
|
619
|
+
);
|
|
620
|
+
const currentVersion = getCurrentVersion(selectedTarget);
|
|
456
621
|
const resumableLocalRelease = await findResumableLocalRelease(
|
|
457
622
|
platform.exec.bind(platform),
|
|
458
|
-
|
|
623
|
+
selectedTarget,
|
|
459
624
|
currentVersion,
|
|
460
625
|
tagFormat,
|
|
461
626
|
);
|
|
462
627
|
const localTagExists = await isVersionReleased(
|
|
463
628
|
platform.exec.bind(platform),
|
|
464
|
-
|
|
629
|
+
selectedTarget,
|
|
465
630
|
currentVersion,
|
|
466
631
|
tagFormat,
|
|
467
632
|
);
|
|
468
633
|
const remoteTagExists = localTagExists
|
|
469
|
-
? await isTagOnRemote(platform.exec.bind(platform),
|
|
634
|
+
? await isTagOnRemote(platform.exec.bind(platform), selectedTarget, currentVersion, tagFormat)
|
|
470
635
|
: false;
|
|
471
636
|
|
|
472
637
|
const sinceArg = lastTag ? `${lastTag}..HEAD` : "HEAD~50..HEAD";
|
|
473
|
-
const releaseScope = getPublishedPackagePaths(
|
|
474
|
-
const gitLogArgs =
|
|
475
|
-
? ["log", sinceArg, "--format=%x1e%H%x1f%s", "--name-only"]
|
|
476
|
-
: ["log", sinceArg, "--oneline"];
|
|
638
|
+
const releaseScope = getPublishedPackagePaths(selectedTarget);
|
|
639
|
+
const gitLogArgs = ["log", sinceArg, "--format=%x1e%H%x1f%s", "--name-only"];
|
|
477
640
|
let gitLogOutput: string;
|
|
478
641
|
try {
|
|
479
|
-
const result = await platform.exec("git", gitLogArgs, { cwd:
|
|
642
|
+
const result = await platform.exec("git", gitLogArgs, { cwd: repoRoot });
|
|
480
643
|
if (result.code !== 0) {
|
|
481
644
|
gitLogOutput = "";
|
|
482
|
-
} else if (releaseScope) {
|
|
483
|
-
gitLogOutput = filterOnelineGitLogToPaths(result.stdout, releaseScope);
|
|
484
645
|
} else {
|
|
485
|
-
gitLogOutput = result.stdout;
|
|
646
|
+
gitLogOutput = filterOnelineGitLogToPaths(result.stdout, releaseScope);
|
|
486
647
|
}
|
|
487
648
|
} catch {
|
|
488
649
|
gitLogOutput = "";
|
|
@@ -535,14 +696,14 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
535
696
|
if (!localTagExists && currentVersion !== "0.0.0") {
|
|
536
697
|
nextVersion = currentVersion;
|
|
537
698
|
skipBump = true;
|
|
538
|
-
progress.skip("version", `${formatTag(currentVersion,
|
|
539
|
-
notifyInfo(ctx, `Using ${formatTag(currentVersion,
|
|
699
|
+
progress.skip("version", `${formatTag(currentVersion, effectiveTagFormat)} (already set, not yet released)`);
|
|
700
|
+
notifyInfo(ctx, `Using ${formatTag(currentVersion, effectiveTagFormat)}`, "Version not yet released — skipping bump");
|
|
540
701
|
} else if (localTagExists && !remoteTagExists) {
|
|
541
702
|
nextVersion = currentVersion;
|
|
542
703
|
skipBump = true;
|
|
543
704
|
skipTag = true;
|
|
544
|
-
progress.skip("version", `${formatTag(currentVersion,
|
|
545
|
-
notifyInfo(ctx, `Resuming ${formatTag(currentVersion,
|
|
705
|
+
progress.skip("version", `${formatTag(currentVersion, effectiveTagFormat)} (tag exists locally, not pushed)`);
|
|
706
|
+
notifyInfo(ctx, `Resuming ${formatTag(currentVersion, effectiveTagFormat)}`, "Tag exists locally but not on remote — will push");
|
|
546
707
|
} else {
|
|
547
708
|
progress.activate("version", "Awaiting version selection");
|
|
548
709
|
const suggested = suggestBump(commits);
|
|
@@ -564,7 +725,7 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
564
725
|
}
|
|
565
726
|
// ── 8. Build changelog ──────────────────────────────────────────────
|
|
566
727
|
progress.activate("changelog", "Generating changelog");
|
|
567
|
-
const rawChangelog = buildChangelogMarkdown(commits, nextVersion,
|
|
728
|
+
const rawChangelog = buildChangelogMarkdown(commits, nextVersion, effectiveTagFormat);
|
|
568
729
|
progress.complete("changelog", `${commitCount} entries`);
|
|
569
730
|
|
|
570
731
|
// ── 9. Polish release notes (default, skip with --raw) ──────────────
|
|
@@ -574,19 +735,27 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
574
735
|
changelog = rawChangelog;
|
|
575
736
|
} else {
|
|
576
737
|
progress.activate("polish", "Polishing release notes");
|
|
577
|
-
const polishPrompt = buildPolishPrompt({ changelog: rawChangelog, version: nextVersion, tagFormat });
|
|
578
|
-
const polishResult = await
|
|
738
|
+
const polishPrompt = buildPolishPrompt({ changelog: rawChangelog, version: nextVersion, tagFormat: effectiveTagFormat });
|
|
739
|
+
const polishResult = await runWithOutputValidation<ReleaseNotePolishOutput>(
|
|
579
740
|
platform.createAgentSession.bind(platform),
|
|
580
|
-
{
|
|
741
|
+
{
|
|
742
|
+
cwd: repoRoot,
|
|
743
|
+
prompt: polishPrompt,
|
|
744
|
+
schema: renderSchemaText(ReleaseNotePolishOutputSchema),
|
|
745
|
+
parse: (raw) => parseStructuredOutput<ReleaseNotePolishOutput>(raw, ReleaseNotePolishOutputSchema),
|
|
746
|
+
reliability: { paths: platform.paths, cwd: repoRoot, command: "release", operation: "note-polish" },
|
|
747
|
+
},
|
|
581
748
|
);
|
|
582
749
|
if (polishResult.status === "ok") {
|
|
583
|
-
changelog = polishResult.
|
|
584
|
-
progress.complete("polish", "Polished");
|
|
750
|
+
changelog = renderPolishedChangelog(polishResult.output);
|
|
751
|
+
progress.complete("polish", polishResult.output.status === "empty" ? "No notable changes" : "Polished");
|
|
585
752
|
} else {
|
|
586
|
-
//
|
|
587
|
-
|
|
588
|
-
progress.fail("polish", polishResult.error
|
|
589
|
-
|
|
753
|
+
// Contract validation failed — halt truthfully rather than ship
|
|
754
|
+
// an unreviewed or ambiguous changelog.
|
|
755
|
+
progress.fail("polish", polishResult.error);
|
|
756
|
+
notifyError(ctx, "Release-note polish failed validation", polishResult.error);
|
|
757
|
+
progress.dispose();
|
|
758
|
+
return;
|
|
590
759
|
}
|
|
591
760
|
}
|
|
592
761
|
|
|
@@ -597,15 +766,15 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
597
766
|
if (isResume) {
|
|
598
767
|
notifyInfo(
|
|
599
768
|
ctx,
|
|
600
|
-
`Resuming release ${formatTag(nextVersion,
|
|
769
|
+
`Resuming release ${formatTag(nextVersion, effectiveTagFormat)}`,
|
|
601
770
|
"Version staged and channels configured — proceeding without confirmation",
|
|
602
771
|
);
|
|
603
772
|
} else {
|
|
604
|
-
const confirmLabel = isDryRun ? `[DRY RUN] Ship ${formatTag(nextVersion,
|
|
773
|
+
const confirmLabel = isDryRun ? `[DRY RUN] Ship ${formatTag(nextVersion, effectiveTagFormat)}?` : `Ship ${formatTag(nextVersion, effectiveTagFormat)}?`;
|
|
605
774
|
// When skipBump=true, currentVersion === nextVersion — avoid the
|
|
606
775
|
// misleading "0.5.0 → 0.5.0" display.
|
|
607
776
|
const versionLine = skipBump
|
|
608
|
-
? `${formatTag(nextVersion,
|
|
777
|
+
? `${formatTag(nextVersion, effectiveTagFormat)} (staged, not yet released)`
|
|
609
778
|
: `${currentVersion} \u2192 ${nextVersion}`;
|
|
610
779
|
const confirmDetail = [
|
|
611
780
|
versionLine,
|
|
@@ -631,14 +800,15 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
631
800
|
try {
|
|
632
801
|
let result = await executeRelease({
|
|
633
802
|
exec: platform.exec.bind(platform),
|
|
634
|
-
cwd:
|
|
803
|
+
cwd: repoRoot,
|
|
804
|
+
target: selectedTarget,
|
|
635
805
|
version: nextVersion,
|
|
636
806
|
changelog,
|
|
637
807
|
channels,
|
|
638
808
|
dryRun: isDryRun,
|
|
639
809
|
skipBump,
|
|
640
810
|
skipTag,
|
|
641
|
-
tagFormat,
|
|
811
|
+
tagFormat: effectiveTagFormat,
|
|
642
812
|
customChannels,
|
|
643
813
|
onProgress: progress.executorProgress(),
|
|
644
814
|
});
|
|
@@ -649,14 +819,15 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
649
819
|
progress.activate("execute", `Retrying with GitHub account ${switchedTo}`);
|
|
650
820
|
result = await executeRelease({
|
|
651
821
|
exec: platform.exec.bind(platform),
|
|
652
|
-
cwd:
|
|
822
|
+
cwd: repoRoot,
|
|
823
|
+
target: selectedTarget,
|
|
653
824
|
version: nextVersion,
|
|
654
825
|
changelog,
|
|
655
826
|
channels,
|
|
656
827
|
dryRun: false,
|
|
657
828
|
skipBump: true,
|
|
658
829
|
skipTag: true,
|
|
659
|
-
tagFormat,
|
|
830
|
+
tagFormat: effectiveTagFormat,
|
|
660
831
|
customChannels,
|
|
661
832
|
onProgress: progress.executorProgress(),
|
|
662
833
|
});
|
|
@@ -696,14 +867,14 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
696
867
|
const prefix = isDryRun ? "[DRY RUN] " : "";
|
|
697
868
|
notifySuccess(
|
|
698
869
|
ctx,
|
|
699
|
-
`${prefix}Released ${formatTag(nextVersion,
|
|
870
|
+
`${prefix}Released ${formatTag(nextVersion, effectiveTagFormat)}`,
|
|
700
871
|
`Tag: ${result.tagCreated ? "✓" : "✗"} | Push: ${result.pushed ? "✓" : "✗"} | ${channelSummary}`,
|
|
701
872
|
);
|
|
702
873
|
} else {
|
|
703
874
|
const detail = result.error
|
|
704
875
|
? result.error
|
|
705
876
|
: `Tag: ${result.tagCreated ? "✓" : "✗"} | Push: ${result.pushed ? "✓" : "✗"}`;
|
|
706
|
-
notifyError(ctx, `Release ${formatTag(nextVersion,
|
|
877
|
+
notifyError(ctx, `Release ${formatTag(nextVersion, effectiveTagFormat)} failed`, detail);
|
|
707
878
|
}
|
|
708
879
|
} catch (err) {
|
|
709
880
|
progress.fail("execute", err instanceof Error ? err.message : "Unknown error");
|
|
@@ -716,16 +887,19 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
716
887
|
);
|
|
717
888
|
} finally {
|
|
718
889
|
if (didStash) {
|
|
719
|
-
const popResult = await platform.exec("git", ["stash", "pop"], { cwd:
|
|
890
|
+
const popResult = await platform.exec("git", ["stash", "pop"], { cwd: repoRoot });
|
|
720
891
|
if (popResult.code !== 0) {
|
|
721
892
|
notifyError(ctx, "Stash pop failed", "Run 'git stash pop' manually to recover your changes");
|
|
722
893
|
}
|
|
723
894
|
}
|
|
724
895
|
}
|
|
725
896
|
} catch (err) {
|
|
726
|
-
progress
|
|
897
|
+
progress?.dispose();
|
|
727
898
|
notifyError(ctx, "Release error", err instanceof Error ? err.message : String(err));
|
|
728
899
|
} finally {
|
|
900
|
+
if (lockedTargetId) {
|
|
901
|
+
releaseReleaseTargetLock(lockedTargetId);
|
|
902
|
+
}
|
|
729
903
|
await modelCleanup();
|
|
730
904
|
}
|
|
731
905
|
})();
|
|
@@ -739,9 +913,10 @@ export async function handleRelease(platform: Platform, ctx: any, args?: string)
|
|
|
739
913
|
*/
|
|
740
914
|
async function runHeadlessChecks(
|
|
741
915
|
platform: Platform,
|
|
742
|
-
|
|
916
|
+
repoRoot: string,
|
|
743
917
|
config: ReturnType<typeof loadConfig>,
|
|
744
918
|
resolved: ResolvedModel,
|
|
919
|
+
selectedTarget: ReleaseTarget,
|
|
745
920
|
): Promise<ReviewReport | null> {
|
|
746
921
|
const enabledGates = Object.entries(config.quality.gates)
|
|
747
922
|
.filter(([, gate]) => gate?.enabled === true);
|
|
@@ -750,9 +925,24 @@ async function runHeadlessChecks(
|
|
|
750
925
|
return null;
|
|
751
926
|
}
|
|
752
927
|
|
|
928
|
+
const repoWideTarget = {
|
|
929
|
+
id: "repo-root",
|
|
930
|
+
name: path.basename(repoRoot) || "repo-root",
|
|
931
|
+
kind: "root" as const,
|
|
932
|
+
repoRoot,
|
|
933
|
+
packageDir: repoRoot,
|
|
934
|
+
manifestPath: path.join(repoRoot, "package.json"),
|
|
935
|
+
relativeDir: ".",
|
|
936
|
+
version: selectedTarget.version,
|
|
937
|
+
private: false,
|
|
938
|
+
packageManager: selectedTarget.packageManager,
|
|
939
|
+
};
|
|
940
|
+
|
|
753
941
|
return runQualityGates({
|
|
754
942
|
platform,
|
|
755
|
-
cwd:
|
|
943
|
+
cwd: repoRoot,
|
|
944
|
+
target: repoWideTarget,
|
|
945
|
+
workspaceTargets: [repoWideTarget],
|
|
756
946
|
gates: config.quality.gates,
|
|
757
947
|
filters: {},
|
|
758
948
|
reviewModel: resolved,
|
|
@@ -760,18 +950,11 @@ async function runHeadlessChecks(
|
|
|
760
950
|
});
|
|
761
951
|
}
|
|
762
952
|
|
|
763
|
-
async function getLastTag(platform: Platform, cwd: string): Promise<string | null> {
|
|
764
|
-
try {
|
|
765
|
-
const result = await platform.exec("git", ["describe", "--tags", "--abbrev=0"], { cwd });
|
|
766
|
-
return result.code === 0 ? result.stdout.trim() : null;
|
|
767
|
-
} catch {
|
|
768
|
-
return null;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
953
|
|
|
772
954
|
async function setupChannels(
|
|
773
955
|
platform: Platform,
|
|
774
956
|
ctx: any,
|
|
957
|
+
configCwd: string,
|
|
775
958
|
detected: ChannelStatus[],
|
|
776
959
|
): Promise<ReleaseChannel[]> {
|
|
777
960
|
const availableChannels = detected.filter((channel) => channel.available);
|
|
@@ -825,7 +1008,7 @@ async function setupChannels(
|
|
|
825
1008
|
}
|
|
826
1009
|
|
|
827
1010
|
// Persist to config
|
|
828
|
-
updateConfig(platform.paths,
|
|
1011
|
+
updateConfig(platform.paths, configCwd, { release: { channels: selected } });
|
|
829
1012
|
ctx.ui.notify(`Release channels set to: ${selected.join(", ")}`, "info");
|
|
830
1013
|
|
|
831
1014
|
return selected;
|