@upturtle/wizard 0.2.1 → 0.2.2
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 +2 -2
- package/package.json +1 -1
- package/src/agents.mjs +11 -0
- package/src/index.mjs +2 -2
- package/src/writers.mjs +56 -0
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ npx @upturtle/wizard
|
|
|
8
8
|
|
|
9
9
|
The installer:
|
|
10
10
|
|
|
11
|
-
1. Detects which coding agent(s) you have installed (Claude Code, Cursor, Antigravity, OpenCode, Zed, Claude Desktop).
|
|
11
|
+
1. Detects which coding agent(s) you have installed (Claude Code, Cursor, OpenAI Codex CLI, Antigravity, OpenCode, Zed, Claude Desktop).
|
|
12
12
|
2. Shows a checkbox list — `↑`/`↓` to move, space to toggle, `a` to toggle all, enter to confirm.
|
|
13
13
|
3. Opens UpTurtle in your browser to authenticate and mint a scoped personal access token.
|
|
14
14
|
4. Writes the MCP server entry into each selected agent's config.
|
|
@@ -23,7 +23,7 @@ By default the wizard writes to your **user-wide** config — the MCP server is
|
|
|
23
23
|
npx @upturtle/wizard --project
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Project scope is supported for `claude-code`, `cursor`, `opencode`, and `zed`. Antigravity and Claude Desktop don't have a project-level config format.
|
|
26
|
+
Project scope is supported for `claude-code`, `cursor`, `opencode`, and `zed`. Antigravity, Codex CLI, and Claude Desktop don't have a project-level config format.
|
|
27
27
|
|
|
28
28
|
## Flags
|
|
29
29
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upturtle/wizard",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Connects your coding agent to UpTurtle's MCP server. Detects the agent, mints a scoped PAT via browser handshake, and writes the right MCP config.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/agents.mjs
CHANGED
|
@@ -14,6 +14,13 @@ export const AGENTS = [
|
|
|
14
14
|
writer: "claude-code",
|
|
15
15
|
scopes: ["user", "project"],
|
|
16
16
|
},
|
|
17
|
+
{
|
|
18
|
+
id: "codex",
|
|
19
|
+
label: "OpenAI Codex CLI",
|
|
20
|
+
detect: () => isOnPath("codex") || existsSync(codexConfigPath()),
|
|
21
|
+
writer: "codex",
|
|
22
|
+
scopes: ["user"],
|
|
23
|
+
},
|
|
17
24
|
{
|
|
18
25
|
id: "cursor",
|
|
19
26
|
label: "Cursor",
|
|
@@ -78,6 +85,10 @@ export function antigravityConfigPath() {
|
|
|
78
85
|
return join(homedir(), ".gemini", "antigravity", "mcp_config.json");
|
|
79
86
|
}
|
|
80
87
|
|
|
88
|
+
export function codexConfigPath() {
|
|
89
|
+
return join(homedir(), ".codex", "config.toml");
|
|
90
|
+
}
|
|
91
|
+
|
|
81
92
|
export function opencodeConfigPath() {
|
|
82
93
|
return join(homedir(), ".config", "opencode", "opencode.json");
|
|
83
94
|
}
|
package/src/index.mjs
CHANGED
|
@@ -271,7 +271,7 @@ Options:
|
|
|
271
271
|
--help, -h Show this message.
|
|
272
272
|
|
|
273
273
|
Project scope is supported for: claude-code, cursor, opencode, zed.
|
|
274
|
-
Antigravity and Claude Desktop only support user scope
|
|
275
|
-
don't have a project-level alternative).
|
|
274
|
+
Antigravity, Codex CLI, and Claude Desktop only support user scope
|
|
275
|
+
(their config formats don't have a project-level alternative).
|
|
276
276
|
`);
|
|
277
277
|
}
|
package/src/writers.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { spawnSync } from "node:child_process";
|
|
|
5
5
|
import {
|
|
6
6
|
antigravityConfigPath,
|
|
7
7
|
claudeDesktopConfigPath,
|
|
8
|
+
codexConfigPath,
|
|
8
9
|
opencodeConfigPath,
|
|
9
10
|
zedConfigPath,
|
|
10
11
|
} from "./agents.mjs";
|
|
@@ -14,6 +15,7 @@ const SERVER_KEY = "upturtle";
|
|
|
14
15
|
export const WRITERS = {
|
|
15
16
|
"claude-code": writeClaudeCode,
|
|
16
17
|
"cursor": writeCursor,
|
|
18
|
+
"codex": writeCodex,
|
|
17
19
|
"antigravity": writeAntigravity,
|
|
18
20
|
"opencode": writeOpenCode,
|
|
19
21
|
"zed": writeZed,
|
|
@@ -135,6 +137,60 @@ function writeZed({ host, secret, scope }) {
|
|
|
135
137
|
});
|
|
136
138
|
}
|
|
137
139
|
|
|
140
|
+
function writeCodex({ host, secret, scope }) {
|
|
141
|
+
if (scope === "project") {
|
|
142
|
+
throw new Error(
|
|
143
|
+
"Codex CLI's MCP config is user-wide only (`~/.codex/config.toml`). " +
|
|
144
|
+
"Re-run without --scope=project for this agent.");
|
|
145
|
+
}
|
|
146
|
+
const path = codexConfigPath();
|
|
147
|
+
const block =
|
|
148
|
+
`[mcp_servers.${SERVER_KEY}]\n` +
|
|
149
|
+
`command = "npx"\n` +
|
|
150
|
+
`args = ["-y", "mcp-remote", "${host}/mcp", "--header", "Authorization: Bearer ${secret}"]\n`;
|
|
151
|
+
|
|
152
|
+
let existing = "";
|
|
153
|
+
if (existsSync(path)) {
|
|
154
|
+
existing = readFileSync(path, "utf8");
|
|
155
|
+
}
|
|
156
|
+
const updated = upsertTomlTable(existing, `mcp_servers.${SERVER_KEY}`, block);
|
|
157
|
+
|
|
158
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
159
|
+
writeFileSync(path, updated, "utf8");
|
|
160
|
+
return { configPath: path };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Naive TOML table upsert: replaces the `[<header>]` block (if present),
|
|
164
|
+
// otherwise appends. The block runs from its `[header]` line to the next
|
|
165
|
+
// top-level `[` or end-of-file. Good enough for our single-block writes —
|
|
166
|
+
// avoids pulling in a TOML parser dependency.
|
|
167
|
+
function upsertTomlTable(source, header, block) {
|
|
168
|
+
const headerLine = `[${header}]`;
|
|
169
|
+
const lines = source.split("\n");
|
|
170
|
+
const startIdx = lines.findIndex((line) => line.trim() === headerLine);
|
|
171
|
+
|
|
172
|
+
const trimmedBlock = block.endsWith("\n") ? block : block + "\n";
|
|
173
|
+
|
|
174
|
+
if (startIdx === -1) {
|
|
175
|
+
const prefix = source.length === 0 || source.endsWith("\n") ? source : source + "\n";
|
|
176
|
+
const separator = prefix.endsWith("\n\n") || prefix.length === 0 ? "" : "\n";
|
|
177
|
+
return prefix + separator + trimmedBlock;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let endIdx = lines.length;
|
|
181
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
182
|
+
if (/^\s*\[/.test(lines[i])) {
|
|
183
|
+
endIdx = i;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const before = lines.slice(0, startIdx).join("\n");
|
|
188
|
+
const after = lines.slice(endIdx).join("\n");
|
|
189
|
+
const beforePart = before.length === 0 ? "" : before.endsWith("\n") ? before : before + "\n";
|
|
190
|
+
const afterPart = after.length === 0 ? "" : after.startsWith("\n") ? after : "\n" + after;
|
|
191
|
+
return beforePart + trimmedBlock + afterPart;
|
|
192
|
+
}
|
|
193
|
+
|
|
138
194
|
function writeClaudeDesktop({ host, secret, scope }) {
|
|
139
195
|
if (scope === "project") {
|
|
140
196
|
throw new Error(
|