pleumrouter 0.5.22 → 0.5.23

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 CHANGED
@@ -74,11 +74,11 @@ SSH/CI에서는 `pleum login --device`를 쓰세요.
74
74
 
75
75
  ## 자동 실행 지원 도구
76
76
 
77
- `claude` · `aider` · `codex` · `goose` · `openhands` · `opencode` · `omp` · `hermes` · `qwen` · `copilot` —
78
- env(또는 격리된 `CODEX_HOME`/임시 설정 문자열)만 세팅하고 바로 실행한다. 사용자의 기존 설정 파일은
79
- 건드리지 않는다.
77
+ `claude` · `codex` · `aider` · `goose` · `openhands` · `opencode` · `omp` · `hermes` · `qwen` · `copilot` · `gemini` · `agy` · `pi` · `crush` —
78
+ env 또는 격리 홈(`CODEX_HOME` / `PI_CODING_AGENT_DIR` / `CRUSH_GLOBAL_CONFIG`)만 세팅하고 바로 실행한다.
79
+ 별칭: `antigravity`→`agy`, `picode`→`pi`, `ohmypi`→`omp`.
80
80
 
81
- `crush` 는 provider 정의가 파일에 필요해 설정 스니펫만 안내한다.
81
+ `agy`만 사용자 `~/.gemini/antigravity-cli/settings.json`에 `modelProvider=gemini`를 병합한다(제품이 이 파일 없이 키를 무시함). Kiro는 공식 BYOK가 없어 지원하지 않는다.
82
82
 
83
83
  ## 환경변수
84
84
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pleumrouter",
3
- "version": "0.5.22",
3
+ "version": "0.5.23",
4
4
  "description": "Official PleumRouter terminal CLI — launch any coding agent against PleumRouter.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,10 +1,10 @@
1
1
  // Shell 자동완성 스크립트 출력. 명령·도구는 정적, 모델명은 `pleum models`에서 동적으로 채운다.
2
2
  // 설치: pleum completion zsh >> ~/.zshrc · pleum completion bash >> ~/.bashrc
3
3
  import { t } from "./i18n.mjs";
4
- import { RECIPES, MANUAL } from "./recipes.mjs";
4
+ import { launchToolNames } from "./recipes.mjs";
5
5
 
6
6
  const COMMANDS = "login logout lang launch wire unwire run models top balance usage health version upgrade completion help";
7
- const TOOLS = [...Object.keys(RECIPES), ...Object.keys(MANUAL)].join(" ");
7
+ const TOOLS = launchToolNames().join(" ");
8
8
 
