infra-kit 0.1.102 → 0.1.107
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/.eslintcache +1 -1
- package/.omc/state/agent-replay-0a58307d-2a37-4c69-851c-83a646502d62.jsonl +1 -0
- package/.omc/state/agent-replay-11c41aa0-51fa-49e1-a1dc-26dcd6ac26cc.jsonl +16 -0
- package/.omc/state/agent-replay-4cf1c186-81b2-497c-b002-d7f84e7839f3.jsonl +9 -0
- package/.omc/state/agent-replay-5c4ab554-64f1-42ae-83e3-21e0237e955c.jsonl +11 -0
- package/.omc/state/agent-replay-a60ac2ec-afbd-449f-a540-6df287392fc2.jsonl +1 -0
- package/.omc/state/agent-replay-afc6290b-40d3-4bef-b3b6-14484c034ab9.jsonl +14 -0
- package/.omc/state/agent-replay-be37e426-6fc8-47f4-8178-221c8494551c.jsonl +3 -0
- package/.omc/state/agent-replay-c967c819-3d1c-447b-ab48-56a8448ef9f8.jsonl +2 -0
- package/.omc/state/agent-replay-e947a3c6-989d-4a60-91dd-6b0ddd827b2d.jsonl +3 -0
- package/.omc/state/idle-notif-cooldown.json +3 -0
- package/.omc/state/last-tool-error.json +4 -4
- package/.omc/state/mission-state.json +53 -0
- package/.omc/state/sessions/0a58307d-2a37-4c69-851c-83a646502d62/pre-tool-advisory-throttle.json +18 -0
- package/.omc/state/sessions/0a58307d-2a37-4c69-851c-83a646502d62/subagent-tracking-state.json +7 -0
- package/.omc/state/sessions/11c41aa0-51fa-49e1-a1dc-26dcd6ac26cc/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/11c41aa0-51fa-49e1-a1dc-26dcd6ac26cc/mission-state.json +117 -0
- package/.omc/state/sessions/11c41aa0-51fa-49e1-a1dc-26dcd6ac26cc/pre-tool-advisory-throttle.json +42 -0
- package/.omc/state/sessions/11c41aa0-51fa-49e1-a1dc-26dcd6ac26cc/subagent-tracking-state.json +53 -0
- package/.omc/state/sessions/4cf1c186-81b2-497c-b002-d7f84e7839f3/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/4cf1c186-81b2-497c-b002-d7f84e7839f3/pre-tool-advisory-throttle.json +18 -0
- package/.omc/state/sessions/4cf1c186-81b2-497c-b002-d7f84e7839f3/subagent-tracking-state.json +7 -0
- package/.omc/state/sessions/5c4ab554-64f1-42ae-83e3-21e0237e955c/mission-state.json +117 -0
- package/.omc/state/sessions/5c4ab554-64f1-42ae-83e3-21e0237e955c/pre-tool-advisory-throttle.json +18 -0
- package/.omc/state/sessions/5c4ab554-64f1-42ae-83e3-21e0237e955c/subagent-tracking-state.json +17 -0
- package/.omc/state/sessions/a60ac2ec-afbd-449f-a540-6df287392fc2/pre-tool-advisory-throttle.json +18 -0
- package/.omc/state/sessions/a60ac2ec-afbd-449f-a540-6df287392fc2/subagent-tracking-state.json +7 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/mission-state.json +89 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/pre-tool-advisory-throttle.json +34 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/ralph-state.json +13 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/skill-active-state.json +15 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/subagent-tracking-state.json +35 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/ultrawork-state.json +11 -0
- package/.omc/state/sessions/c967c819-3d1c-447b-ab48-56a8448ef9f8/pre-tool-advisory-throttle.json +10 -0
- package/.omc/state/sessions/c967c819-3d1c-447b-ab48-56a8448ef9f8/subagent-tracking-state.json +7 -0
- package/.omc/state/sessions/e947a3c6-989d-4a60-91dd-6b0ddd827b2d/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/e947a3c6-989d-4a60-91dd-6b0ddd827b2d/pre-tool-advisory-throttle.json +10 -0
- package/.omc/state/sessions/e947a3c6-989d-4a60-91dd-6b0ddd827b2d/subagent-tracking-state.json +26 -0
- package/.omc/state/subagent-tracking.json +14 -4
- package/.turbo/turbo-build.log +7 -0
- package/.turbo/turbo-check.log +14 -0
- package/.turbo/turbo-prettier-fix.log +2 -1
- package/.turbo/turbo-test.log +28 -5
- package/.turbo/turbo-validate.log +14 -0
- package/dist/cli.js +88 -74
- package/dist/cli.js.map +4 -4
- package/dist/entry/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +7 -0
- package/dist/lib/package-config/package-config.d.ts +71 -0
- package/dist/mcp.js +43 -41
- package/dist/mcp.js.map +4 -4
- package/eslint.config.js +1 -1
- package/infra-kit.config.ts +5 -0
- package/package.json +20 -13
- package/scripts/build.js +32 -3
- package/src/.omc/state/sessions/0a58307d-2a37-4c69-851c-83a646502d62/pre-tool-advisory-throttle.json +18 -0
- package/src/commands/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/pre-tool-advisory-throttle.json +14 -0
- package/src/commands/.omc/state/sessions/c967c819-3d1c-447b-ab48-56a8448ef9f8/pre-tool-advisory-throttle.json +18 -0
- package/src/commands/audit/__tests__/audit.test.ts +59 -0
- package/src/commands/audit/audit.ts +177 -0
- package/src/commands/audit/index.ts +1 -0
- package/src/commands/config/config.ts +49 -7
- package/src/commands/doctor/__tests__/agent-files.test.ts +110 -0
- package/src/commands/doctor/doctor.ts +69 -4
- package/src/commands/env-clear/env-clear.ts +1 -1
- package/src/commands/env-list/env-list.ts +3 -3
- package/src/commands/env-load/env-load.ts +1 -1
- package/src/commands/env-status/env-status.ts +1 -1
- package/src/commands/gh-merge-dev/gh-merge-dev.ts +3 -8
- package/src/commands/gh-release-deliver/gh-release-deliver.ts +47 -21
- package/src/commands/gh-release-deploy-all/gh-release-deploy-all.ts +13 -7
- package/src/commands/gh-release-deploy-selected/gh-release-deploy-selected.ts +12 -6
- package/src/commands/gh-release-list/gh-release-list.ts +19 -8
- package/src/commands/init/__tests__/agent-files.test.ts +147 -0
- package/src/commands/init/__tests__/migrate-config.test.ts +160 -0
- package/src/commands/init/agent-files.ts +199 -0
- package/src/commands/init/index.ts +7 -0
- package/src/commands/init/init.ts +82 -60
- package/src/commands/init/migrate-config.ts +146 -0
- package/src/commands/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/src/commands/release-create/__tests__/release-create.test.ts +55 -0
- package/src/commands/release-create/release-create.ts +142 -38
- package/src/commands/release-desc-edit/release-desc-edit.ts +28 -8
- package/src/commands/version/version.ts +1 -1
- package/src/commands/worktrees-add/worktrees-add.ts +7 -12
- package/src/commands/worktrees-list/worktrees-list.ts +13 -5
- package/src/commands/worktrees-open/worktrees-open.ts +1 -1
- package/src/commands/worktrees-remove/worktrees-remove.ts +6 -10
- package/src/commands/worktrees-sync/worktrees-sync.ts +3 -5
- package/src/entry/cli.ts +50 -7
- package/src/entry/index.ts +5 -0
- package/src/integrations/cmux/open-workspace-with-layout.ts +4 -4
- package/src/integrations/cmux/workspace-title.ts +10 -4
- package/src/integrations/doppler/doppler-project.ts +1 -1
- package/src/integrations/gh/gh-release-prs/__tests__/gh-release-prs.test.ts +115 -0
- package/src/integrations/gh/gh-release-prs/gh-release-prs.ts +49 -32
- package/src/lib/.omc/state/sessions/a60ac2ec-afbd-449f-a540-6df287392fc2/pre-tool-advisory-throttle.json +14 -0
- package/src/lib/constants/index.ts +15 -0
- package/src/lib/git-utils/__tests__/git-utils.test.ts +49 -0
- package/src/lib/git-utils/git-utils.ts +3 -1
- package/src/lib/infra-kit-config/__tests__/infra-kit-config.test.ts +270 -0
- package/src/lib/infra-kit-config/index.ts +7 -1
- package/src/lib/infra-kit-config/infra-kit-config.ts +46 -28
- package/src/lib/managed-block/__tests__/managed-block.test.ts +121 -0
- package/src/lib/managed-block/index.ts +8 -0
- package/src/lib/managed-block/managed-block.ts +145 -0
- package/src/lib/package-config/__tests__/package-config.test.ts +95 -0
- package/src/lib/package-config/index.ts +3 -0
- package/src/lib/package-config/package-config-schema.ts +19 -0
- package/src/lib/package-config/package-config.ts +99 -0
- package/src/lib/package-validator/__tests__/package-validator.test.ts +263 -0
- package/src/lib/package-validator/checks/__tests__/checks.test.ts +130 -0
- package/src/lib/package-validator/checks/config-check.ts +30 -0
- package/src/lib/package-validator/checks/files-check.ts +29 -0
- package/src/lib/package-validator/checks/index.ts +4 -0
- package/src/lib/package-validator/checks/scripts-check.ts +23 -0
- package/src/lib/package-validator/checks/turbo-check.ts +47 -0
- package/src/lib/package-validator/fs-utils.ts +18 -0
- package/src/lib/package-validator/index.ts +3 -0
- package/src/lib/package-validator/loader/config-loader.ts +77 -0
- package/src/lib/package-validator/loader/index.ts +2 -0
- package/src/lib/package-validator/loader/package-discovery.ts +98 -0
- package/src/lib/package-validator/package-validator.ts +48 -0
- package/src/lib/package-validator/types.ts +15 -0
- package/src/lib/release-id/__tests__/release-id.test.ts +351 -0
- package/src/lib/release-id/__tests__/versioned-regression.test.ts +69 -0
- package/src/lib/release-id/index.ts +15 -0
- package/src/lib/release-id/release-id.ts +257 -0
- package/src/lib/release-utils/__tests__/release-utils.test.ts +122 -0
- package/src/lib/release-utils/index.ts +4 -0
- package/src/lib/release-utils/release-utils.ts +85 -17
- package/src/lib/version-utils/__tests__/load-existing-versions.test.ts +37 -0
- package/src/lib/version-utils/__tests__/next-version.test.ts +119 -13
- package/src/lib/version-utils/index.ts +3 -0
- package/src/lib/version-utils/load-existing-versions.ts +29 -10
- package/src/lib/version-utils/next-version.ts +67 -12
- package/src/lib/version-utils/version-utils.ts +13 -4
- package/src/mcp/tools/index.ts +2 -0
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/src/types.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/src/lib/__tests__/infra-kit-config.test.ts +0 -231
- /package/src/integrations/{clickup → linear}/.gitkeep +0 -0
- /package/src/lib/{__tests__ → constants/__tests__}/constants.test.ts +0 -0
- /package/src/lib/{constants.ts → constants/constants.ts} +0 -0
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
2
|
import os from 'node:os'
|
|
3
3
|
import path from 'node:path'
|
|
4
|
-
import { z } from 'zod
|
|
4
|
+
import { z } from 'zod'
|
|
5
5
|
import { $ } from 'zx'
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
AGENTS_IMPORT_END,
|
|
9
|
+
AGENTS_IMPORT_START,
|
|
10
|
+
AGENTS_MARKER_END,
|
|
11
|
+
AGENTS_MARKER_START,
|
|
12
|
+
} from 'src/commands/init/agent-files'
|
|
7
13
|
import { MARKER_END, MARKER_START, buildShellBlock } from 'src/commands/init/init'
|
|
8
14
|
import { getProjectRoot } from 'src/lib/git-utils/git-utils'
|
|
9
15
|
import { getInfraKitConfig, getInfraKitConfigPaths, resetInfraKitConfigCache } from 'src/lib/infra-kit-config'
|
|
10
16
|
import { logger } from 'src/lib/logger'
|
|
17
|
+
import { hasManagedBlock } from 'src/lib/managed-block'
|
|
11
18
|
import { defineMcpTool, textContent } from 'src/types'
|
|
12
19
|
|
|
13
20
|
interface CheckResult {
|
|
@@ -108,7 +115,7 @@ const checkInfraKitConfigValid = async (): Promise<CheckResult> => {
|
|
|
108
115
|
return {
|
|
109
116
|
name,
|
|
110
117
|
status: 'pass',
|
|
111
|
-
message: 'infra-kit.
|
|
118
|
+
message: 'infra-kit.json is valid (user overrides applied if present)',
|
|
112
119
|
}
|
|
113
120
|
} catch (err) {
|
|
114
121
|
return { name, status: 'fail', message: (err as Error).message }
|
|
@@ -125,7 +132,7 @@ const checkInfraKitConfigValid = async (): Promise<CheckResult> => {
|
|
|
125
132
|
* // {
|
|
126
133
|
* // name: 'user override path',
|
|
127
134
|
* // status: 'pass',
|
|
128
|
-
* // message: '~/.infra-kit/projects/api/infra-kit.
|
|
135
|
+
* // message: '~/.infra-kit/projects/api/infra-kit.json (not yet created) — project: api',
|
|
129
136
|
* // }
|
|
130
137
|
*/
|
|
131
138
|
const checkUserOverridePath = async (): Promise<CheckResult> => {
|
|
@@ -196,11 +203,67 @@ const checkRtkConfigured = async (): Promise<CheckResult> => {
|
|
|
196
203
|
}
|
|
197
204
|
}
|
|
198
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Check that the repo agent-instruction files managed by `infra-kit init` exist:
|
|
208
|
+
* the `AGENTS.md` block, the `@AGENTS.md` import region in `CLAUDE.md`, and the
|
|
209
|
+
* `.cursor/rules` block. Presence only. Repo-gated: returns no checks when run
|
|
210
|
+
* outside an infra-kit repo so doctor never crashes there.
|
|
211
|
+
*/
|
|
212
|
+
export const checkAgentFiles = async (): Promise<CheckResult[]> => {
|
|
213
|
+
let mainConfigPath: string
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
mainConfigPath = (await getInfraKitConfigPaths()).main
|
|
217
|
+
} catch {
|
|
218
|
+
return []
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!fs.existsSync(mainConfigPath)) return []
|
|
222
|
+
|
|
223
|
+
const root = path.dirname(mainConfigPath)
|
|
224
|
+
|
|
225
|
+
const blockPresent = (relPath: string, start: string, end: string): boolean => {
|
|
226
|
+
const filePath = path.join(root, relPath)
|
|
227
|
+
const content = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : ''
|
|
228
|
+
|
|
229
|
+
return hasManagedBlock(content, start, end)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const entries = [
|
|
233
|
+
{
|
|
234
|
+
name: 'AGENTS.md block',
|
|
235
|
+
present: blockPresent('AGENTS.md', AGENTS_MARKER_START, AGENTS_MARKER_END),
|
|
236
|
+
okMessage: 'AGENTS.md block present',
|
|
237
|
+
missingMessage: 'infra-kit block missing from AGENTS.md. Run: infra-kit init',
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: 'CLAUDE.md import',
|
|
241
|
+
present: blockPresent('CLAUDE.md', AGENTS_IMPORT_START, AGENTS_IMPORT_END),
|
|
242
|
+
okMessage: 'CLAUDE.md imports @AGENTS.md',
|
|
243
|
+
missingMessage: '@AGENTS.md import block missing from CLAUDE.md. Run: infra-kit init',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: '.cursor/rules block',
|
|
247
|
+
present: blockPresent(path.join('.cursor', 'rules', 'infra-kit.mdc'), AGENTS_MARKER_START, AGENTS_MARKER_END),
|
|
248
|
+
okMessage: '.cursor/rules/infra-kit.mdc block present',
|
|
249
|
+
missingMessage: '.cursor/rules/infra-kit.mdc block missing. Run: infra-kit init',
|
|
250
|
+
},
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
return entries.map((entry): CheckResult => {
|
|
254
|
+
return {
|
|
255
|
+
name: entry.name,
|
|
256
|
+
status: entry.present ? 'pass' : 'fail',
|
|
257
|
+
message: entry.present ? entry.okMessage : entry.missingMessage,
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
}
|
|
261
|
+
|
|
199
262
|
/**
|
|
200
263
|
* Check installation and authentication status of gh, doppler, aws, and rtk CLIs
|
|
201
264
|
*/
|
|
202
265
|
export const doctor = async () => {
|
|
203
|
-
const
|
|
266
|
+
const baseChecks: CheckResult[] = await Promise.all([
|
|
204
267
|
checkCommand(
|
|
205
268
|
'gh installed',
|
|
206
269
|
['gh', '--version'],
|
|
@@ -251,6 +314,8 @@ export const doctor = async () => {
|
|
|
251
314
|
checkUserOverridePath(),
|
|
252
315
|
])
|
|
253
316
|
|
|
317
|
+
const checks: CheckResult[] = [...baseChecks, ...(await checkAgentFiles())]
|
|
318
|
+
|
|
254
319
|
logger.info('Doctor check results:\n')
|
|
255
320
|
|
|
256
321
|
for (const check of checks) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z } from 'zod
|
|
1
|
+
import { z } from 'zod'
|
|
2
2
|
|
|
3
3
|
import { getDopplerProject } from 'src/integrations/doppler/doppler-project'
|
|
4
4
|
import { getInfraKitConfig } from 'src/lib/infra-kit-config'
|
|
@@ -8,7 +8,7 @@ import { defineMcpTool, textContent } from 'src/types'
|
|
|
8
8
|
/**
|
|
9
9
|
* List available Doppler configs for the detected project.
|
|
10
10
|
*
|
|
11
|
-
* Purely local: reads infra-kit.
|
|
11
|
+
* Purely local: reads infra-kit.json and does not call Doppler. We intentionally
|
|
12
12
|
* do not run validateDopplerCliAndAuth here — users listing envs often do so
|
|
13
13
|
* before `doppler login`, and a spurious auth error would be misleading.
|
|
14
14
|
*/
|
|
@@ -38,7 +38,7 @@ export const envList = async () => {
|
|
|
38
38
|
export const envListMcpTool = defineMcpTool({
|
|
39
39
|
name: 'env-list',
|
|
40
40
|
description:
|
|
41
|
-
'List the environments the project is configured to support. Returns the `environments` list declared in infra-kit.
|
|
41
|
+
'List the environments the project is configured to support. Returns the `environments` list declared in infra-kit.json at the project root (not a live fetch from Doppler) plus the Doppler project name resolved from the same file. Read-only.',
|
|
42
42
|
inputSchema: {},
|
|
43
43
|
outputSchema: {
|
|
44
44
|
project: z.string().describe('Detected Doppler project name'),
|
|
@@ -3,7 +3,7 @@ import { Buffer } from 'node:buffer'
|
|
|
3
3
|
import fs from 'node:fs'
|
|
4
4
|
import path from 'node:path'
|
|
5
5
|
import process from 'node:process'
|
|
6
|
-
import { z } from 'zod
|
|
6
|
+
import { z } from 'zod'
|
|
7
7
|
import { $ } from 'zx'
|
|
8
8
|
|
|
9
9
|
import { validateDopplerCliAndAuth } from 'src/integrations/doppler'
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import checkbox from '@inquirer/checkbox'
|
|
3
3
|
import confirm from '@inquirer/confirm'
|
|
4
4
|
import process from 'node:process'
|
|
5
|
-
import { z } from 'zod
|
|
5
|
+
import { z } from 'zod'
|
|
6
6
|
import { $ } from 'zx'
|
|
7
7
|
|
|
8
8
|
import { getReleasePRsWithInfo } from 'src/integrations/gh'
|
|
9
9
|
import { commandEcho } from 'src/lib/command-echo'
|
|
10
10
|
import { OperationError } from 'src/lib/errors/operation-error'
|
|
11
11
|
import { logger } from 'src/lib/logger'
|
|
12
|
-
import { detectReleaseType, formatBranchChoices, getJiraDescriptions } from 'src/lib/release-utils'
|
|
12
|
+
import { detectReleaseType, formatBranchChoices, getJiraDescriptions, releaseBranchLabels } from 'src/lib/release-utils'
|
|
13
13
|
import { defineMcpTool, textContent } from 'src/types'
|
|
14
14
|
import type { RequiredConfirmedOptionArg } from 'src/types'
|
|
15
15
|
|
|
@@ -70,12 +70,7 @@ export const ghMergeDev = async (args: GhMergeDevArgs) => {
|
|
|
70
70
|
if (allSelected) {
|
|
71
71
|
commandEcho.addOption('--all', true)
|
|
72
72
|
} else {
|
|
73
|
-
commandEcho.addOption(
|
|
74
|
-
'--versions',
|
|
75
|
-
selectedReleaseBranches.map((branch) => {
|
|
76
|
-
return branch.replace('release/v', '')
|
|
77
|
-
}),
|
|
78
|
-
)
|
|
73
|
+
commandEcho.addOption('--versions', releaseBranchLabels(selectedReleaseBranches))
|
|
79
74
|
}
|
|
80
75
|
|
|
81
76
|
// Validate input
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import confirm from '@inquirer/confirm'
|
|
2
2
|
import select from '@inquirer/select'
|
|
3
3
|
import process from 'node:process'
|
|
4
|
-
import { z } from 'zod
|
|
4
|
+
import { z } from 'zod'
|
|
5
5
|
import { $ } from 'zx'
|
|
6
6
|
|
|
7
7
|
import { getReleasePRsWithInfo } from 'src/integrations/gh'
|
|
@@ -12,7 +12,14 @@ import { formatZxError } from 'src/lib/errors/format-zx-error'
|
|
|
12
12
|
import { OperationError } from 'src/lib/errors/operation-error'
|
|
13
13
|
import { getCurrentWorktrees, getProjectRoot, getRepoName } from 'src/lib/git-utils'
|
|
14
14
|
import { logger } from 'src/lib/logger'
|
|
15
|
-
import {
|
|
15
|
+
import { displayLabel, formatJiraName, formatRcTitle, parseBranchName } from 'src/lib/release-id'
|
|
16
|
+
import type { ReleaseId } from 'src/lib/release-id'
|
|
17
|
+
import {
|
|
18
|
+
detectReleaseType,
|
|
19
|
+
formatBranchChoices,
|
|
20
|
+
getJiraDescriptions,
|
|
21
|
+
resolveReleaseBranch,
|
|
22
|
+
} from 'src/lib/release-utils'
|
|
16
23
|
import type { ReleaseType } from 'src/lib/release-utils'
|
|
17
24
|
import { removeWorktrees } from 'src/lib/worktrees'
|
|
18
25
|
import { defineMcpTool, textContent } from 'src/types'
|
|
@@ -62,8 +69,8 @@ const fetchPRByHead = async (head: string): Promise<PRStatus | null> => {
|
|
|
62
69
|
* the merge step on resume. Title-matched on purpose: an older MERGED RC PR
|
|
63
70
|
* from a different release must not short-circuit this version's flow.
|
|
64
71
|
*/
|
|
65
|
-
const fetchMergedRcPRForVersion = async (
|
|
66
|
-
const expectedTitle =
|
|
72
|
+
const fetchMergedRcPRForVersion = async (id: ReleaseId): Promise<PRStatus | null> => {
|
|
73
|
+
const expectedTitle = formatRcTitle(id)
|
|
67
74
|
const result = await $`gh pr list --head dev --base main --state merged --json number,state,title --limit 20`
|
|
68
75
|
const prs = JSON.parse(result.stdout) as PRStatus[]
|
|
69
76
|
const match = prs.find((pr) => {
|
|
@@ -92,7 +99,7 @@ interface ResolvedTarget {
|
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
const resolveTargetFromVersion = async (version: string): Promise<ResolvedTarget> => {
|
|
95
|
-
const selectedReleaseBranch =
|
|
102
|
+
const selectedReleaseBranch = resolveReleaseBranch(version)
|
|
96
103
|
const pr = await fetchPRByHead(selectedReleaseBranch)
|
|
97
104
|
|
|
98
105
|
if (!pr) {
|
|
@@ -208,8 +215,9 @@ const mergeReleasePR = async (args: MergeReleasePRArgs): Promise<void> => {
|
|
|
208
215
|
)
|
|
209
216
|
}
|
|
210
217
|
|
|
211
|
-
const resolveRcPRNumber = async (
|
|
212
|
-
const
|
|
218
|
+
const resolveRcPRNumber = async (id: ReleaseId): Promise<number> => {
|
|
219
|
+
const selectedLabel = displayLabel(id)
|
|
220
|
+
const expectedTitle = formatRcTitle(id)
|
|
213
221
|
const existingOpen = await fetchOpenDevToMainPR()
|
|
214
222
|
|
|
215
223
|
// Adopt any existing open dev→main PR. GitHub permits only one open PR per
|
|
@@ -221,7 +229,7 @@ const resolveRcPRNumber = async (selectedVersion: string): Promise<number> => {
|
|
|
221
229
|
|
|
222
230
|
if (existingOpen.title !== expectedTitle) {
|
|
223
231
|
logger.info(
|
|
224
|
-
`Adopting open dev → main PR #${rcNumber} ("${existingOpen.title}") and retitling for
|
|
232
|
+
`Adopting open dev → main PR #${rcNumber} ("${existingOpen.title}") and retitling for ${selectedLabel}`,
|
|
225
233
|
)
|
|
226
234
|
await runStep(
|
|
227
235
|
`retitle dev → main PR #${rcNumber} to "${expectedTitle}"`,
|
|
@@ -236,7 +244,7 @@ const resolveRcPRNumber = async (selectedVersion: string): Promise<number> => {
|
|
|
236
244
|
}
|
|
237
245
|
|
|
238
246
|
await runStep(
|
|
239
|
-
`create RC PR (dev → main) for
|
|
247
|
+
`create RC PR (dev → main) for ${selectedLabel}`,
|
|
240
248
|
`run 'gh pr create --base main --head dev' manually to surface the underlying error (e.g. no commits between dev and main)`,
|
|
241
249
|
async () => {
|
|
242
250
|
await $`gh pr create --base main --head dev --title ${expectedTitle} --body ""`
|
|
@@ -247,7 +255,7 @@ const resolveRcPRNumber = async (selectedVersion: string): Promise<number> => {
|
|
|
247
255
|
|
|
248
256
|
if (!created) {
|
|
249
257
|
throw new OperationError(undefined, {
|
|
250
|
-
operation: `look up RC PR for
|
|
258
|
+
operation: `look up RC PR for ${selectedLabel}`,
|
|
251
259
|
remediation: `verify the RC PR was created ('gh pr list --head dev --base main')`,
|
|
252
260
|
})
|
|
253
261
|
}
|
|
@@ -255,19 +263,20 @@ const resolveRcPRNumber = async (selectedVersion: string): Promise<number> => {
|
|
|
255
263
|
return created.number
|
|
256
264
|
}
|
|
257
265
|
|
|
258
|
-
const ensureRcPRMerged = async (
|
|
259
|
-
const
|
|
266
|
+
const ensureRcPRMerged = async (id: ReleaseId): Promise<void> => {
|
|
267
|
+
const selectedLabel = displayLabel(id)
|
|
268
|
+
const alreadyMerged = await fetchMergedRcPRForVersion(id)
|
|
260
269
|
|
|
261
270
|
if (alreadyMerged) {
|
|
262
|
-
logger.info(`✓ RC PR for
|
|
271
|
+
logger.info(`✓ RC PR for ${selectedLabel} already merged into main — skipping`)
|
|
263
272
|
|
|
264
273
|
return
|
|
265
274
|
}
|
|
266
275
|
|
|
267
|
-
const rcNumber = await resolveRcPRNumber(
|
|
276
|
+
const rcNumber = await resolveRcPRNumber(id)
|
|
268
277
|
|
|
269
278
|
await runStep(
|
|
270
|
-
`merge RC PR #${rcNumber} (dev → main) for
|
|
279
|
+
`merge RC PR #${rcNumber} (dev → main) for ${selectedLabel}`,
|
|
271
280
|
`check 'gh pr view ${rcNumber}' for mergeability and required reviews`,
|
|
272
281
|
async () => {
|
|
273
282
|
await $`gh pr merge ${rcNumber} --squash --admin`
|
|
@@ -299,7 +308,7 @@ const syncMainIntoDev = async (): Promise<void> => {
|
|
|
299
308
|
)
|
|
300
309
|
}
|
|
301
310
|
|
|
302
|
-
const deliverJiraReleaseSafely = async (
|
|
311
|
+
const deliverJiraReleaseSafely = async (id: ReleaseId): Promise<void> => {
|
|
303
312
|
const jiraConfig = await loadJiraConfigOptional()
|
|
304
313
|
|
|
305
314
|
if (!jiraConfig) {
|
|
@@ -309,7 +318,8 @@ const deliverJiraReleaseSafely = async (selectedReleaseBranch: string): Promise<
|
|
|
309
318
|
}
|
|
310
319
|
|
|
311
320
|
try {
|
|
312
|
-
|
|
321
|
+
// Jira fix version name: `v1.2.3` | `<name>` — must match create-time formatJiraName.
|
|
322
|
+
const versionName = formatJiraName(id)
|
|
313
323
|
|
|
314
324
|
await deliverJiraRelease({ versionName }, jiraConfig)
|
|
315
325
|
} catch (error) {
|
|
@@ -333,9 +343,21 @@ export const ghReleaseDeliver = async (args: GhReleaseDeliverArgs) => {
|
|
|
333
343
|
? await resolveTargetFromVersion(version)
|
|
334
344
|
: await resolveTargetInteractively()
|
|
335
345
|
|
|
336
|
-
|
|
346
|
+
// selectedReleaseBranch is always a release branch (operator ref strictly
|
|
347
|
+
// parsed, or picked from discovery-filtered choices) so this cannot be null.
|
|
348
|
+
const releaseId = parseBranchName(selectedReleaseBranch)
|
|
349
|
+
|
|
350
|
+
if (!releaseId) {
|
|
351
|
+
throw new OperationError(undefined, {
|
|
352
|
+
operation: `deliver release ${selectedReleaseBranch}`,
|
|
353
|
+
remediation: 'pass a version (e.g. "1.2.5") or a release name (e.g. "checkout-redesign")',
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const selectedVersion = displayLabel(releaseId)
|
|
337
358
|
|
|
338
359
|
commandEcho.addOption('--version', selectedVersion)
|
|
360
|
+
logger.info(`Delivering ${releaseId.kind === 'name' ? 'named release' : 'version'} ${selectedReleaseBranch}`)
|
|
339
361
|
|
|
340
362
|
const releaseType: ReleaseType = detectReleaseType(releasePrTitle)
|
|
341
363
|
|
|
@@ -363,7 +385,7 @@ export const ghReleaseDeliver = async (args: GhReleaseDeliverArgs) => {
|
|
|
363
385
|
await mergeReleasePR({ selectedReleaseBranch, releaseType })
|
|
364
386
|
|
|
365
387
|
if (releaseType !== 'hotfix') {
|
|
366
|
-
await ensureRcPRMerged(
|
|
388
|
+
await ensureRcPRMerged(releaseId)
|
|
367
389
|
}
|
|
368
390
|
|
|
369
391
|
await dispatchDeployWorkflow()
|
|
@@ -371,7 +393,7 @@ export const ghReleaseDeliver = async (args: GhReleaseDeliverArgs) => {
|
|
|
371
393
|
|
|
372
394
|
$.quiet = false
|
|
373
395
|
|
|
374
|
-
await deliverJiraReleaseSafely(
|
|
396
|
+
await deliverJiraReleaseSafely(releaseId)
|
|
375
397
|
|
|
376
398
|
logger.info(`Successfully delivered ${selectedReleaseBranch} to production!`)
|
|
377
399
|
|
|
@@ -396,7 +418,11 @@ export const ghReleaseDeliverMcpTool = defineMcpTool({
|
|
|
396
418
|
description:
|
|
397
419
|
'Deliver a release to production. For hotfixes: squash-merges the release branch to main and dispatches the deploy-all workflow. For regular releases: squash-merges to dev, opens an RC PR, merges dev into main, dispatches the deploy-all workflow, then syncs main back to dev. Also releases the matching Jira fix version if Jira is configured. Dispatches the deploy workflow fire-and-forget — the tool returns once the workflow is accepted by GitHub, not when the deployment finishes. PR-merge steps are idempotent: re-running after a partial failure skips PRs that are already merged. Irreversible production operation: the confirmation prompt is auto-skipped for MCP calls, so the caller is responsible for gating. "version" is required when invoked via MCP (the picker is unreachable without a TTY).',
|
|
398
420
|
inputSchema: {
|
|
399
|
-
version: z
|
|
421
|
+
version: z
|
|
422
|
+
.string()
|
|
423
|
+
.describe(
|
|
424
|
+
'Accepts a release version (e.g. "1.2.5") OR a release name (e.g. "checkout-redesign") to deliver to production. Required for MCP calls.',
|
|
425
|
+
),
|
|
400
426
|
},
|
|
401
427
|
outputSchema: {
|
|
402
428
|
releaseBranch: z.string().describe('The release branch that was delivered'),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import select from '@inquirer/select'
|
|
2
|
-
import { z } from 'zod
|
|
2
|
+
import { z } from 'zod'
|
|
3
3
|
import { $ } from 'zx'
|
|
4
4
|
|
|
5
5
|
import { getReleasePRsWithInfo } from 'src/integrations/gh'
|
|
@@ -7,7 +7,13 @@ import { commandEcho } from 'src/lib/command-echo'
|
|
|
7
7
|
import { OperationError } from 'src/lib/errors/operation-error'
|
|
8
8
|
import { getInfraKitConfig } from 'src/lib/infra-kit-config'
|
|
9
9
|
import { logger } from 'src/lib/logger'
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
detectReleaseType,
|
|
12
|
+
formatBranchChoices,
|
|
13
|
+
getJiraDescriptions,
|
|
14
|
+
releaseLabelFromBranch,
|
|
15
|
+
resolveReleaseBranch,
|
|
16
|
+
} from 'src/lib/release-utils'
|
|
11
17
|
import type { ReleaseType } from 'src/lib/release-utils'
|
|
12
18
|
import { defineMcpTool, textContent } from 'src/types'
|
|
13
19
|
|
|
@@ -25,10 +31,10 @@ export const ghReleaseDeployAll = async (args: GhReleaseDeployAllArgs) => {
|
|
|
25
31
|
|
|
26
32
|
commandEcho.start('release-deploy-all')
|
|
27
33
|
|
|
28
|
-
let selectedReleaseBranch = '' // "release/v1.8.0"
|
|
34
|
+
let selectedReleaseBranch = '' // "release/v1.8.0" | "release/n/checkout-redesign" | "dev"
|
|
29
35
|
|
|
30
36
|
if (version) {
|
|
31
|
-
selectedReleaseBranch = version === 'dev' ? 'dev' :
|
|
37
|
+
selectedReleaseBranch = version === 'dev' ? 'dev' : resolveReleaseBranch(version)
|
|
32
38
|
} else {
|
|
33
39
|
commandEcho.setInteractive()
|
|
34
40
|
|
|
@@ -52,7 +58,7 @@ export const ghReleaseDeployAll = async (args: GhReleaseDeployAllArgs) => {
|
|
|
52
58
|
})
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
const selectedVersion = selectedReleaseBranch
|
|
61
|
+
const selectedVersion = releaseLabelFromBranch(selectedReleaseBranch)
|
|
56
62
|
|
|
57
63
|
commandEcho.addOption('--version', selectedVersion)
|
|
58
64
|
|
|
@@ -109,7 +115,7 @@ export const ghReleaseDeployAll = async (args: GhReleaseDeployAllArgs) => {
|
|
|
109
115
|
|
|
110
116
|
const structuredContent = {
|
|
111
117
|
releaseBranch: selectedReleaseBranch,
|
|
112
|
-
version:
|
|
118
|
+
version: selectedVersion,
|
|
113
119
|
environment: selectedEnv,
|
|
114
120
|
skipTerraformDeploy: shouldSkipTerraform,
|
|
115
121
|
success: true,
|
|
@@ -137,7 +143,7 @@ export const ghReleaseDeployAllMcpTool = defineMcpTool({
|
|
|
137
143
|
version: z
|
|
138
144
|
.string()
|
|
139
145
|
.describe(
|
|
140
|
-
'
|
|
146
|
+
'Accepts a release version (e.g. "1.2.5") OR a release name (e.g. "checkout-redesign") — resolves to the release/vX.Y.Z or release/n/<name> branch. Pass "dev" to deploy from the dev branch instead. Required for MCP calls.',
|
|
141
147
|
),
|
|
142
148
|
env: z
|
|
143
149
|
.string()
|
|
@@ -3,7 +3,7 @@ import select from '@inquirer/select'
|
|
|
3
3
|
import fs from 'node:fs/promises'
|
|
4
4
|
import { resolve } from 'node:path'
|
|
5
5
|
import yaml from 'yaml'
|
|
6
|
-
import { z } from 'zod
|
|
6
|
+
import { z } from 'zod'
|
|
7
7
|
import { $ } from 'zx'
|
|
8
8
|
|
|
9
9
|
import { getReleasePRsWithInfo } from 'src/integrations/gh'
|
|
@@ -12,7 +12,13 @@ import { OperationError } from 'src/lib/errors/operation-error'
|
|
|
12
12
|
import { getProjectRoot } from 'src/lib/git-utils'
|
|
13
13
|
import { getInfraKitConfig } from 'src/lib/infra-kit-config'
|
|
14
14
|
import { logger } from 'src/lib/logger'
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
detectReleaseType,
|
|
17
|
+
formatBranchChoices,
|
|
18
|
+
getJiraDescriptions,
|
|
19
|
+
releaseLabelFromBranch,
|
|
20
|
+
resolveReleaseBranch,
|
|
21
|
+
} from 'src/lib/release-utils'
|
|
16
22
|
import type { ReleaseType } from 'src/lib/release-utils'
|
|
17
23
|
import { defineMcpTool, textContent } from 'src/types'
|
|
18
24
|
|
|
@@ -35,7 +41,7 @@ export const ghReleaseDeploySelected = async (args: GhReleaseDeploySelectedArgs)
|
|
|
35
41
|
let selectedReleaseBranch = ''
|
|
36
42
|
|
|
37
43
|
if (version) {
|
|
38
|
-
selectedReleaseBranch = version === 'dev' ? 'dev' :
|
|
44
|
+
selectedReleaseBranch = version === 'dev' ? 'dev' : resolveReleaseBranch(version)
|
|
39
45
|
} else {
|
|
40
46
|
commandEcho.setInteractive()
|
|
41
47
|
|
|
@@ -59,7 +65,7 @@ export const ghReleaseDeploySelected = async (args: GhReleaseDeploySelectedArgs)
|
|
|
59
65
|
})
|
|
60
66
|
}
|
|
61
67
|
|
|
62
|
-
const selectedVersion = selectedReleaseBranch
|
|
68
|
+
const selectedVersion = releaseLabelFromBranch(selectedReleaseBranch)
|
|
63
69
|
|
|
64
70
|
commandEcho.addOption('--version', selectedVersion)
|
|
65
71
|
|
|
@@ -172,7 +178,7 @@ export const ghReleaseDeploySelected = async (args: GhReleaseDeploySelectedArgs)
|
|
|
172
178
|
|
|
173
179
|
const structuredContent = {
|
|
174
180
|
releaseBranch: selectedReleaseBranch,
|
|
175
|
-
version:
|
|
181
|
+
version: selectedVersion,
|
|
176
182
|
environment: selectedEnv,
|
|
177
183
|
services: selectedServices,
|
|
178
184
|
skipTerraformDeploy: shouldSkipTerraform,
|
|
@@ -226,7 +232,7 @@ export const ghReleaseDeploySelectedMcpTool = defineMcpTool({
|
|
|
226
232
|
version: z
|
|
227
233
|
.string()
|
|
228
234
|
.describe(
|
|
229
|
-
'
|
|
235
|
+
'Accepts a release version (e.g. "1.2.5") OR a release name (e.g. "checkout-redesign") — resolves to the release/vX.Y.Z or release/n/<name> branch. Pass "dev" to deploy from the dev branch instead. Required for MCP calls.',
|
|
230
236
|
),
|
|
231
237
|
env: z
|
|
232
238
|
.string()
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { z } from 'zod
|
|
1
|
+
import { z } from 'zod'
|
|
2
2
|
|
|
3
3
|
import { getReleasePRsWithInfo } from 'src/integrations/gh'
|
|
4
4
|
import { logger } from 'src/lib/logger'
|
|
5
|
+
import { displayLabel, formatJiraName, parseBranchName } from 'src/lib/release-id'
|
|
5
6
|
import { detectReleaseType, formatVersionLabel, getJiraDescriptions } from 'src/lib/release-utils'
|
|
6
7
|
import { defineMcpTool, textContent } from 'src/types'
|
|
7
8
|
|
|
@@ -11,11 +12,21 @@ import { defineMcpTool, textContent } from 'src/types'
|
|
|
11
12
|
export const ghReleaseList = async () => {
|
|
12
13
|
const releasePRs = await getReleasePRsWithInfo()
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
// Skip branches that do not parse as release ids (lenient discovery source).
|
|
16
|
+
const releases = releasePRs.flatMap((pr) => {
|
|
17
|
+
const id = parseBranchName(pr.branch)
|
|
18
|
+
|
|
19
|
+
if (!id) return []
|
|
20
|
+
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
// Human display label: `1.2.3` | `<name>`.
|
|
24
|
+
version: displayLabel(id),
|
|
25
|
+
// Jira-descriptions map is keyed by the Jira version NAME (`v1.2.3` | `<name>`).
|
|
26
|
+
jiraKey: formatJiraName(id),
|
|
27
|
+
type: detectReleaseType(pr.title),
|
|
28
|
+
},
|
|
29
|
+
]
|
|
19
30
|
})
|
|
20
31
|
|
|
21
32
|
const jiraDescriptions = await getJiraDescriptions()
|
|
@@ -28,7 +39,7 @@ export const ghReleaseList = async () => {
|
|
|
28
39
|
|
|
29
40
|
const formattedLines = releases.map((release) => {
|
|
30
41
|
const label = formatVersionLabel(release.version, release.type, maxVersionLength)
|
|
31
|
-
const description = jiraDescriptions.get(release.
|
|
42
|
+
const description = jiraDescriptions.get(release.jiraKey)
|
|
32
43
|
|
|
33
44
|
if (description) {
|
|
34
45
|
return `${label} ${description}`
|
|
@@ -45,7 +56,7 @@ export const ghReleaseList = async () => {
|
|
|
45
56
|
return {
|
|
46
57
|
version: release.version,
|
|
47
58
|
type: release.type,
|
|
48
|
-
description: jiraDescriptions.get(release.
|
|
59
|
+
description: jiraDescriptions.get(release.jiraKey) || null,
|
|
49
60
|
}
|
|
50
61
|
}),
|
|
51
62
|
count: releases.length,
|