@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,244 @@
|
|
|
1
|
+
import { asRecord, jsonLinesParser, numberField, outputText, parseJsonLine, recordField, stringField } from "../stream/json-lines.mjs";
|
|
2
|
+
import { argvPrompt, jsonFile, mcpServersJson, usageOf } from "./shared.mjs";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
//#region ../../packages/ai-cli/src/adapters/copilot.ts
|
|
7
|
+
/** Usage file (inside the run directory) Copilot writes on exit. */
|
|
8
|
+
const USAGE_FILE = "copilot-usage.json";
|
|
9
|
+
/**
|
|
10
|
+
* GitHub Copilot CLI (`copilot`, npm `@github/copilot`).
|
|
11
|
+
*
|
|
12
|
+
* Verified against the real `copilot --help` of 1.0.98, the JSONL writer in
|
|
13
|
+
* the shipped CLI, the event types of `@github/copilot-sdk` 1.0.22 and the
|
|
14
|
+
* docs (`docs.github.com/copilot/how-tos/copilot-cli/…`, programmatic and
|
|
15
|
+
* command references), 2026-10-17.
|
|
16
|
+
*
|
|
17
|
+
* - Headless: `copilot -p <prompt> --output-format json --stream on`
|
|
18
|
+
* (piped stdin is the prompt when `-p` is absent). Stream lines are
|
|
19
|
+
* session events `{type,data,id,timestamp}`: `assistant.message_delta`
|
|
20
|
+
* (`deltaContent`), `assistant.message` (`content`),
|
|
21
|
+
* `tool.execution_start` (`toolCallId`, `toolName`, `arguments`),
|
|
22
|
+
* `tool.execution_complete` (`success`, `result.content`,
|
|
23
|
+
* `error.message`), `session.error`, and a final CLI-built `result`
|
|
24
|
+
* (`sessionId`, `exitCode`, premium-request usage).
|
|
25
|
+
* - Tokens are not in the stream; the adapter passes `--usage-output-file`
|
|
26
|
+
* and reads `modelMetrics.*.usage` from it when the stream ends. Billing
|
|
27
|
+
* is in premium requests, so no USD cost is reported.
|
|
28
|
+
* - Permissions: unapproved tool requests are auto-denied in `-p` mode;
|
|
29
|
+
* bypass is `--allow-all` (tools, paths and URLs). `--no-ask-user` and
|
|
30
|
+
* `--no-auto-update` are always passed.
|
|
31
|
+
* - MCP: `--additional-mcp-config @<file>` (`mcpServers`, `type`
|
|
32
|
+
* `local`/`http`/`sse`, `tools: ["*"]`), written to the run directory.
|
|
33
|
+
* - Home `COPILOT_HOME`; auth `COPILOT_GITHUB_TOKEN`. Resume with
|
|
34
|
+
* `--session-id <id>`.
|
|
35
|
+
*/
|
|
36
|
+
const copilotAdapter = {
|
|
37
|
+
name: "copilot",
|
|
38
|
+
title: "Copilot CLI",
|
|
39
|
+
command: "copilot",
|
|
40
|
+
apiKeyEnv: ["COPILOT_GITHUB_TOKEN"],
|
|
41
|
+
homeEnv: ["COPILOT_HOME"],
|
|
42
|
+
transports: ["argv", "stdin"],
|
|
43
|
+
effort: true,
|
|
44
|
+
mcp: true,
|
|
45
|
+
probe: {
|
|
46
|
+
help: [["--help"]],
|
|
47
|
+
version: ["--version"]
|
|
48
|
+
},
|
|
49
|
+
verification: {
|
|
50
|
+
version: "1.0.98",
|
|
51
|
+
verifiedBy: "help",
|
|
52
|
+
sources: [
|
|
53
|
+
"https://docs.github.com/en/copilot/how-tos/copilot-cli/automate-copilot-cli/run-cli-programmatically",
|
|
54
|
+
"https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-command-reference",
|
|
55
|
+
"https://www.npmjs.com/package/@github/copilot"
|
|
56
|
+
],
|
|
57
|
+
checkedAt: "2026-10-17"
|
|
58
|
+
},
|
|
59
|
+
plan(context) {
|
|
60
|
+
const args = [
|
|
61
|
+
"--output-format",
|
|
62
|
+
"json",
|
|
63
|
+
"--stream",
|
|
64
|
+
"on",
|
|
65
|
+
"--no-ask-user",
|
|
66
|
+
"--no-auto-update",
|
|
67
|
+
"--usage-output-file",
|
|
68
|
+
path.join(context.runDir, USAGE_FILE)
|
|
69
|
+
];
|
|
70
|
+
const requires = [
|
|
71
|
+
"--output-format",
|
|
72
|
+
"--stream",
|
|
73
|
+
"--no-ask-user",
|
|
74
|
+
"--no-auto-update",
|
|
75
|
+
"--usage-output-file"
|
|
76
|
+
];
|
|
77
|
+
const files = [];
|
|
78
|
+
if (context.model !== void 0) {
|
|
79
|
+
args.push("--model", context.model);
|
|
80
|
+
requires.push("--model");
|
|
81
|
+
}
|
|
82
|
+
if (context.effort !== void 0) {
|
|
83
|
+
args.push("--reasoning-effort", context.effort);
|
|
84
|
+
requires.push("--reasoning-effort");
|
|
85
|
+
}
|
|
86
|
+
if (context.resume !== void 0) {
|
|
87
|
+
args.push("--session-id", context.resume);
|
|
88
|
+
requires.push("--session-id");
|
|
89
|
+
}
|
|
90
|
+
if (context.permission === "bypass") {
|
|
91
|
+
args.push("--allow-all");
|
|
92
|
+
requires.push("--allow-all");
|
|
93
|
+
}
|
|
94
|
+
if (context.mcp !== void 0) {
|
|
95
|
+
const file = path.join(context.runDir, "copilot-mcp.json");
|
|
96
|
+
const servers = mcpServersJson(context.mcp, {
|
|
97
|
+
typeField: true,
|
|
98
|
+
extra: { tools: ["*"] }
|
|
99
|
+
});
|
|
100
|
+
for (const server of Object.values(servers)) if (server.type === "stdio") server.type = "local";
|
|
101
|
+
files.push({
|
|
102
|
+
path: file,
|
|
103
|
+
content: jsonFile({ mcpServers: servers })
|
|
104
|
+
});
|
|
105
|
+
args.push("--additional-mcp-config", `@${file}`);
|
|
106
|
+
requires.push("--additional-mcp-config");
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
args,
|
|
110
|
+
tail: context.transport === "argv" ? ["-p", argvPrompt(context.prompt)] : [],
|
|
111
|
+
stdin: context.transport === "stdin" ? context.prompt : void 0,
|
|
112
|
+
env: context.home !== void 0 ? { COPILOT_HOME: context.home } : void 0,
|
|
113
|
+
files,
|
|
114
|
+
requires: context.transport === "argv" ? [...requires, "-p"] : requires
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
parser: createCopilotParser
|
|
118
|
+
};
|
|
119
|
+
/** Token usage summed over `modelMetrics` of the usage file. */
|
|
120
|
+
function usageFileEvent(context) {
|
|
121
|
+
if (context === void 0) return;
|
|
122
|
+
let report;
|
|
123
|
+
try {
|
|
124
|
+
report = parseJsonLine(readFileSync(path.join(context.runDir, USAGE_FILE), "utf8"));
|
|
125
|
+
} catch {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const metrics = recordField(report, "modelMetrics");
|
|
129
|
+
if (metrics === void 0) return;
|
|
130
|
+
let input = 0;
|
|
131
|
+
let output = 0;
|
|
132
|
+
let cached = 0;
|
|
133
|
+
let written = 0;
|
|
134
|
+
let reasoning = 0;
|
|
135
|
+
for (const value of Object.values(metrics)) {
|
|
136
|
+
const usage = recordField(asRecord(value), "usage");
|
|
137
|
+
input += numberField(usage, "inputTokens") ?? 0;
|
|
138
|
+
output += numberField(usage, "outputTokens") ?? 0;
|
|
139
|
+
cached += numberField(usage, "cacheReadTokens") ?? 0;
|
|
140
|
+
written += numberField(usage, "cacheWriteTokens") ?? 0;
|
|
141
|
+
reasoning += numberField(usage, "reasoningTokens") ?? 0;
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
type: "usage",
|
|
145
|
+
cumulative: true,
|
|
146
|
+
usage: usageOf({
|
|
147
|
+
input,
|
|
148
|
+
output,
|
|
149
|
+
cachedTokens: cached > 0 ? cached : void 0,
|
|
150
|
+
cacheWriteTokens: written > 0 ? written : void 0,
|
|
151
|
+
reasoningTokens: reasoning > 0 ? reasoning : void 0
|
|
152
|
+
}),
|
|
153
|
+
raw: report
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/** Parser for `copilot -p … --output-format json` (plus its usage file). */
|
|
157
|
+
function createCopilotParser(context) {
|
|
158
|
+
let started = false;
|
|
159
|
+
let sessionRef;
|
|
160
|
+
return {
|
|
161
|
+
...jsonLinesParser((record) => {
|
|
162
|
+
const events = [];
|
|
163
|
+
const type = stringField(record, "type");
|
|
164
|
+
const data = recordField(record, "data");
|
|
165
|
+
if (!started) {
|
|
166
|
+
started = true;
|
|
167
|
+
events.push({
|
|
168
|
+
type: "session.started",
|
|
169
|
+
sessionRef: stringField(data, "sessionId"),
|
|
170
|
+
raw: record
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
switch (type) {
|
|
174
|
+
case "assistant.message_delta": {
|
|
175
|
+
const text = stringField(data, "deltaContent") ?? "";
|
|
176
|
+
if (text.length > 0) events.push({
|
|
177
|
+
type: "message",
|
|
178
|
+
text,
|
|
179
|
+
delta: true,
|
|
180
|
+
raw: record
|
|
181
|
+
});
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case "assistant.message": {
|
|
185
|
+
const text = stringField(data, "content") ?? "";
|
|
186
|
+
if (text.length > 0) events.push({
|
|
187
|
+
type: "message",
|
|
188
|
+
text,
|
|
189
|
+
delta: false,
|
|
190
|
+
raw: record
|
|
191
|
+
});
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
case "tool.execution_start":
|
|
195
|
+
events.push({
|
|
196
|
+
type: "tool.call",
|
|
197
|
+
id: stringField(data, "toolCallId") ?? "",
|
|
198
|
+
name: stringField(data, "toolName") ?? "",
|
|
199
|
+
input: data?.arguments,
|
|
200
|
+
raw: record
|
|
201
|
+
});
|
|
202
|
+
break;
|
|
203
|
+
case "tool.execution_complete": {
|
|
204
|
+
const success = data?.success === true;
|
|
205
|
+
events.push({
|
|
206
|
+
type: "tool.result",
|
|
207
|
+
id: stringField(data, "toolCallId") ?? "",
|
|
208
|
+
output: success ? outputText(recordField(data, "result")?.content) : stringField(recordField(data, "error"), "message") ?? "tool failed",
|
|
209
|
+
isError: !success,
|
|
210
|
+
raw: record
|
|
211
|
+
});
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
case "session.error":
|
|
215
|
+
events.push({
|
|
216
|
+
type: "error",
|
|
217
|
+
message: stringField(data, "message") ?? "Copilot session error",
|
|
218
|
+
fatal: true,
|
|
219
|
+
raw: record
|
|
220
|
+
});
|
|
221
|
+
break;
|
|
222
|
+
case "result":
|
|
223
|
+
sessionRef = stringField(record, "sessionId") ?? sessionRef;
|
|
224
|
+
break;
|
|
225
|
+
case "error":
|
|
226
|
+
events.push({
|
|
227
|
+
type: "error",
|
|
228
|
+
message: stringField(record, "message") ?? "error",
|
|
229
|
+
raw: record
|
|
230
|
+
});
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
return events;
|
|
234
|
+
}, () => {
|
|
235
|
+
const usage = usageFileEvent(context);
|
|
236
|
+
return usage !== void 0 ? [usage] : [];
|
|
237
|
+
}),
|
|
238
|
+
sessionRef: () => sessionRef
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
//#endregion
|
|
243
|
+
export { copilotAdapter, createCopilotParser };
|
|
244
|
+
//# sourceMappingURL=copilot.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot.mjs","names":[],"sources":["../../../../../../../../packages/ai-cli/src/adapters/copilot.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type {\n CliAdapter,\n CliLaunchContext,\n CliLaunchFile,\n CliParsedEvent,\n CliStreamParser,\n} from \"../contracts\";\nimport {\n asRecord,\n jsonLinesParser,\n numberField,\n outputText,\n parseJsonLine,\n recordField,\n stringField,\n} from \"../stream/json-lines\";\nimport { argvPrompt, jsonFile, mcpServersJson, usageOf } from \"./shared\";\n\n/** Usage file (inside the run directory) Copilot writes on exit. */\nconst USAGE_FILE = \"copilot-usage.json\";\n\n/**\n * GitHub Copilot CLI (`copilot`, npm `@github/copilot`).\n *\n * Verified against the real `copilot --help` of 1.0.98, the JSONL writer in\n * the shipped CLI, the event types of `@github/copilot-sdk` 1.0.22 and the\n * docs (`docs.github.com/copilot/how-tos/copilot-cli/…`, programmatic and\n * command references), 2026-10-17.\n *\n * - Headless: `copilot -p <prompt> --output-format json --stream on`\n * (piped stdin is the prompt when `-p` is absent). Stream lines are\n * session events `{type,data,id,timestamp}`: `assistant.message_delta`\n * (`deltaContent`), `assistant.message` (`content`),\n * `tool.execution_start` (`toolCallId`, `toolName`, `arguments`),\n * `tool.execution_complete` (`success`, `result.content`,\n * `error.message`), `session.error`, and a final CLI-built `result`\n * (`sessionId`, `exitCode`, premium-request usage).\n * - Tokens are not in the stream; the adapter passes `--usage-output-file`\n * and reads `modelMetrics.*.usage` from it when the stream ends. Billing\n * is in premium requests, so no USD cost is reported.\n * - Permissions: unapproved tool requests are auto-denied in `-p` mode;\n * bypass is `--allow-all` (tools, paths and URLs). `--no-ask-user` and\n * `--no-auto-update` are always passed.\n * - MCP: `--additional-mcp-config @<file>` (`mcpServers`, `type`\n * `local`/`http`/`sse`, `tools: [\"*\"]`), written to the run directory.\n * - Home `COPILOT_HOME`; auth `COPILOT_GITHUB_TOKEN`. Resume with\n * `--session-id <id>`.\n */\nexport const copilotAdapter: CliAdapter = {\n name: \"copilot\",\n title: \"Copilot CLI\",\n command: \"copilot\",\n apiKeyEnv: [\"COPILOT_GITHUB_TOKEN\"],\n homeEnv: [\"COPILOT_HOME\"],\n transports: [\"argv\", \"stdin\"],\n effort: true,\n mcp: true,\n probe: { help: [[\"--help\"]], version: [\"--version\"] },\n verification: {\n version: \"1.0.98\",\n verifiedBy: \"help\",\n sources: [\n \"https://docs.github.com/en/copilot/how-tos/copilot-cli/automate-copilot-cli/run-cli-programmatically\",\n \"https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-command-reference\",\n \"https://www.npmjs.com/package/@github/copilot\",\n ],\n checkedAt: \"2026-10-17\",\n },\n plan(context) {\n const args = [\n \"--output-format\",\n \"json\",\n \"--stream\",\n \"on\",\n \"--no-ask-user\",\n \"--no-auto-update\",\n \"--usage-output-file\",\n path.join(context.runDir, USAGE_FILE),\n ];\n const requires = [\n \"--output-format\",\n \"--stream\",\n \"--no-ask-user\",\n \"--no-auto-update\",\n \"--usage-output-file\",\n ];\n const files: CliLaunchFile[] = [];\n\n if (context.model !== undefined) {\n args.push(\"--model\", context.model);\n requires.push(\"--model\");\n }\n\n if (context.effort !== undefined) {\n // Copilot accepts the framework scale verbatim: none … max.\n args.push(\"--reasoning-effort\", context.effort);\n requires.push(\"--reasoning-effort\");\n }\n\n if (context.resume !== undefined) {\n args.push(\"--session-id\", context.resume);\n requires.push(\"--session-id\");\n }\n\n if (context.permission === \"bypass\") {\n args.push(\"--allow-all\");\n requires.push(\"--allow-all\");\n }\n\n if (context.mcp !== undefined) {\n const file = path.join(context.runDir, \"copilot-mcp.json\");\n const servers = mcpServersJson(context.mcp, { typeField: true, extra: { tools: [\"*\"] } });\n\n for (const server of Object.values(servers)) {\n if (server.type === \"stdio\") {\n server.type = \"local\";\n }\n }\n\n files.push({ path: file, content: jsonFile({ mcpServers: servers }) });\n args.push(\"--additional-mcp-config\", `@${file}`);\n requires.push(\"--additional-mcp-config\");\n }\n\n return {\n args,\n tail: context.transport === \"argv\" ? [\"-p\", argvPrompt(context.prompt)] : [],\n stdin: context.transport === \"stdin\" ? context.prompt : undefined,\n env: context.home !== undefined ? { COPILOT_HOME: context.home } : undefined,\n files,\n requires: context.transport === \"argv\" ? [...requires, \"-p\"] : requires,\n };\n },\n parser: createCopilotParser,\n};\n\n/** Token usage summed over `modelMetrics` of the usage file. */\nfunction usageFileEvent(context: CliLaunchContext | undefined): CliParsedEvent | undefined {\n if (context === undefined) {\n return undefined;\n }\n\n let report: Record<string, unknown> | undefined;\n\n try {\n report = parseJsonLine(readFileSync(path.join(context.runDir, USAGE_FILE), \"utf8\"));\n } catch {\n return undefined;\n }\n\n const metrics = recordField(report, \"modelMetrics\");\n\n if (metrics === undefined) {\n return undefined;\n }\n\n let input = 0;\n let output = 0;\n let cached = 0;\n let written = 0;\n let reasoning = 0;\n\n for (const value of Object.values(metrics)) {\n const usage = recordField(asRecord(value), \"usage\");\n\n input += numberField(usage, \"inputTokens\") ?? 0;\n output += numberField(usage, \"outputTokens\") ?? 0;\n cached += numberField(usage, \"cacheReadTokens\") ?? 0;\n written += numberField(usage, \"cacheWriteTokens\") ?? 0;\n reasoning += numberField(usage, \"reasoningTokens\") ?? 0;\n }\n\n return {\n type: \"usage\",\n cumulative: true,\n usage: usageOf({\n input,\n output,\n cachedTokens: cached > 0 ? cached : undefined,\n cacheWriteTokens: written > 0 ? written : undefined,\n reasoningTokens: reasoning > 0 ? reasoning : undefined,\n }),\n raw: report,\n };\n}\n\n/** Parser for `copilot -p … --output-format json` (plus its usage file). */\nexport function createCopilotParser(context?: CliLaunchContext): CliStreamParser {\n let started = false;\n let sessionRef: string | undefined;\n\n const parser = jsonLinesParser(\n (record): CliParsedEvent[] => {\n const events: CliParsedEvent[] = [];\n const type = stringField(record, \"type\");\n const data = recordField(record, \"data\");\n\n if (!started) {\n started = true;\n events.push({\n type: \"session.started\",\n sessionRef: stringField(data, \"sessionId\"),\n raw: record,\n });\n }\n\n switch (type) {\n case \"assistant.message_delta\": {\n const text = stringField(data, \"deltaContent\") ?? \"\";\n\n if (text.length > 0) {\n events.push({ type: \"message\", text, delta: true, raw: record });\n }\n break;\n }\n case \"assistant.message\": {\n const text = stringField(data, \"content\") ?? \"\";\n\n if (text.length > 0) {\n events.push({ type: \"message\", text, delta: false, raw: record });\n }\n break;\n }\n case \"tool.execution_start\":\n events.push({\n type: \"tool.call\",\n id: stringField(data, \"toolCallId\") ?? \"\",\n name: stringField(data, \"toolName\") ?? \"\",\n input: data?.arguments,\n raw: record,\n });\n break;\n case \"tool.execution_complete\": {\n const success = data?.success === true;\n\n events.push({\n type: \"tool.result\",\n id: stringField(data, \"toolCallId\") ?? \"\",\n output: success\n ? outputText(recordField(data, \"result\")?.content)\n : (stringField(recordField(data, \"error\"), \"message\") ?? \"tool failed\"),\n isError: !success,\n raw: record,\n });\n break;\n }\n case \"session.error\":\n events.push({\n type: \"error\",\n message: stringField(data, \"message\") ?? \"Copilot session error\",\n fatal: true,\n raw: record,\n });\n break;\n case \"result\":\n sessionRef = stringField(record, \"sessionId\") ?? sessionRef;\n break;\n case \"error\":\n events.push({\n type: \"error\",\n message: stringField(record, \"message\") ?? \"error\",\n raw: record,\n });\n break;\n }\n\n return events;\n },\n () => {\n const usage = usageFileEvent(context);\n\n return usage !== undefined ? [usage] : [];\n },\n );\n\n return { ...parser, sessionRef: () => sessionRef };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CliAdapter, CliStreamParser } from "../contracts/adapter.contract.mjs";
|
|
2
|
+
//#region ../../packages/ai-cli/src/adapters/cursor.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Cursor CLI (`cursor-agent`, also installed as `agent`), installed by the
|
|
5
|
+
* `cursor.com/install` script (no official npm package).
|
|
6
|
+
*
|
|
7
|
+
* Verified against the official docs (`cursor.com/docs/cli/{headless,mcp}`,
|
|
8
|
+
* `/docs/cli/reference/{output-format,parameters,authentication,
|
|
9
|
+
* permissions,configuration}`) and the option strings of the
|
|
10
|
+
* 2026.10.16-e3cb207 release bundle (read, not executed), 2026-10-17. The
|
|
11
|
+
* capability probe checks every flag against the installed `--help`.
|
|
12
|
+
*
|
|
13
|
+
* - Headless: `cursor-agent -p --output-format stream-json --trust <prompt>`;
|
|
14
|
+
* with no positional prompt, stdin is the prompt. `--trust` is passed
|
|
15
|
+
* because print mode exits in a directory that was never trusted — the
|
|
16
|
+
* run's `cwd` is the caller's explicit choice.
|
|
17
|
+
* - Stream: `system/init` (`session_id`, `model`), `user` echo, `assistant`
|
|
18
|
+
* messages (`message.content[].text`), `tool_call` `started`/`completed`
|
|
19
|
+
* (`call_id`; the tool is the single key of `tool_call`, e.g.
|
|
20
|
+
* `readToolCall{args,result{success}}`), and a final `result` with
|
|
21
|
+
* `usage{inputTokens (net of cache), outputTokens, cacheReadTokens,
|
|
22
|
+
* cacheWriteTokens}`. Errors are not JSON: stderr and a non-zero exit.
|
|
23
|
+
* - Permissions: headless default is the allowlist mode (unlisted actions are
|
|
24
|
+
* rejected, changes only proposed); bypass is `--force`.
|
|
25
|
+
* - MCP: no flag; `$HOME/.cursor/mcp.json` (the path ignores
|
|
26
|
+
* `CURSOR_CONFIG_DIR`), plus `--approve-mcps` — needs a private home.
|
|
27
|
+
* - Home: `HOME`/`USERPROFILE`, `CURSOR_CONFIG_DIR` and `CURSOR_DATA_DIR`
|
|
28
|
+
* are pointed into it. Auth: `CURSOR_API_KEY`.
|
|
29
|
+
*/
|
|
30
|
+
declare const cursorAdapter: CliAdapter;
|
|
31
|
+
/** Parser for `cursor-agent -p --output-format stream-json`. */
|
|
32
|
+
declare function createCursorParser(): CliStreamParser;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { createCursorParser, cursorAdapter };
|
|
35
|
+
//# sourceMappingURL=cursor.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.d.mts","names":[],"sources":["../../../../../../../../packages/ai-cli/src/adapters/cursor.ts"],"mappings":";;;;;AA+CA;;;;AAwEC;AAmBD;;;;AAAqD;;;;;;;;;;;;;;;cA3FxC,aAAA,EAAe,UAwE3B;;iBAmBe,kBAAA,IAAsB,eAAe"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { asArray, asRecord, jsonLinesParser, numberField, outputText, recordField, stringField } from "../stream/json-lines.mjs";
|
|
2
|
+
import { argvPrompt, jsonFile, mcpServersJson, parseArguments, requirePrivateHome, usageOf } from "./shared.mjs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
//#region ../../packages/ai-cli/src/adapters/cursor.ts
|
|
6
|
+
/**
|
|
7
|
+
* Cursor CLI (`cursor-agent`, also installed as `agent`), installed by the
|
|
8
|
+
* `cursor.com/install` script (no official npm package).
|
|
9
|
+
*
|
|
10
|
+
* Verified against the official docs (`cursor.com/docs/cli/{headless,mcp}`,
|
|
11
|
+
* `/docs/cli/reference/{output-format,parameters,authentication,
|
|
12
|
+
* permissions,configuration}`) and the option strings of the
|
|
13
|
+
* 2026.10.16-e3cb207 release bundle (read, not executed), 2026-10-17. The
|
|
14
|
+
* capability probe checks every flag against the installed `--help`.
|
|
15
|
+
*
|
|
16
|
+
* - Headless: `cursor-agent -p --output-format stream-json --trust <prompt>`;
|
|
17
|
+
* with no positional prompt, stdin is the prompt. `--trust` is passed
|
|
18
|
+
* because print mode exits in a directory that was never trusted — the
|
|
19
|
+
* run's `cwd` is the caller's explicit choice.
|
|
20
|
+
* - Stream: `system/init` (`session_id`, `model`), `user` echo, `assistant`
|
|
21
|
+
* messages (`message.content[].text`), `tool_call` `started`/`completed`
|
|
22
|
+
* (`call_id`; the tool is the single key of `tool_call`, e.g.
|
|
23
|
+
* `readToolCall{args,result{success}}`), and a final `result` with
|
|
24
|
+
* `usage{inputTokens (net of cache), outputTokens, cacheReadTokens,
|
|
25
|
+
* cacheWriteTokens}`. Errors are not JSON: stderr and a non-zero exit.
|
|
26
|
+
* - Permissions: headless default is the allowlist mode (unlisted actions are
|
|
27
|
+
* rejected, changes only proposed); bypass is `--force`.
|
|
28
|
+
* - MCP: no flag; `$HOME/.cursor/mcp.json` (the path ignores
|
|
29
|
+
* `CURSOR_CONFIG_DIR`), plus `--approve-mcps` — needs a private home.
|
|
30
|
+
* - Home: `HOME`/`USERPROFILE`, `CURSOR_CONFIG_DIR` and `CURSOR_DATA_DIR`
|
|
31
|
+
* are pointed into it. Auth: `CURSOR_API_KEY`.
|
|
32
|
+
*/
|
|
33
|
+
const cursorAdapter = {
|
|
34
|
+
name: "cursor",
|
|
35
|
+
title: "Cursor CLI",
|
|
36
|
+
command: "cursor-agent",
|
|
37
|
+
apiKeyEnv: ["CURSOR_API_KEY"],
|
|
38
|
+
homeEnv: [
|
|
39
|
+
"HOME",
|
|
40
|
+
"USERPROFILE",
|
|
41
|
+
"CURSOR_CONFIG_DIR",
|
|
42
|
+
"CURSOR_DATA_DIR"
|
|
43
|
+
],
|
|
44
|
+
transports: ["argv", "stdin"],
|
|
45
|
+
effort: false,
|
|
46
|
+
mcp: true,
|
|
47
|
+
probe: {
|
|
48
|
+
help: [["--help"]],
|
|
49
|
+
version: ["--version"]
|
|
50
|
+
},
|
|
51
|
+
verification: {
|
|
52
|
+
version: "2026.10.16-e3cb207",
|
|
53
|
+
verifiedBy: "docs",
|
|
54
|
+
sources: [
|
|
55
|
+
"https://cursor.com/docs/cli/headless",
|
|
56
|
+
"https://cursor.com/docs/cli/reference/output-format",
|
|
57
|
+
"https://cursor.com/docs/cli/reference/parameters",
|
|
58
|
+
"https://cursor.com/docs/cli/mcp"
|
|
59
|
+
],
|
|
60
|
+
checkedAt: "2026-10-17"
|
|
61
|
+
},
|
|
62
|
+
plan(context) {
|
|
63
|
+
const args = [
|
|
64
|
+
"--print",
|
|
65
|
+
"--output-format",
|
|
66
|
+
"stream-json",
|
|
67
|
+
"--trust"
|
|
68
|
+
];
|
|
69
|
+
const requires = [
|
|
70
|
+
"--print",
|
|
71
|
+
"--output-format",
|
|
72
|
+
"stream-json",
|
|
73
|
+
"--trust"
|
|
74
|
+
];
|
|
75
|
+
const files = [];
|
|
76
|
+
if (context.model !== void 0) {
|
|
77
|
+
args.push("--model", context.model);
|
|
78
|
+
requires.push("--model");
|
|
79
|
+
}
|
|
80
|
+
if (context.resume !== void 0) {
|
|
81
|
+
args.push("--resume", context.resume);
|
|
82
|
+
requires.push("--resume");
|
|
83
|
+
}
|
|
84
|
+
if (context.permission === "bypass") {
|
|
85
|
+
args.push("--force");
|
|
86
|
+
requires.push("--force");
|
|
87
|
+
}
|
|
88
|
+
if (context.mcp !== void 0) {
|
|
89
|
+
const home = requirePrivateHome(context, cursorAdapter, "MCP servers");
|
|
90
|
+
files.push({
|
|
91
|
+
path: path.join(home, ".cursor", "mcp.json"),
|
|
92
|
+
content: jsonFile({ mcpServers: mcpServersJson(context.mcp, { typeField: true }) })
|
|
93
|
+
});
|
|
94
|
+
args.push("--approve-mcps");
|
|
95
|
+
requires.push("--approve-mcps");
|
|
96
|
+
}
|
|
97
|
+
const env = context.home !== void 0 ? {
|
|
98
|
+
HOME: context.home,
|
|
99
|
+
USERPROFILE: context.home,
|
|
100
|
+
CURSOR_CONFIG_DIR: path.join(context.home, ".cursor"),
|
|
101
|
+
CURSOR_DATA_DIR: path.join(context.home, ".cursor")
|
|
102
|
+
} : {};
|
|
103
|
+
return {
|
|
104
|
+
args,
|
|
105
|
+
tail: context.transport === "argv" ? [argvPrompt(context.prompt)] : [],
|
|
106
|
+
stdin: context.transport === "stdin" ? context.prompt : void 0,
|
|
107
|
+
env,
|
|
108
|
+
files,
|
|
109
|
+
requires
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
parser: createCursorParser
|
|
113
|
+
};
|
|
114
|
+
/** `readToolCall` → `read`; the documented `function{name}` fallback is used as-is. */
|
|
115
|
+
function toolOf(toolCall) {
|
|
116
|
+
const fn = recordField(toolCall, "function");
|
|
117
|
+
if (fn !== void 0) return {
|
|
118
|
+
name: stringField(fn, "name") ?? "function",
|
|
119
|
+
body: fn
|
|
120
|
+
};
|
|
121
|
+
const key = Object.keys(toolCall ?? {})[0] ?? "tool";
|
|
122
|
+
return {
|
|
123
|
+
name: key.replace(/ToolCall$/, ""),
|
|
124
|
+
body: recordField(toolCall, key)
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** Parser for `cursor-agent -p --output-format stream-json`. */
|
|
128
|
+
function createCursorParser() {
|
|
129
|
+
return jsonLinesParser((record) => {
|
|
130
|
+
switch (stringField(record, "type")) {
|
|
131
|
+
case "system": return stringField(record, "subtype") === "init" ? [{
|
|
132
|
+
type: "session.started",
|
|
133
|
+
sessionRef: stringField(record, "session_id"),
|
|
134
|
+
model: stringField(record, "model"),
|
|
135
|
+
raw: record
|
|
136
|
+
}] : [];
|
|
137
|
+
case "assistant": {
|
|
138
|
+
const text = asArray(recordField(record, "message")?.content).map((block) => stringField(asRecord(block), "text") ?? "").join("");
|
|
139
|
+
return text.length > 0 ? [{
|
|
140
|
+
type: "message",
|
|
141
|
+
text,
|
|
142
|
+
delta: false,
|
|
143
|
+
raw: record
|
|
144
|
+
}] : [];
|
|
145
|
+
}
|
|
146
|
+
case "tool_call": {
|
|
147
|
+
const id = stringField(record, "call_id") ?? "";
|
|
148
|
+
const { name, body } = toolOf(recordField(record, "tool_call"));
|
|
149
|
+
if (stringField(record, "subtype") === "started") return [{
|
|
150
|
+
type: "tool.call",
|
|
151
|
+
id,
|
|
152
|
+
name,
|
|
153
|
+
input: body?.args ?? parseArguments(body?.arguments),
|
|
154
|
+
raw: record
|
|
155
|
+
}];
|
|
156
|
+
if (stringField(record, "subtype") === "completed") {
|
|
157
|
+
const result = recordField(body, "result");
|
|
158
|
+
const success = recordField(result, "success");
|
|
159
|
+
const content = stringField(success, "content");
|
|
160
|
+
return [{
|
|
161
|
+
type: "tool.result",
|
|
162
|
+
id,
|
|
163
|
+
output: success !== void 0 ? content ?? outputText(success) : outputText(result),
|
|
164
|
+
isError: success === void 0,
|
|
165
|
+
raw: record
|
|
166
|
+
}];
|
|
167
|
+
}
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
case "result": {
|
|
171
|
+
const events = [];
|
|
172
|
+
const usage = recordField(record, "usage");
|
|
173
|
+
if (usage !== void 0) {
|
|
174
|
+
const cacheRead = numberField(usage, "cacheReadTokens") ?? 0;
|
|
175
|
+
const cacheWrite = numberField(usage, "cacheWriteTokens") ?? 0;
|
|
176
|
+
events.push({
|
|
177
|
+
type: "usage",
|
|
178
|
+
cumulative: true,
|
|
179
|
+
usage: usageOf({
|
|
180
|
+
input: (numberField(usage, "inputTokens") ?? 0) + cacheRead + cacheWrite,
|
|
181
|
+
output: numberField(usage, "outputTokens"),
|
|
182
|
+
cachedTokens: cacheRead > 0 ? cacheRead : void 0,
|
|
183
|
+
cacheWriteTokens: cacheWrite > 0 ? cacheWrite : void 0
|
|
184
|
+
}),
|
|
185
|
+
raw: record
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
if (record.is_error === true) events.push({
|
|
189
|
+
type: "error",
|
|
190
|
+
message: stringField(record, "result") || "Cursor CLI reported an error",
|
|
191
|
+
fatal: true,
|
|
192
|
+
raw: record
|
|
193
|
+
});
|
|
194
|
+
return events;
|
|
195
|
+
}
|
|
196
|
+
default: return [];
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
//#endregion
|
|
202
|
+
export { createCursorParser, cursorAdapter };
|
|
203
|
+
//# sourceMappingURL=cursor.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.mjs","names":[],"sources":["../../../../../../../../packages/ai-cli/src/adapters/cursor.ts"],"sourcesContent":["import path from \"node:path\";\nimport type { CliAdapter, CliLaunchFile, CliParsedEvent, CliStreamParser } from \"../contracts\";\nimport {\n asArray,\n asRecord,\n jsonLinesParser,\n numberField,\n outputText,\n recordField,\n stringField,\n} from \"../stream/json-lines\";\nimport {\n argvPrompt,\n jsonFile,\n mcpServersJson,\n parseArguments,\n requirePrivateHome,\n usageOf,\n} from \"./shared\";\n\n/**\n * Cursor CLI (`cursor-agent`, also installed as `agent`), installed by the\n * `cursor.com/install` script (no official npm package).\n *\n * Verified against the official docs (`cursor.com/docs/cli/{headless,mcp}`,\n * `/docs/cli/reference/{output-format,parameters,authentication,\n * permissions,configuration}`) and the option strings of the\n * 2026.10.16-e3cb207 release bundle (read, not executed), 2026-10-17. The\n * capability probe checks every flag against the installed `--help`.\n *\n * - Headless: `cursor-agent -p --output-format stream-json --trust <prompt>`;\n * with no positional prompt, stdin is the prompt. `--trust` is passed\n * because print mode exits in a directory that was never trusted — the\n * run's `cwd` is the caller's explicit choice.\n * - Stream: `system/init` (`session_id`, `model`), `user` echo, `assistant`\n * messages (`message.content[].text`), `tool_call` `started`/`completed`\n * (`call_id`; the tool is the single key of `tool_call`, e.g.\n * `readToolCall{args,result{success}}`), and a final `result` with\n * `usage{inputTokens (net of cache), outputTokens, cacheReadTokens,\n * cacheWriteTokens}`. Errors are not JSON: stderr and a non-zero exit.\n * - Permissions: headless default is the allowlist mode (unlisted actions are\n * rejected, changes only proposed); bypass is `--force`.\n * - MCP: no flag; `$HOME/.cursor/mcp.json` (the path ignores\n * `CURSOR_CONFIG_DIR`), plus `--approve-mcps` — needs a private home.\n * - Home: `HOME`/`USERPROFILE`, `CURSOR_CONFIG_DIR` and `CURSOR_DATA_DIR`\n * are pointed into it. Auth: `CURSOR_API_KEY`.\n */\nexport const cursorAdapter: CliAdapter = {\n name: \"cursor\",\n title: \"Cursor CLI\",\n command: \"cursor-agent\",\n apiKeyEnv: [\"CURSOR_API_KEY\"],\n homeEnv: [\"HOME\", \"USERPROFILE\", \"CURSOR_CONFIG_DIR\", \"CURSOR_DATA_DIR\"],\n transports: [\"argv\", \"stdin\"],\n effort: false,\n mcp: true,\n probe: { help: [[\"--help\"]], version: [\"--version\"] },\n verification: {\n version: \"2026.10.16-e3cb207\",\n verifiedBy: \"docs\",\n sources: [\n \"https://cursor.com/docs/cli/headless\",\n \"https://cursor.com/docs/cli/reference/output-format\",\n \"https://cursor.com/docs/cli/reference/parameters\",\n \"https://cursor.com/docs/cli/mcp\",\n ],\n checkedAt: \"2026-10-17\",\n },\n plan(context) {\n const args = [\"--print\", \"--output-format\", \"stream-json\", \"--trust\"];\n const requires = [\"--print\", \"--output-format\", \"stream-json\", \"--trust\"];\n const files: CliLaunchFile[] = [];\n\n if (context.model !== undefined) {\n args.push(\"--model\", context.model);\n requires.push(\"--model\");\n }\n\n if (context.resume !== undefined) {\n args.push(\"--resume\", context.resume);\n requires.push(\"--resume\");\n }\n\n if (context.permission === \"bypass\") {\n args.push(\"--force\");\n requires.push(\"--force\");\n }\n\n if (context.mcp !== undefined) {\n const home = requirePrivateHome(context, cursorAdapter, \"MCP servers\");\n\n files.push({\n path: path.join(home, \".cursor\", \"mcp.json\"),\n content: jsonFile({ mcpServers: mcpServersJson(context.mcp, { typeField: true }) }),\n });\n args.push(\"--approve-mcps\");\n requires.push(\"--approve-mcps\");\n }\n\n const env: Record<string, string> =\n context.home !== undefined\n ? {\n HOME: context.home,\n USERPROFILE: context.home,\n CURSOR_CONFIG_DIR: path.join(context.home, \".cursor\"),\n CURSOR_DATA_DIR: path.join(context.home, \".cursor\"),\n }\n : {};\n\n return {\n args,\n tail: context.transport === \"argv\" ? [argvPrompt(context.prompt)] : [],\n stdin: context.transport === \"stdin\" ? context.prompt : undefined,\n env,\n files,\n requires,\n };\n },\n parser: createCursorParser,\n};\n\n/** `readToolCall` → `read`; the documented `function{name}` fallback is used as-is. */\nfunction toolOf(toolCall: Record<string, unknown> | undefined): {\n name: string;\n body: Record<string, unknown> | undefined;\n} {\n const fn = recordField(toolCall, \"function\");\n\n if (fn !== undefined) {\n return { name: stringField(fn, \"name\") ?? \"function\", body: fn };\n }\n\n const key = Object.keys(toolCall ?? {})[0] ?? \"tool\";\n\n return { name: key.replace(/ToolCall$/, \"\"), body: recordField(toolCall, key) };\n}\n\n/** Parser for `cursor-agent -p --output-format stream-json`. */\nexport function createCursorParser(): CliStreamParser {\n return jsonLinesParser((record): CliParsedEvent[] => {\n switch (stringField(record, \"type\")) {\n case \"system\":\n return stringField(record, \"subtype\") === \"init\"\n ? [\n {\n type: \"session.started\",\n sessionRef: stringField(record, \"session_id\"),\n model: stringField(record, \"model\"),\n raw: record,\n },\n ]\n : [];\n case \"assistant\": {\n const text = asArray(recordField(record, \"message\")?.content)\n .map((block) => stringField(asRecord(block), \"text\") ?? \"\")\n .join(\"\");\n\n return text.length > 0 ? [{ type: \"message\", text, delta: false, raw: record }] : [];\n }\n case \"tool_call\": {\n const id = stringField(record, \"call_id\") ?? \"\";\n const { name, body } = toolOf(recordField(record, \"tool_call\"));\n\n if (stringField(record, \"subtype\") === \"started\") {\n return [\n {\n type: \"tool.call\",\n id,\n name,\n input: body?.args ?? parseArguments(body?.arguments),\n raw: record,\n },\n ];\n }\n\n if (stringField(record, \"subtype\") === \"completed\") {\n const result = recordField(body, \"result\");\n const success = recordField(result, \"success\");\n const content = stringField(success, \"content\");\n\n return [\n {\n type: \"tool.result\",\n id,\n output: success !== undefined ? (content ?? outputText(success)) : outputText(result),\n isError: success === undefined,\n raw: record,\n },\n ];\n }\n\n return [];\n }\n case \"result\": {\n const events: CliParsedEvent[] = [];\n const usage = recordField(record, \"usage\");\n\n if (usage !== undefined) {\n const cacheRead = numberField(usage, \"cacheReadTokens\") ?? 0;\n const cacheWrite = numberField(usage, \"cacheWriteTokens\") ?? 0;\n\n events.push({\n type: \"usage\",\n cumulative: true,\n usage: usageOf({\n input: (numberField(usage, \"inputTokens\") ?? 0) + cacheRead + cacheWrite,\n output: numberField(usage, \"outputTokens\"),\n cachedTokens: cacheRead > 0 ? cacheRead : undefined,\n cacheWriteTokens: cacheWrite > 0 ? cacheWrite : undefined,\n }),\n raw: record,\n });\n }\n\n if (record.is_error === true) {\n events.push({\n type: \"error\",\n message: stringField(record, \"result\") || \"Cursor CLI reported an error\",\n fatal: true,\n raw: record,\n });\n }\n\n // `result` concatenates every assistant message, so the final answer\n // is taken from the last assistant message instead.\n return events;\n }\n default:\n return [];\n }\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { CliAdapter, CliStreamParser } from "../contracts/adapter.contract.mjs";
|
|
2
|
+
//#region ../../packages/ai-cli/src/adapters/droid.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Factory Droid (`droid`, npm `droid` / `@factory/cli`).
|
|
5
|
+
*
|
|
6
|
+
* Verified against the real `droid exec --help` of 0.244.0, a real
|
|
7
|
+
* `droid exec` run without credentials (init and error lines), the field
|
|
8
|
+
* names in the shipped code and the docs (`docs.factory.ai/cli/droid-exec`,
|
|
9
|
+
* `/cli/configuration/mcp`), 2026-10-17.
|
|
10
|
+
*
|
|
11
|
+
* - Headless: `droid exec -o stream-json <prompt>` (or `-f <file>`).
|
|
12
|
+
* - Stream: `system/init` (`session_id`, `model`), whole `message` records
|
|
13
|
+
* (`role`, `text`), `tool_call` (`id`, `toolName`, `parameters`),
|
|
14
|
+
* `tool_result` (`id`, `isError`, `value` | `error.message`), a final
|
|
15
|
+
* `completion` (`finalText`, `usage` with Anthropic-style
|
|
16
|
+
* `input_tokens`/`cache_read_input_tokens`/`cache_creation_input_tokens`,
|
|
17
|
+
* read with Anthropic semantics) and `error` (`source`, `message`).
|
|
18
|
+
* Billing is in Factory credits; no USD cost is reported.
|
|
19
|
+
* - Permissions: `standard` is Droid's default **read-only** autonomy; raise
|
|
20
|
+
* it with `args: ["--auto", "medium"]` (low | medium | high). Bypass is
|
|
21
|
+
* `--skip-permissions-unsafe`. Blocked actions end the run non-zero.
|
|
22
|
+
* - MCP: `<home>/.factory/mcp.json` (`mcpServers`); the home is relocated
|
|
23
|
+
* with `FACTORY_HOME_OVERRIDE` (found in the shipped code and confirmed
|
|
24
|
+
* with `droid mcp list`), so per-run MCP needs a private home.
|
|
25
|
+
* - Auth: `FACTORY_API_KEY`.
|
|
26
|
+
*/
|
|
27
|
+
declare const droidAdapter: CliAdapter;
|
|
28
|
+
/** Parser for `droid exec -o stream-json`. */
|
|
29
|
+
declare function createDroidParser(): CliStreamParser;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { createDroidParser, droidAdapter };
|
|
32
|
+
//# sourceMappingURL=droid.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"droid.d.mts","names":[],"sources":["../../../../../../../../packages/ai-cli/src/adapters/droid.ts"],"mappings":";;;;;AAoCA;;;;AA8EC;AAGD;;;;AAAoD;;;;;;;;;;;;cAjFvC,YAAA,EAAc,UA8E1B;;iBAGe,iBAAA,IAAqB,eAAe"}
|