infra-kit 0.1.68 โ 0.1.71
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/dist/cli.js +14 -14
- package/dist/cli.js.map +3 -3
- package/dist/mcp.js +14 -14
- package/dist/mcp.js.map +3 -3
- package/package.json +4 -4
- package/src/commands/gh-merge-dev/gh-merge-dev.ts +6 -4
- package/src/commands/gh-release-deliver/gh-release-deliver.ts +6 -4
- package/src/commands/gh-release-deploy-all/gh-release-deploy-all.ts +13 -8
- package/src/commands/gh-release-deploy-selected/gh-release-deploy-selected.ts +24 -14
- package/src/commands/gh-release-deploy-service/gh-release-deploy-service.ts +19 -12
- package/src/commands/gh-release-list/gh-release-list.ts +3 -1
- package/src/commands/release-create-batch/release-create-batch.ts +6 -2
- package/src/commands/worktrees-add/worktrees-add.ts +43 -9
- package/src/commands/worktrees-list/worktrees-list.ts +19 -5
- package/src/commands/worktrees-remove/worktrees-remove.ts +7 -4
- package/src/commands/worktrees-sync/worktrees-sync.ts +7 -2
- package/src/entry/cli.ts +2 -1
- package/src/integrations/gh/gh-release-prs/gh-release-prs.ts +4 -1
- package/src/integrations/jira/api.ts +8 -2
- package/src/lib/git-utils/git-utils.ts +3 -1
- package/src/lib/tool-handler/tool-handler.ts +16 -14
- package/src/lib/version-utils/version-utils.ts +1 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infra-kit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.71",
|
|
5
5
|
"description": "infra-kit",
|
|
6
6
|
"main": "dist/cli.js",
|
|
7
7
|
"module": "dist/cli.js",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"@inquirer/select": "^5.0.4",
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
37
37
|
"commander": "^14.0.3",
|
|
38
|
-
"dotenv": "^17.2.
|
|
39
|
-
"pino": "^10.3.
|
|
38
|
+
"dotenv": "^17.2.4",
|
|
39
|
+
"pino": "^10.3.1",
|
|
40
40
|
"pino-pretty": "^13.1.3",
|
|
41
41
|
"yaml": "^2.8.2",
|
|
42
42
|
"zod": "^3.25.76",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@pkg/eslint-config": "workspace:*",
|
|
47
47
|
"@pkg/vitest-config": "workspace:*",
|
|
48
|
-
"esbuild": "^0.27.
|
|
48
|
+
"esbuild": "^0.27.3",
|
|
49
49
|
"typescript": "^5.9.3"
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -50,10 +50,12 @@ export const ghMergeDev = async (args: GhMergeDevArgs): Promise<ToolsExecutionRe
|
|
|
50
50
|
selectedReleaseBranches = await checkbox({
|
|
51
51
|
required: true,
|
|
52
52
|
message: '๐ฟ Select release branches',
|
|
53
|
-
choices: releasePRsList.map((pr) =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
choices: releasePRsList.map((pr) => {
|
|
54
|
+
return {
|
|
55
|
+
name: pr.replace('release/v', ''),
|
|
56
|
+
value: pr,
|
|
57
|
+
}
|
|
58
|
+
}),
|
|
57
59
|
})
|
|
58
60
|
}
|
|
59
61
|
|
|
@@ -33,10 +33,12 @@ export const ghReleaseDeliver = async (args: GhReleaseDeliverArgs): Promise<Tool
|
|
|
33
33
|
|
|
34
34
|
selectedReleaseBranch = await select({
|
|
35
35
|
message: '๐ฟ Select release branch',
|
|
36
|
-
choices: releasePRsList.map((pr) =>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
choices: releasePRsList.map((pr) => {
|
|
37
|
+
return {
|
|
38
|
+
name: pr.replace('release/v', ''),
|
|
39
|
+
value: pr,
|
|
40
|
+
}
|
|
41
|
+
}),
|
|
40
42
|
})
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -41,10 +41,12 @@ export const ghReleaseDeployAll = async (args: GhReleaseDeployAllArgs): Promise<
|
|
|
41
41
|
|
|
42
42
|
selectedReleaseBranch = await select({
|
|
43
43
|
message: '๐ฟ Select release branch',
|
|
44
|
-
choices: releasePRsList.map((pr) =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
choices: releasePRsList.map((pr) => {
|
|
45
|
+
return {
|
|
46
|
+
name: pr.replace('release/v', ''),
|
|
47
|
+
value: pr,
|
|
48
|
+
}
|
|
49
|
+
}),
|
|
48
50
|
})
|
|
49
51
|
}
|
|
50
52
|
|
|
@@ -67,10 +69,12 @@ export const ghReleaseDeployAll = async (args: GhReleaseDeployAllArgs): Promise<
|
|
|
67
69
|
|
|
68
70
|
selectedEnv = await select({
|
|
69
71
|
message: '๐งช Select environment',
|
|
70
|
-
choices: ENVs.map((env) =>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
choices: ENVs.map((env) => {
|
|
73
|
+
return {
|
|
74
|
+
name: env,
|
|
75
|
+
value: env,
|
|
76
|
+
}
|
|
77
|
+
}),
|
|
74
78
|
})
|
|
75
79
|
}
|
|
76
80
|
|
|
@@ -102,6 +106,7 @@ export const ghReleaseDeployAll = async (args: GhReleaseDeployAllArgs): Promise<
|
|
|
102
106
|
$.quiet = true
|
|
103
107
|
|
|
104
108
|
const skipTerraformFlag = shouldSkipTerraform ? ['-f', 'skip_terraform_deploy=true'] : []
|
|
109
|
+
|
|
105
110
|
await $`gh workflow run deploy-all.yml --ref ${selectedReleaseBranch} -f environment=${selectedEnv} ${skipTerraformFlag}`
|
|
106
111
|
|
|
107
112
|
$.quiet = false
|
|
@@ -46,10 +46,12 @@ export const ghReleaseDeploySelected = async (args: GhReleaseDeploySelectedArgs)
|
|
|
46
46
|
|
|
47
47
|
selectedReleaseBranch = await select({
|
|
48
48
|
message: '๐ฟ Select release branch',
|
|
49
|
-
choices: releasePRsList.map((pr) =>
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
choices: releasePRsList.map((pr) => {
|
|
50
|
+
return {
|
|
51
|
+
name: pr.replace('release/v', ''),
|
|
52
|
+
value: pr,
|
|
53
|
+
}
|
|
54
|
+
}),
|
|
53
55
|
})
|
|
54
56
|
}
|
|
55
57
|
|
|
@@ -72,10 +74,12 @@ export const ghReleaseDeploySelected = async (args: GhReleaseDeploySelectedArgs)
|
|
|
72
74
|
|
|
73
75
|
selectedEnv = await select({
|
|
74
76
|
message: '๐งช Select environment',
|
|
75
|
-
choices: ENVs.map((env) =>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
choices: ENVs.map((env) => {
|
|
78
|
+
return {
|
|
79
|
+
name: env,
|
|
80
|
+
value: env,
|
|
81
|
+
}
|
|
82
|
+
}),
|
|
79
83
|
})
|
|
80
84
|
}
|
|
81
85
|
|
|
@@ -104,10 +108,12 @@ export const ghReleaseDeploySelected = async (args: GhReleaseDeploySelectedArgs)
|
|
|
104
108
|
|
|
105
109
|
selectedServices = await checkbox({
|
|
106
110
|
message: '๐ Select services to deploy (space to select, enter to confirm)',
|
|
107
|
-
choices: availableServices.map((svc) =>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
choices: availableServices.map((svc) => {
|
|
112
|
+
return {
|
|
113
|
+
name: svc,
|
|
114
|
+
value: svc,
|
|
115
|
+
}
|
|
116
|
+
}),
|
|
111
117
|
})
|
|
112
118
|
}
|
|
113
119
|
|
|
@@ -119,7 +125,9 @@ export const ghReleaseDeploySelected = async (args: GhReleaseDeploySelectedArgs)
|
|
|
119
125
|
}
|
|
120
126
|
|
|
121
127
|
// Validate all selected services
|
|
122
|
-
const invalidServices = selectedServices.filter((svc) =>
|
|
128
|
+
const invalidServices = selectedServices.filter((svc) => {
|
|
129
|
+
return !availableServices.includes(svc)
|
|
130
|
+
})
|
|
123
131
|
|
|
124
132
|
if (invalidServices.length > 0) {
|
|
125
133
|
logger.error(
|
|
@@ -149,7 +157,9 @@ export const ghReleaseDeploySelected = async (args: GhReleaseDeploySelectedArgs)
|
|
|
149
157
|
$.quiet = true
|
|
150
158
|
|
|
151
159
|
// Build the workflow command with boolean flags for each selected service
|
|
152
|
-
const serviceFlags = selectedServices.flatMap((svc) =>
|
|
160
|
+
const serviceFlags = selectedServices.flatMap((svc) => {
|
|
161
|
+
return ['-f', `${svc}=true`]
|
|
162
|
+
})
|
|
153
163
|
const skipTerraformFlag = shouldSkipTerraform ? ['-f', 'skip_terraform_deploy=true'] : []
|
|
154
164
|
|
|
155
165
|
await $`gh workflow run deploy-selected-services.yml --ref ${selectedReleaseBranch} -f environment=${selectedEnv} ${serviceFlags} ${skipTerraformFlag}`
|
|
@@ -47,10 +47,12 @@ export const ghReleaseDeployService = async (args: GhReleaseDeployServiceArgs):
|
|
|
47
47
|
|
|
48
48
|
selectedReleaseBranch = await select({
|
|
49
49
|
message: '๐ฟ Select release branch',
|
|
50
|
-
choices: releasePRsList.map((pr) =>
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
choices: releasePRsList.map((pr) => {
|
|
51
|
+
return {
|
|
52
|
+
name: pr.replace('release/v', ''),
|
|
53
|
+
value: pr,
|
|
54
|
+
}
|
|
55
|
+
}),
|
|
54
56
|
})
|
|
55
57
|
}
|
|
56
58
|
|
|
@@ -73,10 +75,12 @@ export const ghReleaseDeployService = async (args: GhReleaseDeployServiceArgs):
|
|
|
73
75
|
|
|
74
76
|
selectedEnv = await select({
|
|
75
77
|
message: '๐งช Select environment',
|
|
76
|
-
choices: ENVs.map((env) =>
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
choices: ENVs.map((env) => {
|
|
79
|
+
return {
|
|
80
|
+
name: env,
|
|
81
|
+
value: env,
|
|
82
|
+
}
|
|
83
|
+
}),
|
|
80
84
|
})
|
|
81
85
|
}
|
|
82
86
|
|
|
@@ -99,10 +103,12 @@ export const ghReleaseDeployService = async (args: GhReleaseDeployServiceArgs):
|
|
|
99
103
|
|
|
100
104
|
selectedService = await select({
|
|
101
105
|
message: '๐ Select service to deploy',
|
|
102
|
-
choices: availableServices.map((svc) =>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
choices: availableServices.map((svc) => {
|
|
107
|
+
return {
|
|
108
|
+
name: svc,
|
|
109
|
+
value: svc,
|
|
110
|
+
}
|
|
111
|
+
}),
|
|
106
112
|
})
|
|
107
113
|
}
|
|
108
114
|
|
|
@@ -134,6 +140,7 @@ export const ghReleaseDeployService = async (args: GhReleaseDeployServiceArgs):
|
|
|
134
140
|
$.quiet = true
|
|
135
141
|
|
|
136
142
|
const skipTerraformFlag = shouldSkipTerraform ? ['-f', 'skip_terraform_deploy=true'] : []
|
|
143
|
+
|
|
137
144
|
await $`gh workflow run deploy-single-service.yml --ref ${selectedReleaseBranch} -f environment=${selectedEnv} -f service=${selectedService} ${skipTerraformFlag}`
|
|
138
145
|
|
|
139
146
|
$.quiet = false
|
|
@@ -10,7 +10,9 @@ import type { ToolsExecutionResult } from 'src/types'
|
|
|
10
10
|
export const ghReleaseList = async (): Promise<ToolsExecutionResult> => {
|
|
11
11
|
const releasePRs = await getReleasePRs()
|
|
12
12
|
|
|
13
|
-
const releasePRsList = releasePRs.map((pr) =>
|
|
13
|
+
const releasePRsList = releasePRs.map((pr) => {
|
|
14
|
+
return pr.replace('release/', '')
|
|
15
|
+
})
|
|
14
16
|
|
|
15
17
|
logger.info('All release branches: \n')
|
|
16
18
|
logger.info(`\n${releasePRsList.join('\n')}`)
|
|
@@ -35,7 +35,9 @@ export const releaseCreateBatch = async (args: ReleaseCreateBatchArgs): Promise<
|
|
|
35
35
|
versionInput = await question('Enter versions by comma (e.g. 1.2.5, 1.2.6): ')
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
const versionsList = versionInput.split(',').map((version) =>
|
|
38
|
+
const versionsList = versionInput.split(',').map((version) => {
|
|
39
|
+
return version.trim()
|
|
40
|
+
})
|
|
39
41
|
|
|
40
42
|
commandEcho.addOption('--versions', versionsList.join(', '))
|
|
41
43
|
|
|
@@ -113,7 +115,9 @@ export const releaseCreateBatch = async (args: ReleaseCreateBatchArgs): Promise<
|
|
|
113
115
|
commandEcho.print()
|
|
114
116
|
|
|
115
117
|
const structuredContent = {
|
|
116
|
-
createdBranches: releases.map((r) =>
|
|
118
|
+
createdBranches: releases.map((r) => {
|
|
119
|
+
return r.branchName
|
|
120
|
+
}),
|
|
117
121
|
successCount,
|
|
118
122
|
failureCount,
|
|
119
123
|
releases,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
/* eslint-disable sonarjs/cognitive-complexity */
|
|
1
2
|
import checkbox from '@inquirer/checkbox'
|
|
2
3
|
import confirm from '@inquirer/confirm'
|
|
4
|
+
import { copyFileSync, existsSync } from 'node:fs'
|
|
3
5
|
import process from 'node:process'
|
|
4
6
|
import { z } from 'zod'
|
|
5
7
|
import { $ } from 'zx'
|
|
@@ -18,6 +20,7 @@ const RELEASE_BRANCH_PREFIX = 'release/v'
|
|
|
18
20
|
|
|
19
21
|
interface WorktreeManagementArgs extends RequiredConfirmedOptionArg {
|
|
20
22
|
all: boolean
|
|
23
|
+
cursor?: boolean
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
/**
|
|
@@ -25,7 +28,7 @@ interface WorktreeManagementArgs extends RequiredConfirmedOptionArg {
|
|
|
25
28
|
* Creates worktrees for active release branches and removes unused ones
|
|
26
29
|
*/
|
|
27
30
|
export const worktreesAdd = async (options: WorktreeManagementArgs): Promise<ToolsExecutionResult> => {
|
|
28
|
-
const { confirmedCommand, all } = options
|
|
31
|
+
const { confirmedCommand, all, cursor } = options
|
|
29
32
|
|
|
30
33
|
commandEcho.start('worktrees-add')
|
|
31
34
|
|
|
@@ -61,10 +64,12 @@ export const worktreesAdd = async (options: WorktreeManagementArgs): Promise<Too
|
|
|
61
64
|
selectedReleaseBranches = await checkbox({
|
|
62
65
|
required: true,
|
|
63
66
|
message: '๐ฟ Select release branches',
|
|
64
|
-
choices: releasePRsList.map((pr) =>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
choices: releasePRsList.map((pr) => {
|
|
68
|
+
return {
|
|
69
|
+
name: pr.replace('release/v', ''),
|
|
70
|
+
value: pr,
|
|
71
|
+
}
|
|
72
|
+
}),
|
|
68
73
|
})
|
|
69
74
|
}
|
|
70
75
|
|
|
@@ -98,15 +103,31 @@ export const worktreesAdd = async (options: WorktreeManagementArgs): Promise<Too
|
|
|
98
103
|
commandEcho.addOption('--yes', true)
|
|
99
104
|
}
|
|
100
105
|
|
|
106
|
+
const openInCursor = cursor ? true : await confirm({ message: 'Open created worktrees in Cursor?' })
|
|
107
|
+
|
|
108
|
+
if (!openInCursor) {
|
|
109
|
+
commandEcho.setInteractive()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (openInCursor) {
|
|
113
|
+
commandEcho.addOption('--cursor', true)
|
|
114
|
+
}
|
|
115
|
+
|
|
101
116
|
const { branchesToCreate } = categorizeWorktrees({
|
|
102
117
|
selectedReleaseBranches,
|
|
103
118
|
currentWorktrees,
|
|
104
119
|
})
|
|
105
120
|
|
|
106
|
-
const createdWorktrees = await createWorktrees(branchesToCreate, worktreeDir)
|
|
121
|
+
const createdWorktrees = await createWorktrees(branchesToCreate, worktreeDir, projectRoot)
|
|
107
122
|
|
|
108
123
|
logResults(createdWorktrees)
|
|
109
124
|
|
|
125
|
+
if (openInCursor) {
|
|
126
|
+
for (const branch of createdWorktrees) {
|
|
127
|
+
await $`cursor ${worktreeDir}/${branch}`
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
110
131
|
commandEcho.print()
|
|
111
132
|
|
|
112
133
|
const structuredContent = {
|
|
@@ -147,9 +168,13 @@ interface CategorizeWorktreesArgs {
|
|
|
147
168
|
const categorizeWorktrees = (args: CategorizeWorktreesArgs): { branchesToCreate: string[] } => {
|
|
148
169
|
const { selectedReleaseBranches, currentWorktrees } = args
|
|
149
170
|
|
|
150
|
-
const currentBranchNames = currentWorktrees.filter((branch) =>
|
|
171
|
+
const currentBranchNames = currentWorktrees.filter((branch) => {
|
|
172
|
+
return branch.startsWith(RELEASE_BRANCH_PREFIX)
|
|
173
|
+
})
|
|
151
174
|
|
|
152
|
-
const branchesToCreate = selectedReleaseBranches.filter((branch) =>
|
|
175
|
+
const branchesToCreate = selectedReleaseBranches.filter((branch) => {
|
|
176
|
+
return !currentBranchNames.includes(branch)
|
|
177
|
+
})
|
|
153
178
|
|
|
154
179
|
return { branchesToCreate }
|
|
155
180
|
}
|
|
@@ -157,7 +182,7 @@ const categorizeWorktrees = (args: CategorizeWorktreesArgs): { branchesToCreate:
|
|
|
157
182
|
/**
|
|
158
183
|
* Create worktrees for the specified branches
|
|
159
184
|
*/
|
|
160
|
-
const createWorktrees = async (branches: string[], worktreeDir: string): Promise<string[]> => {
|
|
185
|
+
const createWorktrees = async (branches: string[], worktreeDir: string, projectRoot: string): Promise<string[]> => {
|
|
161
186
|
const created: string[] = []
|
|
162
187
|
|
|
163
188
|
for (const branch of branches) {
|
|
@@ -166,6 +191,14 @@ const createWorktrees = async (branches: string[], worktreeDir: string): Promise
|
|
|
166
191
|
|
|
167
192
|
await $`git worktree add ${worktreePath} ${branch}`
|
|
168
193
|
|
|
194
|
+
const rootEnvPath = `${projectRoot}/.env`
|
|
195
|
+
|
|
196
|
+
if (existsSync(rootEnvPath)) {
|
|
197
|
+
copyFileSync(rootEnvPath, `${worktreePath}/.env`)
|
|
198
|
+
|
|
199
|
+
logger.info('๐ Copied .env to worktree')
|
|
200
|
+
}
|
|
201
|
+
|
|
169
202
|
created.push(branch)
|
|
170
203
|
} catch (error) {
|
|
171
204
|
logger.error({ error, branch }, `โ Failed to create worktree for ${branch}`)
|
|
@@ -195,6 +228,7 @@ export const worktreesAddMcpTool = {
|
|
|
195
228
|
description: 'Create worktrees for selected release branches',
|
|
196
229
|
inputSchema: {
|
|
197
230
|
all: z.boolean().describe('Add worktrees for all release branches without prompting'),
|
|
231
|
+
cursor: z.boolean().optional().describe('Open created worktrees in Cursor'),
|
|
198
232
|
},
|
|
199
233
|
outputSchema: {
|
|
200
234
|
createdWorktrees: z.array(z.string()).describe('List of created worktree branches'),
|
|
@@ -62,8 +62,12 @@ const processWorktrees = async (
|
|
|
62
62
|
projectRoot: string,
|
|
63
63
|
): Promise<WorktreeInfo[]> => {
|
|
64
64
|
const allWorktrees = [
|
|
65
|
-
...releaseWorktrees.map((branch) =>
|
|
66
|
-
|
|
65
|
+
...releaseWorktrees.map((branch) => {
|
|
66
|
+
return { branch, type: 'release' as const }
|
|
67
|
+
}),
|
|
68
|
+
...featureWorktrees.map((branch) => {
|
|
69
|
+
return { branch, type: 'feature' as const }
|
|
70
|
+
}),
|
|
67
71
|
]
|
|
68
72
|
|
|
69
73
|
const worktreesInfo: WorktreeInfo[] = []
|
|
@@ -97,6 +101,7 @@ const processWorktrees = async (
|
|
|
97
101
|
if (a.type !== b.type) {
|
|
98
102
|
return a.type === 'release' ? -1 : 1
|
|
99
103
|
}
|
|
104
|
+
|
|
100
105
|
return a.branch.localeCompare(b.branch)
|
|
101
106
|
})
|
|
102
107
|
}
|
|
@@ -196,8 +201,12 @@ const logResults = (worktrees: WorktreeInfo[]): void => {
|
|
|
196
201
|
logger.info('โ'.repeat(100))
|
|
197
202
|
|
|
198
203
|
// Separate releases and features
|
|
199
|
-
const releases = worktrees.filter((w) =>
|
|
200
|
-
|
|
204
|
+
const releases = worktrees.filter((w) => {
|
|
205
|
+
return w.type === 'release'
|
|
206
|
+
})
|
|
207
|
+
const features = worktrees.filter((w) => {
|
|
208
|
+
return w.type === 'feature'
|
|
209
|
+
})
|
|
201
210
|
|
|
202
211
|
// Display releases first
|
|
203
212
|
displayWorktreeSection('๐ Releases', releases)
|
|
@@ -210,7 +219,10 @@ const logResults = (worktrees: WorktreeInfo[]): void => {
|
|
|
210
219
|
displayWorktreeSection('โจ Features', features)
|
|
211
220
|
|
|
212
221
|
// Summary
|
|
213
|
-
const current = worktrees.find((w) =>
|
|
222
|
+
const current = worktrees.find((w) => {
|
|
223
|
+
return w.isCurrent
|
|
224
|
+
})
|
|
225
|
+
|
|
214
226
|
logger.info(`\n${'โ'.repeat(100)}`)
|
|
215
227
|
logger.info(
|
|
216
228
|
`๐ Summary: ${worktrees.length} total worktrees (${releases.length} releases, ${features.length} features)`,
|
|
@@ -258,6 +270,7 @@ const displayWorktree = (worktree: WorktreeInfo): void => {
|
|
|
258
270
|
|
|
259
271
|
// Commit and sync info
|
|
260
272
|
const syncInfo = worktree.aheadBehind !== 'unknown' ? ` | ${worktree.aheadBehind}` : ''
|
|
273
|
+
|
|
261
274
|
logger.info(` ๐ ${worktree.commit}${syncInfo}`)
|
|
262
275
|
|
|
263
276
|
// Last commit message
|
|
@@ -265,6 +278,7 @@ const displayWorktree = (worktree: WorktreeInfo): void => {
|
|
|
265
278
|
|
|
266
279
|
// Path (shortened for display)
|
|
267
280
|
const shortPath = worktree.path.split('/').slice(-2).join('/')
|
|
281
|
+
|
|
268
282
|
logger.info(` ๐ ${shortPath}`)
|
|
269
283
|
}
|
|
270
284
|
|
|
@@ -52,10 +52,12 @@ export const worktreesRemove = async (options: WorktreeManagementArgs): Promise<
|
|
|
52
52
|
selectedReleaseBranches = await checkbox({
|
|
53
53
|
required: true,
|
|
54
54
|
message: '๐ฟ Select release branches',
|
|
55
|
-
choices: currentWorktrees.map((pr) =>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
choices: currentWorktrees.map((pr) => {
|
|
56
|
+
return {
|
|
57
|
+
name: pr.replace('release/v', ''),
|
|
58
|
+
value: pr,
|
|
59
|
+
}
|
|
60
|
+
}),
|
|
59
61
|
})
|
|
60
62
|
}
|
|
61
63
|
|
|
@@ -124,6 +126,7 @@ const removeWorktrees = async (branches: string[], worktreeDir: string): Promise
|
|
|
124
126
|
for (const branch of branches) {
|
|
125
127
|
try {
|
|
126
128
|
const worktreePath = `${worktreeDir}/${branch}`
|
|
129
|
+
|
|
127
130
|
await $`git worktree remove ${worktreePath}`
|
|
128
131
|
removed.push(branch)
|
|
129
132
|
} catch (error) {
|
|
@@ -96,9 +96,13 @@ interface CategorizeWorktreesArgs {
|
|
|
96
96
|
const categorizeWorktrees = (args: CategorizeWorktreesArgs): { branchesToRemove: string[] } => {
|
|
97
97
|
const { releasePRsList, currentWorktrees } = args
|
|
98
98
|
|
|
99
|
-
const currentBranchNames = currentWorktrees.filter((branch) =>
|
|
99
|
+
const currentBranchNames = currentWorktrees.filter((branch) => {
|
|
100
|
+
return branch.startsWith(RELEASE_BRANCH_PREFIX)
|
|
101
|
+
})
|
|
100
102
|
|
|
101
|
-
const branchesToRemove = currentBranchNames.filter((branch) =>
|
|
103
|
+
const branchesToRemove = currentBranchNames.filter((branch) => {
|
|
104
|
+
return !releasePRsList.includes(branch)
|
|
105
|
+
})
|
|
102
106
|
|
|
103
107
|
return { branchesToRemove }
|
|
104
108
|
}
|
|
@@ -112,6 +116,7 @@ const removeWorktrees = async (branches: string[], worktreeDir: string): Promise
|
|
|
112
116
|
for (const branch of branches) {
|
|
113
117
|
try {
|
|
114
118
|
const worktreePath = `${worktreeDir}/${branch}`
|
|
119
|
+
|
|
115
120
|
await $`git worktree remove ${worktreePath}`
|
|
116
121
|
removed.push(branch)
|
|
117
122
|
} catch (error) {
|
package/src/entry/cli.ts
CHANGED
|
@@ -132,8 +132,9 @@ program
|
|
|
132
132
|
.description('Add git worktrees for release branches')
|
|
133
133
|
.option('-y, --yes', 'Skip confirmation prompt')
|
|
134
134
|
.option('-a, --all', 'Select all active release branches')
|
|
135
|
+
.option('-c, --cursor', 'Open created worktrees in Cursor')
|
|
135
136
|
.action(async (options) => {
|
|
136
|
-
await worktreesAdd({ confirmedCommand: options.yes, all: options.all })
|
|
137
|
+
await worktreesAdd({ confirmedCommand: options.yes, all: options.all, cursor: options.cursor })
|
|
137
138
|
})
|
|
138
139
|
|
|
139
140
|
program
|
|
@@ -32,7 +32,10 @@ export const getReleasePRs = async (): Promise<string[]> => {
|
|
|
32
32
|
process.exit(1)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
const headRefNames = releasePRsArray.map((pr) =>
|
|
35
|
+
const headRefNames = releasePRsArray.map((pr) => {
|
|
36
|
+
return pr.headRefName
|
|
37
|
+
})
|
|
38
|
+
|
|
36
39
|
return sortVersions(headRefNames)
|
|
37
40
|
} catch (error) {
|
|
38
41
|
logger.error({ error }, 'โ Error fetching release PRs')
|
|
@@ -148,7 +148,9 @@ const getProjectVersions = async (config: JiraConfig): Promise<JiraVersion[]> =>
|
|
|
148
148
|
const findVersionByName = async (versionName: string, config: JiraConfig): Promise<JiraVersion | null> => {
|
|
149
149
|
try {
|
|
150
150
|
const versions = await getProjectVersions(config)
|
|
151
|
-
const version = versions.find((v) =>
|
|
151
|
+
const version = versions.find((v) => {
|
|
152
|
+
return v.name === versionName
|
|
153
|
+
})
|
|
152
154
|
|
|
153
155
|
return version || null
|
|
154
156
|
} catch (error) {
|
|
@@ -280,6 +282,7 @@ export const loadJiraConfig = async (): Promise<JiraConfig> => {
|
|
|
280
282
|
const email = process.env.JIRA_EMAIL
|
|
281
283
|
|
|
282
284
|
const missingVars: string[] = []
|
|
285
|
+
|
|
283
286
|
if (!baseUrl) missingVars.push('JIRA_BASE_URL (e.g., https://your-domain.atlassian.net)')
|
|
284
287
|
if (!token) missingVars.push('JIRA_TOKEN or JIRA_API_TOKEN (your Jira API token)')
|
|
285
288
|
if (!projectIdStr) missingVars.push('JIRA_PROJECT_ID (numeric project ID)')
|
|
@@ -289,7 +292,9 @@ export const loadJiraConfig = async (): Promise<JiraConfig> => {
|
|
|
289
292
|
const errorMessage = [
|
|
290
293
|
'Jira configuration is required but incomplete.',
|
|
291
294
|
'Please configure the following environment variables:',
|
|
292
|
-
...missingVars.map((v) =>
|
|
295
|
+
...missingVars.map((v) => {
|
|
296
|
+
return ` - ${v}`
|
|
297
|
+
}),
|
|
293
298
|
'',
|
|
294
299
|
'You can set these in your .env file or as environment variables.',
|
|
295
300
|
].join('\n')
|
|
@@ -323,6 +328,7 @@ export const loadJiraConfigOptional = async (): Promise<JiraConfig | null> => {
|
|
|
323
328
|
return config
|
|
324
329
|
} catch (error) {
|
|
325
330
|
logger.warn({ error }, 'Jira configuration not available, skipping Jira integration')
|
|
331
|
+
|
|
326
332
|
return null
|
|
327
333
|
}
|
|
328
334
|
}
|
|
@@ -15,7 +15,9 @@ export const getCurrentWorktrees = async (type: 'release' | 'feature'): Promise<
|
|
|
15
15
|
feature: featureWorktreePredicate,
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
return worktreeLines.map(worktreePredicateMap[type]).filter((branch) =>
|
|
18
|
+
return worktreeLines.map(worktreePredicateMap[type]).filter((branch) => {
|
|
19
|
+
return branch !== null
|
|
20
|
+
})
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
const releaseWorktreePredicate = (line: string): string | null => {
|
|
@@ -6,23 +6,25 @@ interface ToolHandlerArgs {
|
|
|
6
6
|
handler: (params: any) => Promise<ToolsExecutionResult>
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export const createToolHandler = (args: ToolHandlerArgs) =>
|
|
10
|
-
|
|
9
|
+
export const createToolHandler = (args: ToolHandlerArgs) => {
|
|
10
|
+
return async (params: unknown) => {
|
|
11
|
+
const { toolName, handler } = args
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
logger.info({ msg: `Tool execution started: ${toolName}`, params })
|
|
14
|
+
try {
|
|
15
|
+
const payload = await handler({ ...(params as object), confirmedCommand: true })
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
logger.info({ msg: `Tool execution successful: ${toolName}` })
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
return payload
|
|
20
|
+
} catch (error) {
|
|
21
|
+
logger.error({
|
|
22
|
+
err: error,
|
|
23
|
+
params,
|
|
24
|
+
msg: `Tool execution failed: ${toolName}`,
|
|
25
|
+
})
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
throw error
|
|
28
|
+
}
|
|
27
29
|
}
|
|
28
30
|
}
|