Skip to content

Commit dae31e7

Browse files
committed
Merge branch 'feature/multi-tool'
2 parents 81f2079 + e73ccfb commit dae31e7

File tree

5 files changed

+46
-47
lines changed

5 files changed

+46
-47
lines changed

README.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ Claude Opus 4.6 costs $5/$25 per Mtok.
1717
Qwen 3 Coder Next costs $0.12/$0.75 per Mtok.
1818
That's 42x cheaper on input, 33x cheaper on output.
1919

20+
Run multiple coding CLIs from one command, with model shortcuts and optional pipeline orchestration.
21+
22+
Supported tools:
23+
- Claude Code (`claude`)
24+
- Gemini CLI (`gemini`)
25+
- Codex CLI (`codex`)
26+
- GitHub Copilot CLI (`copilot`)
27+
- OpenCode (`opencode`, via custom model entries)
28+
2029
## Installation
2130

2231
### Standalone Binary (recommended)
@@ -198,20 +207,13 @@ cloding -m meta-llama/llama-4-scout
198207
cloding -m qwen
199208
cloding -m sonnet-a -p "review this file" # direct Anthropic API
200209
cloding -m opus-p # use Claude paid plan
201-
cloding -m codex-5 # use OpenAI paid plan
202-
cloding -m copilot # use GitHub paid plan
210+
cloding -m codex-5 "implement retry logic"
211+
cloding -m codex-5-a "implement retry logic"
212+
cloding -m copilot
203213
```
204214

205215
Unknown flags are passed through to the underlying CLI.
206216

207-
### Setup
208-
209-
```bash
210-
cloding setup # detect + install missing CLI tools
211-
cloding setup --check # status report only
212-
cloding setup --force # reinstall all tools
213-
```
214-
215217
### Docker Mode
216218

217219
```bash
@@ -276,7 +278,6 @@ Pipeline flags:
276278
> **Tip:** Run `cloding setup` to automatically detect and install all CLI tools.
277279
278280
## Custom Models
279-
280281
Add entries in `models.json`:
281282

