infra-kit 0.1.102 → 0.1.107
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintcache +1 -1
- package/.omc/state/agent-replay-0a58307d-2a37-4c69-851c-83a646502d62.jsonl +1 -0
- package/.omc/state/agent-replay-11c41aa0-51fa-49e1-a1dc-26dcd6ac26cc.jsonl +16 -0
- package/.omc/state/agent-replay-4cf1c186-81b2-497c-b002-d7f84e7839f3.jsonl +9 -0
- package/.omc/state/agent-replay-5c4ab554-64f1-42ae-83e3-21e0237e955c.jsonl +11 -0
- package/.omc/state/agent-replay-a60ac2ec-afbd-449f-a540-6df287392fc2.jsonl +1 -0
- package/.omc/state/agent-replay-afc6290b-40d3-4bef-b3b6-14484c034ab9.jsonl +14 -0
- package/.omc/state/agent-replay-be37e426-6fc8-47f4-8178-221c8494551c.jsonl +3 -0
- package/.omc/state/agent-replay-c967c819-3d1c-447b-ab48-56a8448ef9f8.jsonl +2 -0
- package/.omc/state/agent-replay-e947a3c6-989d-4a60-91dd-6b0ddd827b2d.jsonl +3 -0
- package/.omc/state/idle-notif-cooldown.json +3 -0
- package/.omc/state/last-tool-error.json +4 -4
- package/.omc/state/mission-state.json +53 -0
- package/.omc/state/sessions/0a58307d-2a37-4c69-851c-83a646502d62/pre-tool-advisory-throttle.json +18 -0
- package/.omc/state/sessions/0a58307d-2a37-4c69-851c-83a646502d62/subagent-tracking-state.json +7 -0
- package/.omc/state/sessions/11c41aa0-51fa-49e1-a1dc-26dcd6ac26cc/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/11c41aa0-51fa-49e1-a1dc-26dcd6ac26cc/mission-state.json +117 -0
- package/.omc/state/sessions/11c41aa0-51fa-49e1-a1dc-26dcd6ac26cc/pre-tool-advisory-throttle.json +42 -0
- package/.omc/state/sessions/11c41aa0-51fa-49e1-a1dc-26dcd6ac26cc/subagent-tracking-state.json +53 -0
- package/.omc/state/sessions/4cf1c186-81b2-497c-b002-d7f84e7839f3/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/4cf1c186-81b2-497c-b002-d7f84e7839f3/pre-tool-advisory-throttle.json +18 -0
- package/.omc/state/sessions/4cf1c186-81b2-497c-b002-d7f84e7839f3/subagent-tracking-state.json +7 -0
- package/.omc/state/sessions/5c4ab554-64f1-42ae-83e3-21e0237e955c/mission-state.json +117 -0
- package/.omc/state/sessions/5c4ab554-64f1-42ae-83e3-21e0237e955c/pre-tool-advisory-throttle.json +18 -0
- package/.omc/state/sessions/5c4ab554-64f1-42ae-83e3-21e0237e955c/subagent-tracking-state.json +17 -0
- package/.omc/state/sessions/a60ac2ec-afbd-449f-a540-6df287392fc2/pre-tool-advisory-throttle.json +18 -0
- package/.omc/state/sessions/a60ac2ec-afbd-449f-a540-6df287392fc2/subagent-tracking-state.json +7 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/mission-state.json +89 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/pre-tool-advisory-throttle.json +34 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/ralph-state.json +13 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/skill-active-state.json +15 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/subagent-tracking-state.json +35 -0
- package/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/ultrawork-state.json +11 -0
- package/.omc/state/sessions/c967c819-3d1c-447b-ab48-56a8448ef9f8/pre-tool-advisory-throttle.json +10 -0
- package/.omc/state/sessions/c967c819-3d1c-447b-ab48-56a8448ef9f8/subagent-tracking-state.json +7 -0
- package/.omc/state/sessions/e947a3c6-989d-4a60-91dd-6b0ddd827b2d/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/e947a3c6-989d-4a60-91dd-6b0ddd827b2d/pre-tool-advisory-throttle.json +10 -0
- package/.omc/state/sessions/e947a3c6-989d-4a60-91dd-6b0ddd827b2d/subagent-tracking-state.json +26 -0
- package/.omc/state/subagent-tracking.json +14 -4
- package/.turbo/turbo-build.log +7 -0
- package/.turbo/turbo-check.log +14 -0
- package/.turbo/turbo-prettier-fix.log +2 -1
- package/.turbo/turbo-test.log +28 -5
- package/.turbo/turbo-validate.log +14 -0
- package/dist/cli.js +88 -74
- package/dist/cli.js.map +4 -4
- package/dist/entry/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +7 -0
- package/dist/lib/package-config/package-config.d.ts +71 -0
- package/dist/mcp.js +43 -41
- package/dist/mcp.js.map +4 -4
- package/eslint.config.js +1 -1
- package/infra-kit.config.ts +5 -0
- package/package.json +20 -13
- package/scripts/build.js +32 -3
- package/src/.omc/state/sessions/0a58307d-2a37-4c69-851c-83a646502d62/pre-tool-advisory-throttle.json +18 -0
- package/src/commands/.omc/state/sessions/afc6290b-40d3-4bef-b3b6-14484c034ab9/pre-tool-advisory-throttle.json +14 -0
- package/src/commands/.omc/state/sessions/c967c819-3d1c-447b-ab48-56a8448ef9f8/pre-tool-advisory-throttle.json +18 -0
- package/src/commands/audit/__tests__/audit.test.ts +59 -0
- package/src/commands/audit/audit.ts +177 -0
- package/src/commands/audit/index.ts +1 -0
- package/src/commands/config/config.ts +49 -7
- package/src/commands/doctor/__tests__/agent-files.test.ts +110 -0
- package/src/commands/doctor/doctor.ts +69 -4
- package/src/commands/env-clear/env-clear.ts +1 -1
- package/src/commands/env-list/env-list.ts +3 -3
- package/src/commands/env-load/env-load.ts +1 -1
- package/src/commands/env-status/env-status.ts +1 -1
- package/src/commands/gh-merge-dev/gh-merge-dev.ts +3 -8
- package/src/commands/gh-release-deliver/gh-release-deliver.ts +47 -21
- package/src/commands/gh-release-deploy-all/gh-release-deploy-all.ts +13 -7
- package/src/commands/gh-release-deploy-selected/gh-release-deploy-selected.ts +12 -6
- package/src/commands/gh-release-list/gh-release-list.ts +19 -8
- package/src/commands/init/__tests__/agent-files.test.ts +147 -0
- package/src/commands/init/__tests__/migrate-config.test.ts +160 -0
- package/src/commands/init/agent-files.ts +199 -0
- package/src/commands/init/index.ts +7 -0
- package/src/commands/init/init.ts +82 -60
- package/src/commands/init/migrate-config.ts +146 -0
- package/src/commands/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/src/commands/release-create/__tests__/release-create.test.ts +55 -0
- package/src/commands/release-create/release-create.ts +142 -38
- package/src/commands/release-desc-edit/release-desc-edit.ts +28 -8
- package/src/commands/version/version.ts +1 -1
- package/src/commands/worktrees-add/worktrees-add.ts +7 -12
- package/src/commands/worktrees-list/worktrees-list.ts +13 -5
- package/src/commands/worktrees-open/worktrees-open.ts +1 -1
- package/src/commands/worktrees-remove/worktrees-remove.ts +6 -10
- package/src/commands/worktrees-sync/worktrees-sync.ts +3 -5
- package/src/entry/cli.ts +50 -7
- package/src/entry/index.ts +5 -0
- package/src/integrations/cmux/open-workspace-with-layout.ts +4 -4
- package/src/integrations/cmux/workspace-title.ts +10 -4
- package/src/integrations/doppler/doppler-project.ts +1 -1
- package/src/integrations/gh/gh-release-prs/__tests__/gh-release-prs.test.ts +115 -0
- package/src/integrations/gh/gh-release-prs/gh-release-prs.ts +49 -32
- package/src/lib/.omc/state/sessions/a60ac2ec-afbd-449f-a540-6df287392fc2/pre-tool-advisory-throttle.json +14 -0
- package/src/lib/constants/index.ts +15 -0
- package/src/lib/git-utils/__tests__/git-utils.test.ts +49 -0
- package/src/lib/git-utils/git-utils.ts +3 -1
- package/src/lib/infra-kit-config/__tests__/infra-kit-config.test.ts +270 -0
- package/src/lib/infra-kit-config/index.ts +7 -1
- package/src/lib/infra-kit-config/infra-kit-config.ts +46 -28
- package/src/lib/managed-block/__tests__/managed-block.test.ts +121 -0
- package/src/lib/managed-block/index.ts +8 -0
- package/src/lib/managed-block/managed-block.ts +145 -0
- package/src/lib/package-config/__tests__/package-config.test.ts +95 -0
- package/src/lib/package-config/index.ts +3 -0
- package/src/lib/package-config/package-config-schema.ts +19 -0
- package/src/lib/package-config/package-config.ts +99 -0
- package/src/lib/package-validator/__tests__/package-validator.test.ts +263 -0
- package/src/lib/package-validator/checks/__tests__/checks.test.ts +130 -0
- package/src/lib/package-validator/checks/config-check.ts +30 -0
- package/src/lib/package-validator/checks/files-check.ts +29 -0
- package/src/lib/package-validator/checks/index.ts +4 -0
- package/src/lib/package-validator/checks/scripts-check.ts +23 -0
- package/src/lib/package-validator/checks/turbo-check.ts +47 -0
- package/src/lib/package-validator/fs-utils.ts +18 -0
- package/src/lib/package-validator/index.ts +3 -0
- package/src/lib/package-validator/loader/config-loader.ts +77 -0
- package/src/lib/package-validator/loader/index.ts +2 -0
- package/src/lib/package-validator/loader/package-discovery.ts +98 -0
- package/src/lib/package-validator/package-validator.ts +48 -0
- package/src/lib/package-validator/types.ts +15 -0
- package/src/lib/release-id/__tests__/release-id.test.ts +351 -0
- package/src/lib/release-id/__tests__/versioned-regression.test.ts +69 -0
- package/src/lib/release-id/index.ts +15 -0
- package/src/lib/release-id/release-id.ts +257 -0
- package/src/lib/release-utils/__tests__/release-utils.test.ts +122 -0
- package/src/lib/release-utils/index.ts +4 -0
- package/src/lib/release-utils/release-utils.ts +85 -17
- package/src/lib/version-utils/__tests__/load-existing-versions.test.ts +37 -0
- package/src/lib/version-utils/__tests__/next-version.test.ts +119 -13
- package/src/lib/version-utils/index.ts +3 -0
- package/src/lib/version-utils/load-existing-versions.ts +29 -10
- package/src/lib/version-utils/next-version.ts +67 -12
- package/src/lib/version-utils/version-utils.ts +13 -4
- package/src/mcp/tools/index.ts +2 -0
- package/src/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/src/types.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/src/lib/__tests__/infra-kit-config.test.ts +0 -231
- /package/src/integrations/{clickup → linear}/.gitkeep +0 -0
- /package/src/lib/{__tests__ → constants/__tests__}/constants.test.ts +0 -0
- /package/src/lib/{constants.ts → constants/constants.ts} +0 -0
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.107",
|
|
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,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"entries": {
|
|
4
|
+
"79a93d4a2f8f50b95f852280616242fee1855dc99a3c75211917f55e72e95fae": {
|
|
5
|
+
"last_emitted_at_ms": 1781510589151,
|
|
6
|
+
"message": "Use parallel execution for independent tasks. Use run_in_background for long operations (npm install, builds, tests)."
|
|
7
|
+
},
|
|
8
|
+
"466399dafa2d20f60587180bad0a07358eb7f2bce724df0ff682f038ad33f5ad": {
|
|
9
|
+
"last_emitted_at_ms": 1781510561764,
|
|
10
|
+
"message": "Read multiple files in parallel when possible for faster analysis."
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"updated_at": "2026-06-15T08:03:09.151Z"
|
|
14
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
5
|
+
|
|
6
|
+
import { writeAgentFiles } from 'src/commands/init/agent-files'
|
|
7
|
+
import { getProjectRoot, getRepoName } from 'src/lib/git-utils'
|
|
8
|
+
|
|
9
|
+
import { checkAgentFiles } from '../doctor'
|
|
10
|
+
|
|
11
|
+
vi.mock('src/lib/git-utils', () => {
|
|
12
|
+
return {
|
|
13
|
+
getProjectRoot: vi.fn(),
|
|
14
|
+
getRepoName: vi.fn(),
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const writeFile = (filePath: string, content: string): void => {
|
|
19
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
20
|
+
fs.writeFileSync(filePath, content, 'utf-8')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const withTmpRepo = async (fn: (tmp: string) => Promise<void>, opts: { repo?: boolean } = {}): Promise<void> => {
|
|
24
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'infra-kit-doctor-agents-test-'))
|
|
25
|
+
|
|
26
|
+
vi.mocked(getProjectRoot).mockResolvedValue(tmp)
|
|
27
|
+
vi.mocked(getRepoName).mockResolvedValue(path.basename(tmp))
|
|
28
|
+
|
|
29
|
+
if (opts.repo !== false) {
|
|
30
|
+
writeFile(path.join(tmp, 'infra-kit.json'), '{"environments":["dev"]}\n')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
await fn(tmp)
|
|
35
|
+
} finally {
|
|
36
|
+
fs.rmSync(tmp, { recursive: true, force: true })
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const statusOf = (checks: { name: string; status: string }[], name: string): string | undefined => {
|
|
41
|
+
return checks.find((c) => {
|
|
42
|
+
return c.name === name
|
|
43
|
+
})?.status
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe('checkAgentFiles', () => {
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
vi.clearAllMocks()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
vi.clearAllMocks()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('returns no checks outside an infra-kit repo (never crashes)', async () => {
|
|
56
|
+
await withTmpRepo(
|
|
57
|
+
async () => {
|
|
58
|
+
await expect(checkAgentFiles()).resolves.toEqual([])
|
|
59
|
+
},
|
|
60
|
+
{ repo: false },
|
|
61
|
+
)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('passes all three checks right after init runs the agent-files step', async () => {
|
|
65
|
+
await withTmpRepo(async () => {
|
|
66
|
+
await writeAgentFiles()
|
|
67
|
+
|
|
68
|
+
const checks = await checkAgentFiles()
|
|
69
|
+
|
|
70
|
+
expect(statusOf(checks, 'AGENTS.md block')).toBe('pass')
|
|
71
|
+
expect(statusOf(checks, 'CLAUDE.md import')).toBe('pass')
|
|
72
|
+
expect(statusOf(checks, '.cursor/rules block')).toBe('pass')
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('flags AGENTS.md present but CLAUDE.md import removed', async () => {
|
|
77
|
+
await withTmpRepo(async (tmp) => {
|
|
78
|
+
await writeAgentFiles()
|
|
79
|
+
// User deletes CLAUDE.md (drops the @AGENTS.md import) but keeps AGENTS.md.
|
|
80
|
+
fs.rmSync(path.join(tmp, 'CLAUDE.md'))
|
|
81
|
+
|
|
82
|
+
const checks = await checkAgentFiles()
|
|
83
|
+
|
|
84
|
+
expect(statusOf(checks, 'AGENTS.md block')).toBe('pass')
|
|
85
|
+
expect(statusOf(checks, 'CLAUDE.md import')).toBe('fail')
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('flags a missing .cursor/rules block', async () => {
|
|
90
|
+
await withTmpRepo(async (tmp) => {
|
|
91
|
+
await writeAgentFiles()
|
|
92
|
+
fs.rmSync(path.join(tmp, '.cursor', 'rules', 'infra-kit.mdc'))
|
|
93
|
+
|
|
94
|
+
const checks = await checkAgentFiles()
|
|
95
|
+
|
|
96
|
+
expect(statusOf(checks, '.cursor/rules block')).toBe('fail')
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('flags a missing AGENTS.md block', async () => {
|
|
101
|
+
await withTmpRepo(async (tmp) => {
|
|
102
|
+
await writeAgentFiles()
|
|
103
|
+
fs.rmSync(path.join(tmp, 'AGENTS.md'))
|
|
104
|
+
|
|
105
|
+
const checks = await checkAgentFiles()
|
|
106
|
+
|
|
107
|
+
expect(statusOf(checks, 'AGENTS.md block')).toBe('fail')
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
})
|