@warlock.js/ai-cli 0.0.0-stage → 5.31.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/CHANGELOG.md +31 -0
- package/LICENSE +21 -0
- package/README.md +69 -2
- package/cjs/index.cjs +4576 -0
- package/cjs/index.cjs.map +1 -0
- package/esm/adapters/aider.d.mts +41 -0
- package/esm/adapters/aider.d.mts.map +1 -0
- package/esm/adapters/aider.mjs +294 -0
- package/esm/adapters/aider.mjs.map +1 -0
- package/esm/adapters/amp.d.mts +33 -0
- package/esm/adapters/amp.d.mts.map +1 -0
- package/esm/adapters/amp.mjs +119 -0
- package/esm/adapters/amp.mjs.map +1 -0
- package/esm/adapters/antigravity.d.mts +37 -0
- package/esm/adapters/antigravity.d.mts.map +1 -0
- package/esm/adapters/antigravity.mjs +235 -0
- package/esm/adapters/antigravity.mjs.map +1 -0
- package/esm/adapters/claude-code.d.mts +30 -0
- package/esm/adapters/claude-code.d.mts.map +1 -0
- package/esm/adapters/claude-code.mjs +121 -0
- package/esm/adapters/claude-code.mjs.map +1 -0
- package/esm/adapters/claude-stream.d.mts +32 -0
- package/esm/adapters/claude-stream.d.mts.map +1 -0
- package/esm/adapters/claude-stream.mjs +140 -0
- package/esm/adapters/claude-stream.mjs.map +1 -0
- package/esm/adapters/codex.d.mts +37 -0
- package/esm/adapters/codex.d.mts.map +1 -0
- package/esm/adapters/codex.mjs +320 -0
- package/esm/adapters/codex.mjs.map +1 -0
- package/esm/adapters/copilot.d.mts +35 -0
- package/esm/adapters/copilot.d.mts.map +1 -0
- package/esm/adapters/copilot.mjs +244 -0
- package/esm/adapters/copilot.mjs.map +1 -0
- package/esm/adapters/cursor.d.mts +35 -0
- package/esm/adapters/cursor.d.mts.map +1 -0
- package/esm/adapters/cursor.mjs +203 -0
- package/esm/adapters/cursor.mjs.map +1 -0
- package/esm/adapters/droid.d.mts +32 -0
- package/esm/adapters/droid.d.mts.map +1 -0
- package/esm/adapters/droid.mjs +183 -0
- package/esm/adapters/droid.mjs.map +1 -0
- package/esm/adapters/grok.d.mts +32 -0
- package/esm/adapters/grok.d.mts.map +1 -0
- package/esm/adapters/grok.mjs +199 -0
- package/esm/adapters/grok.mjs.map +1 -0
- package/esm/adapters/index.d.mts +16 -0
- package/esm/adapters/index.mjs +18 -0
- package/esm/adapters/kimi.d.mts +34 -0
- package/esm/adapters/kimi.d.mts.map +1 -0
- package/esm/adapters/kimi.mjs +162 -0
- package/esm/adapters/kimi.mjs.map +1 -0
- package/esm/adapters/mistral-vibe.d.mts +32 -0
- package/esm/adapters/mistral-vibe.d.mts.map +1 -0
- package/esm/adapters/mistral-vibe.mjs +252 -0
- package/esm/adapters/mistral-vibe.mjs.map +1 -0
- package/esm/adapters/opencode.d.mts +36 -0
- package/esm/adapters/opencode.d.mts.map +1 -0
- package/esm/adapters/opencode.mjs +219 -0
- package/esm/adapters/opencode.mjs.map +1 -0
- package/esm/adapters/qwen-code.d.mts +30 -0
- package/esm/adapters/qwen-code.d.mts.map +1 -0
- package/esm/adapters/qwen-code.mjs +94 -0
- package/esm/adapters/qwen-code.mjs.map +1 -0
- package/esm/adapters/reasonix.d.mts +31 -0
- package/esm/adapters/reasonix.d.mts.map +1 -0
- package/esm/adapters/reasonix.mjs +250 -0
- package/esm/adapters/reasonix.mjs.map +1 -0
- package/esm/adapters/shared.d.mts +78 -0
- package/esm/adapters/shared.d.mts.map +1 -0
- package/esm/adapters/shared.mjs +161 -0
- package/esm/adapters/shared.mjs.map +1 -0
- package/esm/cli-agent.d.mts +40 -0
- package/esm/cli-agent.d.mts.map +1 -0
- package/esm/cli-agent.mjs +207 -0
- package/esm/cli-agent.mjs.map +1 -0
- package/esm/cli.d.mts +53 -0
- package/esm/cli.d.mts.map +1 -0
- package/esm/cli.mjs +61 -0
- package/esm/cli.mjs.map +1 -0
- package/esm/contracts/adapter.contract.d.mts +154 -0
- package/esm/contracts/adapter.contract.d.mts.map +1 -0
- package/esm/contracts/events.type.d.mts +111 -0
- package/esm/contracts/events.type.d.mts.map +1 -0
- package/esm/contracts/index.d.mts +4 -0
- package/esm/contracts/mcp.type.d.mts +29 -0
- package/esm/contracts/mcp.type.d.mts.map +1 -0
- package/esm/contracts/options.type.d.mts +87 -0
- package/esm/contracts/options.type.d.mts.map +1 -0
- package/esm/errors.d.mts +46 -0
- package/esm/errors.d.mts.map +1 -0
- package/esm/errors.mjs +32 -0
- package/esm/errors.mjs.map +1 -0
- package/esm/index.d.mts +31 -0
- package/esm/index.mjs +28 -0
- package/esm/model/cli-model.d.mts +48 -0
- package/esm/model/cli-model.d.mts.map +1 -0
- package/esm/model/cli-model.mjs +133 -0
- package/esm/model/cli-model.mjs.map +1 -0
- package/esm/probe/probe.d.mts +18 -0
- package/esm/probe/probe.d.mts.map +1 -0
- package/esm/probe/probe.mjs +148 -0
- package/esm/probe/probe.mjs.map +1 -0
- package/esm/session/aggregate.mjs +86 -0
- package/esm/session/aggregate.mjs.map +1 -0
- package/esm/session/environment.mjs +71 -0
- package/esm/session/environment.mjs.map +1 -0
- package/esm/session/run-directory.mjs +74 -0
- package/esm/session/run-directory.mjs.map +1 -0
- package/esm/session/session.mjs +188 -0
- package/esm/session/session.mjs.map +1 -0
- package/esm/spawn/invocation.d.mts +35 -0
- package/esm/spawn/invocation.d.mts.map +1 -0
- package/esm/spawn/invocation.mjs +76 -0
- package/esm/spawn/invocation.mjs.map +1 -0
- package/esm/spawn/process-tree.mjs +57 -0
- package/esm/spawn/process-tree.mjs.map +1 -0
- package/esm/spawn/resolve-command.d.mts +73 -0
- package/esm/spawn/resolve-command.d.mts.map +1 -0
- package/esm/spawn/resolve-command.mjs +109 -0
- package/esm/spawn/resolve-command.mjs.map +1 -0
- package/esm/stream/event-log.mjs +52 -0
- package/esm/stream/event-log.mjs.map +1 -0
- package/esm/stream/json-lines.d.mts +33 -0
- package/esm/stream/json-lines.d.mts.map +1 -0
- package/esm/stream/json-lines.mjs +65 -0
- package/esm/stream/json-lines.mjs.map +1 -0
- package/esm/stream/line-splitter.mjs +53 -0
- package/esm/stream/line-splitter.mjs.map +1 -0
- package/esm/utils/toml.d.mts +22 -0
- package/esm/utils/toml.d.mts.map +1 -0
- package/esm/utils/toml.mjs +53 -0
- package/esm/utils/toml.mjs.map +1 -0
- package/llms-full.txt +373 -0
- package/llms.txt +10 -0
- package/package.json +39 -4
- package/skills/README.md +13 -0
- package/skills/index.md +22 -0
- package/skills/run-cli-agents/SKILL.md +186 -0
- package/skills/write-a-cli-adapter/SKILL.md +173 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: write-a-cli-adapter
|
|
3
|
+
description: 'Write a CliAdapter for a coding CLI that @warlock.js/ai-cli does not ship. Triggers: `CliAdapter`, `CliLaunchContext`, `CliLaunchPlan`, `CliStreamParser`, `cli.custom`, `createCliAgent`, `jsonLinesParser`, `stringField`, `numberField`, `usageOf`, `mcpServersJson`, `jsonFile`, `toToml`, `mapEffort`, `argvPrompt`, `promptFile`, `requirePrivateHome`, `unsupportedOption`, `createClaudeStreamParser`, `helpLists`, `missingFlags`, `requires`, `verification`; ''add a new coding CLI'', ''support another agent CLI'', ''parse a vendor JSONL stream'', ''adapter for a new CLI version''; typical import `import { cli, jsonLinesParser, type CliAdapter } from "@warlock.js/ai-cli"`. Skip: using the built-in adapters — the `run-cli-agents` topic.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Write a CLI adapter
|
|
7
|
+
|
|
8
|
+
An adapter is a plain object with two jobs: `plan(context)` builds argv, stdin, environment and files for one run, and `parser(context)` turns stdout lines into the unified events. The runner does the rest: executable lookup, the Windows `.cmd` rules, the capability probe, homes, secrets, cancellation and the result.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import {
|
|
13
|
+
cli,
|
|
14
|
+
jsonFile,
|
|
15
|
+
jsonLinesParser,
|
|
16
|
+
mcpServersJson,
|
|
17
|
+
numberField,
|
|
18
|
+
stringField,
|
|
19
|
+
usageOf,
|
|
20
|
+
type CliAdapter,
|
|
21
|
+
type CliLaunchFile,
|
|
22
|
+
} from "@warlock.js/ai-cli";
|
|
23
|
+
|
|
24
|
+
export const acmeAdapter: CliAdapter = {
|
|
25
|
+
name: "acme",
|
|
26
|
+
title: "Acme Agent",
|
|
27
|
+
command: "acme",
|
|
28
|
+
apiKeyEnv: ["ACME_API_KEY"],
|
|
29
|
+
homeEnv: ["ACME_HOME"],
|
|
30
|
+
transports: ["argv", "stdin"],
|
|
31
|
+
effort: false,
|
|
32
|
+
mcp: true,
|
|
33
|
+
probe: { help: [["run", "--help"]], version: ["--version"] },
|
|
34
|
+
verification: {
|
|
35
|
+
version: "3.2.0",
|
|
36
|
+
verifiedBy: "help",
|
|
37
|
+
sources: ["https://acme.dev/docs/cli"],
|
|
38
|
+
checkedAt: "2026-10-17",
|
|
39
|
+
},
|
|
40
|
+
plan(context) {
|
|
41
|
+
const args = ["run", "--json"];
|
|
42
|
+
const requires = ["--json"];
|
|
43
|
+
const files: CliLaunchFile[] = [];
|
|
44
|
+
|
|
45
|
+
if (context.model !== undefined) {
|
|
46
|
+
args.push("--model", context.model);
|
|
47
|
+
requires.push("--model");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (context.resume !== undefined) {
|
|
51
|
+
args.push("--resume", context.resume);
|
|
52
|
+
requires.push("--resume");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (context.permission === "bypass") {
|
|
56
|
+
args.push("--yes-to-all");
|
|
57
|
+
requires.push("--yes-to-all");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (context.mcp !== undefined) {
|
|
61
|
+
const file = path.join(context.runDir, "mcp.json");
|
|
62
|
+
|
|
63
|
+
files.push({ path: file, content: jsonFile({ mcpServers: mcpServersJson(context.mcp) }) });
|
|
64
|
+
args.push("--mcp-config", file);
|
|
65
|
+
requires.push("--mcp-config");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
args,
|
|
70
|
+
tail: context.transport === "argv" ? [context.prompt] : [],
|
|
71
|
+
stdin: context.transport === "stdin" ? context.prompt : undefined,
|
|
72
|
+
env: context.home !== undefined ? { ACME_HOME: context.home } : undefined,
|
|
73
|
+
files,
|
|
74
|
+
requires,
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
parser: () =>
|
|
78
|
+
jsonLinesParser((record) => {
|
|
79
|
+
switch (record.type) {
|
|
80
|
+
case "start":
|
|
81
|
+
return [{ type: "session.started", sessionRef: stringField(record, "id") }];
|
|
82
|
+
case "text":
|
|
83
|
+
return [{ type: "message", text: stringField(record, "text") ?? "", delta: true }];
|
|
84
|
+
case "tool":
|
|
85
|
+
return [
|
|
86
|
+
{
|
|
87
|
+
type: "tool.call",
|
|
88
|
+
id: String(record.id),
|
|
89
|
+
name: String(record.name),
|
|
90
|
+
input: record.input,
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
case "tool_done":
|
|
94
|
+
return [
|
|
95
|
+
{
|
|
96
|
+
type: "tool.result",
|
|
97
|
+
id: String(record.id),
|
|
98
|
+
output: String(record.output),
|
|
99
|
+
isError: record.ok !== true,
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
case "usage":
|
|
103
|
+
return [
|
|
104
|
+
{
|
|
105
|
+
type: "usage",
|
|
106
|
+
cumulative: true,
|
|
107
|
+
usage: usageOf({
|
|
108
|
+
input: numberField(record, "in"),
|
|
109
|
+
output: numberField(record, "out"),
|
|
110
|
+
}),
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
case "error":
|
|
114
|
+
return [{ type: "error", message: String(record.message), fatal: true }];
|
|
115
|
+
default:
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const acme = cli.custom(acmeAdapter);
|
|
122
|
+
const result = await (await acme.run("Add a health check endpoint", { cwd: "/srv/api" })).done();
|
|
123
|
+
|
|
124
|
+
console.log(result.status, result.text);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Rules
|
|
128
|
+
|
|
129
|
+
- **`requires`:** list every flag and subcommand the plan uses, spelled as `--help` prints it. The probe then fails the run up front, naming the flag and the installed version, if a release drops or renames one. Leave out only a flag the CLI accepts but no longer documents, and say so in a comment.
|
|
130
|
+
- **Secrets:** never put one in `args` or `tail`. Use `env`, or a file under `context.runDir` (written 0600, removed after the run). A home-level config file may be written only when `context.homeIsPrivate`; `requirePrivateHome(context, adapter, "MCP servers")` fails closed otherwise.
|
|
131
|
+
- **Prompt placement:** `tail` goes after the caller's `args`. Wrap a positional prompt in `argvPrompt(prompt)`, so a prompt starting with `-` is not read as a flag. For a `"file"` transport, `promptFile(context)` writes the prompt into the run directory.
|
|
132
|
+
- **Effort:** `mapEffort(effort, { low: "low", high: "high" })` follows the framework clamping rule. Set `effort: true` only when you map it. The runner refuses `effort` for adapters that do not.
|
|
133
|
+
- **Unsupported options:** `throw unsupportedOption(adapter, "why")` for an option the CLI cannot honour; never drop it silently.
|
|
134
|
+
- **Failures:** report a vendor failure as `{ type: "error", fatal: true }`, so a CLI that exits 0 still ends `failed`.
|
|
135
|
+
- **Late session ids:** if the id arrives only at the end, return it from the parser's `sessionRef()`. If the CLI writes its result to a file, point it at `context.runDir` and read the file in the parser's `end()`.
|
|
136
|
+
- **Claude-style streams:** for a CLI that reproduces Claude Code's `stream-json`, reuse `createClaudeStreamParser({ usage: "anthropic" | "inclusive" })`.
|
|
137
|
+
- **Verification:** record `verification` — the version, the sources, and `"help"` or `"docs"`.
|
|
138
|
+
|
|
139
|
+
## Test without the real CLI
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
// acme-adapter.spec.ts — `acmeAdapter` from above; `readFileSync` from "node:fs",
|
|
143
|
+
// `missingFlags` from "@warlock.js/ai-cli", `expect`/`it` from "vitest".
|
|
144
|
+
it("normalizes a recorded stream and needs only listed flags", () => {
|
|
145
|
+
const parser = acmeAdapter.parser({
|
|
146
|
+
prompt: "x",
|
|
147
|
+
transport: "argv",
|
|
148
|
+
cwd: "/work",
|
|
149
|
+
permission: "standard",
|
|
150
|
+
homeIsPrivate: true,
|
|
151
|
+
runDir: "/tmp/run",
|
|
152
|
+
platform: "linux",
|
|
153
|
+
});
|
|
154
|
+
const events = readFileSync("fixtures/acme/stream.jsonl", "utf8")
|
|
155
|
+
.split("\n")
|
|
156
|
+
.flatMap((line) => parser.line(line));
|
|
157
|
+
|
|
158
|
+
expect(events[0]).toMatchObject({ type: "session.started" });
|
|
159
|
+
|
|
160
|
+
const plan = acmeAdapter.plan({
|
|
161
|
+
prompt: "x",
|
|
162
|
+
transport: "argv",
|
|
163
|
+
cwd: "/work",
|
|
164
|
+
model: "fast",
|
|
165
|
+
permission: "bypass",
|
|
166
|
+
homeIsPrivate: true,
|
|
167
|
+
runDir: "/tmp/run",
|
|
168
|
+
platform: "linux",
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
expect(missingFlags(readFileSync("fixtures/acme/help.txt", "utf8"), plan.requires)).toEqual([]);
|
|
172
|
+
});
|
|
173
|
+
```
|