infra-kit 0.1.102 → 0.1.105
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-be37e426-6fc8-47f4-8178-221c8494551c.jsonl +3 -0
- package/.omc/state/agent-replay-c967c819-3d1c-447b-ab48-56a8448ef9f8.jsonl +2 -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/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/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 +81 -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/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/doctor.ts +3 -3
- 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__/migrate-config.test.ts +160 -0
- package/src/commands/init/init.ts +48 -35
- 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 +49 -6
- 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/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,12 +1,26 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
|
|
3
|
+
import { InvalidReleaseNameError, formatBranchName, formatJiraName, formatPrTitle } from '../../release-id'
|
|
4
|
+
import type { ReleaseId } from '../../release-id'
|
|
3
5
|
import {
|
|
4
6
|
NoPriorVersionsError,
|
|
5
7
|
collectKnownVersions,
|
|
6
8
|
computeNextVersion,
|
|
9
|
+
hasNextToken,
|
|
7
10
|
parseReleaseSpec,
|
|
8
11
|
resolveReleaseEntries,
|
|
9
12
|
} from '../next-version'
|
|
13
|
+
import type { ReleaseEntry } from '../next-version'
|
|
14
|
+
|
|
15
|
+
const versionId = (raw: string): ReleaseId => {
|
|
16
|
+
const [major, minor, patch] = raw.split('.').map(Number)
|
|
17
|
+
|
|
18
|
+
return { kind: 'version', semver: { major: major!, minor: minor!, patch: patch! }, raw }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const nameId = (name: string): ReleaseId => {
|
|
22
|
+
return { kind: 'name', name, raw: name }
|
|
23
|
+
}
|
|
10
24
|
|
|
11
25
|
describe('collectKnownVersions', () => {
|
|
12
26
|
it('parses remote branch refs and Jira version names and dedupes', () => {
|
|
@@ -123,7 +137,7 @@ describe('parseReleaseSpec', () => {
|
|
|
123
137
|
describe('resolveReleaseEntries', () => {
|
|
124
138
|
const known = collectKnownVersions({ remoteBranches: ['release/v1.63.0'] })
|
|
125
139
|
|
|
126
|
-
it('passes through explicit semver entries
|
|
140
|
+
it('passes through explicit semver entries, wrapping them in a version ReleaseId', () => {
|
|
127
141
|
expect(
|
|
128
142
|
resolveReleaseEntries(
|
|
129
143
|
[
|
|
@@ -133,14 +147,14 @@ describe('resolveReleaseEntries', () => {
|
|
|
133
147
|
known,
|
|
134
148
|
),
|
|
135
149
|
).toEqual([
|
|
136
|
-
{
|
|
137
|
-
{
|
|
150
|
+
{ id: versionId('1.70.0'), type: 'regular' },
|
|
151
|
+
{ id: versionId('1.70.1'), type: 'hotfix' },
|
|
138
152
|
])
|
|
139
153
|
})
|
|
140
154
|
|
|
141
155
|
it('resolves a single "next" using the entry type', () => {
|
|
142
156
|
expect(resolveReleaseEntries([{ version: 'next', type: 'regular' }], known)).toEqual([
|
|
143
|
-
{
|
|
157
|
+
{ id: versionId('1.64.0'), type: 'regular' },
|
|
144
158
|
])
|
|
145
159
|
})
|
|
146
160
|
|
|
@@ -154,8 +168,8 @@ describe('resolveReleaseEntries', () => {
|
|
|
154
168
|
known,
|
|
155
169
|
),
|
|
156
170
|
).toEqual([
|
|
157
|
-
{
|
|
158
|
-
{
|
|
171
|
+
{ id: versionId('1.64.0'), type: 'regular' },
|
|
172
|
+
{ id: versionId('1.65.0'), type: 'regular' },
|
|
159
173
|
])
|
|
160
174
|
})
|
|
161
175
|
|
|
@@ -169,8 +183,8 @@ describe('resolveReleaseEntries', () => {
|
|
|
169
183
|
known,
|
|
170
184
|
),
|
|
171
185
|
).toEqual([
|
|
172
|
-
{
|
|
173
|
-
{
|
|
186
|
+
{ id: versionId('1.64.0'), type: 'regular' },
|
|
187
|
+
{ id: versionId('1.64.1'), type: 'hotfix' },
|
|
174
188
|
])
|
|
175
189
|
})
|
|
176
190
|
|
|
@@ -185,21 +199,21 @@ describe('resolveReleaseEntries', () => {
|
|
|
185
199
|
known,
|
|
186
200
|
),
|
|
187
201
|
).toEqual([
|
|
188
|
-
{
|
|
189
|
-
{
|
|
190
|
-
{
|
|
202
|
+
{ id: versionId('1.64.0'), type: 'regular' },
|
|
203
|
+
{ id: versionId('1.70.0'), type: 'regular' },
|
|
204
|
+
{ id: versionId('1.71.0'), type: 'regular' },
|
|
191
205
|
])
|
|
192
206
|
})
|
|
193
207
|
|
|
194
208
|
it('preserves description through resolution', () => {
|
|
195
209
|
expect(resolveReleaseEntries([{ version: 'next', type: 'regular', description: 'Holiday' }], known)).toEqual([
|
|
196
|
-
{
|
|
210
|
+
{ id: versionId('1.64.0'), type: 'regular', description: 'Holiday' },
|
|
197
211
|
])
|
|
198
212
|
})
|
|
199
213
|
|
|
200
214
|
it('accepts case-insensitive "next"', () => {
|
|
201
215
|
expect(resolveReleaseEntries([{ version: 'NEXT', type: 'regular' }], known)).toEqual([
|
|
202
|
-
{
|
|
216
|
+
{ id: versionId('1.64.0'), type: 'regular' },
|
|
203
217
|
])
|
|
204
218
|
})
|
|
205
219
|
|
|
@@ -214,4 +228,96 @@ describe('resolveReleaseEntries', () => {
|
|
|
214
228
|
return resolveReleaseEntries([{ version: 'next', type: 'regular' }], [])
|
|
215
229
|
}).toThrow(NoPriorVersionsError)
|
|
216
230
|
})
|
|
231
|
+
|
|
232
|
+
describe('named entries', () => {
|
|
233
|
+
it('resolves a valid name into a name ReleaseId', () => {
|
|
234
|
+
expect(resolveReleaseEntries([{ name: 'checkout-redesign', type: 'regular' }], known)).toEqual([
|
|
235
|
+
{ id: nameId('checkout-redesign'), type: 'regular' },
|
|
236
|
+
])
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it('preserves type and description on named entries (named hotfix allowed)', () => {
|
|
240
|
+
expect(
|
|
241
|
+
resolveReleaseEntries([{ name: 'checkout-redesign', type: 'hotfix', description: 'Q3 work' }], known),
|
|
242
|
+
).toEqual([{ id: nameId('checkout-redesign'), type: 'hotfix', description: 'Q3 work' }])
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it('never auto-bumps a named entry (no version interaction)', () => {
|
|
246
|
+
expect(
|
|
247
|
+
resolveReleaseEntries(
|
|
248
|
+
[
|
|
249
|
+
{ name: 'first-thing', type: 'regular' },
|
|
250
|
+
{ version: 'next', type: 'regular' },
|
|
251
|
+
],
|
|
252
|
+
known,
|
|
253
|
+
),
|
|
254
|
+
).toEqual([
|
|
255
|
+
{ id: nameId('first-thing'), type: 'regular' },
|
|
256
|
+
{ id: versionId('1.64.0'), type: 'regular' },
|
|
257
|
+
])
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
it('throws InvalidReleaseNameError on an invalid (non-kebab) name', () => {
|
|
261
|
+
expect(() => {
|
|
262
|
+
return resolveReleaseEntries([{ name: 'Checkout_Redesign', type: 'regular' }], known)
|
|
263
|
+
}).toThrow(InvalidReleaseNameError)
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('throws InvalidReleaseNameError when the name is the reserved word "next"', () => {
|
|
267
|
+
expect(() => {
|
|
268
|
+
return resolveReleaseEntries([{ name: 'next', type: 'regular' }], known)
|
|
269
|
+
}).toThrow(InvalidReleaseNameError)
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
describe('formatting round-trip (zero-regression for versioned, correct for named)', () => {
|
|
274
|
+
const resolve = (entry: Parameters<typeof resolveReleaseEntries>[0][number]): ReleaseEntry => {
|
|
275
|
+
return resolveReleaseEntries([entry], known)[0] as ReleaseEntry
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
it('versioned literal "1.62.0" produces byte-identical branch/PR/Jira output', () => {
|
|
279
|
+
const { id } = resolve({ version: '1.62.0', type: 'regular' })
|
|
280
|
+
|
|
281
|
+
expect(formatBranchName(id)).toBe('release/v1.62.0')
|
|
282
|
+
expect(formatPrTitle(id, 'regular')).toBe('Release v1.62.0')
|
|
283
|
+
expect(formatJiraName(id)).toBe('v1.62.0')
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
it('versioned "next" resolves then formats identically to the equivalent literal', () => {
|
|
287
|
+
const { id } = resolve({ version: 'next', type: 'regular' })
|
|
288
|
+
|
|
289
|
+
// known max is 1.63.0 → regular next is 1.64.0
|
|
290
|
+
expect(formatBranchName(id)).toBe('release/v1.64.0')
|
|
291
|
+
expect(formatPrTitle(id, 'hotfix')).toBe('Hotfix v1.64.0')
|
|
292
|
+
expect(formatJiraName(id)).toBe('v1.64.0')
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
it('named entry produces the named branch/PR/Jira output', () => {
|
|
296
|
+
const { id } = resolve({ name: 'checkout-redesign', type: 'regular' })
|
|
297
|
+
|
|
298
|
+
expect(formatBranchName(id)).toBe('release/n/checkout-redesign')
|
|
299
|
+
expect(formatPrTitle(id, 'regular')).toBe('Release checkout-redesign')
|
|
300
|
+
expect(formatJiraName(id)).toBe('checkout-redesign')
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
describe('hasNextToken', () => {
|
|
306
|
+
it('is true when any versioned entry is "next"', () => {
|
|
307
|
+
expect(
|
|
308
|
+
hasNextToken([
|
|
309
|
+
{ name: 'checkout-redesign', type: 'regular' },
|
|
310
|
+
{ version: 'next', type: 'regular' },
|
|
311
|
+
]),
|
|
312
|
+
).toBe(true)
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
it('is false for explicit versions and named entries only', () => {
|
|
316
|
+
expect(
|
|
317
|
+
hasNextToken([
|
|
318
|
+
{ version: '1.2.3', type: 'regular' },
|
|
319
|
+
{ name: 'next-thing', type: 'regular' },
|
|
320
|
+
]),
|
|
321
|
+
).toBe(false)
|
|
322
|
+
})
|
|
217
323
|
})
|
|
@@ -4,10 +4,13 @@ export {
|
|
|
4
4
|
computeNextVersion,
|
|
5
5
|
type ExistingVersionsSources,
|
|
6
6
|
hasNextToken,
|
|
7
|
+
type NamedReleaseInput,
|
|
7
8
|
NEXT_TOKEN,
|
|
8
9
|
NoPriorVersionsError,
|
|
9
10
|
parseReleaseSpec,
|
|
10
11
|
type ReleaseEntry,
|
|
12
|
+
type ReleaseInput,
|
|
13
|
+
type ReleaseSpec,
|
|
11
14
|
resolveReleaseEntries,
|
|
12
15
|
type SemVer,
|
|
13
16
|
} from './next-version'
|
|
@@ -2,27 +2,46 @@ import { $ } from 'zx'
|
|
|
2
2
|
|
|
3
3
|
import { getProjectVersions, loadJiraConfigOptional } from 'src/integrations/jira'
|
|
4
4
|
import { logger } from 'src/lib/logger'
|
|
5
|
+
import { parseBranchName } from 'src/lib/release-id'
|
|
5
6
|
|
|
6
7
|
import { collectKnownVersions } from './next-version'
|
|
7
8
|
import type { SemVer } from './next-version'
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Extract version-branch tokens from raw `git ls-remote` stdout. Each line is
|
|
12
|
+
* `<sha>\t<ref>`; refs are routed through release-id's lenient
|
|
13
|
+
* {@link parseBranchName} and only `kind: 'version'` ids are kept (named
|
|
14
|
+
* `release/n/*` branches are irrelevant to `next`-bump math and are dropped).
|
|
15
|
+
* Returns the no-`v` semver tokens (e.g. `1.2.3`) that
|
|
16
|
+
* {@link collectKnownVersions} parses as versions. Pure — no I/O — so it is
|
|
17
|
+
* unit-testable without the network.
|
|
18
|
+
*/
|
|
19
|
+
export const extractVersionBranches = (lsRemoteStdout: string): string[] => {
|
|
20
|
+
return lsRemoteStdout
|
|
21
|
+
.split('\n')
|
|
22
|
+
.map((line) => {
|
|
23
|
+
const tab = line.indexOf('\t')
|
|
24
|
+
|
|
25
|
+
if (tab === -1) return null
|
|
26
|
+
|
|
27
|
+
return parseBranchName(line.slice(tab + 1))
|
|
28
|
+
})
|
|
29
|
+
.filter((id): id is NonNullable<typeof id> => {
|
|
30
|
+
return id !== null && id.kind === 'version'
|
|
31
|
+
})
|
|
32
|
+
.map((id) => {
|
|
33
|
+
return id.raw
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
9
37
|
const parseRemoteRefs = async (): Promise<string[]> => {
|
|
10
38
|
const previousQuiet = $.quiet
|
|
11
39
|
|
|
12
40
|
try {
|
|
13
41
|
$.quiet = true
|
|
14
42
|
const result = await $`git ls-remote --heads origin 'release/v*'`
|
|
15
|
-
const lines = result.stdout.split('\n')
|
|
16
|
-
|
|
17
|
-
return lines
|
|
18
|
-
.map((line) => {
|
|
19
|
-
const tab = line.indexOf('\t')
|
|
20
|
-
|
|
21
|
-
if (tab === -1) return ''
|
|
22
43
|
|
|
23
|
-
|
|
24
|
-
})
|
|
25
|
-
.filter(Boolean)
|
|
44
|
+
return extractVersionBranches(result.stdout)
|
|
26
45
|
} finally {
|
|
27
46
|
$.quiet = previousQuiet
|
|
28
47
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { parseReleaseRef, validateName } from 'src/lib/release-id'
|
|
2
|
+
import type { ReleaseId } from 'src/lib/release-id'
|
|
1
3
|
import type { ReleaseType } from 'src/lib/release-utils'
|
|
2
4
|
|
|
3
5
|
import { parseVersion, sortVersions } from './version-utils'
|
|
@@ -97,22 +99,56 @@ const isNextToken = (token: string): boolean => {
|
|
|
97
99
|
return token.trim().toLowerCase() === NEXT_TOKEN
|
|
98
100
|
}
|
|
99
101
|
|
|
100
|
-
|
|
102
|
+
/**
|
|
103
|
+
* A release spec is the parsed form of a versioned release request: a raw
|
|
104
|
+
* version token (`"1.2.5"` or `"next"`) plus its type and optional
|
|
105
|
+
* description. This is the unchanged output of {@link parseReleaseSpec} and the
|
|
106
|
+
* versioned-input shape consumed by {@link resolveReleaseEntries}.
|
|
107
|
+
*/
|
|
108
|
+
export interface ReleaseSpec {
|
|
101
109
|
version: string
|
|
102
110
|
type: ReleaseType
|
|
103
111
|
description?: string
|
|
104
112
|
}
|
|
105
113
|
|
|
114
|
+
/**
|
|
115
|
+
* A named release request: a bare kebab-case name plus its type and optional
|
|
116
|
+
* description. Named releases never auto-bump; `"next"` is version-only.
|
|
117
|
+
*/
|
|
118
|
+
export interface NamedReleaseInput {
|
|
119
|
+
name: string
|
|
120
|
+
type: ReleaseType
|
|
121
|
+
description?: string
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Either a versioned spec or a named release request. */
|
|
125
|
+
export type ReleaseInput = ReleaseSpec | NamedReleaseInput
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* A fully resolved release entry. The {@link ReleaseId} carries the concrete
|
|
129
|
+
* identity (version or name) and is the single source for branch/PR/Jira
|
|
130
|
+
* formatting via the `release-id` module.
|
|
131
|
+
*/
|
|
132
|
+
export interface ReleaseEntry {
|
|
133
|
+
id: ReleaseId
|
|
134
|
+
type: ReleaseType
|
|
135
|
+
description?: string
|
|
136
|
+
}
|
|
137
|
+
|
|
106
138
|
const isReleaseType = (value: string): value is ReleaseType => {
|
|
107
139
|
return value === 'regular' || value === 'hotfix'
|
|
108
140
|
}
|
|
109
141
|
|
|
142
|
+
const isNamedReleaseInput = (input: ReleaseInput): input is NamedReleaseInput => {
|
|
143
|
+
return 'name' in input
|
|
144
|
+
}
|
|
145
|
+
|
|
110
146
|
/**
|
|
111
147
|
* Parse a CLI release spec of the form `version[:type[:description]]`.
|
|
112
148
|
* Type defaults to "regular". Description is everything after the second
|
|
113
149
|
* colon, so colons inside descriptions are preserved.
|
|
114
150
|
*/
|
|
115
|
-
export const parseReleaseSpec = (raw: string):
|
|
151
|
+
export const parseReleaseSpec = (raw: string): ReleaseSpec => {
|
|
116
152
|
const spec = raw.trim()
|
|
117
153
|
|
|
118
154
|
if (spec === '') throw new Error('Release spec is empty')
|
|
@@ -135,23 +171,42 @@ export const parseReleaseSpec = (raw: string): ReleaseEntry => {
|
|
|
135
171
|
throw new Error(`Invalid release type "${typeRaw}". Expected "regular" or "hotfix".`)
|
|
136
172
|
}
|
|
137
173
|
|
|
138
|
-
const entry:
|
|
174
|
+
const entry: ReleaseSpec = { version, type: typeLower }
|
|
139
175
|
|
|
140
176
|
if (description !== '') entry.description = description
|
|
141
177
|
|
|
142
178
|
return entry
|
|
143
179
|
}
|
|
144
180
|
|
|
181
|
+
const withDescription = (base: { id: ReleaseId; type: ReleaseType }, description?: string): ReleaseEntry => {
|
|
182
|
+
return description !== undefined && description !== '' ? { ...base, description } : base
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const resolveNamedInput = (input: NamedReleaseInput): ReleaseEntry => {
|
|
186
|
+
const name = input.name.trim()
|
|
187
|
+
|
|
188
|
+
// validateName throws InvalidReleaseNameError with a specific message.
|
|
189
|
+
validateName(name)
|
|
190
|
+
|
|
191
|
+
return withDescription({ id: { kind: 'name', name, raw: name }, type: input.type }, input.description)
|
|
192
|
+
}
|
|
193
|
+
|
|
145
194
|
/**
|
|
146
|
-
* Resolve a list of release
|
|
147
|
-
*
|
|
148
|
-
* advances based on the running max so successive "next"
|
|
149
|
-
* sequential versions
|
|
195
|
+
* Resolve a list of release inputs into entries carrying a concrete
|
|
196
|
+
* {@link ReleaseId}. Versioned inputs use the existing spec format: the
|
|
197
|
+
* `"next"` token advances based on the running max so successive `"next"`
|
|
198
|
+
* tokens produce sequential versions (even across mixed types), then the
|
|
199
|
+
* concrete version is wrapped in a {@link ReleaseId}. Named inputs are
|
|
200
|
+
* validated via `validateName` and never auto-bump.
|
|
150
201
|
*/
|
|
151
|
-
export const resolveReleaseEntries = (entries:
|
|
202
|
+
export const resolveReleaseEntries = (entries: ReleaseInput[], known: SemVer[]): ReleaseEntry[] => {
|
|
152
203
|
const running: SemVer[] = [...known]
|
|
153
204
|
|
|
154
205
|
return entries.map((entry) => {
|
|
206
|
+
if (isNamedReleaseInput(entry)) {
|
|
207
|
+
return resolveNamedInput(entry)
|
|
208
|
+
}
|
|
209
|
+
|
|
155
210
|
const trimmed = entry.version.trim()
|
|
156
211
|
|
|
157
212
|
if (trimmed === '') {
|
|
@@ -163,7 +218,7 @@ export const resolveReleaseEntries = (entries: ReleaseEntry[], known: SemVer[]):
|
|
|
163
218
|
|
|
164
219
|
running.push(parseVersion(`v${next}`))
|
|
165
220
|
|
|
166
|
-
return {
|
|
221
|
+
return withDescription({ id: parseReleaseRef(next), type: entry.type }, entry.description)
|
|
167
222
|
}
|
|
168
223
|
|
|
169
224
|
const parsed = tryParse(trimmed)
|
|
@@ -176,12 +231,12 @@ export const resolveReleaseEntries = (entries: ReleaseEntry[], known: SemVer[]):
|
|
|
176
231
|
|
|
177
232
|
running.push(parsed)
|
|
178
233
|
|
|
179
|
-
return {
|
|
234
|
+
return withDescription({ id: parseReleaseRef(explicit), type: entry.type }, entry.description)
|
|
180
235
|
})
|
|
181
236
|
}
|
|
182
237
|
|
|
183
|
-
export const hasNextToken = (entries:
|
|
238
|
+
export const hasNextToken = (entries: ReleaseInput[]): boolean => {
|
|
184
239
|
return entries.some((e) => {
|
|
185
|
-
return isNextToken(e.version)
|
|
240
|
+
return !isNamedReleaseInput(e) && isNextToken(e.version)
|
|
186
241
|
})
|
|
187
242
|
}
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Parse
|
|
2
|
+
* Parse a `v<semver>` token into major, minor, patch numbers.
|
|
3
|
+
*
|
|
4
|
+
* SemVer-ONLY: this assumes a `v<semver>` token and will produce `NaN`s for
|
|
5
|
+
* anything else. Callers that may see branch names or named releases
|
|
6
|
+
* (`release/n/<name>`) MUST use the `release-id` module (`parseBranchName` /
|
|
7
|
+
* `parseReleaseRef` + `compareReleaseIds`) instead. Only provably names-free
|
|
8
|
+
* callers (the `collectKnownVersions` / `next`-bump math path) may use this.
|
|
3
9
|
*/
|
|
4
10
|
export const parseVersion = (versionStr: string): [number, number, number] => {
|
|
5
|
-
return versionStr.
|
|
11
|
+
return versionStr.slice(1).split('.').map(Number) as [number, number, number]
|
|
6
12
|
}
|
|
7
13
|
|
|
8
14
|
/**
|
|
9
|
-
* Sort version strings in ascending order
|
|
10
|
-
* Note: Returns a new sorted array without mutating the original
|
|
15
|
+
* Sort version strings in ascending order.
|
|
16
|
+
* Note: Returns a new sorted array without mutating the original.
|
|
17
|
+
*
|
|
18
|
+
* SemVer-ONLY: relies on {@link parseVersion} and will NaN-sort named releases.
|
|
19
|
+
* Name-aware callers MUST use `compareReleaseIds` from the `release-id` module.
|
|
11
20
|
*/
|
|
12
21
|
export const sortVersions = (versions: string[]): string[] => {
|
|
13
22
|
return [...versions].sort((a, b) => {
|
package/src/mcp/tools/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
2
|
|
|
3
|
+
import { auditMcpTool } from 'src/commands/audit'
|
|
3
4
|
import { envClearMcpTool } from 'src/commands/env-clear'
|
|
4
5
|
import { envListMcpTool } from 'src/commands/env-list'
|
|
5
6
|
import { envLoadMcpTool } from 'src/commands/env-load'
|
|
@@ -31,6 +32,7 @@ const tools = [
|
|
|
31
32
|
ghReleaseDeployAllMcpTool,
|
|
32
33
|
ghReleaseDeploySelectedMcpTool,
|
|
33
34
|
ghReleaseListMcpTool,
|
|
35
|
+
auditMcpTool,
|
|
34
36
|
versionMcpTool,
|
|
35
37
|
worktreesAddMcpTool,
|
|
36
38
|
worktreesListMcpTool,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":"4.1.8","results":[[":commands/env-load/__tests__/env-load.test.ts",{"duration":0,"failed":true}]]}
|
package/src/types.ts
CHANGED