9
9
  function bash() {
10
10
  return `# pleum bash completion — eval "$(pleum completion bash)" (${t("or add to ~/.bashrc")})
package/src/launch.mjs CHANGED
@@ -3,7 +3,16 @@ import { mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { join, delimiter, dirname, resolve } from "node:path";
5
5
  import { requireAuth } from "./config.mjs";
6
- import { RECIPES, MANUAL, codexToml } from "./recipes.mjs";
6
+ import {
7
+ RECIPES,
8
+ MANUAL,
9
+ LAUNCH_ALIASES,
10
+ resolveLaunchTool,
11
+ codexToml,
12
+ piModelsJson,
13
+ crushJson,
14
+ mergeAgySettings,
15
+ } from "./recipes.mjs";
7
16
  import { t } from "./i18n.mjs";
8
17
  import { sanitizeTerminalText } from "./ansi.mjs";
9
18
 
@@ -72,28 +81,35 @@ export function launchHelp() {
72
81
  const ok = onPath(r.bin) ? t("✓ installed") : `${t("✗ not installed —")} ${r.install}`;
73
82
  console.log(` ${name.padEnd(11)} ${r.label.padEnd(13)} ${ok}`);
74
83
  }
75
- console.log(`\n${t("Manual setup (needs a provider definition file):")}`);
76
- for (const [name, m] of Object.entries(MANUAL)) {
77
- const ok = onPath(m.bin) ? t("✓ installed") : `${t("✗ not installed —")} ${m.install}`;
78
- console.log(` ${name.padEnd(11)} ${m.label.padEnd(13)} ${ok}`);
84
+ if (Object.keys(MANUAL).length) {
85
+ console.log(`\n${t("Manual setup (needs a provider definition file):")}`);
86
+ for (const [name, m] of Object.entries(MANUAL)) {
87
+ const ok = onPath(m.bin) ? t("✓ installed") : `${t("✗ not installed —")} ${m.install}`;
88
+ console.log(` ${name.padEnd(11)} ${m.label.padEnd(13)} ${ok}`);
89
+ }
79
90
  }
91
+ const aliases = Object.entries(LAUNCH_ALIASES).map(([from, to]) => `${from} → ${to}`).join(", ");
92
+ console.log(`\n${t("Aliases: {list}", { list: aliases })}`);
80
93
  console.log(`\n${t("Example: pleum launch claude --model claude-sonnet-5")}`);
81
94
  }
82
95
 
83
96
  // exit:false면 process.exit 대신 자식 종료 코드를 반환 — 런처 메뉴의 실행 후 복귀용.
84
97
  export async function launch(tool, model, passthrough, { exit = true } = {}) {
85
98
  const key = await requireAuth();
86
- const r = RECIPES[tool];
99
+ const resolved = resolveLaunchTool(tool);
100
+ const r = RECIPES[resolved];
87
101
 
88
102
  if (!r) {
89
- if (MANUAL[tool]) {
90
- printManual(tool, key);
103
+ if (MANUAL[resolved]) {
104
+ printManual(resolved, key);
91
105
  return;
92
106
  }
107
+ const aliases = Object.entries(LAUNCH_ALIASES).map(([from, to]) => `${from} → ${to}`).join(", ");
93
108
  console.error(
94
109
  t("Unsupported tool: {tool}", { tool }) +
95
110
  `\n ${t("Auto-run:")} ${Object.keys(RECIPES).join(", ")}` +
96
- `\n ${t("Manual setup:")} ${Object.keys(MANUAL).join(", ")}`,
111
+ (Object.keys(MANUAL).length ? `\n ${t("Manual setup:")} ${Object.keys(MANUAL).join(", ")}` : "") +
112
+ `\n ${t("Aliases: {list}", { list: aliases })}`,
97
113
  );
98
114
  process.exit(1);
99
115
  }
@@ -102,11 +118,15 @@ export async function launch(tool, model, passthrough, { exit = true } = {}) {
102
118
 
103
119
  // Codex는 격리 CODEX_HOME에 config.toml을 써서 사용자 ~/.codex를 보존한다.
104
120
  if (r.type === "codex-home") {
105
- const home = join(homedir(), ".pleum", "codex-home");
106
- mkdirSync(home, { recursive: true, mode: 0o700 }); // 사용자 데이터 디렉토리 권한 제한(L11)
107
- writeFileSync(join(home, "config.toml"), codexToml(model), { mode: 0o600 });
108
- env.CODEX_HOME = home;
121
+ env.CODEX_HOME = writeIsolatedHome("codex-home", "config.toml", codexToml(model));
122
+ }
123
+ if (r.type === "pi-home") {
124
+ env.PI_CODING_AGENT_DIR = writeIsolatedHome("pi-home", "models.json", piModelsJson(model));
109
125
  }
126
+ if (r.type === "crush-home") {
127
+ env.CRUSH_GLOBAL_CONFIG = writeIsolatedHome("crush-home", "crush.json", crushJson(model));
128
+ }
129
+ if (r.type === "agy-settings") ensureAgySettings();
110
130
 
111
131
  const args = [...r.args(model), ...passthrough];
112
132
  if (r.note) console.log(`ℹ ${t(r.note)}`);
@@ -144,6 +164,21 @@ export async function launch(tool, model, passthrough, { exit = true } = {}) {
144
164
  });
145
165
  }
146
166
 
167
+ function writeIsolatedHome(name, filename, contents) {
168
+ const home = join(homedir(), ".pleum", name);
169
+ mkdirSync(home, { recursive: true, mode: 0o700 });
170
+ writeFileSync(join(home, filename), contents, { mode: 0o600 });
171
+ return home;
172
+ }
173
+
174
+ function ensureAgySettings() {
175
+ const dir = join(homedir(), ".gemini", "antigravity-cli");
176
+ const path = join(dir, "settings.json");
177
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
178
+ const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
179
+ writeFileSync(path, `${JSON.stringify(mergeAgySettings(existing), null, 2)}\n`, { mode: 0o600 });
180
+ }
181
+
147
182
  function printManual(tool, key) {
148
183
  const m = MANUAL[tool];
149
184
  console.log(`${t("{label} needs a provider definition in a file, so this only shows setup — it won't auto-run.", { label: m.label })}\n`);
@@ -20,6 +20,10 @@ export const ES = {
20
20
  "Nous Research's general-purpose agent": "Agente de propósito general de Nous Research",
21
21
  "Alibaba's terminal coding agent": "Agente de codificación de terminal de Alibaba",
22
22
  "GitHub's terminal coding agent": "Agente de codificación de terminal de GitHub",
23
+ "Google's Gemini CLI": "CLI de Gemini de Google",
24
+ "Google Antigravity CLI (agy)": "CLI de Google Antigravity (agy)",
25
+ "Pi coding agent (picode is a Pi package)": "Agente Pi (picode es un paquete de Pi)",
26
+ "Charmbracelet terminal coding agent": "Agente de terminal Charmbracelet",
23
27
  "Available: {list}": "Disponibles: {list}",
24
28
  "Balance": "Saldo",
25
29
  "Balance is below the auto-charge threshold ({threshold} ₩).": "El saldo está por debajo del umbral de recarga automática ({threshold} ₩).",
@@ -62,6 +66,13 @@ export const ES = {
62
66
  "Necesita GitHub Copilot CLI 2026-04 o posterior para el enrutamiento de proveedores BYOK.",
63
67
  "Gemini CLI speaks Google's native generateContent format — set the model after launch with `/model google/<id>`.":
64
68
  "Gemini CLI usa el formato nativo generateContent de Google — establece el modelo tras el inicio con `/model google/<id>`.",
69
+ "Writes modelProvider=gemini into ~/.gemini/antigravity-cli/settings.json. GEMINI_API_KEY alone is ignored. Gemini-family models only.":
70
+ "Escribe modelProvider=gemini en ~/.gemini/antigravity-cli/settings.json. GEMINI_API_KEY sola se ignora. Solo familia Gemini.",
71
+ "Uses an isolated ~/.pleum/pi-home (PI_CODING_AGENT_DIR). Your ~/.pi is untouched.":
72
+ "Usa un ~/.pleum/pi-home aislado (PI_CODING_AGENT_DIR). No toca ~/.pi.",
73
+ "Uses an isolated ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG). Your crush.json is untouched.":
74
+ "Usa un ~/.pleum/crush-home aislado (CRUSH_GLOBAL_CONFIG). No toca crush.json.",
75
+ "Aliases: {list}": "Alias: {list}",
65
76
  "Gateway": "Gateway",
66
77
  "Gateway connection failed: {error}": "Error de conexión al gateway: {error}",
67
78
  "Gateway connectivity · login status": "Conectividad del gateway · estado de la sesión",
@@ -20,6 +20,10 @@ export const JA = {
20
20
  "Nous Research's general-purpose agent": "Nous Research の汎用エージェント",
21
21
  "Alibaba's terminal coding agent": "Alibaba のターミナルコーディングエージェント",
22
22
  "GitHub's terminal coding agent": "GitHub のターミナルコーディングエージェント",
23
+ "Google's Gemini CLI": "Google Gemini CLI",
24
+ "Google Antigravity CLI (agy)": "Google Antigravity CLI (agy)",
25
+ "Pi coding agent (picode is a Pi package)": "Pi コーディングエージェント (picode は Pi パッケージ)",
26
+ "Charmbracelet terminal coding agent": "Charmbracelet ターミナルコーディングエージェント",
23
27
  "Available: {list}": "利用可能: {list}",
24
28
  "Balance": "残高",
25
29
  "Balance is below the auto-charge threshold ({threshold} ₩).": "残高が自動チャージのしきい値 ({threshold} ₩) を下回っています。",
@@ -63,6 +67,13 @@ export const JA = {
63
67
  "BYOK プロバイダールーティングに対応した 2026-04 以降の GitHub Copilot CLI が必要です。",
64
68
  "Gemini CLI speaks Google's native generateContent format — set the model after launch with `/model google/<id>`.":
65
69
  "Gemini CLI は Google のネイティブ generateContent 形式を使用します — 起動後に `/model google/<id>` でモデルを設定してください。",
70
+ "Writes modelProvider=gemini into ~/.gemini/antigravity-cli/settings.json. GEMINI_API_KEY alone is ignored. Gemini-family models only.":
71
+ "~/.gemini/antigravity-cli/settings.json に modelProvider=gemini を書き込みます。GEMINI_API_KEY だけでは無視されます。Gemini 系のみです。",
72
+ "Uses an isolated ~/.pleum/pi-home (PI_CODING_AGENT_DIR). Your ~/.pi is untouched.":
73
+ "隔離した ~/.pleum/pi-home (PI_CODING_AGENT_DIR) だけを使います。~/.pi は触れません。",
74
+ "Uses an isolated ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG). Your crush.json is untouched.":
75
+ "隔離した ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG) だけを使います。crush.json は触れません。",
76
+ "Aliases: {list}": "エイリアス: {list}",
66
77
  "Gateway": "ゲートウェイ",
67
78
  "Gateway connection failed: {error}": "ゲートウェイ接続に失敗しました: {error}",
68
79
  "Gateway connectivity · login status": "ゲートウェイ接続 · ログイン状態",
@@ -52,6 +52,13 @@ export const KO = {
52
52
  "BYOK provider 라우팅을 지원하는 2026-04 이후 버전의 GitHub Copilot CLI가 필요합니다.",
53
53
  "Gemini CLI speaks Google's native generateContent format — set the model after launch with `/model google/<id>`.":
54
54
  "Gemini CLI는 Google의 네이티브 generateContent 포맷을 사용합니다 — 실행 후 `/model google/<id>`로 모델을 설정하세요.",
55
+ "Writes modelProvider=gemini into ~/.gemini/antigravity-cli/settings.json. GEMINI_API_KEY alone is ignored. Gemini-family models only.":
56
+ "~/.gemini/antigravity-cli/settings.json 에 modelProvider=gemini 를 씁니다. GEMINI_API_KEY만 있으면 무시됩니다. Gemini 계열만 됩니다.",
57
+ "Uses an isolated ~/.pleum/pi-home (PI_CODING_AGENT_DIR). Your ~/.pi is untouched.":
58
+ "격리된 ~/.pleum/pi-home (PI_CODING_AGENT_DIR)만 씁니다. ~/.pi는 건드리지 않습니다.",
59
+ "Uses an isolated ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG). Your crush.json is untouched.":
60
+ "격리된 ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG)만 씁니다. crush.json은 건드리지 않습니다.",
61
+ "Aliases: {list}": "별칭: {list}",
55
62
 
56
63
  // completion.mjs
57
64
  "Usage: pleum completion <zsh|bash>": "사용법: pleum completion <zsh|bash>",
@@ -146,6 +153,10 @@ export const KO = {
146
153
  "Nous Research's general-purpose agent": "Nous Research의 범용 에이전트",
147
154
  "Alibaba's terminal coding agent": "Alibaba의 터미널 코딩 에이전트",
148
155
  "GitHub's terminal coding agent": "GitHub의 터미널 코딩 에이전트",
156
+ "Google's Gemini CLI": "Google Gemini CLI",
157
+ "Google Antigravity CLI (agy)": "Google Antigravity CLI (agy)",
158
+ "Pi coding agent (picode is a Pi package)": "Pi 코딩 에이전트 (picode는 Pi 패키지)",
159
+ "Charmbracelet terminal coding agent": "Charmbracelet 터미널 코딩 에이전트",
149
160
  "Log in": "로그인",
150
161
  "Log in via browser and save the API key": "브라우저로 로그인하고 API 키를 저장합니다",
151
162
  "Chat with a model": "모델과 채팅",
@@ -20,6 +20,10 @@ export const ZH = {
20
20
  "Nous Research's general-purpose agent": "Nous Research 的通用代理",
21
21
  "Alibaba's terminal coding agent": "阿里巴巴的终端编程代理",
22
22
  "GitHub's terminal coding agent": "GitHub 的终端编程代理",
23
+ "Google's Gemini CLI": "Google Gemini CLI",
24
+ "Google Antigravity CLI (agy)": "Google Antigravity CLI (agy)",
25
+ "Pi coding agent (picode is a Pi package)": "Pi 编程代理(picode 是 Pi 包)",
26
+ "Charmbracelet terminal coding agent": "Charmbracelet 终端编程代理",
23
27
  "Available: {list}": "可用:{list}",
24
28
  "Balance": "余额",
25
29
  "Balance is below the auto-charge threshold ({threshold} ₩).": "余额低于自动充值阈值({threshold} ₩)。",
@@ -61,7 +65,14 @@ export const ZH = {
61
65
  "Needs GitHub Copilot CLI 2026-04 or later for BYOK provider routing support.":
62
66
  "需要 2026-04 或更新版本的 GitHub Copilot CLI 才支持 BYOK provider 路由。",
63
67
  "Gemini CLI speaks Google's native generateContent format — set the model after launch with `/model google/<id>`.":
64
- "Gemini CLI speaks Google's native generateContent format — set the model after launch with `/model google/<id>`.",
68
+ "Gemini CLI 使用 Google 原生 generateContent 格式 — 启动后用 `/model google/<id>` 设置模型。",
69
+ "Writes modelProvider=gemini into ~/.gemini/antigravity-cli/settings.json. GEMINI_API_KEY alone is ignored. Gemini-family models only.":
70
+ "会把 modelProvider=gemini 写入 ~/.gemini/antigravity-cli/settings.json。只设 GEMINI_API_KEY 会被忽略。仅 Gemini 系列。",
71
+ "Uses an isolated ~/.pleum/pi-home (PI_CODING_AGENT_DIR). Your ~/.pi is untouched.":
72
+ "只用隔离的 ~/.pleum/pi-home(PI_CODING_AGENT_DIR),不改 ~/.pi。",
73
+ "Uses an isolated ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG). Your crush.json is untouched.":
74
+ "只用隔离的 ~/.pleum/crush-home(CRUSH_GLOBAL_CONFIG),不改 crush.json。",
75
+ "Aliases: {list}": "别名:{list}",
65
76
  "Gateway": "网关",
66
77
  "Gateway connection failed: {error}": "网关连接失败:{error}",
67
78
  "Gateway connectivity · login status": "网关连通性 · 登录状态",
package/src/menu.mjs CHANGED
@@ -40,6 +40,10 @@ const AGENT_DESC = {
40
40
  hermes: "Nous Research's general-purpose agent",
41
41
  qwen: "Alibaba's terminal coding agent",
42
42
  copilot: "GitHub's terminal coding agent",
43
+ gemini: "Google's Gemini CLI",
44
+ agy: "Google Antigravity CLI (agy)",
45
+ pi: "Pi coding agent (picode is a Pi package)",
46
+ crush: "Charmbracelet terminal coding agent",
43
47
  };
44
48
 
45
49
  const autoInstallable = (r) => !/^https?:/.test(r.install); // URL이면 수동 설치 안내만
package/src/recipes.mjs CHANGED
@@ -1,15 +1,20 @@
1
1
  import { ROOT, V1 } from "./config.mjs";
2
2
 
3
- // 도구별 설정의 진실 원천. 출처: frontend .../docs/cookbook/{codex,claude-code,...} + 에이전트별 공식 문서 조사(2026-07).
4
- // type "env" → 환경변수만 세팅하고 바로 실행.
5
- // type "codex-home" → 격리된 CODEX_HOME에 config.toml을 써서 실행(사용자 ~/.codex 보존).
3
+ // 도구별 설정의 진실 원천. 출처: frontend .../docs/cookbook/{codex,claude-code,...} + 에이전트별 공식 문서.
4
+ // type "env" → 환경변수만 세팅하고 바로 실행.
5
+ // type "codex-home" → 격리 CODEX_HOME에 config.toml (사용자 ~/.codex 보존).
6
+ // type "pi-home" → 격리 PI_CODING_AGENT_DIR에 models.json (사용자 ~/.pi 보존).
7
+ // type "crush-home" → 격리 CRUSH_GLOBAL_CONFIG 디렉터리에 crush.json (사용자 crush.json 보존).
8
+ // type "agy-settings" → ~/.gemini/antigravity-cli/settings.json 에 modelProvider=gemini 병합.
9
+ // Antigravity는 이 파일이 없으면 GEMINI_API_KEY를 무시한다. 격리 홈 env가 없다.
6
10
  // note 필드는 t() 키(영어 원문)만 담는다 — 여기서 t()를 호출하면 모듈 로드 시점에 번역이
7
11
  // 고정돼 같은 프로세스 안에서 언어를 바꿔도(Settings) 안내문이 갱신되지 않는다.
8
12
  // 실제 번역은 소비 지점(launch.mjs)에서 매번 t()를 호출해 얻는다.
9
13
  //
10
- // 커스텀 엔드포인트를 지원하지 않아 PleumRouter로 라우팅할 수 없는 도구는 등록하지 않는다(조사 결과, 2026-07):
11
- // Gemini CLI(구글 백엔드 전용, PR#2899 머지 거부) · Cursor CLI(구독 백엔드 전용, 공식 확인) ·
12
- // Amp(Sourcegraph 백엔드 전용) · OpenClaw(멀티채널 게이트웨이 데몬 — 세션형 코딩 CLI가 아님 + 파일설정 전용).
14
+ // 커스텀 엔드포인트가 없어 PleumRouter로 라우팅할 수 없으면 등록하지 않는다:
15
+ // Cursor CLI · Amp · Factory Droid · Kiro(공식 BYOK 없음, aws/kiro#9367) ·
16
+ // Cline CLI(cline auth가 사용자 cred를 디스크에 고정) ·
17
+ // OpenClaw(세션형 코딩 CLI가 아님).
13
18
  export const RECIPES = {
14
19
  claude: {
15
20
  label: "Claude Code",
@@ -27,6 +32,8 @@ export const RECIPES = {
27
32
  ANTHROPIC_BASE_URL: ROOT,
28
33
  ANTHROPIC_API_KEY: key,
29
34
  ANTHROPIC_AUTH_TOKEN: key,
35
+ // /model 이 GET {ANTHROPIC_BASE_URL}/v1/models 를 치게 한다. 기본값은 꺼져 있다.
36
+ CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1",
30
37
  }),
31
38
  args: (m) => (m ? ["--model", `anthropic/${m}`] : []),
32
39
  },
@@ -185,8 +192,66 @@ export const RECIPES = {
185
192
  }),
186
193
  args: (m) => (m ? ["--model", `google/${m}`] : []),
187
194
  },
195
+ agy: {
196
+ label: "Antigravity CLI",
197
+ bin: "agy",
198
+ type: "agy-settings",
199
+ install: "https://antigravity.google/cli/install.sh",
200
+ note: "Writes modelProvider=gemini into ~/.gemini/antigravity-cli/settings.json. GEMINI_API_KEY alone is ignored. Gemini-family models only.",
201
+ attribution: {
202
+ title: "Antigravity CLI",
203
+ referer: "https://www.antigravity.google/docs/cli/install",
204
+ },
205
+ env: (key) => ({
206
+ GOOGLE_GEMINI_BASE_URL: ROOT,
207
+ GEMINI_API_KEY: key,
208
+ GOOGLE_API_KEY: key,
209
+ }),
210
+ args: (m) => (m ? ["--model", `google/${m}`] : []),
211
+ },
212
+ pi: {
213
+ label: "Pi",
214
+ bin: "pi",
215
+ type: "pi-home",
216
+ install: "npm i -g @mariozechner/pi-coding-agent",
217
+ note: "Uses an isolated ~/.pleum/pi-home (PI_CODING_AGENT_DIR). Your ~/.pi is untouched.",
218
+ attribution: {
219
+ title: "Pi",
220
+ referer: "https://pi.dev",
221
+ },
222
+ env: (key) => ({ PLEUM_API_KEY: key }),
223
+ args: (m) => ["--model", m || "gpt-5.4-mini"],
224
+ },
225
+ crush: {
226
+ label: "Crush",
227
+ bin: "crush",
228
+ type: "crush-home",
229
+ install: "npm install -g @charmland/crush",
230
+ note: "Uses an isolated ~/.pleum/crush-home (CRUSH_GLOBAL_CONFIG). Your crush.json is untouched.",
231
+ attribution: {
232
+ title: "Crush",
233
+ referer: "https://github.com/charmbracelet/crush",
234
+ },
235
+ env: (key) => ({ PLEUM_API_KEY: key }),
236
+ args: () => [],
237
+ },
188
238
  };
189
239
 
240
+ export const LAUNCH_ALIASES = {
241
+ antigravity: "agy",
242
+ picode: "pi",
243
+ ohmypi: "omp",
244
+ "oh-my-pi": "omp",
245
+ };
246
+
247
+ export function resolveLaunchTool(name) {
248
+ return LAUNCH_ALIASES[name] ?? name;
249
+ }
250
+
251
+ export function launchToolNames() {
252
+ return [...Object.keys(RECIPES), ...Object.keys(MANUAL), ...Object.keys(LAUNCH_ALIASES)];
253
+ }
254
+
190
255
  // Codex는 Chat Completions가 제거돼 Responses API(wire_api="responses")만 지원.
191
256
  // 사용자의 ~/.codex/config.toml을 건드리지 않도록 격리 CODEX_HOME에 이 파일을 쓴다.
192
257
  // TOML basic string encoder. 모델명과 base_url은 CLI/env에서 오므로, 따옴표·개행이
@@ -222,22 +287,45 @@ supports_websockets = false
222
287
  `;
223
288
  }
