terminal-pilot 0.0.6 → 0.0.8

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.
Files changed (51) hide show
  1. package/dist/cli.js +5663 -1269
  2. package/dist/cli.js.map +4 -4
  3. package/dist/commands/close-session.js +268 -87
  4. package/dist/commands/close-session.js.map +4 -4
  5. package/dist/commands/create-session.js +268 -87
  6. package/dist/commands/create-session.js.map +4 -4
  7. package/dist/commands/fill.js +268 -87
  8. package/dist/commands/fill.js.map +4 -4
  9. package/dist/commands/get-session.js +268 -87
  10. package/dist/commands/get-session.js.map +4 -4
  11. package/dist/commands/index.js +2404 -279
  12. package/dist/commands/index.js.map +4 -4
  13. package/dist/commands/install.js +1372 -14
  14. package/dist/commands/install.js.map +4 -4
  15. package/dist/commands/installer.js +178 -15
  16. package/dist/commands/installer.js.map +4 -4
  17. package/dist/commands/list-sessions.js +268 -87
  18. package/dist/commands/list-sessions.js.map +4 -4
  19. package/dist/commands/press-key.js +268 -87
  20. package/dist/commands/press-key.js.map +4 -4
  21. package/dist/commands/read-history.js +268 -87
  22. package/dist/commands/read-history.js.map +4 -4
  23. package/dist/commands/read-screen.js +268 -87
  24. package/dist/commands/read-screen.js.map +4 -4
  25. package/dist/commands/resize.js +268 -87
  26. package/dist/commands/resize.js.map +4 -4
  27. package/dist/commands/runtime.js +27 -85
  28. package/dist/commands/runtime.js.map +4 -4
  29. package/dist/commands/screenshot.js +1033 -91
  30. package/dist/commands/screenshot.js.map +4 -4
  31. package/dist/commands/send-signal.js +268 -87
  32. package/dist/commands/send-signal.js.map +4 -4
  33. package/dist/commands/type.js +268 -87
  34. package/dist/commands/type.js.map +4 -4
  35. package/dist/commands/uninstall.js +419 -17
  36. package/dist/commands/uninstall.js.map +4 -4
  37. package/dist/commands/wait-for-exit.js +268 -87
  38. package/dist/commands/wait-for-exit.js.map +4 -4
  39. package/dist/commands/wait-for.js +268 -87
  40. package/dist/commands/wait-for.js.map +4 -4
  41. package/dist/index.js +19 -83
  42. package/dist/index.js.map +3 -3
  43. package/dist/terminal-pilot.js +19 -83
  44. package/dist/terminal-pilot.js.map +3 -3
  45. package/dist/terminal-session.js +19 -83
  46. package/dist/terminal-session.js.map +3 -3
  47. package/dist/testing/cli-repl.js +5598 -1203
  48. package/dist/testing/cli-repl.js.map +4 -4
  49. package/dist/testing/qa-cli.js +5601 -1206
  50. package/dist/testing/qa-cli.js.map +4 -4
  51. package/package.json +17 -8
@@ -1,18 +1,351 @@
1
- // src/commands/index.ts
2
- import { defineGroup } from "@poe-code/cmdkit";
1
+ // ../cmdkit/src/index.ts
2
+ import { fileURLToPath } from "node:url";
3
3
 
4
- // src/commands/close-session.ts
5
- import { defineCommand, S } from "@poe-code/cmdkit";
4
+ // ../cmdkit-schema/src/index.ts
5
+ function assertValidEnumValues(values) {
6
+ if (values.length === 0) {
7
+ throw new Error("Enum schema requires at least one value");
8
+ }
9
+ const uniqueValues = new Set(values);
10
+ if (uniqueValues.size !== values.length) {
11
+ throw new Error("Enum schema values must be unique");
12
+ }
13
+ }
14
+ var S = {
15
+ String(options = {}) {
16
+ return {
17
+ kind: "string",
18
+ ...options
19
+ };
20
+ },
21
+ Number(options = {}) {
22
+ return {
23
+ kind: "number",
24
+ ...options
25
+ };
26
+ },
27
+ Boolean(options = {}) {
28
+ return {
29
+ kind: "boolean",
30
+ ...options
31
+ };
32
+ },
33
+ Enum(values, options = {}) {
34
+ assertValidEnumValues(values);
35
+ return {
36
+ kind: "enum",
37
+ values,
38
+ ...options
39
+ };
40
+ },
41
+ Array(item, options = {}) {
42
+ return {
43
+ kind: "array",
44
+ item,
45
+ ...options
46
+ };
47
+ },
48
+ Object(shape) {
49
+ return {
50
+ kind: "object",
51
+ shape
52
+ };
53
+ },
54
+ Optional(inner) {
55
+ return {
56
+ kind: "optional",
57
+ inner
58
+ };
59
+ }
60
+ };
6
61
 
7
- // src/commands/runtime.ts
8
- import { UserError } from "@poe-code/cmdkit";
62
+ // ../cmdkit/src/index.ts
63
+ var commandConfigSymbol = /* @__PURE__ */ Symbol("cmdkit.command.config");
64
+ var groupConfigSymbol = /* @__PURE__ */ Symbol("cmdkit.group.config");
65
+ var commandSourcePathSymbol = /* @__PURE__ */ Symbol("cmdkit.command.sourcePath");
66
+ var UserError = class extends Error {
67
+ constructor(message) {
68
+ super(message);
69
+ this.name = "UserError";
70
+ }
71
+ };
72
+ function cloneScope(scope) {
73
+ return scope === void 0 ? void 0 : [...scope];
74
+ }
75
+ function cloneSecretDefinition(secret) {
76
+ return {
77
+ env: secret.env,
78
+ description: secret.description,
79
+ optional: secret.optional
80
+ };
81
+ }
82
+ function cloneSecrets(secrets) {
83
+ if (secrets === void 0) {
84
+ return {};
85
+ }
86
+ return Object.fromEntries(
87
+ Object.entries(secrets).map(([key, secret]) => [key, cloneSecretDefinition(secret)])
88
+ );
89
+ }
90
+ function cloneRequires(requires) {
91
+ if (requires === void 0) {
92
+ return void 0;
93
+ }
94
+ return {
95
+ auth: requires.auth,
96
+ apiVersion: requires.apiVersion,
97
+ check: requires.check
98
+ };
99
+ }
100
+ function parseStackPath(candidate) {
101
+ if (candidate.startsWith("file://")) {
102
+ try {
103
+ return fileURLToPath(candidate);
104
+ } catch {
105
+ return void 0;
106
+ }
107
+ }
108
+ if (candidate.startsWith("/")) {
109
+ return candidate;
110
+ }
111
+ return void 0;
112
+ }
113
+ function extractStackPath(line) {
114
+ const trimmed = line.trim();
115
+ const fileIndex = trimmed.indexOf("file://");
116
+ if (fileIndex >= 0) {
117
+ const location2 = trimmed.slice(fileIndex);
118
+ const separatorIndex2 = location2.lastIndexOf(":");
119
+ const previousSeparatorIndex2 = separatorIndex2 >= 0 ? location2.lastIndexOf(":", separatorIndex2 - 1) : -1;
120
+ const candidate2 = separatorIndex2 >= 0 && previousSeparatorIndex2 >= 0 ? location2.slice(0, previousSeparatorIndex2) : location2;
121
+ return parseStackPath(candidate2);
122
+ }
123
+ const slashIndex = trimmed.indexOf("/");
124
+ if (slashIndex < 0) {
125
+ return void 0;
126
+ }
127
+ const location = trimmed.slice(slashIndex);
128
+ const separatorIndex = location.lastIndexOf(":");
129
+ const previousSeparatorIndex = separatorIndex >= 0 ? location.lastIndexOf(":", separatorIndex - 1) : -1;
130
+ const candidate = separatorIndex >= 0 && previousSeparatorIndex >= 0 ? location.slice(0, previousSeparatorIndex) : location;
131
+ return parseStackPath(candidate);
132
+ }
133
+ function inferCommandSourcePath() {
134
+ const stack = new Error().stack;
135
+ if (typeof stack !== "string") {
136
+ return void 0;
137
+ }
138
+ for (const line of stack.split("\n").slice(1)) {
139
+ const candidate = extractStackPath(line);
140
+ if (candidate === void 0) {
141
+ continue;
142
+ }
143
+ if (candidate.includes("/packages/cmdkit/src/index.ts") || candidate.includes("/packages/cmdkit/dist/index.js")) {
144
+ continue;
145
+ }
146
+ return candidate;
147
+ }
148
+ return void 0;
149
+ }
150
+ function composeChecks(parentCheck, childCheck) {
151
+ if (parentCheck === void 0) {
152
+ return childCheck;
153
+ }
154
+ if (childCheck === void 0) {
155
+ return parentCheck;
156
+ }
157
+ return async (ctx) => {
158
+ const parentResult = await parentCheck(ctx);
159
+ if (!parentResult.ok) {
160
+ return parentResult;
161
+ }
162
+ return childCheck(ctx);
163
+ };
164
+ }
165
+ function mergeRequires(parent, child) {
166
+ if (parent === void 0 && child === void 0) {
167
+ return void 0;
168
+ }
169
+ const merged = {
170
+ auth: child?.auth ?? parent?.auth,
171
+ apiVersion: child?.apiVersion ?? parent?.apiVersion,
172
+ check: composeChecks(parent?.check, child?.check)
173
+ };
174
+ if (merged.auth === void 0 && merged.apiVersion === void 0 && merged.check === void 0) {
175
+ return void 0;
176
+ }
177
+ return merged;
178
+ }
179
+ function mergeSecrets(parent, child) {
180
+ return cloneSecrets({
181
+ ...parent,
182
+ ...child
183
+ });
184
+ }
185
+ function resolveCommandScope(ownScope, inheritedScope) {
186
+ return cloneScope(ownScope ?? inheritedScope) ?? ["cli", "sdk"];
187
+ }
188
+ function resolveGroupScope(ownScope, inheritedScope) {
189
+ return cloneScope(ownScope ?? inheritedScope);
190
+ }
191
+ function createBaseCommand(config) {
192
+ const command = {
193
+ kind: "command",
194
+ name: config.name,
195
+ description: config.description,
196
+ aliases: [...config.aliases ?? []],
197
+ positional: [...config.positional ?? []],
198
+ params: config.params,
199
+ secrets: cloneSecrets(config.secrets),
200
+ scope: resolveCommandScope(config.scope, void 0),
201
+ confirm: config.confirm ?? false,
202
+ requires: cloneRequires(config.requires),
203
+ handler: config.handler,
204
+ render: config.render
205
+ };
206
+ Object.defineProperty(command, commandConfigSymbol, {
207
+ value: {
208
+ scope: cloneScope(config.scope),
209
+ secrets: cloneSecrets(config.secrets),
210
+ requires: cloneRequires(config.requires),
211
+ sourcePath: inferCommandSourcePath()
212
+ }
213
+ });
214
+ return command;
215
+ }
216
+ function createBaseGroup(config) {
217
+ const group = {
218
+ kind: "group",
219
+ name: config.name,
220
+ description: config.description,
221
+ aliases: [...config.aliases ?? []],
222
+ scope: resolveGroupScope(config.scope, void 0),
223
+ secrets: cloneSecrets(config.secrets),
224
+ requires: cloneRequires(config.requires),
225
+ children: [],
226
+ default: void 0
227
+ };
228
+ Object.defineProperty(group, groupConfigSymbol, {
229
+ value: {
230
+ scope: cloneScope(config.scope),
231
+ secrets: cloneSecrets(config.secrets),
232
+ requires: cloneRequires(config.requires),
233
+ children: [...config.children],
234
+ default: config.default
235
+ }
236
+ });
237
+ return group;
238
+ }
239
+ function getInternalCommandConfig(command) {
240
+ return command[commandConfigSymbol];
241
+ }
242
+ function getInternalGroupConfig(group) {
243
+ return group[groupConfigSymbol];
244
+ }
245
+ function materializeCommand(command, inherited) {
246
+ const internal = getInternalCommandConfig(command);
247
+ const materialized = {
248
+ kind: "command",
249
+ name: command.name,
250
+ description: command.description,
251
+ aliases: [...command.aliases],
252
+ positional: [...command.positional],
253
+ params: command.params,
254
+ secrets: mergeSecrets(inherited.secrets, internal.secrets),
255
+ scope: resolveCommandScope(internal.scope, inherited.scope),
256
+ confirm: command.confirm,
257
+ requires: mergeRequires(inherited.requires, internal.requires),
258
+ handler: command.handler,
259
+ render: command.render
260
+ };
261
+ Object.defineProperty(materialized, commandConfigSymbol, {
262
+ value: {
263
+ scope: cloneScope(internal.scope),
264
+ secrets: cloneSecrets(internal.secrets),
265
+ requires: cloneRequires(internal.requires),
266
+ sourcePath: internal.sourcePath
267
+ }
268
+ });
269
+ Object.defineProperty(materialized, commandSourcePathSymbol, {
270
+ value: internal.sourcePath
271
+ });
272
+ return materialized;
273
+ }
274
+ function materializeGroup(group, inherited) {
275
+ const internal = getInternalGroupConfig(group);
276
+ const scope = resolveGroupScope(internal.scope, inherited.scope);
277
+ const secrets = mergeSecrets(inherited.secrets, internal.secrets);
278
+ const requires = mergeRequires(inherited.requires, internal.requires);
279
+ const materializedChildren = internal.children.map(
280
+ (child) => materializeNode(child, {
281
+ scope,
282
+ secrets,
283
+ requires
284
+ })
285
+ );
286
+ let defaultChild;
287
+ if (internal.default !== void 0) {
288
+ const defaultIndex = internal.children.indexOf(internal.default);
289
+ if (defaultIndex === -1) {
290
+ throw new UserError(`Default command "${internal.default.name}" must be listed in children.`);
291
+ }
292
+ const resolvedDefault = materializedChildren[defaultIndex];
293
+ if (resolvedDefault?.kind !== "command") {
294
+ throw new UserError(`Default child "${internal.default.name}" must be a command.`);
295
+ }
296
+ defaultChild = resolvedDefault;
297
+ }
298
+ const materialized = {
299
+ kind: "group",
300
+ name: group.name,
301
+ description: group.description,
302
+ aliases: [...group.aliases],
303
+ scope,
304
+ secrets,
305
+ requires,
306
+ children: materializedChildren,
307
+ default: defaultChild
308
+ };
309
+ Object.defineProperty(materialized, groupConfigSymbol, {
310
+ value: {
311
+ scope: cloneScope(internal.scope),
312
+ secrets: cloneSecrets(internal.secrets),
313
+ requires: cloneRequires(internal.requires),
314
+ children: [...internal.children],
315
+ default: internal.default
316
+ }
317
+ });
318
+ return materialized;
319
+ }
320
+ function materializeNode(node, inherited) {
321
+ if (node.kind === "command") {
322
+ return materializeCommand(node, inherited);
323
+ }
324
+ return materializeGroup(node, inherited);
325
+ }
326
+ function defineCommand(config) {
327
+ return materializeCommand(createBaseCommand(config), {
328
+ scope: void 0,
329
+ secrets: {},
330
+ requires: void 0
331
+ });
332
+ }
333
+ function defineGroup(config) {
334
+ return materializeGroup(createBaseGroup(config), {
335
+ scope: void 0,
336
+ secrets: {},
337
+ requires: void 0
338
+ });
339
+ }
9
340
 
