xcode-mcli 0.1.0
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/README.md +92 -0
- package/bin/xcode-mcli.ts +16 -0
- package/config/mcporter.json +11 -0
- package/package.json +57 -0
- package/skill/SKILL.md +70 -0
- package/skill/agents/openai.yaml +4 -0
- package/skill/references/api-reference.md +409 -0
- package/skill/references/apple-xcode-26.3.surface.json +1466 -0
- package/skill/references/compatibility.md +91 -0
- package/skill/references/setup.md +120 -0
- package/skill/references/troubleshooting.md +160 -0
- package/src/commands/daemon-restart.ts +22 -0
- package/src/commands/daemon-start.ts +22 -0
- package/src/commands/daemon-status.ts +29 -0
- package/src/commands/daemon-stop.ts +22 -0
- package/src/commands/index.ts +26 -0
- package/src/commands/project-build.ts +50 -0
- package/src/commands/setup.ts +26 -0
- package/src/commands/surface-snapshot.ts +45 -0
- package/src/commands/surface-verify.ts +46 -0
- package/src/commands/tool-command.ts +150 -0
- package/src/commands/windows-list.ts +43 -0
- package/src/commands/windows-use.ts +30 -0
- package/src/commands/xcode-tool-commands.ts +460 -0
- package/src/constants.ts +3 -0
- package/src/core/command-definition.ts +131 -0
- package/src/core/command-dispatch.ts +97 -0
- package/src/core/contracts.ts +25 -0
- package/src/core/errors.ts +52 -0
- package/src/core/package-version.ts +30 -0
- package/src/runtime/daemon-host-entry.ts +8 -0
- package/src/runtime/daemon-host.ts +455 -0
- package/src/runtime/daemon-state.ts +75 -0
- package/src/runtime/env.ts +37 -0
- package/src/runtime/mcp-jsonrpc.ts +114 -0
- package/src/runtime/output.ts +128 -0
- package/src/runtime/tab-resolver.ts +44 -0
- package/src/runtime/xcode-client.ts +192 -0
- package/src/runtime/xcode-surface.ts +166 -0
- package/src/runtime/xcode-tool-definition.ts +14 -0
- package/src/runtime/xcode-windows.ts +65 -0
- package/src/runtime/xcrun.ts +39 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Compatibility
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
`xcode-mcli` can verify its Xcode MCP compatibility by diffing the live discovered MCP surface against a pinned baseline snapshot.
|
|
6
|
+
|
|
7
|
+
The compatibility contract is the normalized result of:
|
|
8
|
+
|
|
9
|
+
- `initialize`
|
|
10
|
+
- `tools/list`
|
|
11
|
+
- `prompts/list`
|
|
12
|
+
- `resources/list`
|
|
13
|
+
|
|
14
|
+
The pinned Apple Xcode 26.3 baseline is:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
skill/references/apple-xcode-26.3.surface.json
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
That snapshot currently records:
|
|
21
|
+
|
|
22
|
+
- protocol version `2025-06-18`
|
|
23
|
+
- `20` tools
|
|
24
|
+
- `0` prompts
|
|
25
|
+
- `0` resources
|
|
26
|
+
|
|
27
|
+
## Commands
|
|
28
|
+
|
|
29
|
+
Capture the current live surface:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
xcode-mcli surface snapshot
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Write a canonical snapshot file:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
xcode-mcli surface snapshot --output-file skill/references/apple-xcode-26.3.surface.json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Verify the current Xcode MCP surface against the pinned 26.3 baseline:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
xcode-mcli surface verify --baseline-file skill/references/apple-xcode-26.3.surface.json
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Convenience npm scripts:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm run compat:xcode-mcp:snapshot
|
|
51
|
+
npm run compat:xcode-mcp:pin-26.3
|
|
52
|
+
npm run compat:xcode-mcp:verify
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Verification Behavior
|
|
56
|
+
|
|
57
|
+
`surface snapshot` normalizes the discovered surface before printing or writing it:
|
|
58
|
+
|
|
59
|
+
- sorts tools, prompts, and resources by `name`
|
|
60
|
+
- sorts object keys recursively
|
|
61
|
+
- preserves extra server metadata such as `title` and `outputSchema`
|
|
62
|
+
|
|
63
|
+
`surface verify` compares:
|
|
64
|
+
|
|
65
|
+
- protocol version
|
|
66
|
+
- tool additions, removals, and changed definitions
|
|
67
|
+
- prompt additions, removals, and changed definitions
|
|
68
|
+
- resource additions, removals, and changed definitions
|
|
69
|
+
|
|
70
|
+
An exact match exits successfully.
|
|
71
|
+
|
|
72
|
+
A mismatch exits with a runtime error. In `--json` mode, the error envelope includes structured `error.details` with the categorized diff.
|
|
73
|
+
|
|
74
|
+
## Upgrade Flow
|
|
75
|
+
|
|
76
|
+
When a new Xcode release arrives:
|
|
77
|
+
|
|
78
|
+
1. Run `npm run compat:xcode-mcp:verify`.
|
|
79
|
+
2. If it passes, the pinned baseline still matches the live Xcode MCP contract.
|
|
80
|
+
3. If it fails, inspect the categorized diff.
|
|
81
|
+
4. Confirm whether the change is additive, behavioral, or breaking for the CLI.
|
|
82
|
+
5. Update `xcode-mcli` if the wrapper needs to support the new contract.
|
|
83
|
+
6. Re-pin the baseline with `npm run compat:xcode-mcp:pin-26.3` or a new versioned snapshot path.
|
|
84
|
+
|
|
85
|
+
For machine-readable inspection:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
xcode-mcli surface verify \
|
|
89
|
+
--baseline-file skill/references/apple-xcode-26.3.surface.json \
|
|
90
|
+
--json
|
|
91
|
+
```
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Setup
|
|
2
|
+
|
|
3
|
+
## Requirements
|
|
4
|
+
|
|
5
|
+
- macOS
|
|
6
|
+
- Xcode installed
|
|
7
|
+
- `Xcode Tools` enabled in `Settings > Intelligence`
|
|
8
|
+
- Node.js 25+
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
From the repo:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Global install:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install -g xcode-mcli
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## First Run
|
|
25
|
+
|
|
26
|
+
Verify the bridge and initialize the local state root:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
xcode-mcli setup
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If `setup` reports `Failed to call xcrun mcpbridge --help.`, enable `Xcode Tools` in `Settings > Intelligence` and run `xcode-mcli setup` again.
|
|
33
|
+
|
|
34
|
+
Then confirm Xcode access:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
xcode-mcli windows list
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
If exactly one Xcode workspace window is open, many tab-aware commands can resolve it automatically.
|
|
41
|
+
|
|
42
|
+
To pin the active tab explicitly:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
xcode-mcli windows use --tab-identifier windowtab1
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Daemon
|
|
49
|
+
|
|
50
|
+
The daemon auto-starts on the first daemon-backed command.
|
|
51
|
+
|
|
52
|
+
Explicit lifecycle commands:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
xcode-mcli daemon start
|
|
56
|
+
xcode-mcli daemon status
|
|
57
|
+
xcode-mcli daemon restart
|
|
58
|
+
xcode-mcli daemon stop
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## State Root
|
|
62
|
+
|
|
63
|
+
The default state root is:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
~/Library/Application Support/xcode-mcli
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Files used there:
|
|
70
|
+
|
|
71
|
+
- `daemon.sock`
|
|
72
|
+
- `daemon.pid`
|
|
73
|
+
- `daemon.log`
|
|
74
|
+
- `state.json`
|
|
75
|
+
|
|
76
|
+
## Common Commands
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
xcode-mcli windows list
|
|
80
|
+
xcode-mcli files read --tab-identifier windowtab1 --file-path Path/To/File.swift
|
|
81
|
+
xcode-mcli build log --tab-identifier windowtab1
|
|
82
|
+
xcode-mcli project build --tab-identifier windowtab1
|
|
83
|
+
xcode-mcli tests list --tab-identifier windowtab1
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Machine Output
|
|
87
|
+
|
|
88
|
+
Use `--json` for a stable wrapper envelope:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
xcode-mcli --json windows list
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Use `--verbose` to print the exact Xcode MCP tool name to `stderr` for tool-backed commands:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
xcode-mcli --json --verbose windows list
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`--json`, `--verbose`, and `--tab-identifier <id>` can be passed before the command path or after the specific command.
|
|
101
|
+
|
|
102
|
+
## Compatibility Check
|
|
103
|
+
|
|
104
|
+
Verify the pinned Apple Xcode 26.3 MCP surface:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm run compat:xcode-mcp:verify
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Capture the current live surface:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npm run compat:xcode-mcp:snapshot
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Pin a canonical baseline file:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npm run compat:xcode-mcp:pin-26.3
|
|
120
|
+
```
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
## `setup` fails to locate `mcpbridge`
|
|
4
|
+
|
|
5
|
+
Run:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
xcode-mcli setup
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
If it fails with `Failed to locate xcrun mcpbridge.`:
|
|
12
|
+
|
|
13
|
+
- confirm Xcode is installed
|
|
14
|
+
- confirm `Xcode Tools` is enabled in `Settings > Intelligence`
|
|
15
|
+
- confirm `xcrun` resolves from your shell
|
|
16
|
+
|
|
17
|
+
If it fails with `Failed to call xcrun mcpbridge --help.`:
|
|
18
|
+
|
|
19
|
+
- enable `Xcode Tools` in `Settings > Intelligence`
|
|
20
|
+
- run `xcode-mcli setup` again
|
|
21
|
+
|
|
22
|
+
## `windows list` returns no useful workspace
|
|
23
|
+
|
|
24
|
+
Make sure Xcode is open with a workspace or project window.
|
|
25
|
+
|
|
26
|
+
Then run:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
xcode-mcli windows list
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## A tab-aware command fails because no active window is selected
|
|
33
|
+
|
|
34
|
+
Use one of these paths:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
xcode-mcli windows list
|
|
38
|
+
xcode-mcli windows use --tab-identifier windowtab1
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or pass the tab directly on the command:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
xcode-mcli files read --tab-identifier windowtab1 --file-path Path/To/File.swift
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Multiple Xcode windows are open
|
|
48
|
+
|
|
49
|
+
Pick one explicitly:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
xcode-mcli windows list
|
|
53
|
+
xcode-mcli windows use --tab-identifier windowtab1
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Xcode permission prompts appear
|
|
57
|
+
|
|
58
|
+
The first tool-backed command may trigger an Xcode permission prompt for the CLI process.
|
|
59
|
+
|
|
60
|
+
If you change terminals, shells, or Node installations, macOS may ask again because the calling process identity changed.
|
|
61
|
+
|
|
62
|
+
## The daemon looks stale
|
|
63
|
+
|
|
64
|
+
Restart it:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
xcode-mcli daemon restart
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or stop and start it:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
xcode-mcli daemon stop
|
|
74
|
+
xcode-mcli daemon start
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Inspect status:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
xcode-mcli daemon status
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## JSON output is mixed with diagnostic text
|
|
84
|
+
|
|
85
|
+
`--json` writes the stable envelope to `stdout`.
|
|
86
|
+
|
|
87
|
+
For tool-backed commands, `--verbose` writes the tool name to `stderr`.
|
|
88
|
+
|
|
89
|
+
This keeps `stdout` parseable:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
xcode-mcli windows list --json --verbose
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Mutating commands fail with a confirmation error
|
|
96
|
+
|
|
97
|
+
These commands require `--yes`:
|
|
98
|
+
|
|
99
|
+
- `files rm`
|
|
100
|
+
- `files update`
|
|
101
|
+
- `files write`
|
|
102
|
+
- `files mv --overwrite-existing`
|
|
103
|
+
|
|
104
|
+
Example:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
xcode-mcli files write --tab-identifier windowtab1 --file-path Path/To/File.swift --content 'let x = 1' --yes
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## `files mkdir` or other mutating commands fail with an Xcode tool error
|
|
111
|
+
|
|
112
|
+
Some Xcode mutating tools depend on project structure resolution inside the active workspace.
|
|
113
|
+
|
|
114
|
+
If `files mkdir` reports a message telling you to run `XcodeLS` first, warm the structure with:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
xcode-mcli files ls --path .
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Then retry the mutating command against a path inside an existing project-recognized subtree.
|
|
121
|
+
|
|
122
|
+
If Xcode returns a group-resolution failure for a path that is not already represented in the project structure, use a disposable path under an existing group such as a known source folder.
|
|
123
|
+
|
|
124
|
+
## Inspecting daemon state
|
|
125
|
+
|
|
126
|
+
The default state root is:
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
~/Library/Application Support/xcode-mcli
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Important files:
|
|
133
|
+
|
|
134
|
+
- `daemon.sock`
|
|
135
|
+
- `daemon.pid`
|
|
136
|
+
- `daemon.log`
|
|
137
|
+
- `state.json`
|
|
138
|
+
|
|
139
|
+
## `surface verify` reports a mismatch
|
|
140
|
+
|
|
141
|
+
Run:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npm run compat:xcode-mcp:verify
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
If it fails, inspect the categorized diff with:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
xcode-mcli surface verify --baseline-file skill/references/apple-xcode-26.3.surface.json --json
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Review whether the live Xcode release changed:
|
|
154
|
+
|
|
155
|
+
- protocol version
|
|
156
|
+
- tool definitions
|
|
157
|
+
- prompt definitions
|
|
158
|
+
- resource definitions
|
|
159
|
+
|
|
160
|
+
If the live contract is the one you want to support, re-pin the baseline snapshot after updating the wrapper behavior that depends on it.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { defineCommand } from "../core/command-definition.ts";
|
|
4
|
+
import { restartDaemon } from "../runtime/daemon-host.ts";
|
|
5
|
+
import { printCommandResult } from "../runtime/output.ts";
|
|
6
|
+
|
|
7
|
+
export const daemonRestartCommand = defineCommand({
|
|
8
|
+
path: ["daemon", "restart"],
|
|
9
|
+
description: "Restart the daemon.",
|
|
10
|
+
optionsSchema: z.object({}),
|
|
11
|
+
run: async ({ commandPath, globals }) => {
|
|
12
|
+
await restartDaemon();
|
|
13
|
+
printCommandResult({
|
|
14
|
+
commandPath,
|
|
15
|
+
globals,
|
|
16
|
+
text: "Daemon restarted.",
|
|
17
|
+
data: {
|
|
18
|
+
running: true,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { defineCommand } from "../core/command-definition.ts";
|
|
4
|
+
import { startDaemon } from "../runtime/daemon-host.ts";
|
|
5
|
+
import { printCommandResult } from "../runtime/output.ts";
|
|
6
|
+
|
|
7
|
+
export const daemonStartCommand = defineCommand({
|
|
8
|
+
path: ["daemon", "start"],
|
|
9
|
+
description: "Start the daemon.",
|
|
10
|
+
optionsSchema: z.object({}),
|
|
11
|
+
run: async ({ commandPath, globals }) => {
|
|
12
|
+
await startDaemon();
|
|
13
|
+
printCommandResult({
|
|
14
|
+
commandPath,
|
|
15
|
+
globals,
|
|
16
|
+
text: "Daemon started.",
|
|
17
|
+
data: {
|
|
18
|
+
running: true,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { defineCommand } from "../core/command-definition.ts";
|
|
4
|
+
import { readDaemonStatus } from "../runtime/daemon-host.ts";
|
|
5
|
+
import { printCommandResult } from "../runtime/output.ts";
|
|
6
|
+
|
|
7
|
+
export const daemonStatusCommand = defineCommand({
|
|
8
|
+
path: ["daemon", "status"],
|
|
9
|
+
description: "Show daemon status.",
|
|
10
|
+
optionsSchema: z.object({}),
|
|
11
|
+
run: async ({ commandPath, globals }) => {
|
|
12
|
+
const status = await readDaemonStatus();
|
|
13
|
+
printCommandResult({
|
|
14
|
+
commandPath,
|
|
15
|
+
globals,
|
|
16
|
+
text: status.running
|
|
17
|
+
? `Daemon is running with PID ${status.pid}.`
|
|
18
|
+
: "Daemon is not running.",
|
|
19
|
+
data: status.running
|
|
20
|
+
? {
|
|
21
|
+
running: true,
|
|
22
|
+
pid: status.pid,
|
|
23
|
+
}
|
|
24
|
+
: {
|
|
25
|
+
running: false,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { defineCommand } from "../core/command-definition.ts";
|
|
4
|
+
import { stopDaemon } from "../runtime/daemon-host.ts";
|
|
5
|
+
import { printCommandResult } from "../runtime/output.ts";
|
|
6
|
+
|
|
7
|
+
export const daemonStopCommand = defineCommand({
|
|
8
|
+
path: ["daemon", "stop"],
|
|
9
|
+
description: "Stop the daemon.",
|
|
10
|
+
optionsSchema: z.object({}),
|
|
11
|
+
run: async ({ commandPath, globals }) => {
|
|
12
|
+
await stopDaemon();
|
|
13
|
+
printCommandResult({
|
|
14
|
+
commandPath,
|
|
15
|
+
globals,
|
|
16
|
+
text: "Daemon stopped.",
|
|
17
|
+
data: {
|
|
18
|
+
running: false,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CommandDefinition } from "../core/contracts.ts";
|
|
2
|
+
import { daemonRestartCommand } from "./daemon-restart.ts";
|
|
3
|
+
import { daemonStartCommand } from "./daemon-start.ts";
|
|
4
|
+
import { daemonStatusCommand } from "./daemon-status.ts";
|
|
5
|
+
import { daemonStopCommand } from "./daemon-stop.ts";
|
|
6
|
+
import { projectBuildCommand } from "./project-build.ts";
|
|
7
|
+
import { setupCommand } from "./setup.ts";
|
|
8
|
+
import { surfaceSnapshotCommand } from "./surface-snapshot.ts";
|
|
9
|
+
import { surfaceVerifyCommand } from "./surface-verify.ts";
|
|
10
|
+
import { windowsListCommand } from "./windows-list.ts";
|
|
11
|
+
import { windowsUseCommand } from "./windows-use.ts";
|
|
12
|
+
import { xcodeToolCommandDefinitions } from "./xcode-tool-commands.ts";
|
|
13
|
+
|
|
14
|
+
export const commandDefinitions: CommandDefinition[] = [
|
|
15
|
+
setupCommand,
|
|
16
|
+
daemonStartCommand,
|
|
17
|
+
daemonRestartCommand,
|
|
18
|
+
daemonStatusCommand,
|
|
19
|
+
daemonStopCommand,
|
|
20
|
+
projectBuildCommand,
|
|
21
|
+
surfaceSnapshotCommand,
|
|
22
|
+
surfaceVerifyCommand,
|
|
23
|
+
windowsListCommand,
|
|
24
|
+
windowsUseCommand,
|
|
25
|
+
...xcodeToolCommandDefinitions,
|
|
26
|
+
];
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { defineCommand } from "../core/command-definition.ts";
|
|
4
|
+
import { callDaemonTool } from "../runtime/daemon-host.ts";
|
|
5
|
+
import {
|
|
6
|
+
printCommandResult,
|
|
7
|
+
printVerboseTool,
|
|
8
|
+
toCommandData,
|
|
9
|
+
} from "../runtime/output.ts";
|
|
10
|
+
import { resolveTabIdentifier } from "../runtime/tab-resolver.ts";
|
|
11
|
+
|
|
12
|
+
const projectBuildOptionsSchema = z.object({
|
|
13
|
+
tabIdentifier: z.string().trim().min(1).optional(),
|
|
14
|
+
});
|
|
15
|
+
const projectBuildResultSchema = z.object({
|
|
16
|
+
structuredContent: z.unknown().optional(),
|
|
17
|
+
text: z.string().default(""),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const projectBuildCommand = defineCommand({
|
|
21
|
+
path: ["project", "build"],
|
|
22
|
+
description: "Build the active Xcode project.",
|
|
23
|
+
toolName: "BuildProject",
|
|
24
|
+
configure: (command) => {
|
|
25
|
+
command.option("--tab-identifier <id>", "Active Xcode window tab identifier.");
|
|
26
|
+
},
|
|
27
|
+
optionsSchema: projectBuildOptionsSchema,
|
|
28
|
+
run: async ({ commandPath, globals, options }) => {
|
|
29
|
+
const tabIdentifier = await resolveTabIdentifier({
|
|
30
|
+
explicitTabIdentifier: options.tabIdentifier,
|
|
31
|
+
});
|
|
32
|
+
printVerboseTool(globals, "BuildProject");
|
|
33
|
+
const result = projectBuildResultSchema.parse(
|
|
34
|
+
await callDaemonTool({
|
|
35
|
+
name: "BuildProject",
|
|
36
|
+
arguments: {
|
|
37
|
+
tabIdentifier,
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
printCommandResult({
|
|
42
|
+
commandPath,
|
|
43
|
+
globals,
|
|
44
|
+
text: result.text.trimEnd(),
|
|
45
|
+
data: toCommandData(result),
|
|
46
|
+
tabIdentifier,
|
|
47
|
+
tool: "BuildProject",
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import { defineCommand } from "../core/command-definition.ts";
|
|
5
|
+
import { resolveStateRoot } from "../runtime/env.ts";
|
|
6
|
+
import { printCommandResult } from "../runtime/output.ts";
|
|
7
|
+
import { findMcpbridgePath, verifyMcpbridgeHelp } from "../runtime/xcrun.ts";
|
|
8
|
+
|
|
9
|
+
export const setupCommand = defineCommand({
|
|
10
|
+
path: ["setup"],
|
|
11
|
+
description: "Prepare xcode-mcli for use with Xcode MCP.",
|
|
12
|
+
optionsSchema: z.object({}),
|
|
13
|
+
run: async ({ commandPath, globals }) => {
|
|
14
|
+
await mkdir(resolveStateRoot(), { recursive: true });
|
|
15
|
+
await findMcpbridgePath();
|
|
16
|
+
await verifyMcpbridgeHelp();
|
|
17
|
+
printCommandResult({
|
|
18
|
+
commandPath,
|
|
19
|
+
globals,
|
|
20
|
+
text: "xcode-mcli setup complete.",
|
|
21
|
+
data: {
|
|
22
|
+
message: "xcode-mcli setup complete.",
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { defineCommand } from "../core/command-definition.ts";
|
|
4
|
+
import { printCommandResult } from "../runtime/output.ts";
|
|
5
|
+
import {
|
|
6
|
+
captureXcodeMcpSurfaceSnapshot,
|
|
7
|
+
writeXcodeMcpSurfaceSnapshotFile,
|
|
8
|
+
} from "../runtime/xcode-surface.ts";
|
|
9
|
+
|
|
10
|
+
export const surfaceSnapshotCommand = defineCommand({
|
|
11
|
+
path: ["surface", "snapshot"],
|
|
12
|
+
description: "Capture the live Xcode MCP surface.",
|
|
13
|
+
optionsSchema: z.object({
|
|
14
|
+
outputFile: z.string().trim().min(1).optional(),
|
|
15
|
+
}),
|
|
16
|
+
configure: (command) => {
|
|
17
|
+
command.option("--output-file <path>", "Write the canonical snapshot to a file.");
|
|
18
|
+
},
|
|
19
|
+
run: async ({ commandPath, globals, options, projectDir }) => {
|
|
20
|
+
const snapshot = await captureXcodeMcpSurfaceSnapshot();
|
|
21
|
+
if (options.outputFile) {
|
|
22
|
+
const outputPath = await writeXcodeMcpSurfaceSnapshotFile({
|
|
23
|
+
projectDir,
|
|
24
|
+
filePath: options.outputFile,
|
|
25
|
+
snapshot,
|
|
26
|
+
});
|
|
27
|
+
printCommandResult({
|
|
28
|
+
commandPath,
|
|
29
|
+
globals,
|
|
30
|
+
text: `Wrote Xcode MCP surface snapshot to ${outputPath}`,
|
|
31
|
+
data: {
|
|
32
|
+
outputFile: outputPath,
|
|
33
|
+
snapshot,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
printCommandResult({
|
|
39
|
+
commandPath,
|
|
40
|
+
globals,
|
|
41
|
+
text: JSON.stringify(snapshot, null, 2),
|
|
42
|
+
data: snapshot,
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { defineCommand } from "../core/command-definition.ts";
|
|
4
|
+
import { runtimeError } from "../core/errors.ts";
|
|
5
|
+
import { printCommandResult } from "../runtime/output.ts";
|
|
6
|
+
import {
|
|
7
|
+
captureXcodeMcpSurfaceSnapshot,
|
|
8
|
+
diffXcodeMcpSurfaceSnapshots,
|
|
9
|
+
readXcodeMcpSurfaceSnapshotFile,
|
|
10
|
+
xcodeMcpSurfaceDiffHasChanges,
|
|
11
|
+
} from "../runtime/xcode-surface.ts";
|
|
12
|
+
|
|
13
|
+
export const surfaceVerifyCommand = defineCommand({
|
|
14
|
+
path: ["surface", "verify"],
|
|
15
|
+
description: "Compare the live Xcode MCP surface against a baseline snapshot.",
|
|
16
|
+
optionsSchema: z.object({
|
|
17
|
+
baselineFile: z.string().trim().min(1),
|
|
18
|
+
}),
|
|
19
|
+
configure: (command) => {
|
|
20
|
+
command.requiredOption(
|
|
21
|
+
"--baseline-file <path>",
|
|
22
|
+
"Path to a canonical Xcode MCP surface snapshot.",
|
|
23
|
+
);
|
|
24
|
+
},
|
|
25
|
+
run: async ({ commandPath, globals, options, projectDir }) => {
|
|
26
|
+
const baseline = await readXcodeMcpSurfaceSnapshotFile(projectDir, options.baselineFile);
|
|
27
|
+
const live = await captureXcodeMcpSurfaceSnapshot();
|
|
28
|
+
const diff = diffXcodeMcpSurfaceSnapshots({
|
|
29
|
+
baseline,
|
|
30
|
+
live,
|
|
31
|
+
});
|
|
32
|
+
if (xcodeMcpSurfaceDiffHasChanges(diff)) {
|
|
33
|
+
throw runtimeError("Xcode MCP surface does not match the baseline snapshot.", diff);
|
|
34
|
+
}
|
|
35
|
+
printCommandResult({
|
|
36
|
+
commandPath,
|
|
37
|
+
globals,
|
|
38
|
+
text: "Xcode MCP surface matches the baseline snapshot.",
|
|
39
|
+
data: {
|
|
40
|
+
baselineFile: options.baselineFile,
|
|
41
|
+
compatible: true,
|
|
42
|
+
diff,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
});
|