224
289
 
225
- // provider 정의가 파일에 있어야 하는 도구 — 자동 실행 대신 설정 스니펫만 안내.
226
- export const MANUAL = {
227
- crush: {
228
- label: "Crush",
229
- bin: "crush",
230
- install: "npm install -g @charmland/crush",
231
- file: "crush.json",
232
- snippet: () => `{
233
- "providers": {
234
- "pleum": {
235
- "type": "openai-compat",
236
- "base_url": "${V1}",
237
- "api_key": "$PLEUM_API_KEY",
238
- "models": [{ "id": "gpt-5.4-mini", "name": "gpt-5.4-mini" }]
290
+ export function mergeAgySettings(existingJson) {
291
+ let obj = {};
292
+ if (existingJson && String(existingJson).trim()) {
293
+ obj = JSON.parse(existingJson);
294
+ if (obj === null || typeof obj !== "object" || Array.isArray(obj)) {
295
+ throw new Error("agy settings.json must be a JSON object");
239
296
  }
240
297
  }
241
- }`,
242
- },
243
- };
298
+ return { ...obj, modelProvider: "gemini" };
299
+ }
300
+
301
+ export function piModelsJson(model) {
302
+ const m = model || "gpt-5.4-mini";
303
+ return `${JSON.stringify({
304
+ providers: {
305
+ pleum: {
306
+ baseUrl: V1,
307
+ api: "openai-completions",
308
+ apiKey: "$PLEUM_API_KEY",
309
+ models: [{ id: m }],
310
+ },
311
+ },
312
+ }, null, 2)}\n`;
313
+ }
314
+
315
+ export function crushJson(model) {
316
+ const m = model || "gpt-5.4-mini";
317
+ return `${JSON.stringify({
318
+ providers: {
319
+ pleum: {
320
+ type: "openai-compat",
321
+ base_url: V1,
322
+ api_key: "$PLEUM_API_KEY",
323
+ models: [{ id: m, name: m }],
324
+ },
325
+ },
326
+ }, null, 2)}\n`;
327
+ }
328
+
329
+ // provider 정의가 파일에 있어야 자동 실행이 안 되는 도구. crush는 격리 CRUSH_GLOBAL_CONFIG로
330
+ // RECIPES로 올렸으니 지금은 비어 있다 — 스니펫-only 도구가 다시 생기면 여기 넣는다.
331
+ export const MANUAL = {};
package/src/wire.mjs CHANGED
@@ -10,7 +10,12 @@ export const WIRE_KEY_NAME = "pleum-desktop";
10
10
  export const WIRE_MARKER_START = "# pleum-wire-start";
11
11
  export const WIRE_MARKER_END = "# pleum-wire-end";
12
12
 
13
- const CLAUDE_ENV_KEYS = ["ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_API_KEY"];
13
+ const CLAUDE_ENV_KEYS = [
14
+ "ANTHROPIC_BASE_URL",
15
+ "ANTHROPIC_AUTH_TOKEN",
16
+ "ANTHROPIC_API_KEY",
17
+ "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY",
18
+ ];
14
19
 
15
20
  export function wirePaths(home = homedir()) {
16
21
  const claude = join(home, ".claude", "settings.json");
@@ -81,6 +86,7 @@ export function wireClaudeSettings(existingJson, { root, key }) {
81
86
  ANTHROPIC_BASE_URL: root,
82
87
  ANTHROPIC_AUTH_TOKEN: key,
83
88
  ANTHROPIC_API_KEY: key,
89
+ CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1",
84
90
  };
85
91
  return `${JSON.stringify(obj, null, 2)}\n`;
86
92
  }