10
341
  // src/terminal-pilot.ts
11
342
  import { randomUUID } from "node:crypto";
12
343
 
13
344
  // src/terminal-session.ts
14
- import { spawn as spawnChildProcess } from "node:child_process";
15
345
  import { EventEmitter } from "node:events";
346
+ import { accessSync, chmodSync, constants } from "node:fs";
347
+ import { createRequire } from "node:module";
348
+ import { dirname, join } from "node:path";
16
349
  import * as nodePty from "node-pty";
17
350
 
18
351
  // src/ansi.ts
@@ -923,93 +1256,27 @@ function createPtyProcess({
923
1256
  cols,
924
1257
  rows
925
1258
  }) {
926
- try {
927
- return nodePty.spawn(command, args, {
928
- cwd,
929
- env,
930
- cols,
931
- rows,
932
- encoding: "utf8"
933
- });
934
- } catch {
935
- return createChildProcessFallback({ command, args, cwd, env });
936
- }
937
- }
938
- function createChildProcessFallback({
939
- command,
940
- args,
941
- cwd,
942
- env
943
- }) {
944
- const child = spawnChildProcess(command, args, {
1259
+ ensureSpawnHelperExecutable();
1260
+ return nodePty.spawn(command, args, {
945
1261
  cwd,
946
1262
  env,
947
- stdio: ["pipe", "pipe", "pipe"]
1263
+ cols,
1264
+ rows,
1265
+ encoding: "utf8"
948
1266
  });
949
- return new ChildProcessFallback(child);
950
1267
  }
951
- var ChildProcessFallback = class {
952
- pid;
953
- child;
954
- dataEmitter = new EventEmitter();
955
- exitEmitter = new EventEmitter();
956
- constructor(child) {
957
- this.child = child;
958
- this.pid = child.pid ?? -1;
959
- child.stdout.setEncoding("utf8");
960
- child.stderr.setEncoding("utf8");
961
- child.stdout.on("data", this.handleData);
962
- child.stderr.on("data", this.handleData);
963
- child.on("exit", (exitCode, signal) => {
964
- this.exitEmitter.emit("exit", {
965
- exitCode: exitCode ?? signalToExitCode(signal),
966
- signal: void 0
967
- });
968
- });
969
- }
970
- write(data) {
971
- this.child.stdin.write(data);
972
- }
973
- resize() {
974
- }
975
- kill(signal) {
976
- this.child.kill(signal);
977
- }
978
- onData(listener) {
979
- this.dataEmitter.on("data", listener);
980
- return {
981
- dispose: () => {
982
- this.dataEmitter.off("data", listener);
983
- }
984
- };
985
- }
986
- onExit(listener) {
987
- this.exitEmitter.on("exit", listener);
988
- return {
989
- dispose: () => {
990
- this.exitEmitter.off("exit", listener);
991
- }
992
- };
993
- }
994
- handleData = (chunk) => {
995
- this.dataEmitter.emit("data", String(chunk));
996
- };
997
- };
998
- function signalToExitCode(signal) {
999
- if (signal === null) {
1000
- return 0;
1001
- }
1002
- const signalNumbers = {
1003
- SIGTERM: 15,
1004
- SIGINT: 2,
1005
- SIGHUP: 1,
1006
- SIGKILL: 9
1007
- };
1008
- const signalNumber = signalNumbers[signal];
1009
- if (signalNumber === void 0) {
1010
- return 1;
1268
+ var spawnHelperChecked = false;
1269
+ function ensureSpawnHelperExecutable() {
1270
+ if (spawnHelperChecked) return;
1271
+ spawnHelperChecked = true;
1272
+ const require3 = createRequire(import.meta.url);
1273
+ const nodePtyDir = dirname(require3.resolve("node-pty"));
1274
+ const helper = join(nodePtyDir, "..", "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper");
1275
+ try {
1276
+ accessSync(helper, constants.X_OK);
1277
+ } catch {
1278
+ chmodSync(helper, 493);
1011
1279
  }
1012
- return 128 + signalNumber;
1013
1280
  }
1014
1281
  function matchPattern(buffer, pattern) {
1015
1282
  const clean = normalizeHistoryBuffer(stripAnsi(buffer));
@@ -1263,17 +1530,16 @@ var closeSession = defineCommand({
1263
1530
  });
1264
1531
 
1265
1532
  // src/commands/create-session.ts
1266
- import { defineCommand as defineCommand2, S as S2 } from "@poe-code/cmdkit";
1267
- var params2 = S2.Object({
1268
- command: S2.String({ description: "Command to execute" }),
1269
- args: S2.Optional(S2.Array(S2.String(), { description: "Command arguments" })),
1270
- session: S2.Optional(S2.String({ short: "s", description: "Session name" })),
1271
- cwd: S2.Optional(S2.String({ description: "Working directory" })),
1272
- cols: S2.Optional(S2.Number({ description: "Terminal width in columns" })),
1273
- rows: S2.Optional(S2.Number({ description: "Terminal height in rows" })),
1274
- observe: S2.Optional(S2.Boolean({ description: "Mirror PTY output to stderr" }))
1533
+ var params2 = S.Object({
1534
+ command: S.String({ description: "Command to execute" }),
1535
+ args: S.Optional(S.Array(S.String(), { description: "Command arguments" })),
1536
+ session: S.Optional(S.String({ short: "s", description: "Session name" })),
1537
+ cwd: S.Optional(S.String({ description: "Working directory" })),
1538
+ cols: S.Optional(S.Number({ description: "Terminal width in columns" })),
1539
+ rows: S.Optional(S.Number({ description: "Terminal height in rows" })),
1540
+ observe: S.Optional(S.Boolean({ description: "Mirror PTY output to stderr" }))
1275
1541
  });
1276
- var createSession = defineCommand2({
1542
+ var createSession = defineCommand({
1277
1543
  name: "create-session",
1278
1544
  description: "Spawn an interactive CLI in a PTY",
1279
1545
  scope: ["cli", "mcp", "sdk"],
@@ -1286,12 +1552,11 @@ var createSession = defineCommand2({
1286
1552
  });
1287
1553
 
1288
1554
  // src/commands/fill.ts
1289
- import { defineCommand as defineCommand3, S as S3 } from "@poe-code/cmdkit";
1290
- var params3 = S3.Object({
1291
- text: S3.String({ description: "Text to write to the session" }),
1292
- session: S3.Optional(S3.String({ short: "s", description: "Session name" }))
1555
+ var params3 = S.Object({
1556
+ text: S.String({ description: "Text to write to the session" }),
1557
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1293
1558
  });
1294
- var fill = defineCommand3({
1559
+ var fill = defineCommand({
1295
1560
  name: "fill",
1296
1561
  description: "Write text to an active terminal session all at once (replaces \\n with \\r)",
1297
1562
  scope: ["cli", "mcp", "sdk"],
@@ -1305,11 +1570,10 @@ var fill = defineCommand3({
1305
1570
  });
1306
1571
 
1307
1572
  // src/commands/get-session.ts
1308
- import { defineCommand as defineCommand4, S as S4 } from "@poe-code/cmdkit";
1309
- var params4 = S4.Object({
1310
- session: S4.Optional(S4.String({ short: "s", description: "Session name" }))
1573
+ var params4 = S.Object({
1574
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1311
1575
  });
1312
- var getSession = defineCommand4({
1576
+ var getSession = defineCommand({
1313
1577
  name: "get-session",
1314
1578
  description: "Get session metadata (name, pid, command, exitCode)",
1315
1579
  scope: ["cli", "mcp", "sdk"],
@@ -1325,138 +1589,1249 @@ var getSession = defineCommand4({
1325
1589
  }
1326
1590
  });
1327
1591
 
1328
- // src/commands/install.ts
1329
- import { installSkill } from "@poe-code/agent-skill-config";
1330
- import { defineCommand as defineCommand5, S as S5 } from "@poe-code/cmdkit";
1331
-
1332
- // src/commands/installer.ts
1592
+ // ../agent-skill-config/src/configs.ts
1333
1593
  import os from "node:os";
1334
1594
  import path from "node:path";
1335
- import * as nodeFs from "node:fs/promises";
1336
- import { readFile } from "node:fs/promises";
1337
- import { UserError as UserError2 } from "@poe-code/cmdkit";
1338
- import {
1339
- getAgentConfig,
1340
- resolveAgentSupport as resolveSkillAgentSupport,
1341
- supportedAgents as skillSupportedAgents
1342
- } from "@poe-code/agent-skill-config";
1343
- var DEFAULT_INSTALL_AGENT = "claude-code";
1344
- var DEFAULT_INSTALL_SCOPE = "local";
1345
- var TERMINAL_PILOT_SKILL_NAME = "terminal-pilot";
1346
- var installableAgents = skillSupportedAgents;
1347
- function isNotFoundError(error) {
1348
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
1349
- }
1350
- function resolveInstallerServices(installer) {
1351
- return {
1352
- fs: installer?.fs ?? nodeFs,
1353
- cwd: installer?.cwd ?? process.cwd(),
1354
- homeDir: installer?.homeDir ?? os.homedir(),
1355
- platform: installer?.platform ?? process.platform
1356
- };
1357
- }
1358
- function throwUnsupportedAgent(agent) {
1359
- throw new UserError2(`Unsupported agent: ${agent}`);
1360
- }
1361
- function resolveInstallableAgent(agent) {
1362
- const skillSupport = resolveSkillAgentSupport(agent);
1363
- if (skillSupport.status !== "supported" || !skillSupport.id) {
1364
- throwUnsupportedAgent(agent);
1595
+
1596
+ // ../agent-defs/src/agents/claude-code.ts
1597
+ var claudeCodeAgent = {
1598
+ id: "claude-code",
1599
+ name: "claude-code",
1600
+ label: "Claude Code",
1601
+ summary: "Configure Claude Code to route through Poe.",
1602
+ aliases: ["claude"],
1603
+ binaryName: "claude",
1604
+ configPath: "~/.claude/settings.json",
1605
+ branding: {
1606
+ colors: {
1607
+ dark: "#C15F3C",
1608
+ light: "#C15F3C"
1609
+ }
1365
1610
  }
1366
- return skillSupport.id;
1367
- }
1368
- function resolveInstallScope(input) {
1369
- if (input.local && input.global) {
1370
- throw new UserError2("Use either --local or --global, not both.");
1611
+ };
1612
+
1613
+ // ../agent-defs/src/agents/claude-desktop.ts
1614
+ var claudeDesktopAgent = {
1615
+ id: "claude-desktop",
1616
+ name: "claude-desktop",
1617
+ label: "Claude Desktop",
1618
+ summary: "Anthropic's official desktop application for Claude",
1619
+ configPath: "~/.claude/settings.json",
1620
+ branding: {
1621
+ colors: {
1622
+ dark: "#D97757",
1623
+ light: "#D97757"
1624
+ }
1371
1625
  }
1372
- if (input.local) {
1373
- return "local";
1626
+ };
1627
+
1628
+ // ../agent-defs/src/agents/codex.ts
1629
+ var codexAgent = {
1630
+ id: "codex",
1631
+ name: "codex",
1632
+ label: "Codex",
1633
+ summary: "Configure Codex to use Poe as the model provider.",
1634
+ binaryName: "codex",
1635
+ configPath: "~/.codex/config.toml",
1636
+ branding: {
1637
+ colors: {
1638
+ dark: "#D5D9DF",
1639
+ light: "#7A7F86"
1640
+ }
1374
1641
  }
1375
- if (input.global) {
1376
- return "global";
1642
+ };
1643
+
1644
+ // ../agent-defs/src/agents/opencode.ts
1645
+ var openCodeAgent = {
1646
+ id: "opencode",
1647
+ name: "opencode",
1648
+ label: "OpenCode CLI",
1649
+ summary: "Configure OpenCode CLI to use the Poe API.",
1650
+ binaryName: "opencode",
1651
+ configPath: "~/.config/opencode/config.json",
1652
+ branding: {
1653
+ colors: {
1654
+ dark: "#4A4F55",
1655
+ light: "#2F3338"
1656
+ }
1377
1657
  }
1378
- return DEFAULT_INSTALL_SCOPE;
1379
- }
1380
- var terminalPilotTemplateCache;
1381
- async function loadTerminalPilotTemplate() {
1382
- if (terminalPilotTemplateCache !== void 0) {
1383
- return terminalPilotTemplateCache;
1658
+ };
1659
+
1660
+ // ../agent-defs/src/agents/kimi.ts
1661
+ var kimiAgent = {
1662
+ id: "kimi",
1663
+ name: "kimi",
1664
+ label: "Kimi",
1665
+ summary: "Configure Kimi CLI to use Poe API",
1666
+ aliases: ["kimi-cli"],
1667
+ binaryName: "kimi",
1668
+ configPath: "~/.kimi/config.toml",
1669
+ branding: {
1670
+ colors: {
1671
+ dark: "#7B68EE",
1672
+ light: "#6A5ACD"
1673
+ }
1384
1674
  }
1385
- const candidates = [
1386
- new URL("./templates/terminal-pilot.md", import.meta.url),
1387
- new URL("../templates/terminal-pilot.md", import.meta.url),
1388
- new URL("../../../agent-skill-config/src/templates/terminal-pilot.md", import.meta.url)
1389
- ];
1390
- for (const candidate of candidates) {
1391
- try {
1392
- terminalPilotTemplateCache = await readFile(candidate, "utf8");
1393
- return terminalPilotTemplateCache;
1394
- } catch (error) {
1395
- if (!isNotFoundError(error)) {
1396
- throw error;
1397
- }
1675
+ };
1676
+
1677
+ // ../agent-defs/src/registry.ts
1678
+ var allAgents = [
1679
+ claudeCodeAgent,
1680
+ claudeDesktopAgent,
1681
+ codexAgent,
1682
+ openCodeAgent,
1683
+ kimiAgent
1684
+ ];
1685
+ var lookup = /* @__PURE__ */ new Map();
1686
+ for (const agent of allAgents) {
1687
+ const values = [agent.id, agent.name, ...agent.aliases ?? []];
1688
+ for (const value of values) {
1689
+ const normalized = value.toLowerCase();
1690
+ if (!lookup.has(normalized)) {
1691
+ lookup.set(normalized, agent.id);
1398
1692
  }
1399
1693
  }
1400
- throw new UserError2("terminal-pilot skill template is missing.");
1401
1694
  }
1402
- function resolveHomeRelativePath(targetPath, homeDir) {
1403
- if (targetPath === "~") {
1404
- return homeDir;
1405
- }
1406
- if (targetPath.startsWith("~/")) {
1407
- return path.join(homeDir, targetPath.slice(2));
1695
+ function resolveAgentId(input) {
1696
+ if (!input) {
1697
+ return void 0;
1408
1698
  }
1409
- return targetPath;
1699
+ return lookup.get(input.toLowerCase());
1410
1700
  }
1411
- function getSkillFolderWithHome(agent, scope, cwd, homeDir) {
1412
- const config = getAgentConfig(agent);
1701
+
1702
+ // ../agent-skill-config/src/configs.ts
1703
+ var agentSkillConfigs = {
1704
+ "claude-code": {
1705
+ globalSkillDir: "~/.claude/skills",
1706
+ localSkillDir: ".claude/skills"
1707
+ },
1708
+ codex: {
1709
+ globalSkillDir: "~/.codex/skills",
1710
+ localSkillDir: ".codex/skills"
1711
+ },
1712
+ opencode: {
1713
+ globalSkillDir: "~/.config/opencode/skills",
1714
+ localSkillDir: ".opencode/skills"
1715
+ }
1716
+ };
1717
+ var supportedAgents = Object.keys(agentSkillConfigs);
1718
+ function resolveAgentSupport(input, registry = agentSkillConfigs) {
1719
+ const resolvedId = resolveAgentId(input);
1720
+ if (!resolvedId) {
1721
+ return { status: "unknown", input };
1722
+ }
1723
+ const config = registry[resolvedId];
1413
1724
  if (!config) {
1414
- throwUnsupportedAgent(agent);
1725
+ return { status: "unsupported", input, id: resolvedId };
1415
1726
  }
1727
+ return { status: "supported", input, id: resolvedId, config };
1728
+ }
1729
+ function getAgentConfig(agentId) {
1730
+ const support = resolveAgentSupport(agentId);
1731
+ return support.status === "supported" ? support.config : void 0;
1732
+ }
1733
+
1734
+ // ../config-mutations/src/mutations/file-mutation.ts
1735
+ function ensureDirectory(options) {
1416
1736
  return {
1417
- displayPath: path.join(
1418
- scope === "global" ? config.globalSkillDir : config.localSkillDir,
1419
- TERMINAL_PILOT_SKILL_NAME
1420
- ),
1421
- fullPath: path.join(
1422
- scope === "global" ? resolveHomeRelativePath(config.globalSkillDir, homeDir) : path.resolve(cwd, config.localSkillDir),
1423
- TERMINAL_PILOT_SKILL_NAME
1424
- )
1737
+ kind: "ensureDirectory",
1738
+ path: options.path,
1739
+ label: options.label
1425
1740
  };
1426
1741
  }
1427
- async function removeSkillFolder(fs, folderPath) {
1428
- try {
1429
- await fs.stat(folderPath);
1430
- } catch (error) {
1431
- if (isNotFoundError(error)) {
1432
- return false;
1433
- }
1434
- throw error;
1435
- }
1436
- if (typeof fs.rm !== "function") {
1437
- throw new UserError2("The configured filesystem does not support removing directories.");
1438
- }
1439
- await fs.rm(folderPath, { recursive: true, force: true });
1440
- return true;
1742
+ function remove(options) {
1743
+ return {
1744
+ kind: "removeFile",
1745
+ target: options.target,
1746
+ whenEmpty: options.whenEmpty,
1747
+ whenContentMatches: options.whenContentMatches,
1748
+ label: options.label
1749
+ };
1750
+ }
1751
+ function removeDirectory(options) {
1752
+ return {
1753
+ kind: "removeDirectory",
1754
+ path: options.path,
1755
+ force: options.force,
1756
+ label: options.label
1757
+ };
1758
+ }
1759
+ function chmod(options) {
1760
+ return {
1761
+ kind: "chmod",
1762
+ target: options.target,
1763
+ mode: options.mode,
1764
+ label: options.label
1765
+ };
1441
1766
  }
1767
+ function backup(options) {
1768
+ return {
1769
+ kind: "backup",
1770
+ target: options.target,
1771
+ label: options.label
1772
+ };
1773
+ }
1774
+ var fileMutation = {
1775
+ ensureDirectory,
1776
+ remove,
1777
+ removeDirectory,
1778
+ chmod,
1779
+ backup
1780
+ };
1442
1781
 
1443
- // src/commands/install.ts
1444
- var params5 = S5.Object({
1445
- agent: S5.Enum(installableAgents, {
1446
- description: "Agent to install terminal-pilot for",
1447
- default: DEFAULT_INSTALL_AGENT
1448
- }),
1449
- local: S5.Optional(S5.Boolean({ description: "Install the skill in the current project" })),
1450
- global: S5.Optional(S5.Boolean({ description: "Install the skill in the user home directory" }))
1451
- });
1452
- var install = defineCommand5({
1453
- name: "install",
1454
- description: "Install the terminal-pilot CLI skill.",
1455
- scope: ["cli"],
1456
- positional: ["agent"],
1457
- params: params5,
1458
- handler: async ({ params: params17, terminalPilotInstaller }) => {
1459
- const services = resolveInstallerServices(terminalPilotInstaller);
1782
+ // ../config-mutations/src/mutations/template-mutation.ts
1783
+ function write(options) {
1784
+ return {
1785
+ kind: "templateWrite",
1786
+ target: options.target,
1787
+ templateId: options.templateId,
1788
+ context: options.context,
1789
+ label: options.label
1790
+ };
1791
+ }
1792
+ function mergeToml(options) {
1793
+ return {
1794
+ kind: "templateMergeToml",
1795
+ target: options.target,
1796
+ templateId: options.templateId,
1797
+ context: options.context,
1798
+ label: options.label
1799
+ };
1800
+ }
1801
+ function mergeJson(options) {
1802
+ return {
1803
+ kind: "templateMergeJson",
1804
+ target: options.target,
1805
+ templateId: options.templateId,
1806
+ context: options.context,
1807
+ label: options.label
1808
+ };
1809
+ }
1810
+ var templateMutation = {
1811
+ write,
1812
+ mergeToml,
1813
+ mergeJson
1814
+ };
1815
+
1816
+ // ../config-mutations/src/execution/apply-mutation.ts
1817
+ import Mustache from "mustache";
1818
+
1819
+ // ../config-mutations/src/formats/json.ts
1820
+ import * as jsonc from "jsonc-parser";
1821
+ function isConfigObject(value) {
1822
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1823
+ }
1824
+ function parse2(content) {
1825
+ if (!content || content.trim() === "") {
1826
+ return {};
1827
+ }
1828
+ const errors = [];
1829
+ const parsed = jsonc.parse(content, errors, {
1830
+ allowTrailingComma: true,
1831
+ disallowComments: false
1832
+ });
1833
+ if (errors.length > 0) {
1834
+ throw new Error(`JSON parse error: ${jsonc.printParseErrorCode(errors[0].error)}`);
1835
+ }
1836
+ if (parsed === null || parsed === void 0) {
1837
+ return {};
1838
+ }
1839
+ if (!isConfigObject(parsed)) {
1840
+ throw new Error("Expected JSON object.");
1841
+ }
1842
+ return parsed;
1843
+ }
1844
+ function serialize(obj) {
1845
+ return `${JSON.stringify(obj, null, 2)}
1846
+ `;
1847
+ }
1848
+ function merge(base, patch) {
1849
+ const result = { ...base };
1850
+ for (const [key, value] of Object.entries(patch)) {
1851
+ if (value === void 0) {
1852
+ continue;
1853
+ }
1854
+ const existing = result[key];
1855
+ if (isConfigObject(existing) && isConfigObject(value)) {
1856
+ result[key] = merge(existing, value);
1857
+ continue;
1858
+ }
1859
+ result[key] = value;
1860
+ }
1861
+ return result;
1862
+ }
1863
+ function prune(obj, shape) {
1864
+ let changed = false;
1865
+ const result = { ...obj };
1866
+ for (const [key, pattern] of Object.entries(shape)) {
1867
+ if (!(key in result)) {
1868
+ continue;
1869
+ }
1870
+ const current = result[key];
1871
+ if (isConfigObject(pattern) && Object.keys(pattern).length === 0) {
1872
+ delete result[key];
1873
+ changed = true;
1874
+ continue;
1875
+ }
1876
+ if (isConfigObject(pattern) && isConfigObject(current)) {
1877
+ const { changed: childChanged, result: childResult } = prune(
1878
+ current,
1879
+ pattern
1880
+ );
1881
+ if (childChanged) {
1882
+ changed = true;
1883
+ }
1884
+ if (Object.keys(childResult).length === 0) {
1885
+ delete result[key];
1886
+ } else {
1887
+ result[key] = childResult;
1888
+ }
1889
+ continue;
1890
+ }
1891
+ delete result[key];
1892
+ changed = true;
1893
+ }
1894
+ return { changed, result };
1895
+ }
1896
+ var jsonFormat = {
1897
+ parse: parse2,
1898
+ serialize,
1899
+ merge,
1900
+ prune
1901
+ };
1902
+
1903
+ // ../config-mutations/src/formats/toml.ts
1904
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
1905
+ function isConfigObject2(value) {
1906
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1907
+ }
1908
+ function parse3(content) {
1909
+ if (!content || content.trim() === "") {
1910
+ return {};
1911
+ }
1912
+ const parsed = parseToml(content);
1913
+ if (!isConfigObject2(parsed)) {
1914
+ throw new Error("Expected TOML document to be a table.");
1915
+ }
1916
+ return parsed;
1917
+ }
1918
+ function serialize2(obj) {
1919
+ const serialized = stringifyToml(obj);
1920
+ return serialized.endsWith("\n") ? serialized : `${serialized}
1921
+ `;
1922
+ }
1923
+ function merge2(base, patch) {
1924
+ const result = { ...base };
1925
+ for (const [key, value] of Object.entries(patch)) {
1926
+ if (value === void 0) {
1927
+ continue;
1928
+ }
1929
+ const existing = result[key];
1930
+ if (isConfigObject2(existing) && isConfigObject2(value)) {
1931
+ result[key] = merge2(existing, value);
1932
+ continue;
1933
+ }
1934
+ result[key] = value;
1935
+ }
1936
+ return result;
1937
+ }
1938
+ function prune2(obj, shape) {
1939
+ let changed = false;
1940
+ const result = { ...obj };
1941
+ for (const [key, pattern] of Object.entries(shape)) {
1942
+ if (!(key in result)) {
1943
+ continue;
1944
+ }
1945
+ const current = result[key];
1946
+ if (isConfigObject2(pattern) && Object.keys(pattern).length === 0) {
1947
+ delete result[key];
1948
+ changed = true;
1949
+ continue;
1950
+ }
1951
+ if (isConfigObject2(pattern) && isConfigObject2(current)) {
1952
+ const { changed: childChanged, result: childResult } = prune2(
1953
+ current,
1954
+ pattern
1955
+ );
1956
+ if (childChanged) {
1957
+ changed = true;
1958
+ }
1959
+ if (Object.keys(childResult).length === 0) {
1960
+ delete result[key];
1961
+ } else {
1962
+ result[key] = childResult;
1963
+ }
1964
+ continue;
1965
+ }
1966
+ delete result[key];
1967
+ changed = true;
1968
+ }
1969
+ return { changed, result };
1970
+ }
1971
+ var tomlFormat = {
1972
+ parse: parse3,
1973
+ serialize: serialize2,
1974
+ merge: merge2,
1975
+ prune: prune2
1976
+ };
1977
+
1978
+ // ../config-mutations/src/formats/index.ts
1979
+ var formatRegistry = {
1980
+ json: jsonFormat,
1981
+ toml: tomlFormat
1982
+ };
1983
+ var extensionMap = {
1984
+ ".json": "json",
1985
+ ".toml": "toml"
1986
+ };
1987
+ function getConfigFormat(pathOrFormat) {
1988
+ if (pathOrFormat in formatRegistry) {
1989
+ return formatRegistry[pathOrFormat];
1990
+ }
1991
+ const ext = getExtension(pathOrFormat);
1992
+ const formatName = extensionMap[ext];
1993
+ if (!formatName) {
1994
+ throw new Error(
1995
+ `Unsupported config format. Cannot detect format from "${pathOrFormat}". Supported extensions: ${Object.keys(extensionMap).join(", ")}. Supported format names: ${Object.keys(formatRegistry).join(", ")}.`
1996
+ );
1997
+ }
1998
+ return formatRegistry[formatName];
1999
+ }
2000
+ function detectFormat(path4) {
2001
+ const ext = getExtension(path4);
2002
+ return extensionMap[ext];
2003
+ }
2004
+ function getExtension(path4) {
2005
+ const lastDot = path4.lastIndexOf(".");
2006
+ if (lastDot === -1) {
2007
+ return "";
2008
+ }
2009
+ return path4.slice(lastDot).toLowerCase();
2010
+ }
2011
+
2012
+ // ../config-mutations/src/execution/path-utils.ts
2013
+ import path2 from "node:path";
2014
+ function expandHome(targetPath, homeDir) {
2015
+ if (!targetPath?.startsWith("~")) {
2016
+ return targetPath;
2017
+ }
2018
+ if (targetPath.startsWith("~./")) {
2019
+ targetPath = `~/.${targetPath.slice(3)}`;
2020
+ }
2021
+ let remainder = targetPath.slice(1);
2022
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
2023
+ remainder = remainder.slice(1);
2024
+ } else if (remainder.startsWith(".")) {
2025
+ remainder = remainder.slice(1);
2026
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
2027
+ remainder = remainder.slice(1);
2028
+ }
2029
+ }
2030
+ return remainder.length === 0 ? homeDir : path2.join(homeDir, remainder);
2031
+ }
2032
+ function validateHomePath(targetPath) {
2033
+ if (typeof targetPath !== "string" || targetPath.length === 0) {
2034
+ throw new Error("Target path must be a non-empty string.");
2035
+ }
2036
+ if (!targetPath.startsWith("~")) {
2037
+ throw new Error(
2038
+ `All target paths must be home-relative (start with ~). Received: "${targetPath}"`
2039
+ );
2040
+ }
2041
+ }
2042
+ function resolvePath(rawPath, homeDir, pathMapper) {
2043
+ validateHomePath(rawPath);
2044
+ const expanded = expandHome(rawPath, homeDir);
2045
+ if (!pathMapper) {
2046
+ return expanded;
2047
+ }
2048
+ const rawDirectory = path2.dirname(expanded);
2049
+ const mappedDirectory = pathMapper.mapTargetDirectory({
2050
+ targetDirectory: rawDirectory
2051
+ });
2052
+ const filename = path2.basename(expanded);
2053
+ return filename.length === 0 ? mappedDirectory : path2.join(mappedDirectory, filename);
2054
+ }
2055
+
2056
+ // ../config-mutations/src/fs-utils.ts
2057
+ function isNotFound(error) {
2058
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2059
+ }
2060
+ async function readFileIfExists(fs, target) {
2061
+ try {
2062
+ return await fs.readFile(target, "utf8");
2063
+ } catch (error) {
2064
+ if (isNotFound(error)) {
2065
+ return null;
2066
+ }
2067
+ throw error;
2068
+ }
2069
+ }
2070
+ async function pathExists(fs, target) {
2071
+ try {
2072
+ await fs.stat(target);
2073
+ return true;
2074
+ } catch (error) {
2075
+ if (isNotFound(error)) {
2076
+ return false;
2077
+ }
2078
+ throw error;
2079
+ }
2080
+ }
2081
+ function createTimestamp() {
2082
+ return (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-");
2083
+ }
2084
+
2085
+ // ../config-mutations/src/execution/apply-mutation.ts
2086
+ function resolveValue(resolver, options) {
2087
+ if (typeof resolver === "function") {
2088
+ return resolver(options);
2089
+ }
2090
+ return resolver;
2091
+ }
2092
+ function createInvalidDocumentBackupPath(targetPath) {
2093
+ const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
2094
+ return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
2095
+ }
2096
+ async function backupInvalidDocument(fs, targetPath, content) {
2097
+ const backupPath = createInvalidDocumentBackupPath(targetPath);
2098
+ await fs.writeFile(backupPath, content, { encoding: "utf8" });
2099
+ }
2100
+ function describeMutation(kind, targetPath) {
2101
+ const displayPath = targetPath ?? "target";
2102
+ switch (kind) {
2103
+ case "ensureDirectory":
2104
+ return `Create ${displayPath}`;
2105
+ case "removeDirectory":
2106
+ return `Remove directory ${displayPath}`;
2107
+ case "backup":
2108
+ return `Backup ${displayPath}`;
2109
+ case "templateWrite":
2110
+ return `Write ${displayPath}`;
2111
+ case "chmod":
2112
+ return `Set permissions on ${displayPath}`;
2113
+ case "removeFile":
2114
+ return `Remove ${displayPath}`;
2115
+ case "configMerge":
2116
+ case "configPrune":
2117
+ case "configTransform":
2118
+ case "templateMergeToml":
2119
+ case "templateMergeJson":
2120
+ return `Update ${displayPath}`;
2121
+ default:
2122
+ return "Operation";
2123
+ }
2124
+ }
2125
+ function pruneKeysByPrefix(table, prefix) {
2126
+ const result = {};
2127
+ for (const [key, value] of Object.entries(table)) {
2128
+ if (!key.startsWith(prefix)) {
2129
+ result[key] = value;
2130
+ }
2131
+ }
2132
+ return result;
2133
+ }
2134
+ function isConfigObject3(value) {
2135
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2136
+ }
2137
+ function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
2138
+ const result = { ...base };
2139
+ const prefixMap = pruneByPrefix ?? {};
2140
+ for (const [key, value] of Object.entries(patch)) {
2141
+ const current = result[key];
2142
+ const prefix = prefixMap[key];
2143
+ if (isConfigObject3(current) && isConfigObject3(value)) {
2144
+ if (prefix) {
2145
+ const pruned = pruneKeysByPrefix(current, prefix);
2146
+ result[key] = { ...pruned, ...value };
2147
+ } else {
2148
+ result[key] = mergeWithPruneByPrefix(
2149
+ current,
2150
+ value,
2151
+ prefixMap
2152
+ );
2153
+ }
2154
+ continue;
2155
+ }
2156
+ result[key] = value;
2157
+ }
2158
+ return result;
2159
+ }
2160
+ async function applyMutation(mutation, context, options) {
2161
+ switch (mutation.kind) {
2162
+ case "ensureDirectory":
2163
+ return applyEnsureDirectory(mutation, context, options);
2164
+ case "removeDirectory":
2165
+ return applyRemoveDirectory(mutation, context, options);
2166
+ case "removeFile":
2167
+ return applyRemoveFile(mutation, context, options);
2168
+ case "chmod":
2169
+ return applyChmod(mutation, context, options);
2170
+ case "backup":
2171
+ return applyBackup(mutation, context, options);
2172
+ case "configMerge":
2173
+ return applyConfigMerge(mutation, context, options);
2174
+ case "configPrune":
2175
+ return applyConfigPrune(mutation, context, options);
2176
+ case "configTransform":
2177
+ return applyConfigTransform(mutation, context, options);
2178
+ case "templateWrite":
2179
+ return applyTemplateWrite(mutation, context, options);
2180
+ case "templateMergeToml":
2181
+ return applyTemplateMerge(mutation, context, options, "toml");
2182
+ case "templateMergeJson":
2183
+ return applyTemplateMerge(mutation, context, options, "json");
2184
+ default: {
2185
+ const never = mutation;
2186
+ throw new Error(`Unknown mutation kind: ${never.kind}`);
2187
+ }
2188
+ }
2189
+ }
2190
+ async function applyEnsureDirectory(mutation, context, options) {
2191
+ const rawPath = resolveValue(mutation.path, options);
2192
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2193
+ const details = {
2194
+ kind: mutation.kind,
2195
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2196
+ targetPath
2197
+ };
2198
+ const existed = await pathExists(context.fs, targetPath);
2199
+ if (!context.dryRun) {
2200
+ await context.fs.mkdir(targetPath, { recursive: true });
2201
+ }
2202
+ return {
2203
+ outcome: {
2204
+ changed: !existed,
2205
+ effect: "mkdir",
2206
+ detail: existed ? "noop" : "create"
2207
+ },
2208
+ details
2209
+ };
2210
+ }
2211
+ async function applyRemoveDirectory(mutation, context, options) {
2212
+ const rawPath = resolveValue(mutation.path, options);
2213
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2214
+ const details = {
2215
+ kind: mutation.kind,
2216
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2217
+ targetPath
2218
+ };
2219
+ const existed = await pathExists(context.fs, targetPath);
2220
+ if (!existed) {
2221
+ return {
2222
+ outcome: { changed: false, effect: "none", detail: "noop" },
2223
+ details
2224
+ };
2225
+ }
2226
+ if (typeof context.fs.rm !== "function") {
2227
+ return {
2228
+ outcome: { changed: false, effect: "none", detail: "noop" },
2229
+ details
2230
+ };
2231
+ }
2232
+ if (mutation.force) {
2233
+ if (!context.dryRun) {
2234
+ await context.fs.rm(targetPath, { recursive: true, force: true });
2235
+ }
2236
+ return {
2237
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2238
+ details
2239
+ };
2240
+ }
2241
+ const entries = await context.fs.readdir(targetPath);
2242
+ if (entries.length > 0) {
2243
+ return {
2244
+ outcome: { changed: false, effect: "none", detail: "noop" },
2245
+ details
2246
+ };
2247
+ }
2248
+ if (!context.dryRun) {
2249
+ await context.fs.rm(targetPath, { recursive: true, force: true });
2250
+ }
2251
+ return {
2252
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2253
+ details
2254
+ };
2255
+ }
2256
+ async function applyRemoveFile(mutation, context, options) {
2257
+ const rawPath = resolveValue(mutation.target, options);
2258
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2259
+ const details = {
2260
+ kind: mutation.kind,
2261
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2262
+ targetPath
2263
+ };
2264
+ try {
2265
+ const content = await context.fs.readFile(targetPath, "utf8");
2266
+ const trimmed = content.trim();
2267
+ if (mutation.whenContentMatches && !mutation.whenContentMatches.test(trimmed)) {
2268
+ return {
2269
+ outcome: { changed: false, effect: "none", detail: "noop" },
2270
+ details
2271
+ };
2272
+ }
2273
+ if (mutation.whenEmpty && trimmed.length > 0) {
2274
+ return {
2275
+ outcome: { changed: false, effect: "none", detail: "noop" },
2276
+ details
2277
+ };
2278
+ }
2279
+ if (!context.dryRun) {
2280
+ await context.fs.unlink(targetPath);
2281
+ }
2282
+ return {
2283
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2284
+ details
2285
+ };
2286
+ } catch (error) {
2287
+ if (isNotFound(error)) {
2288
+ return {
2289
+ outcome: { changed: false, effect: "none", detail: "noop" },
2290
+ details
2291
+ };
2292
+ }
2293
+ throw error;
2294
+ }
2295
+ }
2296
+ async function applyChmod(mutation, context, options) {
2297
+ const rawPath = resolveValue(mutation.target, options);
2298
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2299
+ const details = {
2300
+ kind: mutation.kind,
2301
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2302
+ targetPath
2303
+ };
2304
+ if (typeof context.fs.chmod !== "function") {
2305
+ return {
2306
+ outcome: { changed: false, effect: "none", detail: "noop" },
2307
+ details
2308
+ };
2309
+ }
2310
+ try {
2311
+ const stat = await context.fs.stat(targetPath);
2312
+ const currentMode = typeof stat.mode === "number" ? stat.mode & 511 : null;
2313
+ if (currentMode === mutation.mode) {
2314
+ return {
2315
+ outcome: { changed: false, effect: "none", detail: "noop" },
2316
+ details
2317
+ };
2318
+ }
2319
+ if (!context.dryRun) {
2320
+ await context.fs.chmod(targetPath, mutation.mode);
2321
+ }
2322
+ return {
2323
+ outcome: { changed: true, effect: "chmod", detail: "update" },
2324
+ details
2325
+ };
2326
+ } catch (error) {
2327
+ if (isNotFound(error)) {
2328
+ return {
2329
+ outcome: { changed: false, effect: "none", detail: "noop" },
2330
+ details
2331
+ };
2332
+ }
2333
+ throw error;
2334
+ }
2335
+ }
2336
+ async function applyBackup(mutation, context, options) {
2337
+ const rawPath = resolveValue(mutation.target, options);
2338
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2339
+ const details = {
2340
+ kind: mutation.kind,
2341
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2342
+ targetPath
2343
+ };
2344
+ const content = await readFileIfExists(context.fs, targetPath);
2345
+ if (content === null) {
2346
+ return {
2347
+ outcome: { changed: false, effect: "none", detail: "noop" },
2348
+ details
2349
+ };
2350
+ }
2351
+ if (!context.dryRun) {
2352
+ const backupPath = `${targetPath}.backup-${createTimestamp()}`;
2353
+ await context.fs.writeFile(backupPath, content, { encoding: "utf8" });
2354
+ }
2355
+ return {
2356
+ outcome: { changed: true, effect: "copy", detail: "backup" },
2357
+ details
2358
+ };
2359
+ }
2360
+ async function applyConfigMerge(mutation, context, options) {
2361
+ const rawPath = resolveValue(mutation.target, options);
2362
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2363
+ const details = {
2364
+ kind: mutation.kind,
2365
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2366
+ targetPath
2367
+ };
2368
+ const formatName = mutation.format ?? detectFormat(rawPath);
2369
+ if (!formatName) {
2370
+ throw new Error(
2371
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
2372
+ );
2373
+ }
2374
+ const format = getConfigFormat(formatName);
2375
+ const rawContent = await readFileIfExists(context.fs, targetPath);
2376
+ let current;
2377
+ try {
2378
+ current = rawContent === null ? {} : format.parse(rawContent);
2379
+ } catch {
2380
+ if (rawContent !== null) {
2381
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
2382
+ }
2383
+ current = {};
2384
+ }
2385
+ const value = resolveValue(mutation.value, options);
2386
+ let merged;
2387
+ if (mutation.pruneByPrefix) {
2388
+ merged = mergeWithPruneByPrefix(current, value, mutation.pruneByPrefix);
2389
+ } else {
2390
+ merged = format.merge(current, value);
2391
+ }
2392
+ const serialized = format.serialize(merged);
2393
+ const changed = serialized !== rawContent;
2394
+ if (changed && !context.dryRun) {
2395
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
2396
+ }
2397
+ return {
2398
+ outcome: {
2399
+ changed,
2400
+ effect: changed ? "write" : "none",
2401
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
2402
+ },
2403
+ details
2404
+ };
2405
+ }
2406
+ async function applyConfigPrune(mutation, context, options) {
2407
+ const rawPath = resolveValue(mutation.target, options);
2408
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2409
+ const details = {
2410
+ kind: mutation.kind,
2411
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2412
+ targetPath
2413
+ };
2414
+ const rawContent = await readFileIfExists(context.fs, targetPath);
2415
+ if (rawContent === null) {
2416
+ return {
2417
+ outcome: { changed: false, effect: "none", detail: "noop" },
2418
+ details
2419
+ };
2420
+ }
2421
+ const formatName = mutation.format ?? detectFormat(rawPath);
2422
+ if (!formatName) {
2423
+ throw new Error(
2424
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
2425
+ );
2426
+ }
2427
+ const format = getConfigFormat(formatName);
2428
+ let current;
2429
+ try {
2430
+ current = format.parse(rawContent);
2431
+ } catch {
2432
+ return {
2433
+ outcome: { changed: false, effect: "none", detail: "noop" },
2434
+ details
2435
+ };
2436
+ }
2437
+ if (mutation.onlyIf && !mutation.onlyIf(current, options)) {
2438
+ return {
2439
+ outcome: { changed: false, effect: "none", detail: "noop" },
2440
+ details
2441
+ };
2442
+ }
2443
+ const shape = resolveValue(mutation.shape, options);
2444
+ const { changed, result } = format.prune(current, shape);
2445
+ if (!changed) {
2446
+ return {
2447
+ outcome: { changed: false, effect: "none", detail: "noop" },
2448
+ details
2449
+ };
2450
+ }
2451
+ if (Object.keys(result).length === 0) {
2452
+ if (!context.dryRun) {
2453
+ await context.fs.unlink(targetPath);
2454
+ }
2455
+ return {
2456
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2457
+ details
2458
+ };
2459
+ }
2460
+ const serialized = format.serialize(result);
2461
+ if (!context.dryRun) {
2462
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
2463
+ }
2464
+ return {
2465
+ outcome: { changed: true, effect: "write", detail: "update" },
2466
+ details
2467
+ };
2468
+ }
2469
+ async function applyConfigTransform(mutation, context, options) {
2470
+ const rawPath = resolveValue(mutation.target, options);
2471
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2472
+ const details = {
2473
+ kind: mutation.kind,
2474
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2475
+ targetPath
2476
+ };
2477
+ const formatName = mutation.format ?? detectFormat(rawPath);
2478
+ if (!formatName) {
2479
+ throw new Error(
2480
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
2481
+ );
2482
+ }
2483
+ const format = getConfigFormat(formatName);
2484
+ const rawContent = await readFileIfExists(context.fs, targetPath);
2485
+ let current;
2486
+ try {
2487
+ current = rawContent === null ? {} : format.parse(rawContent);
2488
+ } catch {
2489
+ if (rawContent !== null) {
2490
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
2491
+ }
2492
+ current = {};
2493
+ }
2494
+ const { content: transformed, changed } = mutation.transform(current, options);
2495
+ if (!changed) {
2496
+ return {
2497
+ outcome: { changed: false, effect: "none", detail: "noop" },
2498
+ details
2499
+ };
2500
+ }
2501
+ if (transformed === null) {
2502
+ if (rawContent === null) {
2503
+ return {
2504
+ outcome: { changed: false, effect: "none", detail: "noop" },
2505
+ details
2506
+ };
2507
+ }
2508
+ if (!context.dryRun) {
2509
+ await context.fs.unlink(targetPath);
2510
+ }
2511
+ return {
2512
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2513
+ details
2514
+ };
2515
+ }
2516
+ const serialized = format.serialize(transformed);
2517
+ if (!context.dryRun) {
2518
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
2519
+ }
2520
+ return {
2521
+ outcome: {
2522
+ changed: true,
2523
+ effect: "write",
2524
+ detail: rawContent === null ? "create" : "update"
2525
+ },
2526
+ details
2527
+ };
2528
+ }
2529
+ async function applyTemplateWrite(mutation, context, options) {
2530
+ if (!context.templates) {
2531
+ throw new Error(
2532
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
2533
+ );
2534
+ }
2535
+ const rawPath = resolveValue(mutation.target, options);
2536
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2537
+ const details = {
2538
+ kind: mutation.kind,
2539
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2540
+ targetPath
2541
+ };
2542
+ const template = await context.templates(mutation.templateId);
2543
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
2544
+ const rendered = Mustache.render(template, templateContext);
2545
+ const existed = await pathExists(context.fs, targetPath);
2546
+ if (!context.dryRun) {
2547
+ await context.fs.writeFile(targetPath, rendered, { encoding: "utf8" });
2548
+ }
2549
+ return {
2550
+ outcome: {
2551
+ changed: true,
2552
+ effect: "write",
2553
+ detail: existed ? "update" : "create"
2554
+ },
2555
+ details
2556
+ };
2557
+ }
2558
+ async function applyTemplateMerge(mutation, context, options, formatName) {
2559
+ if (!context.templates) {
2560
+ throw new Error(
2561
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
2562
+ );
2563
+ }
2564
+ const rawPath = resolveValue(mutation.target, options);
2565
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2566
+ const details = {
2567
+ kind: mutation.kind,
2568
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2569
+ targetPath
2570
+ };
2571
+ const format = getConfigFormat(formatName);
2572
+ const template = await context.templates(mutation.templateId);
2573
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
2574
+ const rendered = Mustache.render(template, templateContext);
2575
+ let templateDoc;
2576
+ try {
2577
+ templateDoc = format.parse(rendered);
2578
+ } catch (error) {
2579
+ throw new Error(
2580
+ `Failed to parse rendered template "${mutation.templateId}" as ${formatName.toUpperCase()}: ${error}`,
2581
+ { cause: error }
2582
+ );
2583
+ }
2584
+ const rawContent = await readFileIfExists(context.fs, targetPath);
2585
+ let current;
2586
+ try {
2587
+ current = rawContent === null ? {} : format.parse(rawContent);
2588
+ } catch {
2589
+ if (rawContent !== null) {
2590
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
2591
+ }
2592
+ current = {};
2593
+ }
2594
+ const merged = format.merge(current, templateDoc);
2595
+ const serialized = format.serialize(merged);
2596
+ const changed = serialized !== rawContent;
2597
+ if (changed && !context.dryRun) {
2598
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
2599
+ }
2600
+ return {
2601
+ outcome: {
2602
+ changed,
2603
+ effect: changed ? "write" : "none",
2604
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
2605
+ },
2606
+ details
2607
+ };
2608
+ }
2609
+
2610
+ // ../config-mutations/src/execution/run-mutations.ts
2611
+ async function runMutations(mutations, context, options) {
2612
+ const effects = [];
2613
+ let anyChanged = false;
2614
+ const resolverOptions = options ?? {};
2615
+ for (const mutation of mutations) {
2616
+ const { outcome } = await executeMutation(
2617
+ mutation,
2618
+ context,
2619
+ resolverOptions
2620
+ );
2621
+ effects.push(outcome);
2622
+ if (outcome.changed) {
2623
+ anyChanged = true;
2624
+ }
2625
+ }
2626
+ return {
2627
+ changed: anyChanged,
2628
+ effects
2629
+ };
2630
+ }
2631
+ async function executeMutation(mutation, context, options) {
2632
+ context.observers?.onStart?.({
2633
+ kind: mutation.kind,
2634
+ label: mutation.label ?? mutation.kind,
2635
+ targetPath: void 0
2636
+ // Will be resolved during apply
2637
+ });
2638
+ try {
2639
+ const { outcome, details } = await applyMutation(mutation, context, options);
2640
+ context.observers?.onComplete?.(details, outcome);
2641
+ return { outcome, details };
2642
+ } catch (error) {
2643
+ context.observers?.onError?.(
2644
+ {
2645
+ kind: mutation.kind,
2646
+ label: mutation.label ?? mutation.kind,
2647
+ targetPath: void 0
2648
+ },
2649
+ error
2650
+ );
2651
+ throw error;
2652
+ }
2653
+ }
2654
+
2655
+ // ../config-mutations/src/template/render.ts
2656
+ import Mustache2 from "mustache";
2657
+ var originalEscape = Mustache2.escape;
2658
+
2659
+ // ../agent-skill-config/src/apply.ts
2660
+ var UnsupportedAgentError = class extends Error {
2661
+ constructor(agentId) {
2662
+ super(`Unsupported agent: ${agentId}`);
2663
+ this.name = "UnsupportedAgentError";
2664
+ }
2665
+ };
2666
+ function toHomeRelative(localSkillDir) {
2667
+ if (localSkillDir.startsWith("~/") || localSkillDir === "~") {
2668
+ return localSkillDir;
2669
+ }
2670
+ const normalized = localSkillDir.startsWith("./") ? localSkillDir.slice(2) : localSkillDir;
2671
+ return `~/${normalized}`;
2672
+ }
2673
+ var SKILL_TEMPLATE_ID = "__skill_content__";
2674
+ async function installSkill(agentId, skill, options) {
2675
+ const support = resolveAgentSupport(agentId);
2676
+ if (support.status !== "supported") {
2677
+ throw new UnsupportedAgentError(agentId);
2678
+ }
2679
+ const scope = options.scope ?? "local";
2680
+ const config = support.config;
2681
+ const skillDir = scope === "global" ? config.globalSkillDir : toHomeRelative(config.localSkillDir);
2682
+ const skillFolderPath = `${skillDir}/${skill.name}`;
2683
+ const skillFilePath = `${skillFolderPath}/SKILL.md`;
2684
+ const displayPath = `${scope === "global" ? config.globalSkillDir : config.localSkillDir}/${skill.name}/SKILL.md`;
2685
+ await runMutations(
2686
+ [
2687
+ fileMutation.ensureDirectory({
2688
+ path: skillFolderPath,
2689
+ label: `Ensure skill directory ${skill.name}`
2690
+ }),
2691
+ templateMutation.write({
2692
+ target: skillFilePath,
2693
+ templateId: SKILL_TEMPLATE_ID,
2694
+ label: `Write skill ${skill.name}`
2695
+ })
2696
+ ],
2697
+ {
2698
+ fs: options.fs,
2699
+ homeDir: scope === "global" ? options.homeDir : options.cwd,
2700
+ dryRun: options.dryRun,
2701
+ observers: options.observers,
2702
+ templates: async (templateId) => {
2703
+ if (templateId === SKILL_TEMPLATE_ID) {
2704
+ return skill.content;
2705
+ }
2706
+ throw new Error(`Unknown template: ${templateId}`);
2707
+ }
2708
+ }
2709
+ );
2710
+ return { skillPath: skillFilePath, displayPath };
2711
+ }
2712
+
2713
+ // src/commands/installer.ts
2714
+ import os2 from "node:os";
2715
+ import path3 from "node:path";
2716
+ import * as nodeFs from "node:fs/promises";
2717
+ import { readFile } from "node:fs/promises";
2718
+ var DEFAULT_INSTALL_AGENT = "claude-code";
2719
+ var DEFAULT_INSTALL_SCOPE = "local";
2720
+ var TERMINAL_PILOT_SKILL_NAME = "terminal-pilot";
2721
+ var installableAgents = supportedAgents;
2722
+ function isNotFoundError(error) {
2723
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2724
+ }
2725
+ function resolveInstallerServices(installer) {
2726
+ return {
2727
+ fs: installer?.fs ?? nodeFs,
2728
+ cwd: installer?.cwd ?? process.cwd(),
2729
+ homeDir: installer?.homeDir ?? os2.homedir(),
2730
+ platform: installer?.platform ?? process.platform
2731
+ };
2732
+ }
2733
+ function throwUnsupportedAgent(agent) {
2734
+ throw new UserError(`Unsupported agent: ${agent}`);
2735
+ }
2736
+ function resolveInstallableAgent(agent) {
2737
+ const skillSupport = resolveAgentSupport(agent);
2738
+ if (skillSupport.status !== "supported" || !skillSupport.id) {
2739
+ throwUnsupportedAgent(agent);
2740
+ }
2741
+ return skillSupport.id;
2742
+ }
2743
+ function resolveInstallScope(input) {
2744
+ if (input.local && input.global) {
2745
+ throw new UserError("Use either --local or --global, not both.");
2746
+ }
2747
+ if (input.local) {
2748
+ return "local";
2749
+ }
2750
+ if (input.global) {
2751
+ return "global";
2752
+ }
2753
+ return DEFAULT_INSTALL_SCOPE;
2754
+ }
2755
+ var terminalPilotTemplateCache;
2756
+ async function loadTerminalPilotTemplate() {
2757
+ if (terminalPilotTemplateCache !== void 0) {
2758
+ return terminalPilotTemplateCache;
2759
+ }
2760
+ const candidates = [
2761
+ new URL("./templates/terminal-pilot.md", import.meta.url),
2762
+ new URL("../templates/terminal-pilot.md", import.meta.url),
2763
+ new URL("../../../agent-skill-config/src/templates/terminal-pilot.md", import.meta.url)
2764
+ ];
2765
+ for (const candidate of candidates) {
2766
+ try {
2767
+ terminalPilotTemplateCache = await readFile(candidate, "utf8");
2768
+ return terminalPilotTemplateCache;
2769
+ } catch (error) {
2770
+ if (!isNotFoundError(error)) {
2771
+ throw error;
2772
+ }
2773
+ }
2774
+ }
2775
+ throw new UserError("terminal-pilot skill template is missing.");
2776
+ }
2777
+ function resolveHomeRelativePath(targetPath, homeDir) {
2778
+ if (targetPath === "~") {
2779
+ return homeDir;
2780
+ }
2781
+ if (targetPath.startsWith("~/")) {
2782
+ return path3.join(homeDir, targetPath.slice(2));
2783
+ }
2784
+ return targetPath;
2785
+ }
2786
+ function getSkillFolderWithHome(agent, scope, cwd, homeDir) {
2787
+ const config = getAgentConfig(agent);
2788
+ if (!config) {
2789
+ throwUnsupportedAgent(agent);
2790
+ }
2791
+ return {
2792
+ displayPath: path3.join(
2793
+ scope === "global" ? config.globalSkillDir : config.localSkillDir,
2794
+ TERMINAL_PILOT_SKILL_NAME
2795
+ ),
2796
+ fullPath: path3.join(
2797
+ scope === "global" ? resolveHomeRelativePath(config.globalSkillDir, homeDir) : path3.resolve(cwd, config.localSkillDir),
2798
+ TERMINAL_PILOT_SKILL_NAME
2799
+ )
2800
+ };
2801
+ }
2802
+ async function removeSkillFolder(fs, folderPath) {
2803
+ try {
2804
+ await fs.stat(folderPath);
2805
+ } catch (error) {
2806
+ if (isNotFoundError(error)) {
2807
+ return false;
2808
+ }
2809
+ throw error;
2810
+ }
2811
+ if (typeof fs.rm !== "function") {
2812
+ throw new UserError("The configured filesystem does not support removing directories.");
2813
+ }
2814
+ await fs.rm(folderPath, { recursive: true, force: true });
2815
+ return true;
2816
+ }
2817
+
2818
+ // src/commands/install.ts
2819
+ var params5 = S.Object({
2820
+ agent: S.Enum(installableAgents, {
2821
+ description: "Agent to install terminal-pilot for",
2822
+ default: DEFAULT_INSTALL_AGENT
2823
+ }),
2824
+ local: S.Optional(S.Boolean({ description: "Install the skill in the current project" })),
2825
+ global: S.Optional(S.Boolean({ description: "Install the skill in the user home directory" }))
2826
+ });
2827
+ var install = defineCommand({
2828
+ name: "install",
2829
+ description: "Install the terminal-pilot CLI skill.",
2830
+ scope: ["cli"],
2831
+ positional: ["agent"],
2832
+ params: params5,
2833
+ handler: async ({ params: params17, terminalPilotInstaller }) => {
2834
+ const services = resolveInstallerServices(terminalPilotInstaller);
1460
2835
  const agent = resolveInstallableAgent(params17.agent);
1461
2836
  const scope = resolveInstallScope(params17);
1462
2837
  const template = await loadTerminalPilotTemplate();
@@ -1482,9 +2857,8 @@ var install = defineCommand5({
1482
2857
  });
1483
2858
 
1484
2859
  // src/commands/list-sessions.ts
1485
- import { defineCommand as defineCommand6, S as S6 } from "@poe-code/cmdkit";
1486
- var params6 = S6.Object({});
1487
- var listSessions = defineCommand6({
2860
+ var params6 = S.Object({});
2861
+ var listSessions = defineCommand({
1488
2862
  name: "list-sessions",
1489
2863
  description: "List active terminal sessions",
1490
2864
  scope: ["cli", "mcp", "sdk"],
@@ -1502,12 +2876,11 @@ var listSessions = defineCommand6({
1502
2876
  });
1503
2877
 
1504
2878
  // src/commands/press-key.ts
1505
- import { defineCommand as defineCommand7, S as S7 } from "@poe-code/cmdkit";
1506
- var params7 = S7.Object({
1507
- key: S7.String({ description: "Named key to press" }),
1508
- session: S7.Optional(S7.String({ short: "s", description: "Session name" }))
2879
+ var params7 = S.Object({
2880
+ key: S.String({ description: "Named key to press" }),
2881
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1509
2882
  });
1510
- var pressKey = defineCommand7({
2883
+ var pressKey = defineCommand({
1511
2884
  name: "press-key",
1512
2885
  description: "Send a named key press to an active terminal session",
1513
2886
  scope: ["cli", "mcp", "sdk"],
@@ -1521,12 +2894,11 @@ var pressKey = defineCommand7({
1521
2894
  });
1522
2895
 
1523
2896
  // src/commands/read-history.ts
1524
- import { defineCommand as defineCommand8, S as S8 } from "@poe-code/cmdkit";
1525
- var params8 = S8.Object({
1526
- session: S8.Optional(S8.String({ short: "s", description: "Session name" })),
1527
- last: S8.Optional(S8.Number({ short: "n", description: "Return only the last N lines" }))
2897
+ var params8 = S.Object({
2898
+ session: S.Optional(S.String({ short: "s", description: "Session name" })),
2899
+ last: S.Optional(S.Number({ short: "n", description: "Return only the last N lines" }))
1528
2900
  });
1529
- var readHistory = defineCommand8({
2901
+ var readHistory = defineCommand({
1530
2902
  name: "read-history",
1531
2903
  description: "Read terminal output history",
1532
2904
  scope: ["cli", "mcp", "sdk"],
@@ -1539,11 +2911,10 @@ var readHistory = defineCommand8({
1539
2911
  });
1540
2912
 
1541
2913
  // src/commands/read-screen.ts
1542
- import { defineCommand as defineCommand9, S as S9 } from "@poe-code/cmdkit";
1543
- var params9 = S9.Object({
1544
- session: S9.Optional(S9.String({ short: "s", description: "Session name" }))
2914
+ var params9 = S.Object({
2915
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1545
2916
  });
1546
- var readScreen = defineCommand9({
2917
+ var readScreen = defineCommand({
1547
2918
  name: "read-screen",
1548
2919
  description: "Read the current visible terminal screen",
1549
2920
  scope: ["cli", "mcp", "sdk"],
@@ -1561,13 +2932,12 @@ var readScreen = defineCommand9({
1561
2932
  });
1562
2933
 
1563
2934
  // src/commands/resize.ts
1564
- import { defineCommand as defineCommand10, S as S10 } from "@poe-code/cmdkit";
1565
- var params10 = S10.Object({
1566
- cols: S10.Number({ description: "Terminal width in columns" }),
1567
- rows: S10.Number({ description: "Terminal height in rows" }),
1568
- session: S10.Optional(S10.String({ short: "s", description: "Session name" }))
2935
+ var params10 = S.Object({
2936
+ cols: S.Number({ description: "Terminal width in columns" }),
2937
+ rows: S.Number({ description: "Terminal height in rows" }),
2938
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1569
2939
  });
1570
- var resize = defineCommand10({
2940
+ var resize = defineCommand({
1571
2941
  name: "resize",
1572
2942
  description: "Resize an active terminal session",
1573
2943
  scope: ["cli", "mcp", "sdk"],
@@ -1579,16 +2949,776 @@ var resize = defineCommand10({
1579
2949
  }
1580
2950
  });
1581
2951
 
2952
+ // ../terminal-png/src/index.ts
2953
+ import { writeFile } from "node:fs/promises";
2954
+
2955
+ // ../terminal-png/src/ansi-parser.ts
2956
+ var ESC2 = "\x1B";
2957
+ function createDefaultStyle() {
2958
+ return {
2959
+ fg: null,
2960
+ bg: null,
2961
+ bold: false,
2962
+ italic: false,
2963
+ underline: false,
2964
+ strikethrough: false,
2965
+ dim: false
2966
+ };
2967
+ }
2968
+ function cloneStyle(style) {
2969
+ return { ...style };
2970
+ }
2971
+ function stylesEqual(left, right) {
2972
+ return left.bold === right.bold && left.italic === right.italic && left.underline === right.underline && left.strikethrough === right.strikethrough && left.dim === right.dim && colorsEqual(left.fg, right.fg) && colorsEqual(left.bg, right.bg);
2973
+ }
2974
+ function colorsEqual(left, right) {
2975
+ if (left === right) {
2976
+ return true;
2977
+ }
2978
+ if (!left || !right || left.type !== right.type) {
2979
+ return false;
2980
+ }
2981
+ if (left.type === "rgb" && right.type === "rgb") {
2982
+ return left.r === right.r && left.g === right.g && left.b === right.b;
2983
+ }
2984
+ if ((left.type === "ansi4" || left.type === "ansi8") && left.type === right.type) {
2985
+ return left.index === right.index;
2986
+ }
2987
+ return false;
2988
+ }
2989
+ function pushRun(runs, style, text) {
2990
+ if (text.length === 0) {
2991
+ return;
2992
+ }
2993
+ const previous = runs.at(-1);
2994
+ if (previous && text !== "\n" && previous.text !== "\n" && stylesEqual(previous, style)) {
2995
+ previous.text += text;
2996
+ return;
2997
+ }
2998
+ runs.push({
2999
+ text,
3000
+ fg: style.fg,
3001
+ bg: style.bg,
3002
+ bold: style.bold,
3003
+ italic: style.italic,
3004
+ underline: style.underline,
3005
+ strikethrough: style.strikethrough,
3006
+ dim: style.dim
3007
+ });
3008
+ }
3009
+ function parseCsi(input, start) {
3010
+ let index = start + 2;
3011
+ while (index < input.length) {
3012
+ const code = input.charCodeAt(index);
3013
+ if (code >= 64 && code <= 126) {
3014
+ return {
3015
+ end: index + 1,
3016
+ final: input[index],
3017
+ params: input.slice(start + 2, index)
3018
+ };
3019
+ }
3020
+ index += 1;
3021
+ }
3022
+ return {
3023
+ end: input.length,
3024
+ final: null,
3025
+ params: input.slice(start + 2)
3026
+ };
3027
+ }
3028
+ function toInteger(value) {
3029
+ if (!value || value.length === 0) {
3030
+ return null;
3031
+ }
3032
+ for (const char of value) {
3033
+ if (char < "0" || char > "9") {
3034
+ return null;
3035
+ }
3036
+ }
3037
+ return Number.parseInt(value, 10);
3038
+ }
3039
+ function clampByte(value) {
3040
+ if (value < 0) {
3041
+ return 0;
3042
+ }
3043
+ if (value > 255) {
3044
+ return 255;
3045
+ }
3046
+ return value;
3047
+ }
3048
+ function applyExtendedColor(params17, start) {
3049
+ const mode = params17[start];
3050
+ if (mode === 5) {
3051
+ const index = params17[start + 1];
3052
+ if (index === void 0) {
3053
+ return null;
3054
+ }
3055
+ return {
3056
+ color: { type: "ansi8", index: clampByte(index) },
3057
+ consumed: 2
3058
+ };
3059
+ }
3060
+ if (mode === 2) {
3061
+ const r = params17[start + 1];
3062
+ const g = params17[start + 2];
3063
+ const b = params17[start + 3];
3064
+ if (r === void 0 || g === void 0 || b === void 0) {
3065
+ return null;
3066
+ }
3067
+ return {
3068
+ color: { type: "rgb", r: clampByte(r), g: clampByte(g), b: clampByte(b) },
3069
+ consumed: 4
3070
+ };
3071
+ }
3072
+ return null;
3073
+ }
3074
+ function applySgr(style, paramsText) {
3075
+ const nextStyle = cloneStyle(style);
3076
+ const rawParams = paramsText.length === 0 ? ["0"] : paramsText.split(";");
3077
+ const params17 = [];
3078
+ for (const rawParam of rawParams) {
3079
+ const value = toInteger(rawParam);
3080
+ if (value === null) {
3081
+ return nextStyle;
3082
+ }
3083
+ params17.push(value);
3084
+ }
3085
+ for (let index = 0; index < params17.length; index += 1) {
3086
+ const value = params17[index];
3087
+ if (value === 0) {
3088
+ Object.assign(nextStyle, createDefaultStyle());
3089
+ continue;
3090
+ }
3091
+ if (value === 1) {
3092
+ nextStyle.bold = true;
3093
+ continue;
3094
+ }
3095
+ if (value === 2) {
3096
+ nextStyle.dim = true;
3097
+ continue;
3098
+ }
3099
+ if (value === 22) {
3100
+ nextStyle.bold = false;
3101
+ nextStyle.dim = false;
3102
+ continue;
3103
+ }
3104
+ if (value === 3) {
3105
+ nextStyle.italic = true;
3106
+ continue;
3107
+ }
3108
+ if (value === 23) {
3109
+ nextStyle.italic = false;
3110
+ continue;
3111
+ }
3112
+ if (value === 4) {
3113
+ nextStyle.underline = true;
3114
+ continue;
3115
+ }
3116
+ if (value === 24) {
3117
+ nextStyle.underline = false;
3118
+ continue;
3119
+ }
3120
+ if (value === 9) {
3121
+ nextStyle.strikethrough = true;
3122
+ continue;
3123
+ }
3124
+ if (value === 29) {
3125
+ nextStyle.strikethrough = false;
3126
+ continue;
3127
+ }
3128
+ if (value === 39) {
3129
+ nextStyle.fg = null;
3130
+ continue;
3131
+ }
3132
+ if (value === 49) {
3133
+ nextStyle.bg = null;
3134
+ continue;
3135
+ }
3136
+ if (value >= 30 && value <= 37) {
3137
+ nextStyle.fg = { type: "ansi4", index: value - 30 };
3138
+ continue;
3139
+ }
3140
+ if (value >= 90 && value <= 97) {
3141
+ nextStyle.fg = { type: "ansi4", index: value - 90 + 8 };
3142
+ continue;
3143
+ }
3144
+ if (value >= 40 && value <= 47) {
3145
+ nextStyle.bg = { type: "ansi4", index: value - 40 };
3146
+ continue;
3147
+ }
3148
+ if (value >= 100 && value <= 107) {
3149
+ nextStyle.bg = { type: "ansi4", index: value - 100 + 8 };
3150
+ continue;
3151
+ }
3152
+ if (value === 38 || value === 48) {
3153
+ const extended = applyExtendedColor(params17, index + 1);
3154
+ if (!extended) {
3155
+ continue;
3156
+ }
3157
+ if (value === 38) {
3158
+ nextStyle.fg = extended.color;
3159
+ } else {
3160
+ nextStyle.bg = extended.color;
3161
+ }
3162
+ index += extended.consumed;
3163
+ }
3164
+ }
3165
+ return nextStyle;
3166
+ }
3167
+ function parseAnsi(input) {
3168
+ const runs = [];
3169
+ let style = createDefaultStyle();
3170
+ let textStart = 0;
3171
+ let index = 0;
3172
+ while (index < input.length) {
3173
+ const char = input[index];
3174
+ if (char === "\n") {
3175
+ pushRun(runs, style, input.slice(textStart, index));
3176
+ pushRun(runs, style, "\n");
3177
+ index += 1;
3178
+ textStart = index;
3179
+ continue;
3180
+ }
3181
+ if (char === ESC2 && input[index + 1] === "[") {
3182
+ pushRun(runs, style, input.slice(textStart, index));
3183
+ const sequence = parseCsi(input, index);
3184
+ if (sequence.final === "m") {
3185
+ style = applySgr(style, sequence.params);
3186
+ }
3187
+ index = sequence.end;
3188
+ textStart = index;
3189
+ continue;
3190
+ }
3191
+ index += 1;
3192
+ }
3193
+ pushRun(runs, style, input.slice(textStart));
3194
+ return runs;
3195
+ }
3196
+
3197
+ // ../terminal-png/src/png-renderer.ts
3198
+ import { Resvg } from "@resvg/resvg-js";
3199
+
3200
+ // ../terminal-png/src/font.ts
3201
+ import { readFileSync } from "node:fs";
3202
+ import { createRequire as createRequire2 } from "node:module";
3203
+ import { dirname as dirname2, join as join2 } from "node:path";
3204
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
3205
+ var require2 = createRequire2(import.meta.url);
3206
+ var fontPackageRoot = dirname2(require2.resolve("jetbrains-mono/package.json"));
3207
+ var webfontRoot = join2(fontPackageRoot, "fonts/webfonts");
3208
+ function readWebfontBase64(filename) {
3209
+ return readFileSync(join2(webfontRoot, filename)).toString("base64");
3210
+ }
3211
+ function resolveAssetPath(filename) {
3212
+ return fileURLToPath2(new URL(`../assets/${filename}`, import.meta.url));
3213
+ }
3214
+ function createFontFace(base64, weight, style) {
3215
+ return `@font-face {
3216
+ font-family: 'JetBrains Mono';
3217
+ font-style: ${style};
3218
+ font-weight: ${weight};
3219
+ src: url('data:font/woff2;base64,${base64}') format('woff2');
3220
+ }`;
3221
+ }
3222
+ var JETBRAINS_MONO_BASE64 = readWebfontBase64("JetBrainsMono-Regular.woff2");
3223
+ var JETBRAINS_MONO_FONT_FILES = [
3224
+ resolveAssetPath("jetbrains-mono-400-normal.ttf"),
3225
+ resolveAssetPath("jetbrains-mono-700-normal.ttf"),
3226
+ resolveAssetPath("jetbrains-mono-400-italic.ttf"),
3227
+ resolveAssetPath("jetbrains-mono-700-italic.ttf")
3228
+ ];
3229
+ var JETBRAINS_MONO_TTF_PATH = JETBRAINS_MONO_FONT_FILES[0];
3230
+ var FONT_FACE_CSS = [
3231
+ createFontFace(JETBRAINS_MONO_BASE64, 400, "normal"),
3232
+ createFontFace(readWebfontBase64("JetBrainsMono-Bold.woff2"), 700, "normal"),
3233
+ createFontFace(readWebfontBase64("JetBrainsMono-Italic.woff2"), 400, "italic"),
3234
+ createFontFace(readWebfontBase64("JetBrainsMono-BoldItalic.woff2"), 700, "italic")
3235
+ ].join("\n");
3236
+
3237
+ // ../terminal-png/src/png-renderer.ts
3238
+ function renderPng(svg) {
3239
+ const resvg = new Resvg(svg, {
3240
+ font: {
3241
+ defaultFontFamily: "JetBrains Mono",
3242
+ fontFiles: [...JETBRAINS_MONO_FONT_FILES],
3243
+ loadSystemFonts: false,
3244
+ monospaceFamily: "JetBrains Mono"
3245
+ },
3246
+ fitTo: {
3247
+ mode: "zoom",
3248
+ value: 4
3249
+ }
3250
+ });
3251
+ const png = resvg.render();
3252
+ return Buffer.from(png.asPng());
3253
+ }
3254
+
3255
+ // ../terminal-png/src/svg-renderer.ts
3256
+ var ANSI_16_PALETTE = [
3257
+ "#282a2e",
3258
+ "#D74E6F",
3259
+ "#31BB71",
3260
+ "#D3E561",
3261
+ "#8056FF",
3262
+ "#ED61D7",
3263
+ "#04D7D7",
3264
+ "#C5C8C6",
3265
+ "#4B4B4B",
3266
+ "#FE5F86",
3267
+ "#00D787",
3268
+ "#EBFF71",
3269
+ "#8F69FF",
3270
+ "#FF7AEA",
3271
+ "#00FEFE",
3272
+ "#FFFFFF"
3273
+ ];
3274
+ var XTERM_256_PALETTE = [
3275
+ "#000000",
3276
+ "#800000",
3277
+ "#008000",
3278
+ "#808000",
3279
+ "#000080",
3280
+ "#800080",
3281
+ "#008080",
3282
+ "#c0c0c0",
3283
+ "#808080",
3284
+ "#ff0000",
3285
+ "#00ff00",
3286
+ "#ffff00",
3287
+ "#0000ff",
3288
+ "#ff00ff",
3289
+ "#00ffff",
3290
+ "#ffffff",
3291
+ "#000000",
3292
+ "#00005f",
3293
+ "#000087",
3294
+ "#0000af",
3295
+ "#0000d7",
3296
+ "#0000ff",
3297
+ "#005f00",
3298
+ "#005f5f",
3299
+ "#005f87",
3300
+ "#005faf",
3301
+ "#005fd7",
3302
+ "#005fff",
3303
+ "#008700",
3304
+ "#00875f",
3305
+ "#008787",
3306
+ "#0087af",
3307
+ "#0087d7",
3308
+ "#0087ff",
3309
+ "#00af00",
3310
+ "#00af5f",
3311
+ "#00af87",
3312
+ "#00afaf",
3313
+ "#00afd7",
3314
+ "#00afff",
3315
+ "#00d700",
3316
+ "#00d75f",
3317
+ "#00d787",
3318
+ "#00d7af",
3319
+ "#00d7d7",
3320
+ "#00d7ff",
3321
+ "#00ff00",
3322
+ "#00ff5f",
3323
+ "#00ff87",
3324
+ "#00ffaf",
3325
+ "#00ffd7",
3326
+ "#00ffff",
3327
+ "#5f0000",
3328
+ "#5f005f",
3329
+ "#5f0087",
3330
+ "#5f00af",
3331
+ "#5f00d7",
3332
+ "#5f00ff",
3333
+ "#5f5f00",
3334
+ "#5f5f5f",
3335
+ "#5f5f87",
3336
+ "#5f5faf",
3337
+ "#5f5fd7",
3338
+ "#5f5fff",
3339
+ "#5f8700",
3340
+ "#5f875f",
3341
+ "#5f8787",
3342
+ "#5f87af",
3343
+ "#5f87d7",
3344
+ "#5f87ff",
3345
+ "#5faf00",
3346
+ "#5faf5f",
3347
+ "#5faf87",
3348
+ "#5fafaf",
3349
+ "#5fafd7",
3350
+ "#5fafff",
3351
+ "#5fd700",
3352
+ "#5fd75f",
3353
+ "#5fd787",
3354
+ "#5fd7af",
3355
+ "#5fd7d7",
3356
+ "#5fd7ff",
3357
+ "#5fff00",
3358
+ "#5fff5f",
3359
+ "#5fff87",
3360
+ "#5fffaf",
3361
+ "#5fffd7",
3362
+ "#5fffff",
3363
+ "#870000",
3364
+ "#87005f",
3365
+ "#870087",
3366
+ "#8700af",
3367
+ "#8700d7",
3368
+ "#8700ff",
3369
+ "#875f00",
3370
+ "#875f5f",
3371
+ "#875f87",
3372
+ "#875faf",
3373
+ "#875fd7",
3374
+ "#875fff",
3375
+ "#878700",
3376
+ "#87875f",
3377
+ "#878787",
3378
+ "#8787af",
3379
+ "#8787d7",
3380
+ "#8787ff",
3381
+ "#87af00",
3382
+ "#87af5f",
3383
+ "#87af87",
3384
+ "#87afaf",
3385
+ "#87afd7",
3386
+ "#87afff",
3387
+ "#87d700",
3388
+ "#87d75f",
3389
+ "#87d787",
3390
+ "#87d7af",
3391
+ "#87d7d7",
3392
+ "#87d7ff",
3393
+ "#87ff00",
3394
+ "#87ff5f",
3395
+ "#87ff87",
3396
+ "#87ffaf",
3397
+ "#87ffd7",
3398
+ "#87ffff",
3399
+ "#af0000",
3400
+ "#af005f",
3401
+ "#af0087",
3402
+ "#af00af",
3403
+ "#af00d7",
3404
+ "#af00ff",
3405
+ "#af5f00",
3406
+ "#af5f5f",
3407
+ "#af5f87",
3408
+ "#af5faf",
3409
+ "#af5fd7",
3410
+ "#af5fff",
3411
+ "#af8700",
3412
+ "#af875f",
3413
+ "#af8787",
3414
+ "#af87af",
3415
+ "#af87d7",
3416
+ "#af87ff",
3417
+ "#afaf00",
3418
+ "#afaf5f",
3419
+ "#afaf87",
3420
+ "#afafaf",
3421
+ "#afafd7",
3422
+ "#afafff",
3423
+ "#afd700",
3424
+ "#afd75f",
3425
+ "#afd787",
3426
+ "#afd7af",
3427
+ "#afd7d7",
3428
+ "#afd7ff",
3429
+ "#afff00",
3430
+ "#afff5f",
3431
+ "#afff87",
3432
+ "#afffaf",
3433
+ "#afffd7",
3434
+ "#afffff",
3435
+ "#d70000",
3436
+ "#d7005f",
3437
+ "#d70087",
3438
+ "#d700af",
3439
+ "#d700d7",
3440
+ "#d700ff",
3441
+ "#d75f00",
3442
+ "#d75f5f",
3443
+ "#d75f87",
3444
+ "#d75faf",
3445
+ "#d75fd7",
3446
+ "#d75fff",
3447
+ "#d78700",
3448
+ "#d7875f",
3449
+ "#d78787",
3450
+ "#d787af",
3451
+ "#d787d7",
3452
+ "#d787ff",
3453
+ "#d7af00",
3454
+ "#d7af5f",
3455
+ "#d7af87",
3456
+ "#d7afaf",
3457
+ "#d7afd7",
3458
+ "#d7afff",
3459
+ "#d7d700",
3460
+ "#d7d75f",
3461
+ "#d7d787",
3462
+ "#d7d7af",
3463
+ "#d7d7d7",
3464
+ "#d7d7ff",
3465
+ "#d7ff00",
3466
+ "#d7ff5f",
3467
+ "#d7ff87",
3468
+ "#d7ffaf",
3469
+ "#d7ffd7",
3470
+ "#d7ffff",
3471
+ "#ff0000",
3472
+ "#ff005f",
3473
+ "#ff0087",
3474
+ "#ff00af",
3475
+ "#ff00d7",
3476
+ "#ff00ff",
3477
+ "#ff5f00",
3478
+ "#ff5f5f",
3479
+ "#ff5f87",
3480
+ "#ff5faf",
3481
+ "#ff5fd7",
3482
+ "#ff5fff",
3483
+ "#ff8700",
3484
+ "#ff875f",
3485
+ "#ff8787",
3486
+ "#ff87af",
3487
+ "#ff87d7",
3488
+ "#ff87ff",
3489
+ "#ffaf00",
3490
+ "#ffaf5f",
3491
+ "#ffaf87",
3492
+ "#ffafaf",
3493
+ "#ffafd7",
3494
+ "#ffafff",
3495
+ "#ffd700",
3496
+ "#ffd75f",
3497
+ "#ffd787",
3498
+ "#ffd7af",
3499
+ "#ffd7d7",
3500
+ "#ffd7ff",
3501
+ "#ffff00",
3502
+ "#ffff5f",
3503
+ "#ffff87",
3504
+ "#ffffaf",
3505
+ "#ffffd7",
3506
+ "#ffffff",
3507
+ "#080808",
3508
+ "#121212",
3509
+ "#1c1c1c",
3510
+ "#262626",
3511
+ "#303030",
3512
+ "#3a3a3a",
3513
+ "#444444",
3514
+ "#4e4e4e",
3515
+ "#585858",
3516
+ "#606060",
3517
+ "#666666",
3518
+ "#767676",
3519
+ "#808080",
3520
+ "#8a8a8a",
3521
+ "#949494",
3522
+ "#9e9e9e",
3523
+ "#a8a8a8",
3524
+ "#b2b2b2",
3525
+ "#bcbcbc",
3526
+ "#c6c6c6",
3527
+ "#d0d0d0",
3528
+ "#dadada",
3529
+ "#e4e4e4",
3530
+ "#eeeeee"
3531
+ ];
3532
+ var BACKGROUND = "#171717";
3533
+ var DEFAULT_FOREGROUND = "#c4c4c4";
3534
+ var FONT_FAMILY = "JetBrains Mono";
3535
+ var FONT_SIZE = 14;
3536
+ var LINE_HEIGHT = 1.2;
3537
+ var CHARACTER_WIDTH = 8.412666666666667;
3538
+ var TEXT_BOTTOM_PADDING = 11.6;
3539
+ var DEFAULT_PADDING = {
3540
+ top: 20,
3541
+ right: 40,
3542
+ bottom: 20,
3543
+ left: 20
3544
+ };
3545
+ var WINDOW_BAR_HEIGHT = 15;
3546
+ function renderSvg(runs, options = {}) {
3547
+ const padding = options.padding === void 0 ? DEFAULT_PADDING : {
3548
+ top: options.padding,
3549
+ right: options.padding,
3550
+ bottom: options.padding,
3551
+ left: options.padding
3552
+ };
3553
+ const showWindow = options.window ?? true;
3554
+ const titleBarHeight = showWindow ? WINDOW_BAR_HEIGHT : 0;
3555
+ const textStartX = padding.left;
3556
+ const lineHeightPx = FONT_SIZE * LINE_HEIGHT;
3557
+ const textStartY = titleBarHeight + padding.top + lineHeightPx;
3558
+ const lines = splitIntoLines(runs);
3559
+ const contentWidth = measureLines(lines);
3560
+ const width = padding.left + contentWidth + padding.right;
3561
+ const height = titleBarHeight + padding.top + lines.length * lineHeightPx + padding.bottom + TEXT_BOTTOM_PADDING;
3562
+ const svgWidth = formatNumber(width);
3563
+ const svgHeight = formatNumber(height);
3564
+ const viewBox = `0 0 ${svgWidth} ${svgHeight}`;
3565
+ const textElements = renderLines(lines, textStartX, textStartY, lineHeightPx);
3566
+ return [
3567
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${svgWidth}" height="${svgHeight}" viewBox="${viewBox}">`,
3568
+ "<defs>",
3569
+ "<style><![CDATA[",
3570
+ FONT_FACE_CSS,
3571
+ "]]></style>",
3572
+ "</defs>",
3573
+ `<rect x="0" y="0" width="${svgWidth}" height="${svgHeight}" fill="${BACKGROUND}" />`,
3574
+ showWindow ? renderWindowControls() : "",
3575
+ `<g font-family="${FONT_FAMILY}" font-size="${formatNumber(FONT_SIZE)}px" fill="${DEFAULT_FOREGROUND}">`,
3576
+ textElements,
3577
+ "</g>",
3578
+ "</svg>"
3579
+ ].join("");
3580
+ }
3581
+ function splitIntoLines(runs) {
3582
+ const lines = [[]];
3583
+ for (const run of runs) {
3584
+ if (run.text === "\n") {
3585
+ lines.push([]);
3586
+ continue;
3587
+ }
3588
+ lines[lines.length - 1]?.push(run);
3589
+ }
3590
+ return lines;
3591
+ }
3592
+ function measureLines(lines) {
3593
+ return Math.max(
3594
+ ...lines.map((line) => line.reduce((width, run) => width + displayWidth(run.text) * CHARACTER_WIDTH, 0)),
3595
+ 0
3596
+ );
3597
+ }
3598
+ function displayWidth(text) {
3599
+ const segmenter = new Intl.Segmenter();
3600
+ let width = 0;
3601
+ for (const { segment } of segmenter.segment(text)) {
3602
+ const cp = segment.codePointAt(0);
3603
+ width += cp !== void 0 && isWideCodePoint(cp) ? 2 : 1;
3604
+ }
3605
+ return width;
3606
+ }
3607
+ function isWideCodePoint(cp) {
3608
+ return cp >= 4352 && cp <= 4447 || // Hangul Jamo
3609
+ cp === 9001 || cp === 9002 || // Angle brackets
3610
+ cp >= 11904 && cp <= 12350 || // CJK Radicals, Kangxi
3611
+ cp >= 12353 && cp <= 13247 || // Hiragana, Katakana, CJK symbols
3612
+ cp >= 13312 && cp <= 19903 || // CJK Extension A
3613
+ cp >= 19968 && cp <= 42191 || // CJK Unified Ideographs
3614
+ cp >= 43360 && cp <= 43391 || // Hangul Jamo Extended-A
3615
+ cp >= 44032 && cp <= 55215 || // Hangul Syllables
3616
+ cp >= 63744 && cp <= 64255 || // CJK Compatibility Ideographs
3617
+ cp >= 65040 && cp <= 65049 || // Vertical Forms
3618
+ cp >= 65072 && cp <= 65135 || // CJK Compatibility Forms
3619
+ cp >= 65280 && cp <= 65376 || // Fullwidth Latin, Halfwidth Katakana
3620
+ cp >= 65504 && cp <= 65510 || // Fullwidth Signs
3621
+ cp >= 110592 && cp <= 110847 || // Kana Supplement
3622
+ cp >= 126980 && cp <= 126980 || // Mahjong tile
3623
+ cp >= 127183 && cp <= 127183 || // Playing card black joker
3624
+ cp >= 127488 && cp <= 131069 || // Enclosed CJK + Emoji
3625
+ cp >= 131072 && cp <= 196605 || // CJK Extension B–F
3626
+ cp >= 196608 && cp <= 262141;
3627
+ }
3628
+ function renderLines(lines, textStartX, textStartY, lineHeightPx) {
3629
+ return lines.map((line, index) => {
3630
+ const y = formatNumber(textStartY + index * lineHeightPx);
3631
+ if (line.length === 0) {
3632
+ return `<text x="${formatNumber(textStartX)}" y="${y}" xml:space="preserve"/>`;
3633
+ }
3634
+ return [
3635
+ `<text x="${formatNumber(textStartX)}" y="${y}" xml:space="preserve">`,
3636
+ line.map(renderRun).join(""),
3637
+ "</text>"
3638
+ ].join("");
3639
+ }).join("");
3640
+ }
3641
+ function renderRun(run) {
3642
+ const attributes = ['xml:space="preserve"'];
3643
+ const color = resolveColor(run.fg);
3644
+ const textDecorations = [];
3645
+ if (color !== DEFAULT_FOREGROUND) {
3646
+ attributes.push(`fill="${escapeXmlAttribute(color)}"`);
3647
+ }
3648
+ if (run.bold) {
3649
+ attributes.push('font-weight="bold"');
3650
+ }
3651
+ if (run.italic) {
3652
+ attributes.push('font-style="italic"');
3653
+ }
3654
+ if (run.underline) {
3655
+ textDecorations.push("underline");
3656
+ }
3657
+ if (run.strikethrough) {
3658
+ textDecorations.push("line-through");
3659
+ }
3660
+ if (textDecorations.length > 0) {
3661
+ attributes.push(`text-decoration="${textDecorations.join(" ")}"`);
3662
+ }
3663
+ if (run.dim) {
3664
+ attributes.push('opacity="0.7"');
3665
+ }
3666
+ return `<tspan ${attributes.join(" ")}>${escapeXmlText(run.text)}</tspan>`;
3667
+ }
3668
+ function renderWindowControls() {
3669
+ return [
3670
+ '<circle cx="13.5" cy="12" r="5.5" fill="#FF5A54" />',
3671
+ '<circle cx="32.5" cy="12" r="5.5" fill="#E6BF29" />',
3672
+ '<circle cx="51.5" cy="12" r="5.5" fill="#52C12B" />'
3673
+ ].join("");
3674
+ }
3675
+ function resolveColor(color) {
3676
+ if (color === null) {
3677
+ return DEFAULT_FOREGROUND;
3678
+ }
3679
+ if (color.type === "ansi4") {
3680
+ return ANSI_16_PALETTE[color.index] ?? DEFAULT_FOREGROUND;
3681
+ }
3682
+ if (color.type === "ansi8") {
3683
+ if (color.index < ANSI_16_PALETTE.length) {
3684
+ return ANSI_16_PALETTE[color.index] ?? DEFAULT_FOREGROUND;
3685
+ }
3686
+ return XTERM_256_PALETTE[color.index] ?? DEFAULT_FOREGROUND;
3687
+ }
3688
+ return `rgb(${color.r},${color.g},${color.b})`;
3689
+ }
3690
+ function escapeXmlText(value) {
3691
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
3692
+ }
3693
+ function escapeXmlAttribute(value) {
3694
+ return escapeXmlText(value).replaceAll('"', "&quot;").replaceAll("'", "&apos;");
3695
+ }
3696
+ function formatNumber(value) {
3697
+ return value.toFixed(2);
3698
+ }
3699
+
3700
+ // ../terminal-png/src/index.ts
3701
+ async function renderTerminalPng(ansiText, options = {}) {
3702
+ const runs = parseAnsi(ansiText);
3703
+ const svg = renderSvg(runs, {
3704
+ padding: options.padding,
3705
+ window: options.window
3706
+ });
3707
+ const png = renderPng(svg);
3708
+ if (options.output) {
3709
+ await writeFile(options.output, png);
3710
+ }
3711
+ return png;
3712
+ }
3713
+
1582
3714
  // src/commands/screenshot.ts
1583
- import { defineCommand as defineCommand11, S as S11 } from "@poe-code/cmdkit";
1584
- import { renderTerminalPng } from "terminal-png";
1585
- var params11 = S11.Object({
1586
- session: S11.Optional(S11.String({ short: "s", description: "Session name" })),
1587
- output: S11.String({ short: "o", description: "Path to the output PNG file" }),
1588
- window: S11.Optional(S11.Boolean({ description: "Include terminal window chrome", default: true })),
1589
- padding: S11.Optional(S11.Number({ short: "p", description: "Padding around terminal content" }))
3715
+ var params11 = S.Object({
3716
+ session: S.Optional(S.String({ short: "s", description: "Session name" })),
3717
+ output: S.String({ short: "o", description: "Path to the output PNG file" }),
3718
+ window: S.Optional(S.Boolean({ description: "Include terminal window chrome", default: true })),
3719
+ padding: S.Optional(S.Number({ short: "p", description: "Padding around terminal content" }))
1590
3720
  });
1591
- var screenshot = defineCommand11({
3721
+ var screenshot = defineCommand({
1592
3722
  name: "screenshot",
1593
3723
  description: "Capture the current terminal screen as a PNG image",
1594
3724
  scope: ["cli"],
@@ -1606,12 +3736,11 @@ var screenshot = defineCommand11({
1606
3736
  });
1607
3737
 
1608
3738
  // src/commands/send-signal.ts
1609
- import { defineCommand as defineCommand12, S as S12 } from "@poe-code/cmdkit";
1610
- var params12 = S12.Object({
1611
- signal: S12.String({ description: "Signal to send to the session process" }),
1612
- session: S12.Optional(S12.String({ short: "s", description: "Session name" }))
3739
+ var params12 = S.Object({
3740
+ signal: S.String({ description: "Signal to send to the session process" }),
3741
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1613
3742
  });
1614
- var sendSignal = defineCommand12({
3743
+ var sendSignal = defineCommand({
1615
3744
  name: "send-signal",
1616
3745
  description: "Send a process signal to an active terminal session",
1617
3746
  scope: ["cli", "mcp", "sdk"],
@@ -1625,12 +3754,11 @@ var sendSignal = defineCommand12({
1625
3754
  });
1626
3755
 
1627
3756
  // src/commands/type.ts
1628
- import { defineCommand as defineCommand13, S as S13 } from "@poe-code/cmdkit";
1629
- var params13 = S13.Object({
1630
- text: S13.String({ description: "Text to write to the session" }),
1631
- session: S13.Optional(S13.String({ short: "s", description: "Session name" }))
3757
+ var params13 = S.Object({
3758
+ text: S.String({ description: "Text to write to the session" }),
3759
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1632
3760
  });
1633
- var type = defineCommand13({
3761
+ var type = defineCommand({
1634
3762
  name: "type",
1635
3763
  description: "Write text to an active terminal session character-by-character with delay",
1636
3764
  scope: ["cli", "mcp", "sdk"],
@@ -1644,14 +3772,13 @@ var type = defineCommand13({
1644
3772
  });
1645
3773
 
1646
3774
  // src/commands/uninstall.ts
1647
- import { defineCommand as defineCommand14, S as S14 } from "@poe-code/cmdkit";
1648
- var params14 = S14.Object({
1649
- agent: S14.Enum(installableAgents, {
3775
+ var params14 = S.Object({
3776
+ agent: S.Enum(installableAgents, {
1650
3777
  description: "Agent to uninstall terminal-pilot from",
1651
3778
  default: DEFAULT_INSTALL_AGENT
1652
3779
  })
1653
3780
  });
1654
- var uninstall = defineCommand14({
3781
+ var uninstall = defineCommand({
1655
3782
  name: "uninstall",
1656
3783
  description: "Remove the terminal-pilot CLI skill.",
1657
3784
  scope: ["cli"],
@@ -1687,19 +3814,18 @@ var uninstall = defineCommand14({
1687
3814
  });
1688
3815
 
1689
3816
  // src/commands/wait-for.ts
1690
- import { defineCommand as defineCommand15, S as S15 } from "@poe-code/cmdkit";
1691
- var params15 = S15.Object({
1692
- pattern: S15.String({ description: "Regular expression pattern to wait for" }),
1693
- session: S15.Optional(S15.String({ short: "s", description: "Session name" })),
1694
- timeout: S15.Optional(S15.Number({ short: "t", description: "Maximum wait time in milliseconds" })),
1695
- literal: S15.Optional(
1696
- S15.Boolean({
3817
+ var params15 = S.Object({
3818
+ pattern: S.String({ description: "Regular expression pattern to wait for" }),
3819
+ session: S.Optional(S.String({ short: "s", description: "Session name" })),
3820
+ timeout: S.Optional(S.Number({ short: "t", description: "Maximum wait time in milliseconds" })),
3821
+ literal: S.Optional(
3822
+ S.Boolean({
1697
3823
  short: "l",
1698
3824
  description: "When true, treat pattern as a literal string instead of a regex"
1699
3825
  })
1700
3826
  )
1701
3827
  });
1702
- var waitFor = defineCommand15({
3828
+ var waitFor = defineCommand({
1703
3829
  name: "wait-for",
1704
3830
  description: "Wait for terminal output to match a pattern",
1705
3831
  scope: ["cli", "mcp", "sdk"],
@@ -1714,12 +3840,11 @@ var waitFor = defineCommand15({
1714
3840
  });
1715
3841
 
1716
3842
  // src/commands/wait-for-exit.ts
1717
- import { defineCommand as defineCommand16, S as S16 } from "@poe-code/cmdkit";
1718
- var params16 = S16.Object({
1719
- session: S16.Optional(S16.String({ short: "s", description: "Session name" })),
1720
- timeout: S16.Optional(S16.Number({ short: "t", description: "Maximum wait time in milliseconds" }))
3843
+ var params16 = S.Object({
3844
+ session: S.Optional(S.String({ short: "s", description: "Session name" })),
3845
+ timeout: S.Optional(S.Number({ short: "t", description: "Maximum wait time in milliseconds" }))
1721
3846
  });
1722
- var waitForExit2 = defineCommand16({
3847
+ var waitForExit2 = defineCommand({
1723
3848
  name: "wait-for-exit",
1724
3849
  description: "Wait for a terminal session process to finish",
1725
3850
  scope: ["cli", "mcp", "sdk"],