infra-kit 0.1.91 → 0.1.94
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/.turbo/turbo-eslint-check.log +1 -1
- package/.turbo/turbo-prettier-check.log +1 -1
- package/.turbo/turbo-test.log +3 -3
- package/.turbo/turbo-ts-check.log +1 -1
- package/dist/cli.js +31 -27
- package/dist/cli.js.map +4 -4
- package/dist/mcp.js +26 -22
- package/dist/mcp.js.map +4 -4
- package/package.json +1 -1
- package/src/commands/worktrees-add/worktrees-add.ts +3 -3
- package/src/commands/worktrees-remove/worktrees-remove.ts +22 -3
- package/src/integrations/cmux/close-workspace-by-title.ts +51 -0
- package/src/integrations/cmux/index.ts +2 -0
- package/src/integrations/cmux/open-workspace-with-layout.ts +30 -2
- package/src/integrations/cmux/workspace-title.ts +17 -0
- package/src/integrations/gh/gh-release-prs/gh-release-prs.ts +5 -2
- package/src/lib/release-utils/release-utils.ts +1 -1
- package/src/mcp/tools/index.ts +1 -0
- package/tsconfig.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import process from 'node:process'
|
|
|
5
5
|
import { z } from 'zod'
|
|
6
6
|
import { $ } from 'zx'
|
|
7
7
|
|
|
8
|
-
import { openCmuxWorkspaceWithLayout } from 'src/integrations/cmux'
|
|
8
|
+
import { buildCmuxWorkspaceTitle, openCmuxWorkspaceWithLayout } from 'src/integrations/cmux'
|
|
9
9
|
import { getReleasePRsWithInfo } from 'src/integrations/gh'
|
|
10
10
|
import { commandEcho } from 'src/lib/command-echo'
|
|
11
11
|
import { WORKTREES_DIR_SUFFIX } from 'src/lib/constants'
|
|
@@ -187,11 +187,11 @@ export const worktreesAdd = async (options: WorktreeManagementArgs): Promise<Too
|
|
|
187
187
|
const repoName = await getRepoName()
|
|
188
188
|
|
|
189
189
|
for (const branch of createdWorktrees) {
|
|
190
|
-
const
|
|
190
|
+
const title = buildCmuxWorkspaceTitle({ repoName, branch })
|
|
191
191
|
|
|
192
192
|
await openCmuxWorkspaceWithLayout({
|
|
193
193
|
cwd: `${worktreeDir}/${branch}`,
|
|
194
|
-
title
|
|
194
|
+
title,
|
|
195
195
|
})
|
|
196
196
|
}
|
|
197
197
|
}
|
|
@@ -4,10 +4,11 @@ import process from 'node:process'
|
|
|
4
4
|
import { z } from 'zod'
|
|
5
5
|
import { $ } from 'zx'
|
|
6
6
|
|
|
7
|
+
import { buildCmuxWorkspaceTitle, closeCmuxWorkspaceByTitle } from 'src/integrations/cmux'
|
|
7
8
|
import { getReleasePRsWithInfo } from 'src/integrations/gh'
|
|
8
9
|
import { commandEcho } from 'src/lib/command-echo'
|
|
9
10
|
import { WORKTREES_DIR_SUFFIX } from 'src/lib/constants'
|
|
10
|
-
import { getCurrentWorktrees, getProjectRoot } from 'src/lib/git-utils'
|
|
11
|
+
import { getCurrentWorktrees, getProjectRoot, getRepoName } from 'src/lib/git-utils'
|
|
11
12
|
import { logger } from 'src/lib/logger'
|
|
12
13
|
import { detectReleaseType, formatBranchChoices, getJiraDescriptions } from 'src/lib/release-utils'
|
|
13
14
|
import type { ReleaseType } from 'src/lib/release-utils'
|
|
@@ -107,7 +108,13 @@ export const worktreesRemove = async (options: WorktreeManagementArgs): Promise<
|
|
|
107
108
|
commandEcho.addOption('--yes', true)
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
const
|
|
111
|
+
const repoName = await getRepoName()
|
|
112
|
+
|
|
113
|
+
const removedWorktrees = await removeWorktrees({
|
|
114
|
+
branches: selectedReleaseBranches,
|
|
115
|
+
worktreeDir,
|
|
116
|
+
repoName,
|
|
117
|
+
})
|
|
111
118
|
|
|
112
119
|
logResults(removedWorktrees)
|
|
113
120
|
|
|
@@ -133,14 +140,26 @@ export const worktreesRemove = async (options: WorktreeManagementArgs): Promise<
|
|
|
133
140
|
}
|
|
134
141
|
}
|
|
135
142
|
|
|
143
|
+
interface RemoveWorktreesArgs {
|
|
144
|
+
branches: string[]
|
|
145
|
+
worktreeDir: string
|
|
146
|
+
repoName: string
|
|
147
|
+
}
|
|
148
|
+
|
|
136
149
|
/**
|
|
137
150
|
* Remove worktrees for the specified branches and whole folder
|
|
138
151
|
*/
|
|
139
|
-
const removeWorktrees = async (
|
|
152
|
+
const removeWorktrees = async (args: RemoveWorktreesArgs): Promise<string[]> => {
|
|
153
|
+
const { branches, worktreeDir, repoName } = args
|
|
154
|
+
|
|
140
155
|
const results = await Promise.allSettled(
|
|
141
156
|
branches.map(async (branch) => {
|
|
142
157
|
const worktreePath = `${worktreeDir}/${branch}`
|
|
143
158
|
|
|
159
|
+
const title = buildCmuxWorkspaceTitle({ repoName, branch })
|
|
160
|
+
|
|
161
|
+
await closeCmuxWorkspaceByTitle(title)
|
|
162
|
+
|
|
144
163
|
await $`git worktree remove ${worktreePath}`
|
|
145
164
|
|
|
146
165
|
return branch
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { $ } from 'zx'
|
|
2
|
+
|
|
3
|
+
import { logger } from 'src/lib/logger'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Best-effort close of the cmux workspace whose title exactly matches `title`.
|
|
7
|
+
* Silently no-ops if cmux isn't running, the workspace isn't found, or close fails.
|
|
8
|
+
*/
|
|
9
|
+
export const closeCmuxWorkspaceByTitle = async (title: string): Promise<void> => {
|
|
10
|
+
try {
|
|
11
|
+
const listOutput = (await $`cmux list-workspaces`.quiet()).stdout
|
|
12
|
+
|
|
13
|
+
const ref = findWorkspaceRefByTitle(listOutput, title)
|
|
14
|
+
|
|
15
|
+
if (!ref) {
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
await $`cmux close-workspace --workspace ${ref}`.quiet()
|
|
20
|
+
} catch (error) {
|
|
21
|
+
logger.debug({ error, title }, 'cmux: skipped closing workspace')
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Parses `cmux list-workspaces` output and returns the workspace ref whose
|
|
27
|
+
* title exactly matches `title`, or undefined if no match.
|
|
28
|
+
*
|
|
29
|
+
* Each line looks like:
|
|
30
|
+
* " workspace:8 hulyo-monorepo v1.48.0"
|
|
31
|
+
* "* workspace:6 obsidian-workspace [selected]"
|
|
32
|
+
*/
|
|
33
|
+
const findWorkspaceRefByTitle = (output: string, title: string): string | undefined => {
|
|
34
|
+
for (const rawLine of output.split('\n')) {
|
|
35
|
+
// eslint-disable-next-line sonarjs/slow-regex, regexp/no-super-linear-backtracking
|
|
36
|
+
const match = rawLine.match(/^[* ]\s*(workspace:\d+)\s+(.+?)(?:\s+\[selected\])?\s*$/)
|
|
37
|
+
|
|
38
|
+
if (!match) {
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const ref = match[1]
|
|
43
|
+
const lineTitle = match[2]?.trim() ?? ''
|
|
44
|
+
|
|
45
|
+
if (lineTitle === title) {
|
|
46
|
+
return ref
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return undefined
|
|
51
|
+
}
|
|
@@ -14,9 +14,9 @@ interface OpenCmuxWorkspaceArgs {
|
|
|
14
14
|
export const openCmuxWorkspaceWithLayout = async (args: OpenCmuxWorkspaceArgs): Promise<void> => {
|
|
15
15
|
const { cwd, title } = args
|
|
16
16
|
|
|
17
|
-
await $`cmux new-workspace --cwd ${cwd}`
|
|
17
|
+
const newWorkspaceOutput = (await $`cmux new-workspace --cwd ${cwd}`).stdout
|
|
18
18
|
|
|
19
|
-
const workspaceRef = (
|
|
19
|
+
const workspaceRef = parseWorkspaceRef(newWorkspaceOutput)
|
|
20
20
|
|
|
21
21
|
const surfacesOutput = (await $`cmux list-pane-surfaces --workspace ${workspaceRef}`).stdout
|
|
22
22
|
|
|
@@ -30,6 +30,15 @@ export const openCmuxWorkspaceWithLayout = async (args: OpenCmuxWorkspaceArgs):
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Extracts the first `surface:<id>` reference from the output of
|
|
35
|
+
* `cmux list-pane-surfaces`. Used to locate the initial (primary) pane
|
|
36
|
+
* surface so subsequent splits can be anchored relative to it.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const output = 'surface:12 (active)\nsurface:13\n'
|
|
40
|
+
* parseFirstSurfaceRef(output) // => 'surface:12'
|
|
41
|
+
*/
|
|
33
42
|
const parseFirstSurfaceRef = (output: string): string => {
|
|
34
43
|
const match = output.match(/surface:\d+/)
|
|
35
44
|
|
|
@@ -39,3 +48,22 @@ const parseFirstSurfaceRef = (output: string): string => {
|
|
|
39
48
|
|
|
40
49
|
return match[0]
|
|
41
50
|
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Extracts the `workspace:<id>` reference from the output of
|
|
54
|
+
* `cmux new-workspace`. The returned ref is used to target the newly
|
|
55
|
+
* created workspace in follow-up `cmux` commands (splits, rename, etc.).
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* const output = 'created workspace:7\n'
|
|
59
|
+
* parseWorkspaceRef(output) // => 'workspace:7'
|
|
60
|
+
*/
|
|
61
|
+
const parseWorkspaceRef = (output: string): string => {
|
|
62
|
+
const match = output.match(/workspace:\d+/)
|
|
63
|
+
|
|
64
|
+
if (!match) {
|
|
65
|
+
throw new Error('cmux: could not locate workspace ref in new-workspace output')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return match[0]
|
|
69
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface BuildCmuxWorkspaceTitleArgs {
|
|
2
|
+
repoName: string
|
|
3
|
+
branch: string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Builds the cmux workspace title used by `worktrees-add` and looked up by
|
|
8
|
+
* `worktrees-remove`. The `release/` prefix is stripped so the title reads
|
|
9
|
+
* e.g. `"hulyo-monorepo v1.48.0"` for branch `"release/v1.48.0"`.
|
|
10
|
+
*/
|
|
11
|
+
export const buildCmuxWorkspaceTitle = (args: BuildCmuxWorkspaceTitleArgs): string => {
|
|
12
|
+
const { repoName, branch } = args
|
|
13
|
+
|
|
14
|
+
const version = branch.replace('release/', '')
|
|
15
|
+
|
|
16
|
+
return `${repoName} ${version}`
|
|
17
|
+
}
|
|
@@ -113,18 +113,21 @@ interface CreateReleaseBranchArgs {
|
|
|
113
113
|
version: string
|
|
114
114
|
jiraVersionUrl: string
|
|
115
115
|
type: ReleaseType
|
|
116
|
+
description?: string
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
// Function to create a release branch
|
|
119
120
|
export const createReleaseBranch = async (
|
|
120
121
|
args: CreateReleaseBranchArgs,
|
|
121
122
|
): Promise<{ branchName: string; prUrl: string }> => {
|
|
122
|
-
const { version, jiraVersionUrl, type } = args
|
|
123
|
+
const { version, jiraVersionUrl, type, description } = args
|
|
123
124
|
const titlePrefix = type === 'hotfix' ? 'Hotfix' : 'Release'
|
|
124
125
|
const baseBranch = getBaseBranch(type)
|
|
125
126
|
|
|
126
127
|
const branchName = `release/v${version}`
|
|
127
128
|
|
|
129
|
+
const body = description && description.trim() !== '' ? `${jiraVersionUrl}\n\n${description}` : `${jiraVersionUrl} \n`
|
|
130
|
+
|
|
128
131
|
try {
|
|
129
132
|
$.quiet = true
|
|
130
133
|
|
|
@@ -137,7 +140,7 @@ export const createReleaseBranch = async (
|
|
|
137
140
|
|
|
138
141
|
// Create PR and capture URL
|
|
139
142
|
const prResult =
|
|
140
|
-
await $`gh pr create --title "${titlePrefix} v${version}" --body
|
|
143
|
+
await $`gh pr create --title "${titlePrefix} v${version}" --body ${body} --base ${baseBranch} --head ${branchName}`
|
|
141
144
|
|
|
142
145
|
const prLink = prResult.stdout.trim()
|
|
143
146
|
|
|
@@ -68,7 +68,7 @@ export const createSingleRelease = async (args: CreateSingleReleaseArgs): Promis
|
|
|
68
68
|
const jiraVersionUrl = `${jiraConfig.baseUrl}/projects/${result.version!.projectId}/versions/${result.version!.id}/tab/release-report-all-issues`
|
|
69
69
|
|
|
70
70
|
// 2. Create GitHub release branch
|
|
71
|
-
const releaseInfo = await createReleaseBranch({ version, jiraVersionUrl, type })
|
|
71
|
+
const releaseInfo = await createReleaseBranch({ version, jiraVersionUrl, type, description })
|
|
72
72
|
|
|
73
73
|
return {
|
|
74
74
|
version,
|
package/src/mcp/tools/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { ghReleaseDeploySelectedMcpTool } from 'src/commands/gh-release-deploy-s
|
|
|
11
11
|
import { ghReleaseListMcpTool } from 'src/commands/gh-release-list'
|
|
12
12
|
import { releaseCreateMcpTool } from 'src/commands/release-create'
|
|
13
13
|
import { releaseCreateBatchMcpTool } from 'src/commands/release-create-batch'
|
|
14
|
+
import { versionMcpTool } from 'src/commands/version'
|
|
14
15
|
import { worktreesAddMcpTool } from 'src/commands/worktrees-add'
|
|
15
16
|
import { worktreesListMcpTool } from 'src/commands/worktrees-list'
|
|
16
17
|
import { worktreesRemoveMcpTool } from 'src/commands/worktrees-remove'
|