infra-kit 0.1.110 → 0.1.113
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-127837d3-a000-4656-9453-2324ab353723.jsonl +5 -0
- package/.omc/state/agent-replay-15c409af-24fa-4026-88d3-a82dd1e68b87.jsonl +11 -0
- package/.omc/state/agent-replay-74afa912-1443-4011-b4f2-db9f055cdd9f.jsonl +3 -0
- package/.omc/state/agent-replay-8dd0ad2f-3a90-4158-b9de-ec8e19a7c983.jsonl +3 -0
- package/.omc/state/agent-replay-d5aae73c-340d-4aa2-9fe0-1bf2d79b69dc.jsonl +10 -0
- package/.omc/state/idle-notif-cooldown.json +1 -1
- package/.omc/state/sessions/127837d3-a000-4656-9453-2324ab353723/mission-state.json +61 -0
- package/.omc/state/sessions/127837d3-a000-4656-9453-2324ab353723/pre-tool-advisory-throttle.json +22 -0
- package/.omc/state/sessions/127837d3-a000-4656-9453-2324ab353723/skill-active-state.json +15 -0
- package/.omc/state/sessions/127837d3-a000-4656-9453-2324ab353723/subagent-tracking-state.json +26 -0
- package/.omc/state/sessions/15c409af-24fa-4026-88d3-a82dd1e68b87/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/15c409af-24fa-4026-88d3-a82dd1e68b87/mission-state.json +89 -0
- package/.omc/state/sessions/15c409af-24fa-4026-88d3-a82dd1e68b87/pre-tool-advisory-throttle.json +34 -0
- package/.omc/state/sessions/15c409af-24fa-4026-88d3-a82dd1e68b87/ralph-state.json +13 -0
- package/.omc/state/sessions/15c409af-24fa-4026-88d3-a82dd1e68b87/skill-active-state.json +15 -0
- package/.omc/state/sessions/15c409af-24fa-4026-88d3-a82dd1e68b87/subagent-tracking-state.json +44 -0
- package/.omc/state/sessions/15c409af-24fa-4026-88d3-a82dd1e68b87/ultrawork-state.json +11 -0
- package/.omc/state/sessions/74afa912-1443-4011-b4f2-db9f055cdd9f/pre-tool-advisory-throttle.json +18 -0
- package/.omc/state/sessions/74afa912-1443-4011-b4f2-db9f055cdd9f/skill-active-state.json +15 -0
- package/.omc/state/sessions/74afa912-1443-4011-b4f2-db9f055cdd9f/subagent-tracking-state.json +17 -0
- package/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/pre-tool-advisory-throttle.json +14 -0
- package/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/ralph-state.json +13 -0
- package/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/subagent-tracking-state.json +17 -0
- package/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/ultrawork-state.json +11 -0
- package/.omc/state/sessions/d5aae73c-340d-4aa2-9fe0-1bf2d79b69dc/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/d5aae73c-340d-4aa2-9fe0-1bf2d79b69dc/mission-state.json +103 -0
- package/.omc/state/sessions/d5aae73c-340d-4aa2-9fe0-1bf2d79b69dc/pre-tool-advisory-throttle.json +30 -0
- package/.omc/state/sessions/d5aae73c-340d-4aa2-9fe0-1bf2d79b69dc/ralph-state.json +13 -0
- package/.omc/state/sessions/d5aae73c-340d-4aa2-9fe0-1bf2d79b69dc/subagent-tracking-state.json +62 -0
- package/.omc/state/sessions/d5aae73c-340d-4aa2-9fe0-1bf2d79b69dc/ultrawork-state.json +11 -0
- package/.turbo/turbo-build.log +6 -6
- package/.turbo/turbo-eslint-check.log +1 -1
- package/.turbo/turbo-prettier-check.log +1 -1
- package/.turbo/turbo-prettier-fix.log +1 -2
- package/.turbo/turbo-test.log +79 -70
- package/.turbo/turbo-ts-check.log +1 -1
- package/dist/cli.js +64 -56
- package/dist/cli.js.map +4 -4
- package/dist/mcp.js +39 -39
- package/dist/mcp.js.map +4 -4
- package/package.json +1 -1
- package/src/.omc/state/agent-replay-8dd0ad2f-3a90-4158-b9de-ec8e19a7c983.jsonl +11 -0
- package/src/.omc/state/idle-notif-cooldown.json +3 -0
- package/src/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/mission-state.json +79 -0
- package/src/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/pre-tool-advisory-throttle.json +22 -0
- package/src/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/ralph-state.json +13 -0
- package/src/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/subagent-tracking-state.json +26 -0
- package/src/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/ultrawork-state.json +11 -0
- package/src/commands/doctor/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/pre-tool-advisory-throttle.json +14 -0
- package/src/commands/doctor/__tests__/check-ide-installed.test.ts +142 -0
- package/src/commands/doctor/doctor.ts +98 -1
- package/src/commands/gh-release-deliver/gh-release-deliver.ts +32 -1
- package/src/commands/init/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/pre-tool-advisory-throttle.json +18 -0
- package/src/commands/init/__tests__/migrate-config.test.ts +132 -1
- package/src/commands/init/init.ts +19 -6
- package/src/commands/init/migrate-config.ts +104 -0
- package/src/commands/release-create/release-create.ts +5 -13
- package/src/commands/worktrees-add/index.ts +1 -2
- package/src/commands/worktrees-add/worktrees-add.ts +24 -60
- package/src/commands/worktrees-open/worktrees-open.ts +55 -68
- package/src/commands/worktrees-reload/__tests__/reload-cmux.test.ts +119 -0
- package/src/commands/worktrees-reload/index.ts +1 -0
- package/src/commands/worktrees-reload/worktrees-reload.ts +229 -0
- package/src/commands/worktrees-remove/worktrees-remove.ts +2 -44
- package/src/commands/worktrees-sync/worktrees-sync.ts +2 -44
- package/src/entry/cli.ts +35 -25
- package/src/integrations/cursor/index.ts +1 -0
- package/src/integrations/cursor/open-cursor-workspace.ts +70 -0
- package/src/integrations/ide/__tests__/ide-facade.test.ts +242 -0
- package/src/integrations/ide/add-ide-worktree-folders.ts +79 -0
- package/src/integrations/ide/index.ts +6 -0
- package/src/integrations/ide/open-ide-workspace.ts +51 -0
- package/src/integrations/ide/provider-label.ts +24 -0
- package/src/integrations/ide/remove-ide-worktree-folders.ts +80 -0
- package/src/integrations/ide/types.ts +32 -0
- package/src/integrations/zed/__tests__/open-zed-workspace.test.ts +85 -0
- package/src/integrations/zed/add-folders-to-zed-workspace.ts +30 -0
- package/src/integrations/zed/index.ts +2 -0
- package/src/integrations/zed/open-zed-workspace.ts +57 -0
- package/src/lib/assert-never/assert-never.ts +15 -0
- package/src/lib/assert-never/index.ts +1 -0
- package/src/lib/git-utils/__tests__/git-primitives.test.ts +122 -1
- package/src/lib/git-utils/git-utils.ts +35 -0
- package/src/lib/git-utils/index.ts +2 -0
- package/src/lib/infra-kit-config/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/pre-tool-advisory-throttle.json +18 -0
- package/src/lib/infra-kit-config/__tests__/infra-kit-config.test.ts +172 -9
- package/src/lib/infra-kit-config/index.ts +2 -1
- package/src/lib/infra-kit-config/infra-kit-config.ts +80 -22
- package/src/lib/package-config/.omc/state/sessions/dde22a92-a0e9-4948-905d-25c9a812ab35/last-tool-error-state.json +7 -0
- package/src/lib/package-config/.omc/state/sessions/dde22a92-a0e9-4948-905d-25c9a812ab35/pre-tool-advisory-throttle.json +10 -0
- package/src/lib/version-utils/__tests__/next-version.test.ts +84 -0
- package/src/lib/version-utils/index.ts +1 -0
- package/src/lib/version-utils/next-version.ts +56 -15
- package/src/mcp/tools/index.ts +2 -0
- package/tsconfig.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{"t":0,"agent":"a5fb1bc","agent_type":"architect","event":"agent_start","parent_mode":"none"}
|
|
2
|
+
{"t":0,"agent":"a5fb1bc","agent_type":"architect","event":"agent_stop","success":true,"duration_ms":171283}
|
|
3
|
+
{"t":0,"agent":"ad5fb86","agent_type":"critic","event":"agent_start","parent_mode":"none"}
|
|
4
|
+
{"t":0,"agent":"ad5fb86","agent_type":"critic","event":"agent_stop","success":true,"duration_ms":150448}
|
|
5
|
+
{"t":0,"agent":"ad5fb86","agent_type":"critic","event":"agent_start","parent_mode":"none"}
|
|
6
|
+
{"t":0,"agent":"ad5fb86","agent_type":"critic","event":"agent_stop","success":true,"duration_ms":34852}
|
|
7
|
+
{"t":0,"agent":"system","event":"keyword_detected","keyword":"ralph"}
|
|
8
|
+
{"t":0,"agent":"system","event":"mode_change","mode_from":"none","mode_to":"ralph"}
|
|
9
|
+
{"t":0,"agent":"system","event":"skill_invoked","skill_name":"oh-my-claudecode:cancel"}
|
|
10
|
+
{"t":0,"agent":"system","event":"keyword_detected","keyword":"ralph"}
|
|
11
|
+
{"t":0,"agent":"system","event":"mode_change","mode_from":"none","mode_to":"ralph"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"updatedAt": "2026-06-21T11:08:54.543Z",
|
|
3
|
+
"missions": [
|
|
4
|
+
{
|
|
5
|
+
"id": "session:8dd0ad2f-3a90-4158-b9de-ec8e19a7c983:none",
|
|
6
|
+
"source": "session",
|
|
7
|
+
"name": "none",
|
|
8
|
+
"objective": "Session mission",
|
|
9
|
+
"createdAt": "2026-06-21T11:00:23.527Z",
|
|
10
|
+
"updatedAt": "2026-06-21T11:08:54.543Z",
|
|
11
|
+
"status": "done",
|
|
12
|
+
"workerCount": 2,
|
|
13
|
+
"taskCounts": {
|
|
14
|
+
"total": 2,
|
|
15
|
+
"pending": 0,
|
|
16
|
+
"blocked": 0,
|
|
17
|
+
"inProgress": 0,
|
|
18
|
+
"completed": 2,
|
|
19
|
+
"failed": 0
|
|
20
|
+
},
|
|
21
|
+
"agents": [
|
|
22
|
+
{
|
|
23
|
+
"name": "architect:a5fb1bc",
|
|
24
|
+
"role": "architect",
|
|
25
|
+
"ownership": "a5fb1bc4dd2207983",
|
|
26
|
+
"status": "done",
|
|
27
|
+
"currentStep": null,
|
|
28
|
+
"latestUpdate": "completed",
|
|
29
|
+
"completedSummary": null,
|
|
30
|
+
"updatedAt": "2026-06-21T11:03:14.810Z"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "critic:ad5fb86",
|
|
34
|
+
"role": "critic",
|
|
35
|
+
"ownership": "ad5fb865bf84b1924",
|
|
36
|
+
"status": "done",
|
|
37
|
+
"currentStep": null,
|
|
38
|
+
"latestUpdate": "completed",
|
|
39
|
+
"completedSummary": null,
|
|
40
|
+
"updatedAt": "2026-06-21T11:08:54.543Z"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"timeline": [
|
|
44
|
+
{
|
|
45
|
+
"id": "session-start:ad5fb865bf84b1924:2026-06-21T11:04:55.259Z",
|
|
46
|
+
"at": "2026-06-21T11:04:55.259Z",
|
|
47
|
+
"kind": "update",
|
|
48
|
+
"agent": "critic:ad5fb86",
|
|
49
|
+
"detail": "started critic:ad5fb86",
|
|
50
|
+
"sourceKey": "session-start:ad5fb865bf84b1924"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "session-stop:ad5fb865bf84b1924:2026-06-21T11:07:25.707Z",
|
|
54
|
+
"at": "2026-06-21T11:07:25.707Z",
|
|
55
|
+
"kind": "completion",
|
|
56
|
+
"agent": "critic:ad5fb86",
|
|
57
|
+
"detail": "completed",
|
|
58
|
+
"sourceKey": "session-stop:ad5fb865bf84b1924"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "session-start:ad5fb865bf84b1924:2026-06-21T11:08:19.691Z",
|
|
62
|
+
"at": "2026-06-21T11:08:19.691Z",
|
|
63
|
+
"kind": "update",
|
|
64
|
+
"agent": "critic:ad5fb86",
|
|
65
|
+
"detail": "started critic:ad5fb86",
|
|
66
|
+
"sourceKey": "session-start:ad5fb865bf84b1924"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "session-stop:ad5fb865bf84b1924:2026-06-21T11:08:54.543Z",
|
|
70
|
+
"at": "2026-06-21T11:08:54.543Z",
|
|
71
|
+
"kind": "completion",
|
|
72
|
+
"agent": "critic:ad5fb86",
|
|
73
|
+
"detail": "completed",
|
|
74
|
+
"sourceKey": "session-stop:ad5fb865bf84b1924"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
package/src/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/pre-tool-advisory-throttle.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"entries": {
|
|
4
|
+
"79a93d4a2f8f50b95f852280616242fee1855dc99a3c75211917f55e72e95fae": {
|
|
5
|
+
"last_emitted_at_ms": 1782045856683,
|
|
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": 1782045791692,
|
|
10
|
+
"message": "Verify changes work after editing. Test functionality before marking complete."
|
|
11
|
+
},
|
|
12
|
+
"466399dafa2d20f60587180bad0a07358eb7f2bce724df0ff682f038ad33f5ad": {
|
|
13
|
+
"last_emitted_at_ms": 1782045769114,
|
|
14
|
+
"message": "Read multiple files in parallel when possible for faster analysis."
|
|
15
|
+
},
|
|
16
|
+
"1bb6914bc42b891c26d46c8ae2fd4f44071f59862e89dc550f4c82877b700216": {
|
|
17
|
+
"last_emitted_at_ms": 1782045534124,
|
|
18
|
+
"message": "The boulder never stops. Continue until all tasks complete."
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"updated_at": "2026-06-21T12:44:16.683Z"
|
|
22
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"active": true,
|
|
3
|
+
"iteration": 1,
|
|
4
|
+
"max_iterations": 100,
|
|
5
|
+
"started_at": "2026-06-21T12:41:30.413Z",
|
|
6
|
+
"prompt": "/ralph do it",
|
|
7
|
+
"session_id": "8dd0ad2f-3a90-4158-b9de-ec8e19a7c983",
|
|
8
|
+
"project_path": "/Users/arthur/projects/infra-kit/apps/infra-kit/cli/src",
|
|
9
|
+
"linked_ultrawork": true,
|
|
10
|
+
"awaiting_confirmation": true,
|
|
11
|
+
"awaiting_confirmation_set_at": "2026-06-21T12:41:30.413Z",
|
|
12
|
+
"last_checked_at": "2026-06-21T12:41:30.413Z"
|
|
13
|
+
}
|
package/src/.omc/state/sessions/8dd0ad2f-3a90-4158-b9de-ec8e19a7c983/subagent-tracking-state.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agents": [
|
|
3
|
+
{
|
|
4
|
+
"agent_id": "a5fb1bc4dd2207983",
|
|
5
|
+
"agent_type": "oh-my-claudecode:architect",
|
|
6
|
+
"started_at": "2026-06-21T11:00:23.527Z",
|
|
7
|
+
"parent_mode": "none",
|
|
8
|
+
"status": "completed",
|
|
9
|
+
"completed_at": "2026-06-21T11:03:14.810Z",
|
|
10
|
+
"duration_ms": 171283
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"agent_id": "ad5fb865bf84b1924",
|
|
14
|
+
"agent_type": "oh-my-claudecode:critic",
|
|
15
|
+
"started_at": "2026-06-21T11:08:19.691Z",
|
|
16
|
+
"parent_mode": "none",
|
|
17
|
+
"status": "completed",
|
|
18
|
+
"completed_at": "2026-06-21T11:08:54.543Z",
|
|
19
|
+
"duration_ms": 34852
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"total_spawned": 3,
|
|
23
|
+
"total_completed": 3,
|
|
24
|
+
"total_failed": 0,
|
|
25
|
+
"last_updated": "2026-06-21T11:08:54.646Z"
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"active": true,
|
|
3
|
+
"started_at": "2026-06-21T12:41:30.430Z",
|
|
4
|
+
"original_prompt": "/ralph do it",
|
|
5
|
+
"session_id": "8dd0ad2f-3a90-4158-b9de-ec8e19a7c983",
|
|
6
|
+
"project_path": "/Users/arthur/projects/infra-kit/apps/infra-kit/cli/src",
|
|
7
|
+
"reinforcement_count": 0,
|
|
8
|
+
"awaiting_confirmation": true,
|
|
9
|
+
"awaiting_confirmation_set_at": "2026-06-21T12:41:30.430Z",
|
|
10
|
+
"last_checked_at": "2026-06-21T12:41:30.430Z"
|
|
11
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"entries": {
|
|
4
|
+
"79a93d4a2f8f50b95f852280616242fee1855dc99a3c75211917f55e72e95fae": {
|
|
5
|
+
"last_emitted_at_ms": 1782040957960,
|
|
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": 1782040931719,
|
|
10
|
+
"message": "Verify changes work after editing. Test functionality before marking complete."
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"updated_at": "2026-06-21T11:22:37.960Z"
|
|
14
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { checkIdeInstalled } from '../doctor'
|
|
4
|
+
|
|
5
|
+
const config = vi.hoisted(() => {
|
|
6
|
+
return { value: {} as { ide?: unknown }, shouldThrow: false }
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
vi.mock('src/lib/infra-kit-config', () => {
|
|
10
|
+
return {
|
|
11
|
+
resetInfraKitConfigCache: vi.fn(),
|
|
12
|
+
getInfraKitConfig: vi.fn(() => {
|
|
13
|
+
if (config.shouldThrow) {
|
|
14
|
+
return Promise.reject(new Error('bad config'))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return Promise.resolve(config.value)
|
|
18
|
+
}),
|
|
19
|
+
// Mirror the real normalizer (single → [ide], array → as-is, undefined → []).
|
|
20
|
+
resolveConfiguredIdes: vi.fn((cfg: { ide?: unknown }) => {
|
|
21
|
+
const ide = cfg.ide
|
|
22
|
+
|
|
23
|
+
if (!ide) return []
|
|
24
|
+
|
|
25
|
+
return Array.isArray(ide) ? ide : [ide]
|
|
26
|
+
}),
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const zx = vi.hoisted(() => {
|
|
31
|
+
return { shouldThrow: false }
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
vi.mock('zx', () => {
|
|
35
|
+
return {
|
|
36
|
+
$: vi.fn(() => {
|
|
37
|
+
if (zx.shouldThrow) {
|
|
38
|
+
return Promise.reject(new Error('command not found'))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return Promise.resolve({ stdout: '' })
|
|
42
|
+
}),
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
vi.mock('src/lib/logger', () => {
|
|
47
|
+
return { logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() } }
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('checkIdeInstalled', () => {
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
config.value = {}
|
|
53
|
+
config.shouldThrow = false
|
|
54
|
+
zx.shouldThrow = false
|
|
55
|
+
vi.clearAllMocks()
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('passes informationally when no IDE is configured', async () => {
|
|
59
|
+
config.value = { ide: undefined }
|
|
60
|
+
|
|
61
|
+
const result = await checkIdeInstalled()
|
|
62
|
+
|
|
63
|
+
expect(result.status).toBe('pass')
|
|
64
|
+
expect(result.message).toMatch(/No IDE configured/)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('passes when the configured Cursor binary is present', async () => {
|
|
68
|
+
config.value = { ide: { provider: 'cursor', config: { workspaceConfigPath: 'ws' } } }
|
|
69
|
+
|
|
70
|
+
const result = await checkIdeInstalled()
|
|
71
|
+
|
|
72
|
+
expect(result.status).toBe('pass')
|
|
73
|
+
expect(result.message).toMatch(/Installed: Cursor/)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('fails when the configured Cursor binary is missing', async () => {
|
|
77
|
+
config.value = { ide: { provider: 'cursor', config: { workspaceConfigPath: 'ws' } } }
|
|
78
|
+
zx.shouldThrow = true
|
|
79
|
+
|
|
80
|
+
const result = await checkIdeInstalled()
|
|
81
|
+
|
|
82
|
+
expect(result.status).toBe('fail')
|
|
83
|
+
expect(result.message).toMatch(/Cursor is not installed/)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('passes when the configured Zed binary is present', async () => {
|
|
87
|
+
config.value = { ide: { provider: 'zed', config: {} } }
|
|
88
|
+
|
|
89
|
+
const result = await checkIdeInstalled()
|
|
90
|
+
|
|
91
|
+
expect(result.status).toBe('pass')
|
|
92
|
+
expect(result.message).toMatch(/Installed: Zed/)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('fails when the configured Zed binary is missing', async () => {
|
|
96
|
+
config.value = { ide: { provider: 'zed', config: {} } }
|
|
97
|
+
zx.shouldThrow = true
|
|
98
|
+
|
|
99
|
+
const result = await checkIdeInstalled()
|
|
100
|
+
|
|
101
|
+
expect(result.status).toBe('fail')
|
|
102
|
+
expect(result.message).toMatch(/Zed is not installed/)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('passes listing all editors when multiple are configured and present', async () => {
|
|
106
|
+
config.value = {
|
|
107
|
+
ide: [
|
|
108
|
+
{ provider: 'cursor', config: { workspaceConfigPath: 'ws' } },
|
|
109
|
+
{ provider: 'zed', config: {} },
|
|
110
|
+
],
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const result = await checkIdeInstalled()
|
|
114
|
+
|
|
115
|
+
expect(result.status).toBe('pass')
|
|
116
|
+
expect(result.message).toMatch(/Installed: Cursor, Zed/)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('fails when one of several configured editors is missing', async () => {
|
|
120
|
+
config.value = {
|
|
121
|
+
ide: [
|
|
122
|
+
{ provider: 'cursor', config: { workspaceConfigPath: 'ws' } },
|
|
123
|
+
{ provider: 'zed', config: {} },
|
|
124
|
+
],
|
|
125
|
+
}
|
|
126
|
+
zx.shouldThrow = true
|
|
127
|
+
|
|
128
|
+
const result = await checkIdeInstalled()
|
|
129
|
+
|
|
130
|
+
expect(result.status).toBe('fail')
|
|
131
|
+
expect(result.message).toMatch(/Cursor is not installed/)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('passes informationally when the config cannot be read', async () => {
|
|
135
|
+
config.shouldThrow = true
|
|
136
|
+
|
|
137
|
+
const result = await checkIdeInstalled()
|
|
138
|
+
|
|
139
|
+
expect(result.status).toBe('pass')
|
|
140
|
+
expect(result.message).toMatch(/Skipped/)
|
|
141
|
+
})
|
|
142
|
+
})
|
|
@@ -12,7 +12,12 @@ import {
|
|
|
12
12
|
} from 'src/commands/init/agent-files'
|
|
13
13
|
import { MARKER_END, MARKER_START, buildShellBlock } from 'src/commands/init/init'
|
|
14
14
|
import { getProjectRoot } from 'src/lib/git-utils/git-utils'
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
getInfraKitConfig,
|
|
17
|
+
getInfraKitConfigPaths,
|
|
18
|
+
resetInfraKitConfigCache,
|
|
19
|
+
resolveConfiguredIdes,
|
|
20
|
+
} from 'src/lib/infra-kit-config'
|
|
16
21
|
import { logger } from 'src/lib/logger'
|
|
17
22
|
import { hasManagedBlock } from 'src/lib/managed-block'
|
|
18
23
|
import { defineMcpTool, textContent } from 'src/types'
|
|
@@ -155,6 +160,97 @@ const checkUserOverridePath = async (): Promise<CheckResult> => {
|
|
|
155
160
|
}
|
|
156
161
|
}
|
|
157
162
|
|
|
163
|
+
interface IdeProbe {
|
|
164
|
+
ok: boolean
|
|
165
|
+
label: string
|
|
166
|
+
failMsg: string
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const IDE_PROBE_META: Record<'cursor' | 'zed', { command: string[]; label: string; failMsg: string }> = {
|
|
170
|
+
cursor: {
|
|
171
|
+
command: ['cursor', '--version'],
|
|
172
|
+
label: 'Cursor',
|
|
173
|
+
failMsg: 'Cursor is not installed. Install from: https://cursor.com/',
|
|
174
|
+
},
|
|
175
|
+
zed: {
|
|
176
|
+
command: ['zed', '--version'],
|
|
177
|
+
label: 'Zed',
|
|
178
|
+
failMsg: 'Zed is not installed. Install from: https://zed.dev/',
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const probeIde = async (provider: 'cursor' | 'zed'): Promise<IdeProbe> => {
|
|
183
|
+
const meta = IDE_PROBE_META[provider]
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
await $`${meta.command}`
|
|
187
|
+
|
|
188
|
+
return { ok: true, label: meta.label, failMsg: meta.failMsg }
|
|
189
|
+
} catch {
|
|
190
|
+
return { ok: false, label: meta.label, failMsg: meta.failMsg }
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Check that every editor configured under `ide` is installed. Reads the merged
|
|
196
|
+
* infra-kit config and probes each matching binary (`cursor`/`zed`). Passes only
|
|
197
|
+
* if all configured editors are present; fails listing any that are missing.
|
|
198
|
+
* Informational pass when no IDE is configured or the config can't be read — an
|
|
199
|
+
* unconfigured editor is a valid setup, and config validity is reported
|
|
200
|
+
* separately by `checkInfraKitConfigValid`.
|
|
201
|
+
*/
|
|
202
|
+
export const checkIdeInstalled = async (): Promise<CheckResult> => {
|
|
203
|
+
const name = 'ide installed'
|
|
204
|
+
|
|
205
|
+
let providers: ('cursor' | 'zed')[]
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
resetInfraKitConfigCache()
|
|
209
|
+
const config = await getInfraKitConfig()
|
|
210
|
+
|
|
211
|
+
providers = resolveConfiguredIdes(config).map((ide) => {
|
|
212
|
+
return ide.provider
|
|
213
|
+
})
|
|
214
|
+
} catch {
|
|
215
|
+
return { name, status: 'pass', message: 'Skipped — infra-kit config could not be read (see config check)' }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (providers.length === 0) {
|
|
219
|
+
return { name, status: 'pass', message: 'No IDE configured (ide unset)' }
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const probes = await Promise.all(
|
|
223
|
+
providers.map((provider) => {
|
|
224
|
+
return probeIde(provider)
|
|
225
|
+
}),
|
|
226
|
+
)
|
|
227
|
+
const missing = probes.filter((probe) => {
|
|
228
|
+
return !probe.ok
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
if (missing.length === 0) {
|
|
232
|
+
return {
|
|
233
|
+
name,
|
|
234
|
+
status: 'pass',
|
|
235
|
+
message: `Installed: ${probes
|
|
236
|
+
.map((probe) => {
|
|
237
|
+
return probe.label
|
|
238
|
+
})
|
|
239
|
+
.join(', ')}`,
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
name,
|
|
245
|
+
status: 'fail',
|
|
246
|
+
message: missing
|
|
247
|
+
.map((probe) => {
|
|
248
|
+
return probe.failMsg
|
|
249
|
+
})
|
|
250
|
+
.join('; '),
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
158
254
|
const RTK_REQUIRED_INDEXES = [1, 2, 3, 5, 7] as const
|
|
159
255
|
|
|
160
256
|
const checkRtkConfigured = async (): Promise<CheckResult> => {
|
|
@@ -312,6 +408,7 @@ export const doctor = async () => {
|
|
|
312
408
|
checkPnpmWorkspaceVirtualStore(),
|
|
313
409
|
checkInfraKitConfigValid(),
|
|
314
410
|
checkUserOverridePath(),
|
|
411
|
+
checkIdeInstalled(),
|
|
315
412
|
])
|
|
316
413
|
|
|
317
414
|
const checks: CheckResult[] = [...baseChecks, ...(await checkAgentFiles())]
|
|
@@ -11,7 +11,13 @@ import { WORKTREES_DIR_SUFFIX } from 'src/lib/constants'
|
|
|
11
11
|
import { formatZxError } from 'src/lib/errors/format-zx-error'
|
|
12
12
|
import { OperationError } from 'src/lib/errors/operation-error'
|
|
13
13
|
import { assertManagementContext } from 'src/lib/git-guard'
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
deleteLocalBranch,
|
|
16
|
+
deleteRemoteBranch,
|
|
17
|
+
getCurrentWorktrees,
|
|
18
|
+
getProjectRoot,
|
|
19
|
+
getRepoName,
|
|
20
|
+
} from 'src/lib/git-utils'
|
|
15
21
|
import { logger } from 'src/lib/logger'
|
|
16
22
|
import { displayLabel, formatJiraName, formatRcTitle, parseBranchName } from 'src/lib/release-id'
|
|
17
23
|
import type { ReleaseId } from 'src/lib/release-id'
|
|
@@ -309,6 +315,30 @@ const syncMainIntoDev = async (): Promise<void> => {
|
|
|
309
315
|
)
|
|
310
316
|
}
|
|
311
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Best-effort cleanup of the delivered release branch, locally and on `origin`.
|
|
320
|
+
*
|
|
321
|
+
* Idempotent backstop to `gh pr merge --delete-branch` (in `mergeReleasePR`):
|
|
322
|
+
* that flag is skipped when the release PR was already merged on a prior
|
|
323
|
+
* attempt, so an idempotent re-run would otherwise leave the branch behind.
|
|
324
|
+
* Each delete is isolated — a failure (e.g. the branch checked out in a stray
|
|
325
|
+
* worktree, or a remote hiccup) logs a warning naming the branch and never
|
|
326
|
+
* aborts delivery, since the irreversible merge and deploy already happened.
|
|
327
|
+
*/
|
|
328
|
+
const removeDeliveredReleaseBranch = async (branch: string): Promise<void> => {
|
|
329
|
+
try {
|
|
330
|
+
await deleteLocalBranch(branch)
|
|
331
|
+
} catch (error) {
|
|
332
|
+
logger.warn({ err: formatZxError(error) }, `Failed to delete local branch ${branch} (non-blocking)`)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
await deleteRemoteBranch(branch)
|
|
337
|
+
} catch (error) {
|
|
338
|
+
logger.warn({ err: formatZxError(error) }, `Failed to delete remote branch ${branch} (non-blocking)`)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
312
342
|
const deliverJiraReleaseSafely = async (id: ReleaseId): Promise<void> => {
|
|
313
343
|
const jiraConfig = await loadJiraConfigOptional()
|
|
314
344
|
|
|
@@ -395,6 +425,7 @@ export const ghReleaseDeliver = async (args: GhReleaseDeliverArgs) => {
|
|
|
395
425
|
|
|
396
426
|
await dispatchDeployWorkflow()
|
|
397
427
|
await syncMainIntoDev()
|
|
428
|
+
await removeDeliveredReleaseBranch(selectedReleaseBranch)
|
|
398
429
|
|
|
399
430
|
$.quiet = false
|
|
400
431
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"entries": {
|
|
4
|
+
"445ed27a3872b681d98190bae61ccb84954e1bc4e140df5370be958dee776b3a": {
|
|
5
|
+
"last_emitted_at_ms": 1782045486541,
|
|
6
|
+
"message": "Verify changes work after editing. Test functionality before marking complete."
|
|
7
|
+
},
|
|
8
|
+
"79a93d4a2f8f50b95f852280616242fee1855dc99a3c75211917f55e72e95fae": {
|
|
9
|
+
"last_emitted_at_ms": 1782045453587,
|
|
10
|
+
"message": "Use parallel execution for independent tasks. Use run_in_background for long operations (npm install, builds, tests)."
|
|
11
|
+
},
|
|
12
|
+
"466399dafa2d20f60587180bad0a07358eb7f2bce724df0ff682f038ad33f5ad": {
|
|
13
|
+
"last_emitted_at_ms": 1782045414993,
|
|
14
|
+
"message": "Read multiple files in parallel when possible for faster analysis."
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"updated_at": "2026-06-21T12:38:06.541Z"
|
|
18
|
+
}
|
|
@@ -6,7 +6,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
6
6
|
// Import AFTER the mock is declared so the module picks up the mocked dep.
|
|
7
7
|
import { getProjectRoot, getRepoName } from 'src/lib/git-utils'
|
|
8
8
|
|
|
9
|
-
import { migrateLegacyConfig } from '../migrate-config'
|
|
9
|
+
import { migrateLegacyConfig, normalizeLegacyIdeStructures } from '../migrate-config'
|
|
10
10
|
|
|
11
11
|
vi.mock('src/lib/git-utils', () => {
|
|
12
12
|
return {
|
|
@@ -158,3 +158,134 @@ describe('migrateLegacyConfig', () => {
|
|
|
158
158
|
})
|
|
159
159
|
})
|
|
160
160
|
})
|
|
161
|
+
|
|
162
|
+
describe('normalizeLegacyIdeStructures', () => {
|
|
163
|
+
beforeEach(() => {
|
|
164
|
+
vi.clearAllMocks()
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
afterEach(() => {
|
|
168
|
+
vi.clearAllMocks()
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('strips a legacy ide.config.mode from infra-kit.json (single provider) and preserves the rest', async () => {
|
|
172
|
+
await withTmpRepo(async (tmp) => {
|
|
173
|
+
const jsonPath = path.join(tmp, 'infra-kit.json')
|
|
174
|
+
|
|
175
|
+
writeFile(
|
|
176
|
+
jsonPath,
|
|
177
|
+
JSON.stringify({
|
|
178
|
+
environments: ['dev'],
|
|
179
|
+
envManagement: { provider: 'doppler', config: { name: 'p' } },
|
|
180
|
+
ide: { provider: 'cursor', config: { mode: 'workspace', workspaceConfigPath: './ws.code-workspace' } },
|
|
181
|
+
}),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
await normalizeLegacyIdeStructures()
|
|
185
|
+
|
|
186
|
+
const result = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'))
|
|
187
|
+
|
|
188
|
+
expect(result.ide).toEqual({ provider: 'cursor', config: { workspaceConfigPath: './ws.code-workspace' } })
|
|
189
|
+
// Everything else preserved verbatim.
|
|
190
|
+
expect(result.environments).toEqual(['dev'])
|
|
191
|
+
expect(result.envManagement).toEqual({ provider: 'doppler', config: { name: 'p' } })
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it('strips ide.config.mode from every entry of an array ide', async () => {
|
|
196
|
+
await withTmpRepo(async (tmp) => {
|
|
197
|
+
const jsonPath = path.join(tmp, 'infra-kit.json')
|
|
198
|
+
|
|
199
|
+
writeFile(
|
|
200
|
+
jsonPath,
|
|
201
|
+
JSON.stringify({
|
|
202
|
+
environments: ['dev'],
|
|
203
|
+
envManagement: { provider: 'doppler', config: { name: 'p' } },
|
|
204
|
+
ide: [
|
|
205
|
+
{ provider: 'cursor', config: { mode: 'workspace', workspaceConfigPath: 'ws' } },
|
|
206
|
+
{ provider: 'zed', config: { mode: 'windows' } },
|
|
207
|
+
],
|
|
208
|
+
}),
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
await normalizeLegacyIdeStructures()
|
|
212
|
+
|
|
213
|
+
const result = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'))
|
|
214
|
+
|
|
215
|
+
expect(result.ide).toEqual([
|
|
216
|
+
{ provider: 'cursor', config: { workspaceConfigPath: 'ws' } },
|
|
217
|
+
{ provider: 'zed', config: {} },
|
|
218
|
+
])
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('normalizes the user-global config layer too', async () => {
|
|
223
|
+
await withTmpRepo(async (tmp) => {
|
|
224
|
+
const userGlobalJson = path.join(tmp, '.infra-kit', 'config.json')
|
|
225
|
+
|
|
226
|
+
writeFile(userGlobalJson, JSON.stringify({ ide: { provider: 'zed', config: { mode: 'windows' } } }))
|
|
227
|
+
|
|
228
|
+
await normalizeLegacyIdeStructures()
|
|
229
|
+
|
|
230
|
+
expect(JSON.parse(fs.readFileSync(userGlobalJson, 'utf-8')).ide).toEqual({ provider: 'zed', config: {} })
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it('normalizes the user-project config layer too', async () => {
|
|
235
|
+
await withTmpRepo(async (tmp) => {
|
|
236
|
+
const projectName = path.basename(tmp)
|
|
237
|
+
const userProjectJson = path.join(tmp, '.infra-kit', 'projects', projectName, 'infra-kit.json')
|
|
238
|
+
|
|
239
|
+
writeFile(
|
|
240
|
+
userProjectJson,
|
|
241
|
+
JSON.stringify({ ide: { provider: 'cursor', config: { mode: 'workspace', workspaceConfigPath: 'ws' } } }),
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
await normalizeLegacyIdeStructures()
|
|
245
|
+
|
|
246
|
+
expect(JSON.parse(fs.readFileSync(userProjectJson, 'utf-8')).ide).toEqual({
|
|
247
|
+
provider: 'cursor',
|
|
248
|
+
config: { workspaceConfigPath: 'ws' },
|
|
249
|
+
})
|
|
250
|
+
})
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('leaves an already-clean config byte-for-byte untouched (idempotent)', async () => {
|
|
254
|
+
await withTmpRepo(async (tmp) => {
|
|
255
|
+
const jsonPath = path.join(tmp, 'infra-kit.json')
|
|
256
|
+
const json =
|
|
257
|
+
'{"environments":["dev"],"envManagement":{"provider":"doppler","config":{"name":"p"}},"ide":{"provider":"zed","config":{}}}'
|
|
258
|
+
|
|
259
|
+
writeFile(jsonPath, json)
|
|
260
|
+
|
|
261
|
+
await normalizeLegacyIdeStructures()
|
|
262
|
+
|
|
263
|
+
expect(fs.readFileSync(jsonPath, 'utf-8')).toBe(json)
|
|
264
|
+
})
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('is a no-op when there is no ide config', async () => {
|
|
268
|
+
await withTmpRepo(async (tmp) => {
|
|
269
|
+
const jsonPath = path.join(tmp, 'infra-kit.json')
|
|
270
|
+
const json = '{"environments":["dev"],"envManagement":{"provider":"doppler","config":{"name":"p"}}}'
|
|
271
|
+
|
|
272
|
+
writeFile(jsonPath, json)
|
|
273
|
+
|
|
274
|
+
await normalizeLegacyIdeStructures()
|
|
275
|
+
|
|
276
|
+
expect(fs.readFileSync(jsonPath, 'utf-8')).toBe(json)
|
|
277
|
+
})
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
it('warns and skips malformed JSON without throwing', async () => {
|
|
281
|
+
await withTmpRepo(async (tmp) => {
|
|
282
|
+
const jsonPath = path.join(tmp, 'infra-kit.json')
|
|
283
|
+
|
|
284
|
+
writeFile(jsonPath, '{ not valid json ')
|
|
285
|
+
|
|
286
|
+
await expect(normalizeLegacyIdeStructures()).resolves.toBeUndefined()
|
|
287
|
+
|
|
288
|
+
expect(fs.readFileSync(jsonPath, 'utf-8')).toBe('{ not valid json ')
|
|
289
|
+
})
|
|
290
|
+
})
|
|
291
|
+
})
|