Skip to content

Commit 8366aeb

Browse files
committed
add cli-to-js skill to .agents/skills
1 parent 63fde7d commit 8366aeb

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed

.agents/skills/cli-to-js/SKILL.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
---
2+
name: cli-to-js
3+
description: Use when wrapping CLI binaries in JavaScript, automating shell workflows in TypeScript, composing multiple CLIs into scripts, or building agent tool-use. Covers convertCliToJs, $command, $validate, $spawn, script(), .text()/.lines()/.json() output parsing.
4+
---
5+
6+
# cli-to-js
7+
8+
You are using cli-to-js to turn CLI binaries into callable JavaScript APIs.
9+
10+
## REQUIRED for every cli-to-js usage:
11+
12+
1. Call `convertCliToJs("binary")` once, reuse the returned API
13+
2. Use `.text()`, `.lines()`, or `.json()` for typed output — never manually split `result.stdout`
14+
3. Use `$validate` before executing when inputs come from untrusted sources
15+
4. Use `$spawn` + `for await` for streaming — never callbacks unless specifically asked
16+
17+
## Flag mapping
18+
19+
```ts
20+
// JS option key → CLI output
21+
// { verbose: true } → --verbose
22+
// { verbose: false } → (omitted)
23+
// { output: "file.txt" } → --output file.txt
24+
// { dryRun: true } → --dry-run
25+
// { v: true } → -v
26+
// { include: ["a", "b"] } → --include a --include b
27+
// { _: ["file.txt"] } → file.txt
28+
```
29+
30+
## API quick reference
31+
32+
```ts
33+
import { convertCliToJs, script } from "cli-to-js";
34+
const tool = await convertCliToJs("binary-name");
35+
36+
// Run + typed output
37+
await tool.subcommand({ flag: "val", _: ["pos"] }).text();
38+
await tool.subcommand({ json: true }).json<MyType>();
39+
await tool.subcommand().lines();
40+
41+
// Streaming
42+
for await (const line of tool.$spawn.subcommand({ watch: true })) {}
43+
44+
// Validation (did-you-mean, choices, required flags, exclusive flags)
45+
const errors = tool.$validate({ misspeled: true });
46+
47+
// Shell string without executing
48+
tool.$command.subcommand({ flag: "val" });
49+
// → "binary-name subcommand --flag val"
50+
51+
// Compose into runnable script
52+
const deploy = script(git.$command.push(), docker.$command.build({ tag: "app", _: ["."] }));
53+
deploy.run(); // execute with &&
54+
console.log(`${deploy}`); // get the string
55+
```
56+
57+
## When building tools
58+
59+
**Bad:**
60+
```ts
61+
const result = await api.status();
62+
const lines = result.stdout.trim().split("\n");
63+
```
64+
65+
**Good:**
66+
```ts
67+
const statusLines = await api.status().lines();
68+
```
69+
70+
**Bad:**
71+
```ts
72+
const api1 = await convertCliToJs("git");
73+
const api2 = await convertCliToJs("git"); // wasteful duplicate
74+
```
75+
76+
**Good:**
77+
```ts
78+
const git = await convertCliToJs("git");
79+
// reuse git everywhere
80+
```
81+
82+
**Bad:**
83+
```ts
84+
await api.commit({ mesage: "fix" }); // typo silently becomes unknown flag
85+
```
86+
87+
**Good:**
88+
```ts
89+
const errors = api.$validate({ mesage: "fix" });
90+
// [{ kind: "unknown-flag", suggestion: "message" }]
91+
if (errors.length > 0) throw new Error(errors[0].message);
92+
await api.commit({ message: "fix" });
93+
```
94+
95+
## Multi-CLI workflow pattern
96+
97+
```ts
98+
const git = await convertCliToJs("git");
99+
const claude = await convertCliToJs("claude");
100+
101+
const files = await git.diff({ nameOnly: true, _: ["HEAD~1"] }).lines();
102+
103+
for (const file of files) {
104+
const review = await claude({
105+
print: true,
106+
model: "sonnet",
107+
_: [`Review ${file} for bugs`],
108+
}).text();
109+
110+
if (review.includes("no issues")) continue;
111+
console.log(`${file}: ${review}`);
112+
}
113+
```
114+
115+
## Full API surface
116+
117+
| Method | Returns | Use for |
118+
|------------------------------|--------------------|--------------------------------|
119+
| `api.sub(opts)` | `CommandPromise` | Run subcommand |
120+
| `.text()` | `Promise<string>` | Trimmed stdout |
121+
| `.lines()` | `Promise<string[]>`| Split by newlines |
122+
| `.json<T>()` | `Promise<T>` | Parse JSON output |
123+
| `api.$spawn.sub(opts)` | `CommandProcess` | `for await` streaming |
124+
| `api.$command.sub(opts)` | `string` | Shell string, no execution |
125+
| `api.$validate(opts)` | `ValidationError[]`| Pre-flight flag checking |
126+
| `api.$validate("sub", opts)` | `ValidationError[]`| Subcommand flag checking |
127+
| `api.$schema` | `CliSchema` | Parsed schema from --help |
128+
| `api.$parse("sub")` | `ParsedCommand` | Lazily enrich subcommand |
129+
| `script(...cmds)` | `{ run, toString }`| Compose && chain |

0 commit comments

Comments
 (0)