282283
```json

bin/cloding.js

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ function printModels(models) {
315315
const savings = m.out > 0 ? Math.round(opusOut / m.out) : 0;
316316
const savingsStr =
317317
shortcut === "opus" ? " baseline" :
318-
savings > 0 ? ` ${savings}x cheaper` : " n/a";
318+
savings > 0 ? ` ${savings}x cheaper` : " n/a";
319319
console.log(
320320
` ${shortcut.padEnd(11)} ${m.name.padEnd(24)} $${m.in.toFixed(2).padStart(6)} $${m.out.toFixed(2).padStart(6)}${savingsStr}`
321321
);
@@ -801,9 +801,9 @@ function dockerBuild() {
801801
if (!dockerDir) {
802802
console.error(
803803
"Error: Dockerfile not found.\n\n" +
804-
"Docker mode requires the full repository (not the npm package).\n" +
805-
" git clone https://github.com/claudlos/cloding\n" +
806-
" cd cloding && cloding docker build\n"
804+
"Docker mode requires the full repository (not the npm package).\n" +
805+
" git clone https://github.com/claudlos/cloding\n" +
806+
" cd cloding && cloding docker build\n"
807807
);
808808
process.exit(1);
809809
}
@@ -906,21 +906,21 @@ function dockerRun(dockerArgs, models, interactive) {
906906
if (!interactive && !prompt) {
907907
const modelHint = modelArg
908908
? "\nDid you mean:\n" +
909-
` cloding docker run -m ${modelArg} "your prompt here"\n` +
910-
` cloding docker shell -m ${modelArg}\n`
909+
` cloding docker run -m ${modelArg} "your prompt here"\n` +
910+
` cloding docker shell -m ${modelArg}\n`
911911
: "";
912912
console.error(
913913
'Error: No prompt provided.\n\n' +
914-
' Usage: cloding docker run "your prompt here"\n' +
915-
modelHint
914+
' Usage: cloding docker run "your prompt here"\n' +
915+
modelHint
916916
);
917917
process.exit(1);
918918
}
919919

920920
if (!dockerImageExists()) {
921921
console.error(
922922
`Error: Docker image '${DOCKER_IMAGE}' not found.\n\n` +
923-
" Build it first: cloding docker build\n"
923+
" Build it first: cloding docker build\n"
924924
);
925925
process.exit(1);
926926
}
@@ -936,18 +936,17 @@ function dockerRun(dockerArgs, models, interactive) {
936936
if (apiKeyEnv === "OPENROUTER_API_KEY") {
937937
console.error(
938938
"Error: OPENROUTER_API_KEY not set.\n\n" +
939-
"Get your key at https://openrouter.ai/keys\n" +
940-
`Then: ${envSetHint("OPENROUTER_API_KEY", "sk-or-v1-...")}\n`
939+
"Get your key at https://openrouter.ai/keys\n" +
940+
`Then: ${envSetHint("OPENROUTER_API_KEY", "sk-or-v1-...")}\n`
941941
);
942942
} else {
943943
console.error(
944944
`Error: ${apiKeyEnv} not set.\n\n` +
945-
`Please set it: ${envSetHint(apiKeyEnv, "...")}`
945+
`Please set it: ${envSetHint(apiKeyEnv, "...")}`
946946
);
947947
}
948948
process.exit(1);
949949
}
950-
951950
// Block plan-mode tools in Docker: subscription auth (OAuth tokens, system
952951
// credential stores) cannot be passed into containers. Suggest alternatives.
953952
if (isPlanProvider) {
@@ -976,11 +975,11 @@ function dockerRun(dockerArgs, models, interactive) {
976975
);
977976
console.error(
978977
`Error: Plan/subscription mode (${modelArg}) cannot authenticate inside Docker.\n\n` +
979-
`Subscription auth tokens are stored on your host (system credential\n` +
980-
`store, browser sessions, etc.) and cannot be passed into containers.\n\n` +
981-
`Alternatives:\n` +
982-
altLines.join("\n") +
983-
"\n"
978+
`Subscription auth tokens are stored on your host (system credential\n` +
979+
`store, browser sessions, etc.) and cannot be passed into containers.\n\n` +
980+
`Alternatives:\n` +
981+
altLines.join("\n") +
982+
"\n"
984983
);
985984
process.exit(1);
986985
}
@@ -1156,7 +1155,7 @@ function dockerRun(dockerArgs, models, interactive) {
11561155

11571156
// Clean up env file on exit (contains API key)
11581157
function cleanupEnvFile() {
1159-
try { fs.unlinkSync(envFilePath); } catch {}
1158+
try { fs.unlinkSync(envFilePath); } catch { }
11601159
}
11611160

11621161
// Spawn docker — uses argument array (no shell interpretation)
@@ -1322,8 +1321,8 @@ function handleDocker(args) {
13221321
if (!dockerAvailable()) {
13231322
console.error(
13241323
"Error: Docker not found.\n\n" +
1325-
"Install Docker Desktop:\n" +
1326-
" https://docs.docker.com/get-docker/\n"
1324+
"Install Docker Desktop:\n" +
1325+
" https://docs.docker.com/get-docker/\n"
13271326
);
13281327
process.exit(1);
13291328
}
@@ -1412,9 +1411,9 @@ function main() {
14121411
if (!fs.existsSync(pipelineDir)) {
14131412
console.error(
14141413
"Error: Pipeline not found.\n\n" +
1415-
"Pipeline mode requires the full repository (not the npm package).\n" +
1416-
" git clone https://github.com/claudlos/cloding\n" +
1417-
" cd cloding/pipeline && pip install -e .\n"
1414+
"Pipeline mode requires the full repository (not the npm package).\n" +
1415+
" git clone https://github.com/claudlos/cloding\n" +
1416+
" cd cloding/pipeline && pip install -e .\n"
14181417
);
14191418
process.exit(1);
14201419
}
@@ -1452,8 +1451,8 @@ function main() {
14521451
if (nodeMajor < 24) {
14531452
console.error(
14541453
`Error: Copilot CLI requires Node.js v24+.\n` +
1455-
`Current Node version: v${process.versions.node}\n\n` +
1456-
"Upgrade Node, then run: cloding -m copilot"
1454+
`Current Node version: v${process.versions.node}\n\n` +
1455+
"Upgrade Node, then run: cloding -m copilot"
14571456
);
14581457
process.exit(1);
14591458
}
@@ -1467,7 +1466,7 @@ function main() {
14671466
if (!apiKey && !usingLinkedCliAuth && !isPlanProvider) {
14681467
console.error(
14691468
`Error: ${apiKeyEnv} not set.\n\n` +
1470-
`Please set it: ${envSetHint(apiKeyEnv, "...")}`
1469+
`Please set it: ${envSetHint(apiKeyEnv, "...")}`
14711470
);
14721471
process.exit(1);
14731472
}
@@ -1585,7 +1584,7 @@ function main() {
15851584
// Add common ones just in case (only if they exist)
15861585
if (runEnv.OPENAI_API_KEY) wslenv.push("OPENAI_API_KEY/u");
15871586
if (runEnv.OPENROUTER_API_KEY) wslenv.push("OPENROUTER_API_KEY/u");
1588-
1587+
15891588
if (process.env.WSLENV) {
15901589
runEnv.WSLENV = `${process.env.WSLENV}:${wslenv.join(":")}`;
15911590
} else {
@@ -1628,13 +1627,13 @@ function main() {
16281627
if (tool === "copilot") {
16291628
console.error(
16301629
"Error: 'copilot' command not found.\n\n" +
1631-
"Install with: npm i -g @github/copilot\n" +
1632-
"Then ensure your npm global bin is in PATH."
1630+
"Install with: npm i -g @github/copilot\n" +
1631+
"Then ensure your npm global bin is in PATH."
16331632
);
16341633
} else {
16351634
console.error(
16361635
`Error: '${tool}' command not found.\n\n` +
1637-
`Install ${tool} first.`
1636+
`Install ${tool} first.`
16381637
);
16391638
}
16401639
} else {

models.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@
154154
"description": "Codex via ChatGPT paid plan/subscription. No API key needed."
155155
},
156156
"codex-5-a": {
157-
"id": "gpt-5.2-codex",
158-
"name": "Codex (API)",
157+
"id": "gpt-5.3-codex-high",
158+
"name": "Codex 5.3 High (API)",
159159
"tool": "codex",
160160
"provider": "openai",
161161
"api_key_env": "OPENAI_API_KEY",
@@ -173,4 +173,4 @@
173173
"out": 0.0,
174174
"description": "GitHub Copilot agent. Requires Copilot subscription."
175175
}
176-
}
176+
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,4 @@
5353
"README.md",
5454
"LICENSE"
5555
]
56-
}
56+
}

pipeline/docker/Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ RUN npm install -g @anthropic-ai/claude-code
1616

1717
# Install Gemini CLI, OpenCode, Codex, and Copilot
1818
RUN npm install -g @google/gemini-cli opencode-ai@latest @openai/codex @github/copilot
19-
2019
# Add entrypoint wrappers for tools that need pre-auth
2120
COPY codex-entrypoint.sh /usr/local/bin/codex-entrypoint.sh
2221
RUN chmod +x /usr/local/bin/codex-entrypoint.sh

0 commit comments

Comments
 (0)