infra-kit 0.1.101 → 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 +41 -39
- 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 +77 -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 +8 -67
- 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/lib/worktrees/index.ts +1 -0
- package/src/lib/worktrees/remove-worktrees.ts +65 -0
- 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
package/eslint.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infra-kit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.105",
|
|
5
5
|
"description": "infra-kit",
|
|
6
|
-
"main": "dist/
|
|
7
|
-
"module": "dist/
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/entry/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/entry/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
8
15
|
"bin": {
|
|
9
|
-
"infra-kit": "dist/cli.js"
|
|
10
|
-
"ik": "dist/cli.js"
|
|
16
|
+
"infra-kit": "dist/cli.js"
|
|
11
17
|
},
|
|
12
18
|
"engines": {
|
|
13
19
|
"node": ">=24.x"
|
|
@@ -15,6 +21,7 @@
|
|
|
15
21
|
"scripts": {
|
|
16
22
|
"inspector": "npx @modelcontextprotocol/inspector node ./dist/mcp.js --debug",
|
|
17
23
|
"build": "pnpm run clean-artifacts && node ./scripts/build.js",
|
|
24
|
+
"check": "node ./dist/cli.js check",
|
|
18
25
|
"clean-artifacts": "rm -rf dist",
|
|
19
26
|
"clean-cache": "rm -rf node_modules/.cache .eslintcache tsconfig.tsbuildinfo .turbo .swc",
|
|
20
27
|
"prettier-fix": "pnpm exec prettier **/* --write --no-error-on-unmatched-pattern --log-level silent --ignore-path ../../../.prettierignore",
|
|
@@ -30,21 +37,21 @@
|
|
|
30
37
|
"fix": "pnpm run prettier-fix && pnpm run eslint-fix && pnpm run qa"
|
|
31
38
|
},
|
|
32
39
|
"dependencies": {
|
|
33
|
-
"@inquirer/checkbox": "^5.1
|
|
34
|
-
"@inquirer/confirm": "^6.
|
|
35
|
-
"@inquirer/select": "^5.1
|
|
40
|
+
"@inquirer/checkbox": "^5.2.1",
|
|
41
|
+
"@inquirer/confirm": "^6.1.1",
|
|
42
|
+
"@inquirer/select": "^5.2.1",
|
|
36
43
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37
|
-
"commander": "^
|
|
44
|
+
"commander": "^15.0.0",
|
|
38
45
|
"pino": "^10.3.1",
|
|
39
46
|
"pino-pretty": "^13.1.3",
|
|
40
47
|
"yaml": "^2.9.0",
|
|
41
|
-
"zod": "^
|
|
48
|
+
"zod": "^4.4.3",
|
|
42
49
|
"zx": "^8.8.5"
|
|
43
50
|
},
|
|
44
51
|
"devDependencies": {
|
|
45
|
-
"@
|
|
46
|
-
"@
|
|
47
|
-
"esbuild": "^0.28.
|
|
52
|
+
"@wl/eslint-config": "workspace:*",
|
|
53
|
+
"@wl/vitest-config": "workspace:*",
|
|
54
|
+
"esbuild": "^0.28.1",
|
|
48
55
|
"typescript": "^6.0.3"
|
|
49
56
|
}
|
|
50
57
|
}
|
package/scripts/build.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
/* eslint-disable sonarjs/no-os-command-from-path */
|
|
1
2
|
import * as esbuild from 'esbuild'
|
|
3
|
+
import { execFileSync } from 'node:child_process'
|
|
2
4
|
import fs from 'node:fs'
|
|
3
5
|
import { dirname, resolve } from 'node:path'
|
|
4
6
|
import { fileURLToPath } from 'node:url'
|
|
@@ -8,10 +10,14 @@ import packageJson from '../package.json' with { type: 'json' }
|
|
|
8
10
|
const __filename = fileURLToPath(import.meta.url)
|
|
9
11
|
const __dirname = dirname(__filename)
|
|
10
12
|
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
+
const PKG_DIR = resolve(__dirname, '..')
|
|
14
|
+
const OUT_DIR = resolve(PKG_DIR, 'dist')
|
|
15
|
+
const ENTRY_DIR = resolve(PKG_DIR, 'src/entry')
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
// 1. Bundle the JavaScript with esbuild (fast, but cannot emit .d.ts files).
|
|
18
|
+
const entryPoints = fs.readdirSync(ENTRY_DIR).map((file) => {
|
|
19
|
+
return resolve(ENTRY_DIR, file)
|
|
20
|
+
})
|
|
15
21
|
|
|
16
22
|
await esbuild.build({
|
|
17
23
|
entryPoints,
|
|
@@ -34,3 +40,26 @@ for (const entryPoint of entryPoints) {
|
|
|
34
40
|
|
|
35
41
|
console.log('✅ Build was completed successfully: ', fileName, '-', +(stat.size / 1024 / 1024).toPrecision(3), 'MB')
|
|
36
42
|
}
|
|
43
|
+
|
|
44
|
+
// 2. Emit the public type declarations with tsc (esbuild does not generate them).
|
|
45
|
+
// The library entry (src/entry/index.ts) only re-exports the package-config
|
|
46
|
+
// API via relative imports, so tsc needs no project config — a direct CLI
|
|
47
|
+
// call replaces the separate tsconfig.build.json. `--ignoreConfig` is
|
|
48
|
+
// required because tsconfig.json is present alongside the input file.
|
|
49
|
+
execFileSync(
|
|
50
|
+
'tsc',
|
|
51
|
+
[
|
|
52
|
+
'src/entry/index.ts',
|
|
53
|
+
'--ignoreConfig',
|
|
54
|
+
'--declaration',
|
|
55
|
+
'--emitDeclarationOnly',
|
|
56
|
+
'--rootDir',
|
|
57
|
+
'src',
|
|
58
|
+
'--outDir',
|
|
59
|
+
'dist',
|
|
60
|
+
'--skipLibCheck',
|
|
61
|
+
],
|
|
62
|
+
{ cwd: PKG_DIR, stdio: 'inherit' },
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
console.log('✅ Type declarations emitted: dist/entry/index.d.ts')
|
package/src/.omc/state/sessions/0a58307d-2a37-4c69-851c-83a646502d62/pre-tool-advisory-throttle.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"entries": {
|
|
4
|
+
"79a93d4a2f8f50b95f852280616242fee1855dc99a3c75211917f55e72e95fae": {
|
|
5
|
+
"last_emitted_at_ms": 1781185365793,
|
|
6
|
+
"message": "Use parallel execution for independent tasks. Use run_in_background for long operations (npm install, builds, tests)."
|
|
7
|
+
},
|
|
8
|
+
"445ed27a3872b681d98190bae61ccb84954e1bc4e140df5370be958dee776b3a": {
|
|
9
|
+
"last_emitted_at_ms": 1781185292480,
|
|
10
|
+
"message": "Verify changes work after editing. Test functionality before marking complete."
|
|
11
|
+
},
|
|
12
|
+
"466399dafa2d20f60587180bad0a07358eb7f2bce724df0ff682f038ad33f5ad": {
|
|
13
|
+
"last_emitted_at_ms": 1781185283907,
|
|
14
|
+
"message": "Read multiple files in parallel when possible for faster analysis."
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"updated_at": "2026-06-11T13:42:45.793Z"
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"entries": {
|
|
4
|
+
"79a93d4a2f8f50b95f852280616242fee1855dc99a3c75211917f55e72e95fae": {
|
|
5
|
+
"last_emitted_at_ms": 1780905267223,
|
|
6
|
+
"message": "Use parallel execution for independent tasks. Use run_in_background for long operations (npm install, builds, tests)."
|
|
7
|
+
},
|
|
8
|
+
"445ed27a3872b681d98190bae61ccb84954e1bc4e140df5370be958dee776b3a": {
|
|
9
|
+
"last_emitted_at_ms": 1780905232864,
|
|
10
|
+
"message": "Verify changes work after editing. Test functionality before marking complete."
|
|
11
|
+
},
|
|
12
|
+
"466399dafa2d20f60587180bad0a07358eb7f2bce724df0ff682f038ad33f5ad": {
|
|
13
|
+
"last_emitted_at_ms": 1780905206915,
|
|
14
|
+
"message": "Read multiple files in parallel when possible for faster analysis."
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"updated_at": "2026-06-08T07:54:27.223Z"
|
|
18
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
5
|
+
|
|
6
|
+
import { audit, auditMcpTool } from '../audit'
|
|
7
|
+
|
|
8
|
+
const tmpDirs: string[] = []
|
|
9
|
+
|
|
10
|
+
const makeTmpPackage = (config: string, packageJson: Record<string, unknown>): string => {
|
|
11
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'audit-cmd-'))
|
|
12
|
+
|
|
13
|
+
tmpDirs.push(dir)
|
|
14
|
+
fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify(packageJson))
|
|
15
|
+
fs.writeFileSync(path.join(dir, 'infra-kit.config.ts'), config)
|
|
16
|
+
|
|
17
|
+
return dir
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
while (tmpDirs.length > 0) {
|
|
22
|
+
const dir = tmpDirs.pop()
|
|
23
|
+
|
|
24
|
+
if (dir) {
|
|
25
|
+
fs.rmSync(dir, { recursive: true, force: true })
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe('audit', () => {
|
|
31
|
+
it('passes a package that satisfies its resolved rules', async () => {
|
|
32
|
+
const dir = makeTmpPackage('export default { requiredScripts: [], requiredFiles: [] }', {
|
|
33
|
+
name: '@x/ok',
|
|
34
|
+
type: 'module',
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const result = await audit({ cwd: dir })
|
|
38
|
+
|
|
39
|
+
expect(result.structuredContent.allPassed).toBe(true)
|
|
40
|
+
expect(result.structuredContent.packages[0]?.name).toBe('@x/ok')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('fails a package missing infra-kit.config.ts', async () => {
|
|
44
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'audit-cmd-'))
|
|
45
|
+
|
|
46
|
+
tmpDirs.push(dir)
|
|
47
|
+
fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: '@x/no-config', type: 'module' }))
|
|
48
|
+
|
|
49
|
+
const result = await audit({ cwd: dir })
|
|
50
|
+
|
|
51
|
+
expect(result.structuredContent.allPassed).toBe(false)
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
describe('mCP tool registration', () => {
|
|
56
|
+
it('exposes the canonical `audit` tool', () => {
|
|
57
|
+
expect(auditMcpTool.name).toBe('audit')
|
|
58
|
+
})
|
|
59
|
+
})
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
import { getProjectRoot } from 'src/lib/git-utils'
|
|
6
|
+
import { logger } from 'src/lib/logger'
|
|
7
|
+
import { ROOT_DEFAULT_RULES } from 'src/lib/package-config'
|
|
8
|
+
import type { ResolvedPackageRules } from 'src/lib/package-config'
|
|
9
|
+
import { discoverPackages, pathExists, validatePackage } from 'src/lib/package-validator'
|
|
10
|
+
import type { PackageValidationResult } from 'src/lib/package-validator'
|
|
11
|
+
import { defineMcpTool, textContent } from 'src/types'
|
|
12
|
+
|
|
13
|
+
// TODO [DO]: extract `audit` into its own standalone CLI tool, decoupled from infra-kit.
|
|
14
|
+
|
|
15
|
+
interface AuditOptions {
|
|
16
|
+
/** Audit every non-vendor workspace package instead of just the current one. */
|
|
17
|
+
all?: boolean
|
|
18
|
+
/** Audit the monorepo root (turbo pipeline + root commands) instead of a package. */
|
|
19
|
+
root?: boolean
|
|
20
|
+
/** Directory to resolve the current package from. Defaults to `process.cwd()`. */
|
|
21
|
+
cwd?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** A directory to audit plus the under-the-hood defaults that apply to it. */
|
|
25
|
+
interface AuditTarget {
|
|
26
|
+
dir: string
|
|
27
|
+
baseline?: Readonly<ResolvedPackageRules>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Walk upward from `start` to the nearest directory containing a package.json.
|
|
32
|
+
* Used so `infra-kit audit` (no `--all`) targets the package whose package.json
|
|
33
|
+
* script invoked it, regardless of the exact working directory.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* await findPackageRoot('/repo/packages/serverless-config/src')
|
|
37
|
+
* // => '/repo/packages/serverless-config'
|
|
38
|
+
*/
|
|
39
|
+
const findPackageRoot = async (start: string): Promise<string> => {
|
|
40
|
+
let current = path.resolve(start)
|
|
41
|
+
|
|
42
|
+
while (current !== path.dirname(current)) {
|
|
43
|
+
if (await pathExists(path.join(current, 'package.json'))) {
|
|
44
|
+
return current
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
current = path.dirname(current)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (await pathExists(path.join(current, 'package.json'))) {
|
|
51
|
+
return current
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
throw new Error(`No package.json found in or above ${start}`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Resolve which directories to audit, and the baseline defaults each uses:
|
|
59
|
+
* `root` → the monorepo root with {@link ROOT_DEFAULT_RULES}; `all` → every
|
|
60
|
+
* discovered non-vendor package; otherwise the package walked up from cwd.
|
|
61
|
+
*/
|
|
62
|
+
const resolveTargets = async (options: AuditOptions): Promise<AuditTarget[]> => {
|
|
63
|
+
if (options.root) {
|
|
64
|
+
return [{ dir: await getProjectRoot(), baseline: ROOT_DEFAULT_RULES }]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (options.all) {
|
|
68
|
+
const dirs = await discoverPackages(await getProjectRoot())
|
|
69
|
+
|
|
70
|
+
return dirs.map((dir) => {
|
|
71
|
+
return { dir }
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return [{ dir: await findPackageRoot(options.cwd ?? process.cwd()) }]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Print a package's audit result as doctor-style `[PASS]`/`[FAIL]` lines.
|
|
80
|
+
*/
|
|
81
|
+
const logResult = (result: PackageValidationResult): void => {
|
|
82
|
+
const header = result.passed ? 'PASS' : 'FAIL'
|
|
83
|
+
|
|
84
|
+
logger.info(`\n${result.packageName} — ${header}`)
|
|
85
|
+
|
|
86
|
+
for (const check of result.checks) {
|
|
87
|
+
const icon = check.status === 'pass' ? '[PASS]' : '[FAIL]'
|
|
88
|
+
|
|
89
|
+
logger.info(` ${icon} ${check.name}: ${check.message}`)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Audit the monorepo root (`root`), every non-vendor workspace package (`all`),
|
|
95
|
+
* or the package resolved by walking up from the working directory (default —
|
|
96
|
+
* the shape used by a package's `"check": "infra-kit audit"` script). The
|
|
97
|
+
* returned `structuredContent.allPassed` lets the CLI set a non-zero exit code so
|
|
98
|
+
* the audit fails CI; this function never calls `process.exit` so the MCP tool
|
|
99
|
+
* can reuse it.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* // CLI inside packages/serverless-config: `infra-kit audit`
|
|
103
|
+
* await audit() // audits the current package
|
|
104
|
+
* await audit({ all: true }) // audits every non-vendor workspace package
|
|
105
|
+
* await audit({ root: true }) // audits the monorepo root (turbo + root commands)
|
|
106
|
+
*/
|
|
107
|
+
export const audit = async (options: AuditOptions = {}) => {
|
|
108
|
+
const targets = await resolveTargets(options)
|
|
109
|
+
|
|
110
|
+
const results: PackageValidationResult[] = []
|
|
111
|
+
|
|
112
|
+
for (const target of targets) {
|
|
113
|
+
results.push(await validatePackage(target.dir, target.baseline))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
for (const result of results) {
|
|
117
|
+
logResult(result)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const allPassed = results.every((result) => {
|
|
121
|
+
return result.passed
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
logger.info(`\n${allPassed ? '✅ All valid' : '❌ Audit failed'} (${results.length} checked)`)
|
|
125
|
+
|
|
126
|
+
const structuredContent = {
|
|
127
|
+
allPassed,
|
|
128
|
+
packages: results.map((result) => {
|
|
129
|
+
return {
|
|
130
|
+
name: result.packageName,
|
|
131
|
+
passed: result.passed,
|
|
132
|
+
checks: result.checks,
|
|
133
|
+
}
|
|
134
|
+
}),
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
content: textContent(JSON.stringify(structuredContent, null, 2)),
|
|
139
|
+
structuredContent,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const auditInputSchema = {
|
|
144
|
+
all: z.boolean().optional().describe('Audit every non-vendor workspace package'),
|
|
145
|
+
root: z.boolean().optional().describe('Audit the monorepo root (turbo pipeline + root commands)'),
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const auditOutputSchema = {
|
|
149
|
+
allPassed: z.boolean().describe('Whether every audited package passed all checks'),
|
|
150
|
+
packages: z
|
|
151
|
+
.array(
|
|
152
|
+
z.object({
|
|
153
|
+
name: z.string(),
|
|
154
|
+
passed: z.boolean(),
|
|
155
|
+
checks: z.array(
|
|
156
|
+
z.object({
|
|
157
|
+
name: z.string(),
|
|
158
|
+
status: z.enum(['pass', 'fail']),
|
|
159
|
+
message: z.string(),
|
|
160
|
+
}),
|
|
161
|
+
),
|
|
162
|
+
}),
|
|
163
|
+
)
|
|
164
|
+
.describe('Per-package check results'),
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// MCP Tool Registration
|
|
168
|
+
export const auditMcpTool = defineMcpTool({
|
|
169
|
+
name: 'audit',
|
|
170
|
+
description:
|
|
171
|
+
'Audit packages against infra-kit.config.ts rules (config present and valid, required scripts, required files, and turbo tasks for the root). Defaults to the current package; all=true audits every non-vendor workspace package; root=true audits the monorepo root.',
|
|
172
|
+
inputSchema: auditInputSchema,
|
|
173
|
+
outputSchema: auditOutputSchema,
|
|
174
|
+
handler: (params) => {
|
|
175
|
+
return audit({ all: params.all, root: params.root })
|
|
176
|
+
},
|
|
177
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { audit, auditMcpTool } from './audit'
|
|
@@ -31,7 +31,7 @@ const fileExists = async (filePath: string): Promise<boolean> => {
|
|
|
31
31
|
*
|
|
32
32
|
* @example
|
|
33
33
|
* // os.homedir() === '/Users/arthur'
|
|
34
|
-
* tildify('/Users/arthur/.infra-kit/config.
|
|
34
|
+
* tildify('/Users/arthur/.infra-kit/config.json') // => '~/.infra-kit/config.json'
|
|
35
35
|
* tildify('/etc/hosts') // => '/etc/hosts'
|
|
36
36
|
*/
|
|
37
37
|
const tildify = (filePath: string): string => {
|
|
@@ -49,9 +49,9 @@ const tildify = (filePath: string): string => {
|
|
|
49
49
|
* // CLI: `infra-kit config path`
|
|
50
50
|
* // INFO: Project name: api
|
|
51
51
|
* // INFO: Config merge chain (later overrides earlier):
|
|
52
|
-
* // INFO: [✓] project (committed) ~/projects/api/infra-kit.
|
|
53
|
-
* // INFO: [ ] user global ~/.infra-kit/config.
|
|
54
|
-
* // INFO: [✓] user project ~/.infra-kit/projects/api/infra-kit.
|
|
52
|
+
* // INFO: [✓] project (committed) ~/projects/api/infra-kit.json
|
|
53
|
+
* // INFO: [ ] user global ~/.infra-kit/config.json
|
|
54
|
+
* // INFO: [✓] user project ~/.infra-kit/projects/api/infra-kit.json
|
|
55
55
|
*/
|
|
56
56
|
export const configPath = async (): Promise<ToolsExecutionResult> => {
|
|
57
57
|
const paths = await getInfraKitConfigPaths()
|
|
@@ -95,7 +95,8 @@ export const configPath = async (): Promise<ToolsExecutionResult> => {
|
|
|
95
95
|
*
|
|
96
96
|
* @example
|
|
97
97
|
* // CLI: `infra-kit config edit`
|
|
98
|
-
* // first run — creates ~/.infra-kit/projects/api/infra-kit.
|
|
98
|
+
* // first run — creates ~/.infra-kit/projects/api/infra-kit.json ({}) + a sibling
|
|
99
|
+
* // infra-kit.example.jsonc reference, then $EDITOR opens the .json
|
|
99
100
|
* // subsequent runs — opens the existing file as-is
|
|
100
101
|
*/
|
|
101
102
|
export const configEdit = async (): Promise<ToolsExecutionResult> => {
|
|
@@ -105,9 +106,15 @@ export const configEdit = async (): Promise<ToolsExecutionResult> => {
|
|
|
105
106
|
await fs.mkdir(path.dirname(paths.userProject), { recursive: true })
|
|
106
107
|
|
|
107
108
|
if (!(await fileExists(paths.userProject))) {
|
|
108
|
-
|
|
109
|
+
// JSON can't carry comments, so seed an empty-but-valid config and drop the
|
|
110
|
+
// annotated guidance next to it in a non-loaded .example.jsonc the loader
|
|
111
|
+
// never reads (it only globs the three exact `infra-kit.json` filenames).
|
|
112
|
+
const examplePath = exampleSiblingPath(paths.userProject)
|
|
109
113
|
|
|
110
|
-
await fs.writeFile(paths.userProject,
|
|
114
|
+
await fs.writeFile(paths.userProject, '{}\n', 'utf-8')
|
|
115
|
+
await fs.writeFile(examplePath, buildUserProjectExample(paths.projectName), 'utf-8')
|
|
116
|
+
|
|
117
|
+
logger.info(`Created ${tildify(paths.userProject)} — see ${tildify(examplePath)} for the annotated reference.`)
|
|
111
118
|
}
|
|
112
119
|
|
|
113
120
|
logger.info(`Opening ${tildify(paths.userProject)} in ${editor}`)
|
|
@@ -123,3 +130,38 @@ export const configEdit = async (): Promise<ToolsExecutionResult> => {
|
|
|
123
130
|
structuredContent,
|
|
124
131
|
}
|
|
125
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Derive the non-loaded `.example.jsonc` sibling for a config path.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* exampleSiblingPath('/u/.infra-kit/projects/api/infra-kit.json')
|
|
139
|
+
* // => '/u/.infra-kit/projects/api/infra-kit.example.jsonc'
|
|
140
|
+
*/
|
|
141
|
+
const exampleSiblingPath = (jsonPath: string): string => {
|
|
142
|
+
return jsonPath.replace(/\.json$/, '.example.jsonc')
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Annotated JSONC reference for the user-scope per-project override layer.
|
|
147
|
+
* Written alongside the real `{}` config so the guidance the old YAML stub
|
|
148
|
+
* carried in comments survives the move to JSON.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* buildUserProjectExample('api')
|
|
152
|
+
* // => '// infra-kit user override for api …\n{ … }\n'
|
|
153
|
+
*/
|
|
154
|
+
const buildUserProjectExample = (projectName: string): string => {
|
|
155
|
+
return `// infra-kit user override for ${projectName} — ~/.infra-kit/projects/${projectName}/infra-kit.json
|
|
156
|
+
//
|
|
157
|
+
// Layer 3 (highest precedence) of the config merge chain. Shallow-merged on top
|
|
158
|
+
// of <repo>/infra-kit.json and ~/.infra-kit/config.json — top-level keys
|
|
159
|
+
// (environments, envManagement, ide, taskManager, worktrees) replace wholesale.
|
|
160
|
+
//
|
|
161
|
+
// This .example.jsonc is reference only — it is NOT loaded. Put real overrides
|
|
162
|
+
// in the sibling infra-kit.json (strict JSON: no comments, double-quoted keys).
|
|
163
|
+
{
|
|
164
|
+
// "worktrees": { "openInGithubDesktop": false, "openInCmux": true }
|
|
165
|
+
}
|
|
166
|
+
`
|
|
167
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
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
7
|
import { MARKER_END, MARKER_START, buildShellBlock } from 'src/commands/init/init'
|
|
@@ -108,7 +108,7 @@ const checkInfraKitConfigValid = async (): Promise<CheckResult> => {
|
|
|
108
108
|
return {
|
|
109
109
|
name,
|
|
110
110
|
status: 'pass',
|
|
111
|
-
message: 'infra-kit.
|
|
111
|
+
message: 'infra-kit.json is valid (user overrides applied if present)',
|
|
112
112
|
}
|
|
113
113
|
} catch (err) {
|
|
114
114
|
return { name, status: 'fail', message: (err as Error).message }
|
|
@@ -125,7 +125,7 @@ const checkInfraKitConfigValid = async (): Promise<CheckResult> => {
|
|
|
125
125
|
* // {
|
|
126
126
|
* // name: 'user override path',
|
|
127
127
|
* // status: 'pass',
|
|
128
|
-
* // message: '~/.infra-kit/projects/api/infra-kit.
|
|
128
|
+
* // message: '~/.infra-kit/projects/api/infra-kit.json (not yet created) — project: api',
|
|
129
129
|
* // }
|
|
130
130
|
*/
|
|
131
131
|
const checkUserOverridePath = async (): Promise<CheckResult> => {
|
|
@@ -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
|