terminal-pilot 0.0.6 → 0.0.7

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 (45) hide show
  1. package/dist/cli.js +5708 -1261
  2. package/dist/cli.js.map +4 -4
  3. package/dist/commands/close-session.js +249 -4
  4. package/dist/commands/close-session.js.map +4 -4
  5. package/dist/commands/create-session.js +249 -4
  6. package/dist/commands/create-session.js.map +4 -4
  7. package/dist/commands/fill.js +249 -4
  8. package/dist/commands/fill.js.map +4 -4
  9. package/dist/commands/get-session.js +249 -4
  10. package/dist/commands/get-session.js.map +4 -4
  11. package/dist/commands/index.js +2376 -184
  12. package/dist/commands/index.js.map +4 -4
  13. package/dist/commands/install.js +1377 -16
  14. package/dist/commands/install.js.map +4 -4
  15. package/dist/commands/installer.js +181 -15
  16. package/dist/commands/installer.js.map +4 -4
  17. package/dist/commands/list-sessions.js +249 -4
  18. package/dist/commands/list-sessions.js.map +4 -4
  19. package/dist/commands/press-key.js +249 -4
  20. package/dist/commands/press-key.js.map +4 -4
  21. package/dist/commands/read-history.js +249 -4
  22. package/dist/commands/read-history.js.map +4 -4
  23. package/dist/commands/read-screen.js +249 -4
  24. package/dist/commands/read-screen.js.map +4 -4
  25. package/dist/commands/resize.js +249 -4
  26. package/dist/commands/resize.js.map +4 -4
  27. package/dist/commands/runtime.js +8 -2
  28. package/dist/commands/runtime.js.map +3 -3
  29. package/dist/commands/screenshot.js +1014 -8
  30. package/dist/commands/screenshot.js.map +4 -4
  31. package/dist/commands/send-signal.js +249 -4
  32. package/dist/commands/send-signal.js.map +4 -4
  33. package/dist/commands/type.js +249 -4
  34. package/dist/commands/type.js.map +4 -4
  35. package/dist/commands/uninstall.js +421 -16
  36. package/dist/commands/uninstall.js.map +4 -4
  37. package/dist/commands/wait-for-exit.js +249 -4
  38. package/dist/commands/wait-for-exit.js.map +4 -4
  39. package/dist/commands/wait-for.js +249 -4
  40. package/dist/commands/wait-for.js.map +4 -4
  41. package/dist/testing/cli-repl.js +5633 -1185
  42. package/dist/testing/cli-repl.js.map +4 -4
  43. package/dist/testing/qa-cli.js +5674 -1226
  44. package/dist/testing/qa-cli.js.map +4 -4
  45. package/package.json +17 -8
@@ -1,11 +1,342 @@
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";
@@ -1263,17 +1594,16 @@ var closeSession = defineCommand({
1263
1594
  });
1264
1595
 
1265
1596
  // 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" }))
1597
+ var params2 = S.Object({
1598
+ command: S.String({ description: "Command to execute" }),
1599
+ args: S.Optional(S.Array(S.String(), { description: "Command arguments" })),
1600
+ session: S.Optional(S.String({ short: "s", description: "Session name" })),
1601
+ cwd: S.Optional(S.String({ description: "Working directory" })),
1602
+ cols: S.Optional(S.Number({ description: "Terminal width in columns" })),
1603
+ rows: S.Optional(S.Number({ description: "Terminal height in rows" })),
1604
+ observe: S.Optional(S.Boolean({ description: "Mirror PTY output to stderr" }))
1275
1605
  });
1276
- var createSession = defineCommand2({
1606
+ var createSession = defineCommand({
1277
1607
  name: "create-session",
1278
1608
  description: "Spawn an interactive CLI in a PTY",
1279
1609
  scope: ["cli", "mcp", "sdk"],
@@ -1286,12 +1616,11 @@ var createSession = defineCommand2({
1286
1616
  });
1287
1617
 
1288
1618
  // 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" }))
1619
+ var params3 = S.Object({
1620
+ text: S.String({ description: "Text to write to the session" }),
1621
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1293
1622
  });
1294
- var fill = defineCommand3({
1623
+ var fill = defineCommand({
1295
1624
  name: "fill",
1296
1625
  description: "Write text to an active terminal session all at once (replaces \\n with \\r)",
1297
1626
  scope: ["cli", "mcp", "sdk"],
@@ -1305,11 +1634,10 @@ var fill = defineCommand3({
1305
1634
  });
1306
1635
 
1307
1636
  // 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" }))
1637
+ var params4 = S.Object({
1638
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1311
1639
  });
1312
- var getSession = defineCommand4({
1640
+ var getSession = defineCommand({
1313
1641
  name: "get-session",
1314
1642
  description: "Get session metadata (name, pid, command, exitCode)",
1315
1643
  scope: ["cli", "mcp", "sdk"],
@@ -1325,131 +1653,1245 @@ var getSession = defineCommand4({
1325
1653
  }
1326
1654
  });
1327
1655
 
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
1656
+ // ../agent-skill-config/src/configs.ts
1333
1657
  import os from "node:os";
1334
1658
  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";
1659
+
1660
+ // ../agent-defs/src/agents/claude-code.ts
1661
+ var claudeCodeAgent = {
1662
+ id: "claude-code",
1663
+ name: "claude-code",
1664
+ label: "Claude Code",
1665
+ summary: "Configure Claude Code to route through Poe.",
1666
+ aliases: ["claude"],
1667
+ binaryName: "claude",
1668
+ configPath: "~/.claude/settings.json",
1669
+ branding: {
1670
+ colors: {
1671
+ dark: "#C15F3C",
1672
+ light: "#C15F3C"
1673
+ }
1674
+ }
1675
+ };
1676
+
1677
+ // ../agent-defs/src/agents/claude-desktop.ts
1678
+ var claudeDesktopAgent = {
1679
+ id: "claude-desktop",
1680
+ name: "claude-desktop",
1681
+ label: "Claude Desktop",
1682
+ summary: "Anthropic's official desktop application for Claude",
1683
+ configPath: "~/.claude/settings.json",
1684
+ branding: {
1685
+ colors: {
1686
+ dark: "#D97757",
1687
+ light: "#D97757"
1688
+ }
1689
+ }
1690
+ };
1691
+
1692
+ // ../agent-defs/src/agents/codex.ts
1693
+ var codexAgent = {
1694
+ id: "codex",
1695
+ name: "codex",
1696
+ label: "Codex",
1697
+ summary: "Configure Codex to use Poe as the model provider.",
1698
+ binaryName: "codex",
1699
+ configPath: "~/.codex/config.toml",
1700
+ branding: {
1701
+ colors: {
1702
+ dark: "#D5D9DF",
1703
+ light: "#7A7F86"
1704
+ }
1705
+ }
1706
+ };
1707
+
1708
+ // ../agent-defs/src/agents/opencode.ts
1709
+ var openCodeAgent = {
1710
+ id: "opencode",
1711
+ name: "opencode",
1712
+ label: "OpenCode CLI",
1713
+ summary: "Configure OpenCode CLI to use the Poe API.",
1714
+ binaryName: "opencode",
1715
+ configPath: "~/.config/opencode/config.json",
1716
+ branding: {
1717
+ colors: {
1718
+ dark: "#4A4F55",
1719
+ light: "#2F3338"
1720
+ }
1721
+ }
1722
+ };
1723
+
1724
+ // ../agent-defs/src/agents/kimi.ts
1725
+ var kimiAgent = {
1726
+ id: "kimi",
1727
+ name: "kimi",
1728
+ label: "Kimi",
1729
+ summary: "Configure Kimi CLI to use Poe API",
1730
+ aliases: ["kimi-cli"],
1731
+ binaryName: "kimi",
1732
+ configPath: "~/.kimi/config.toml",
1733
+ branding: {
1734
+ colors: {
1735
+ dark: "#7B68EE",
1736
+ light: "#6A5ACD"
1737
+ }
1738
+ }
1739
+ };
1740
+
1741
+ // ../agent-defs/src/registry.ts
1742
+ var allAgents = [
1743
+ claudeCodeAgent,
1744
+ claudeDesktopAgent,
1745
+ codexAgent,
1746
+ openCodeAgent,
1747
+ kimiAgent
1748
+ ];
1749
+ var lookup = /* @__PURE__ */ new Map();
1750
+ for (const agent of allAgents) {
1751
+ const values = [agent.id, agent.name, ...agent.aliases ?? []];
1752
+ for (const value of values) {
1753
+ const normalized = value.toLowerCase();
1754
+ if (!lookup.has(normalized)) {
1755
+ lookup.set(normalized, agent.id);
1756
+ }
1757
+ }
1349
1758
  }
1350
- function resolveInstallerServices(installer) {
1759
+ function resolveAgentId(input) {
1760
+ if (!input) {
1761
+ return void 0;
1762
+ }
1763
+ return lookup.get(input.toLowerCase());
1764
+ }
1765
+
1766
+ // ../agent-skill-config/src/configs.ts
1767
+ var agentSkillConfigs = {
1768
+ "claude-code": {
1769
+ globalSkillDir: "~/.claude/skills",
1770
+ localSkillDir: ".claude/skills"
1771
+ },
1772
+ codex: {
1773
+ globalSkillDir: "~/.codex/skills",
1774
+ localSkillDir: ".codex/skills"
1775
+ },
1776
+ opencode: {
1777
+ globalSkillDir: "~/.config/opencode/skills",
1778
+ localSkillDir: ".opencode/skills"
1779
+ }
1780
+ };
1781
+ var supportedAgents = Object.keys(agentSkillConfigs);
1782
+ function resolveAgentSupport(input, registry = agentSkillConfigs) {
1783
+ const resolvedId = resolveAgentId(input);
1784
+ if (!resolvedId) {
1785
+ return { status: "unknown", input };
1786
+ }
1787
+ const config = registry[resolvedId];
1788
+ if (!config) {
1789
+ return { status: "unsupported", input, id: resolvedId };
1790
+ }
1791
+ return { status: "supported", input, id: resolvedId, config };
1792
+ }
1793
+ function getAgentConfig(agentId) {
1794
+ const support = resolveAgentSupport(agentId);
1795
+ return support.status === "supported" ? support.config : void 0;
1796
+ }
1797
+
1798
+ // ../config-mutations/src/mutations/file-mutation.ts
1799
+ function ensureDirectory(options) {
1351
1800
  return {
1352
- fs: installer?.fs ?? nodeFs,
1353
- cwd: installer?.cwd ?? process.cwd(),
1354
- homeDir: installer?.homeDir ?? os.homedir(),
1355
- platform: installer?.platform ?? process.platform
1801
+ kind: "ensureDirectory",
1802
+ path: options.path,
1803
+ label: options.label
1356
1804
  };
1357
1805
  }
1358
- function throwUnsupportedAgent(agent) {
1359
- throw new UserError2(`Unsupported agent: ${agent}`);
1806
+ function remove(options) {
1807
+ return {
1808
+ kind: "removeFile",
1809
+ target: options.target,
1810
+ whenEmpty: options.whenEmpty,
1811
+ whenContentMatches: options.whenContentMatches,
1812
+ label: options.label
1813
+ };
1360
1814
  }
1361
- function resolveInstallableAgent(agent) {
1362
- const skillSupport = resolveSkillAgentSupport(agent);
1363
- if (skillSupport.status !== "supported" || !skillSupport.id) {
1364
- throwUnsupportedAgent(agent);
1365
- }
1366
- return skillSupport.id;
1815
+ function removeDirectory(options) {
1816
+ return {
1817
+ kind: "removeDirectory",
1818
+ path: options.path,
1819
+ force: options.force,
1820
+ label: options.label
1821
+ };
1367
1822
  }
1368
- function resolveInstallScope(input) {
1369
- if (input.local && input.global) {
1370
- throw new UserError2("Use either --local or --global, not both.");
1823
+ function chmod(options) {
1824
+ return {
1825
+ kind: "chmod",
1826
+ target: options.target,
1827
+ mode: options.mode,
1828
+ label: options.label
1829
+ };
1830
+ }
1831
+ function backup(options) {
1832
+ return {
1833
+ kind: "backup",
1834
+ target: options.target,
1835
+ label: options.label
1836
+ };
1837
+ }
1838
+ var fileMutation = {
1839
+ ensureDirectory,
1840
+ remove,
1841
+ removeDirectory,
1842
+ chmod,
1843
+ backup
1844
+ };
1845
+
1846
+ // ../config-mutations/src/mutations/template-mutation.ts
1847
+ function write(options) {
1848
+ return {
1849
+ kind: "templateWrite",
1850
+ target: options.target,
1851
+ templateId: options.templateId,
1852
+ context: options.context,
1853
+ label: options.label
1854
+ };
1855
+ }
1856
+ function mergeToml(options) {
1857
+ return {
1858
+ kind: "templateMergeToml",
1859
+ target: options.target,
1860
+ templateId: options.templateId,
1861
+ context: options.context,
1862
+ label: options.label
1863
+ };
1864
+ }
1865
+ function mergeJson(options) {
1866
+ return {
1867
+ kind: "templateMergeJson",
1868
+ target: options.target,
1869
+ templateId: options.templateId,
1870
+ context: options.context,
1871
+ label: options.label
1872
+ };
1873
+ }
1874
+ var templateMutation = {
1875
+ write,
1876
+ mergeToml,
1877
+ mergeJson
1878
+ };
1879
+
1880
+ // ../config-mutations/src/execution/apply-mutation.ts
1881
+ import Mustache from "mustache";
1882
+
1883
+ // ../config-mutations/src/formats/json.ts
1884
+ import * as jsonc from "jsonc-parser";
1885
+ function isConfigObject(value) {
1886
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1887
+ }
1888
+ function parse2(content) {
1889
+ if (!content || content.trim() === "") {
1890
+ return {};
1891
+ }
1892
+ const errors = [];
1893
+ const parsed = jsonc.parse(content, errors, {
1894
+ allowTrailingComma: true,
1895
+ disallowComments: false
1896
+ });
1897
+ if (errors.length > 0) {
1898
+ throw new Error(`JSON parse error: ${jsonc.printParseErrorCode(errors[0].error)}`);
1371
1899
  }
1372
- if (input.local) {
1373
- return "local";
1900
+ if (parsed === null || parsed === void 0) {
1901
+ return {};
1374
1902
  }
1375
- if (input.global) {
1376
- return "global";
1903
+ if (!isConfigObject(parsed)) {
1904
+ throw new Error("Expected JSON object.");
1377
1905
  }
1378
- return DEFAULT_INSTALL_SCOPE;
1906
+ return parsed;
1379
1907
  }
1380
- var terminalPilotTemplateCache;
1381
- async function loadTerminalPilotTemplate() {
1382
- if (terminalPilotTemplateCache !== void 0) {
1383
- return terminalPilotTemplateCache;
1908
+ function serialize(obj) {
1909
+ return `${JSON.stringify(obj, null, 2)}
1910
+ `;
1911
+ }
1912
+ function merge(base, patch) {
1913
+ const result = { ...base };
1914
+ for (const [key, value] of Object.entries(patch)) {
1915
+ if (value === void 0) {
1916
+ continue;
1917
+ }
1918
+ const existing = result[key];
1919
+ if (isConfigObject(existing) && isConfigObject(value)) {
1920
+ result[key] = merge(existing, value);
1921
+ continue;
1922
+ }
1923
+ result[key] = value;
1384
1924
  }
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;
1925
+ return result;
1926
+ }
1927
+ function prune(obj, shape) {
1928
+ let changed = false;
1929
+ const result = { ...obj };
1930
+ for (const [key, pattern] of Object.entries(shape)) {
1931
+ if (!(key in result)) {
1932
+ continue;
1933
+ }
1934
+ const current = result[key];
1935
+ if (isConfigObject(pattern) && Object.keys(pattern).length === 0) {
1936
+ delete result[key];
1937
+ changed = true;
1938
+ continue;
1939
+ }
1940
+ if (isConfigObject(pattern) && isConfigObject(current)) {
1941
+ const { changed: childChanged, result: childResult } = prune(
1942
+ current,
1943
+ pattern
1944
+ );
1945
+ if (childChanged) {
1946
+ changed = true;
1947
+ }
1948
+ if (Object.keys(childResult).length === 0) {
1949
+ delete result[key];
1950
+ } else {
1951
+ result[key] = childResult;
1397
1952
  }
1953
+ continue;
1398
1954
  }
1955
+ delete result[key];
1956
+ changed = true;
1399
1957
  }
1400
- throw new UserError2("terminal-pilot skill template is missing.");
1958
+ return { changed, result };
1401
1959
  }
1402
- function resolveHomeRelativePath(targetPath, homeDir) {
1403
- if (targetPath === "~") {
1404
- return homeDir;
1960
+ var jsonFormat = {
1961
+ parse: parse2,
1962
+ serialize,
1963
+ merge,
1964
+ prune
1965
+ };
1966
+
1967
+ // ../config-mutations/src/formats/toml.ts
1968
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
1969
+ function isConfigObject2(value) {
1970
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1971
+ }
1972
+ function parse3(content) {
1973
+ if (!content || content.trim() === "") {
1974
+ return {};
1405
1975
  }
1406
- if (targetPath.startsWith("~/")) {
1407
- return path.join(homeDir, targetPath.slice(2));
1976
+ const parsed = parseToml(content);
1977
+ if (!isConfigObject2(parsed)) {
1978
+ throw new Error("Expected TOML document to be a table.");
1408
1979
  }
1409
- return targetPath;
1980
+ return parsed;
1410
1981
  }
1411
- function getSkillFolderWithHome(agent, scope, cwd, homeDir) {
1412
- const config = getAgentConfig(agent);
1413
- if (!config) {
1414
- throwUnsupportedAgent(agent);
1415
- }
1416
- 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
- )
1425
- };
1982
+ function serialize2(obj) {
1983
+ const serialized = stringifyToml(obj);
1984
+ return serialized.endsWith("\n") ? serialized : `${serialized}
1985
+ `;
1426
1986
  }
1427
- async function removeSkillFolder(fs, folderPath) {
1428
- try {
1429
- await fs.stat(folderPath);
1430
- } catch (error) {
1431
- if (isNotFoundError(error)) {
1432
- return false;
1987
+ function merge2(base, patch) {
1988
+ const result = { ...base };
1989
+ for (const [key, value] of Object.entries(patch)) {
1990
+ if (value === void 0) {
1991
+ continue;
1433
1992
  }
1434
- throw error;
1435
- }
1436
- if (typeof fs.rm !== "function") {
1437
- throw new UserError2("The configured filesystem does not support removing directories.");
1993
+ const existing = result[key];
1994
+ if (isConfigObject2(existing) && isConfigObject2(value)) {
1995
+ result[key] = merge2(existing, value);
1996
+ continue;
1997
+ }
1998
+ result[key] = value;
1438
1999
  }
1439
- await fs.rm(folderPath, { recursive: true, force: true });
1440
- return true;
2000
+ return result;
1441
2001
  }
1442
-
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({
2002
+ function prune2(obj, shape) {
2003
+ let changed = false;
2004
+ const result = { ...obj };
2005
+ for (const [key, pattern] of Object.entries(shape)) {
2006
+ if (!(key in result)) {
2007
+ continue;
2008
+ }
2009
+ const current = result[key];
2010
+ if (isConfigObject2(pattern) && Object.keys(pattern).length === 0) {
2011
+ delete result[key];
2012
+ changed = true;
2013
+ continue;
2014
+ }
2015
+ if (isConfigObject2(pattern) && isConfigObject2(current)) {
2016
+ const { changed: childChanged, result: childResult } = prune2(
2017
+ current,
2018
+ pattern
2019
+ );
2020
+ if (childChanged) {
2021
+ changed = true;
2022
+ }
2023
+ if (Object.keys(childResult).length === 0) {
2024
+ delete result[key];
2025
+ } else {
2026
+ result[key] = childResult;
2027
+ }
2028
+ continue;
2029
+ }
2030
+ delete result[key];
2031
+ changed = true;
2032
+ }
2033
+ return { changed, result };
2034
+ }
2035
+ var tomlFormat = {
2036
+ parse: parse3,
2037
+ serialize: serialize2,
2038
+ merge: merge2,
2039
+ prune: prune2
2040
+ };
2041
+
2042
+ // ../config-mutations/src/formats/index.ts
2043
+ var formatRegistry = {
2044
+ json: jsonFormat,
2045
+ toml: tomlFormat
2046
+ };
2047
+ var extensionMap = {
2048
+ ".json": "json",
2049
+ ".toml": "toml"
2050
+ };
2051
+ function getConfigFormat(pathOrFormat) {
2052
+ if (pathOrFormat in formatRegistry) {
2053
+ return formatRegistry[pathOrFormat];
2054
+ }
2055
+ const ext = getExtension(pathOrFormat);
2056
+ const formatName = extensionMap[ext];
2057
+ if (!formatName) {
2058
+ throw new Error(
2059
+ `Unsupported config format. Cannot detect format from "${pathOrFormat}". Supported extensions: ${Object.keys(extensionMap).join(", ")}. Supported format names: ${Object.keys(formatRegistry).join(", ")}.`
2060
+ );
2061
+ }
2062
+ return formatRegistry[formatName];
2063
+ }
2064
+ function detectFormat(path4) {
2065
+ const ext = getExtension(path4);
2066
+ return extensionMap[ext];
2067
+ }
2068
+ function getExtension(path4) {
2069
+ const lastDot = path4.lastIndexOf(".");
2070
+ if (lastDot === -1) {
2071
+ return "";
2072
+ }
2073
+ return path4.slice(lastDot).toLowerCase();
2074
+ }
2075
+
2076
+ // ../config-mutations/src/execution/path-utils.ts
2077
+ import path2 from "node:path";
2078
+ function expandHome(targetPath, homeDir) {
2079
+ if (!targetPath?.startsWith("~")) {
2080
+ return targetPath;
2081
+ }
2082
+ if (targetPath.startsWith("~./")) {
2083
+ targetPath = `~/.${targetPath.slice(3)}`;
2084
+ }
2085
+ let remainder = targetPath.slice(1);
2086
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
2087
+ remainder = remainder.slice(1);
2088
+ } else if (remainder.startsWith(".")) {
2089
+ remainder = remainder.slice(1);
2090
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
2091
+ remainder = remainder.slice(1);
2092
+ }
2093
+ }
2094
+ return remainder.length === 0 ? homeDir : path2.join(homeDir, remainder);
2095
+ }
2096
+ function validateHomePath(targetPath) {
2097
+ if (typeof targetPath !== "string" || targetPath.length === 0) {
2098
+ throw new Error("Target path must be a non-empty string.");
2099
+ }
2100
+ if (!targetPath.startsWith("~")) {
2101
+ throw new Error(
2102
+ `All target paths must be home-relative (start with ~). Received: "${targetPath}"`
2103
+ );
2104
+ }
2105
+ }
2106
+ function resolvePath(rawPath, homeDir, pathMapper) {
2107
+ validateHomePath(rawPath);
2108
+ const expanded = expandHome(rawPath, homeDir);
2109
+ if (!pathMapper) {
2110
+ return expanded;
2111
+ }
2112
+ const rawDirectory = path2.dirname(expanded);
2113
+ const mappedDirectory = pathMapper.mapTargetDirectory({
2114
+ targetDirectory: rawDirectory
2115
+ });
2116
+ const filename = path2.basename(expanded);
2117
+ return filename.length === 0 ? mappedDirectory : path2.join(mappedDirectory, filename);
2118
+ }
2119
+
2120
+ // ../config-mutations/src/fs-utils.ts
2121
+ function isNotFound(error) {
2122
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2123
+ }
2124
+ async function readFileIfExists(fs, target) {
2125
+ try {
2126
+ return await fs.readFile(target, "utf8");
2127
+ } catch (error) {
2128
+ if (isNotFound(error)) {
2129
+ return null;
2130
+ }
2131
+ throw error;
2132
+ }
2133
+ }
2134
+ async function pathExists(fs, target) {
2135
+ try {
2136
+ await fs.stat(target);
2137
+ return true;
2138
+ } catch (error) {
2139
+ if (isNotFound(error)) {
2140
+ return false;
2141
+ }
2142
+ throw error;
2143
+ }
2144
+ }
2145
+ function createTimestamp() {
2146
+ return (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-");
2147
+ }
2148
+
2149
+ // ../config-mutations/src/execution/apply-mutation.ts
2150
+ function resolveValue(resolver, options) {
2151
+ if (typeof resolver === "function") {
2152
+ return resolver(options);
2153
+ }
2154
+ return resolver;
2155
+ }
2156
+ function createInvalidDocumentBackupPath(targetPath) {
2157
+ const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
2158
+ return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
2159
+ }
2160
+ async function backupInvalidDocument(fs, targetPath, content) {
2161
+ const backupPath = createInvalidDocumentBackupPath(targetPath);
2162
+ await fs.writeFile(backupPath, content, { encoding: "utf8" });
2163
+ }
2164
+ function describeMutation(kind, targetPath) {
2165
+ const displayPath = targetPath ?? "target";
2166
+ switch (kind) {
2167
+ case "ensureDirectory":
2168
+ return `Create ${displayPath}`;
2169
+ case "removeDirectory":
2170
+ return `Remove directory ${displayPath}`;
2171
+ case "backup":
2172
+ return `Backup ${displayPath}`;
2173
+ case "templateWrite":
2174
+ return `Write ${displayPath}`;
2175
+ case "chmod":
2176
+ return `Set permissions on ${displayPath}`;
2177
+ case "removeFile":
2178
+ return `Remove ${displayPath}`;
2179
+ case "configMerge":
2180
+ case "configPrune":
2181
+ case "configTransform":
2182
+ case "templateMergeToml":
2183
+ case "templateMergeJson":
2184
+ return `Update ${displayPath}`;
2185
+ default:
2186
+ return "Operation";
2187
+ }
2188
+ }
2189
+ function pruneKeysByPrefix(table, prefix) {
2190
+ const result = {};
2191
+ for (const [key, value] of Object.entries(table)) {
2192
+ if (!key.startsWith(prefix)) {
2193
+ result[key] = value;
2194
+ }
2195
+ }
2196
+ return result;
2197
+ }
2198
+ function isConfigObject3(value) {
2199
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2200
+ }
2201
+ function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
2202
+ const result = { ...base };
2203
+ const prefixMap = pruneByPrefix ?? {};
2204
+ for (const [key, value] of Object.entries(patch)) {
2205
+ const current = result[key];
2206
+ const prefix = prefixMap[key];
2207
+ if (isConfigObject3(current) && isConfigObject3(value)) {
2208
+ if (prefix) {
2209
+ const pruned = pruneKeysByPrefix(current, prefix);
2210
+ result[key] = { ...pruned, ...value };
2211
+ } else {
2212
+ result[key] = mergeWithPruneByPrefix(
2213
+ current,
2214
+ value,
2215
+ prefixMap
2216
+ );
2217
+ }
2218
+ continue;
2219
+ }
2220
+ result[key] = value;
2221
+ }
2222
+ return result;
2223
+ }
2224
+ async function applyMutation(mutation, context, options) {
2225
+ switch (mutation.kind) {
2226
+ case "ensureDirectory":
2227
+ return applyEnsureDirectory(mutation, context, options);
2228
+ case "removeDirectory":
2229
+ return applyRemoveDirectory(mutation, context, options);
2230
+ case "removeFile":
2231
+ return applyRemoveFile(mutation, context, options);
2232
+ case "chmod":
2233
+ return applyChmod(mutation, context, options);
2234
+ case "backup":
2235
+ return applyBackup(mutation, context, options);
2236
+ case "configMerge":
2237
+ return applyConfigMerge(mutation, context, options);
2238
+ case "configPrune":
2239
+ return applyConfigPrune(mutation, context, options);
2240
+ case "configTransform":
2241
+ return applyConfigTransform(mutation, context, options);
2242
+ case "templateWrite":
2243
+ return applyTemplateWrite(mutation, context, options);
2244
+ case "templateMergeToml":
2245
+ return applyTemplateMerge(mutation, context, options, "toml");
2246
+ case "templateMergeJson":
2247
+ return applyTemplateMerge(mutation, context, options, "json");
2248
+ default: {
2249
+ const never = mutation;
2250
+ throw new Error(`Unknown mutation kind: ${never.kind}`);
2251
+ }
2252
+ }
2253
+ }
2254
+ async function applyEnsureDirectory(mutation, context, options) {
2255
+ const rawPath = resolveValue(mutation.path, options);
2256
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2257
+ const details = {
2258
+ kind: mutation.kind,
2259
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2260
+ targetPath
2261
+ };
2262
+ const existed = await pathExists(context.fs, targetPath);
2263
+ if (!context.dryRun) {
2264
+ await context.fs.mkdir(targetPath, { recursive: true });
2265
+ }
2266
+ return {
2267
+ outcome: {
2268
+ changed: !existed,
2269
+ effect: "mkdir",
2270
+ detail: existed ? "noop" : "create"
2271
+ },
2272
+ details
2273
+ };
2274
+ }
2275
+ async function applyRemoveDirectory(mutation, context, options) {
2276
+ const rawPath = resolveValue(mutation.path, options);
2277
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2278
+ const details = {
2279
+ kind: mutation.kind,
2280
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2281
+ targetPath
2282
+ };
2283
+ const existed = await pathExists(context.fs, targetPath);
2284
+ if (!existed) {
2285
+ return {
2286
+ outcome: { changed: false, effect: "none", detail: "noop" },
2287
+ details
2288
+ };
2289
+ }
2290
+ if (typeof context.fs.rm !== "function") {
2291
+ return {
2292
+ outcome: { changed: false, effect: "none", detail: "noop" },
2293
+ details
2294
+ };
2295
+ }
2296
+ if (mutation.force) {
2297
+ if (!context.dryRun) {
2298
+ await context.fs.rm(targetPath, { recursive: true, force: true });
2299
+ }
2300
+ return {
2301
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2302
+ details
2303
+ };
2304
+ }
2305
+ const entries = await context.fs.readdir(targetPath);
2306
+ if (entries.length > 0) {
2307
+ return {
2308
+ outcome: { changed: false, effect: "none", detail: "noop" },
2309
+ details
2310
+ };
2311
+ }
2312
+ if (!context.dryRun) {
2313
+ await context.fs.rm(targetPath, { recursive: true, force: true });
2314
+ }
2315
+ return {
2316
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2317
+ details
2318
+ };
2319
+ }
2320
+ async function applyRemoveFile(mutation, context, options) {
2321
+ const rawPath = resolveValue(mutation.target, options);
2322
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2323
+ const details = {
2324
+ kind: mutation.kind,
2325
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2326
+ targetPath
2327
+ };
2328
+ try {
2329
+ const content = await context.fs.readFile(targetPath, "utf8");
2330
+ const trimmed = content.trim();
2331
+ if (mutation.whenContentMatches && !mutation.whenContentMatches.test(trimmed)) {
2332
+ return {
2333
+ outcome: { changed: false, effect: "none", detail: "noop" },
2334
+ details
2335
+ };
2336
+ }
2337
+ if (mutation.whenEmpty && trimmed.length > 0) {
2338
+ return {
2339
+ outcome: { changed: false, effect: "none", detail: "noop" },
2340
+ details
2341
+ };
2342
+ }
2343
+ if (!context.dryRun) {
2344
+ await context.fs.unlink(targetPath);
2345
+ }
2346
+ return {
2347
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2348
+ details
2349
+ };
2350
+ } catch (error) {
2351
+ if (isNotFound(error)) {
2352
+ return {
2353
+ outcome: { changed: false, effect: "none", detail: "noop" },
2354
+ details
2355
+ };
2356
+ }
2357
+ throw error;
2358
+ }
2359
+ }
2360
+ async function applyChmod(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
+ if (typeof context.fs.chmod !== "function") {
2369
+ return {
2370
+ outcome: { changed: false, effect: "none", detail: "noop" },
2371
+ details
2372
+ };
2373
+ }
2374
+ try {
2375
+ const stat = await context.fs.stat(targetPath);
2376
+ const currentMode = typeof stat.mode === "number" ? stat.mode & 511 : null;
2377
+ if (currentMode === mutation.mode) {
2378
+ return {
2379
+ outcome: { changed: false, effect: "none", detail: "noop" },
2380
+ details
2381
+ };
2382
+ }
2383
+ if (!context.dryRun) {
2384
+ await context.fs.chmod(targetPath, mutation.mode);
2385
+ }
2386
+ return {
2387
+ outcome: { changed: true, effect: "chmod", detail: "update" },
2388
+ details
2389
+ };
2390
+ } catch (error) {
2391
+ if (isNotFound(error)) {
2392
+ return {
2393
+ outcome: { changed: false, effect: "none", detail: "noop" },
2394
+ details
2395
+ };
2396
+ }
2397
+ throw error;
2398
+ }
2399
+ }
2400
+ async function applyBackup(mutation, context, options) {
2401
+ const rawPath = resolveValue(mutation.target, options);
2402
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2403
+ const details = {
2404
+ kind: mutation.kind,
2405
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2406
+ targetPath
2407
+ };
2408
+ const content = await readFileIfExists(context.fs, targetPath);
2409
+ if (content === null) {
2410
+ return {
2411
+ outcome: { changed: false, effect: "none", detail: "noop" },
2412
+ details
2413
+ };
2414
+ }
2415
+ if (!context.dryRun) {
2416
+ const backupPath = `${targetPath}.backup-${createTimestamp()}`;
2417
+ await context.fs.writeFile(backupPath, content, { encoding: "utf8" });
2418
+ }
2419
+ return {
2420
+ outcome: { changed: true, effect: "copy", detail: "backup" },
2421
+ details
2422
+ };
2423
+ }
2424
+ async function applyConfigMerge(mutation, context, options) {
2425
+ const rawPath = resolveValue(mutation.target, options);
2426
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2427
+ const details = {
2428
+ kind: mutation.kind,
2429
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2430
+ targetPath
2431
+ };
2432
+ const formatName = mutation.format ?? detectFormat(rawPath);
2433
+ if (!formatName) {
2434
+ throw new Error(
2435
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
2436
+ );
2437
+ }
2438
+ const format = getConfigFormat(formatName);
2439
+ const rawContent = await readFileIfExists(context.fs, targetPath);
2440
+ let current;
2441
+ try {
2442
+ current = rawContent === null ? {} : format.parse(rawContent);
2443
+ } catch {
2444
+ if (rawContent !== null) {
2445
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
2446
+ }
2447
+ current = {};
2448
+ }
2449
+ const value = resolveValue(mutation.value, options);
2450
+ let merged;
2451
+ if (mutation.pruneByPrefix) {
2452
+ merged = mergeWithPruneByPrefix(current, value, mutation.pruneByPrefix);
2453
+ } else {
2454
+ merged = format.merge(current, value);
2455
+ }
2456
+ const serialized = format.serialize(merged);
2457
+ const changed = serialized !== rawContent;
2458
+ if (changed && !context.dryRun) {
2459
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
2460
+ }
2461
+ return {
2462
+ outcome: {
2463
+ changed,
2464
+ effect: changed ? "write" : "none",
2465
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
2466
+ },
2467
+ details
2468
+ };
2469
+ }
2470
+ async function applyConfigPrune(mutation, context, options) {
2471
+ const rawPath = resolveValue(mutation.target, options);
2472
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2473
+ const details = {
2474
+ kind: mutation.kind,
2475
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2476
+ targetPath
2477
+ };
2478
+ const rawContent = await readFileIfExists(context.fs, targetPath);
2479
+ if (rawContent === null) {
2480
+ return {
2481
+ outcome: { changed: false, effect: "none", detail: "noop" },
2482
+ details
2483
+ };
2484
+ }
2485
+ const formatName = mutation.format ?? detectFormat(rawPath);
2486
+ if (!formatName) {
2487
+ throw new Error(
2488
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
2489
+ );
2490
+ }
2491
+ const format = getConfigFormat(formatName);
2492
+ let current;
2493
+ try {
2494
+ current = format.parse(rawContent);
2495
+ } catch {
2496
+ return {
2497
+ outcome: { changed: false, effect: "none", detail: "noop" },
2498
+ details
2499
+ };
2500
+ }
2501
+ if (mutation.onlyIf && !mutation.onlyIf(current, options)) {
2502
+ return {
2503
+ outcome: { changed: false, effect: "none", detail: "noop" },
2504
+ details
2505
+ };
2506
+ }
2507
+ const shape = resolveValue(mutation.shape, options);
2508
+ const { changed, result } = format.prune(current, shape);
2509
+ if (!changed) {
2510
+ return {
2511
+ outcome: { changed: false, effect: "none", detail: "noop" },
2512
+ details
2513
+ };
2514
+ }
2515
+ if (Object.keys(result).length === 0) {
2516
+ if (!context.dryRun) {
2517
+ await context.fs.unlink(targetPath);
2518
+ }
2519
+ return {
2520
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2521
+ details
2522
+ };
2523
+ }
2524
+ const serialized = format.serialize(result);
2525
+ if (!context.dryRun) {
2526
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
2527
+ }
2528
+ return {
2529
+ outcome: { changed: true, effect: "write", detail: "update" },
2530
+ details
2531
+ };
2532
+ }
2533
+ async function applyConfigTransform(mutation, context, options) {
2534
+ const rawPath = resolveValue(mutation.target, options);
2535
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2536
+ const details = {
2537
+ kind: mutation.kind,
2538
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2539
+ targetPath
2540
+ };
2541
+ const formatName = mutation.format ?? detectFormat(rawPath);
2542
+ if (!formatName) {
2543
+ throw new Error(
2544
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
2545
+ );
2546
+ }
2547
+ const format = getConfigFormat(formatName);
2548
+ const rawContent = await readFileIfExists(context.fs, targetPath);
2549
+ let current;
2550
+ try {
2551
+ current = rawContent === null ? {} : format.parse(rawContent);
2552
+ } catch {
2553
+ if (rawContent !== null) {
2554
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
2555
+ }
2556
+ current = {};
2557
+ }
2558
+ const { content: transformed, changed } = mutation.transform(current, options);
2559
+ if (!changed) {
2560
+ return {
2561
+ outcome: { changed: false, effect: "none", detail: "noop" },
2562
+ details
2563
+ };
2564
+ }
2565
+ if (transformed === null) {
2566
+ if (rawContent === null) {
2567
+ return {
2568
+ outcome: { changed: false, effect: "none", detail: "noop" },
2569
+ details
2570
+ };
2571
+ }
2572
+ if (!context.dryRun) {
2573
+ await context.fs.unlink(targetPath);
2574
+ }
2575
+ return {
2576
+ outcome: { changed: true, effect: "delete", detail: "delete" },
2577
+ details
2578
+ };
2579
+ }
2580
+ const serialized = format.serialize(transformed);
2581
+ if (!context.dryRun) {
2582
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
2583
+ }
2584
+ return {
2585
+ outcome: {
2586
+ changed: true,
2587
+ effect: "write",
2588
+ detail: rawContent === null ? "create" : "update"
2589
+ },
2590
+ details
2591
+ };
2592
+ }
2593
+ async function applyTemplateWrite(mutation, context, options) {
2594
+ if (!context.templates) {
2595
+ throw new Error(
2596
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
2597
+ );
2598
+ }
2599
+ const rawPath = resolveValue(mutation.target, options);
2600
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2601
+ const details = {
2602
+ kind: mutation.kind,
2603
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2604
+ targetPath
2605
+ };
2606
+ const template = await context.templates(mutation.templateId);
2607
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
2608
+ const rendered = Mustache.render(template, templateContext);
2609
+ const existed = await pathExists(context.fs, targetPath);
2610
+ if (!context.dryRun) {
2611
+ await context.fs.writeFile(targetPath, rendered, { encoding: "utf8" });
2612
+ }
2613
+ return {
2614
+ outcome: {
2615
+ changed: true,
2616
+ effect: "write",
2617
+ detail: existed ? "update" : "create"
2618
+ },
2619
+ details
2620
+ };
2621
+ }
2622
+ async function applyTemplateMerge(mutation, context, options, formatName) {
2623
+ if (!context.templates) {
2624
+ throw new Error(
2625
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
2626
+ );
2627
+ }
2628
+ const rawPath = resolveValue(mutation.target, options);
2629
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
2630
+ const details = {
2631
+ kind: mutation.kind,
2632
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
2633
+ targetPath
2634
+ };
2635
+ const format = getConfigFormat(formatName);
2636
+ const template = await context.templates(mutation.templateId);
2637
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
2638
+ const rendered = Mustache.render(template, templateContext);
2639
+ let templateDoc;
2640
+ try {
2641
+ templateDoc = format.parse(rendered);
2642
+ } catch (error) {
2643
+ throw new Error(
2644
+ `Failed to parse rendered template "${mutation.templateId}" as ${formatName.toUpperCase()}: ${error}`,
2645
+ { cause: error }
2646
+ );
2647
+ }
2648
+ const rawContent = await readFileIfExists(context.fs, targetPath);
2649
+ let current;
2650
+ try {
2651
+ current = rawContent === null ? {} : format.parse(rawContent);
2652
+ } catch {
2653
+ if (rawContent !== null) {
2654
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
2655
+ }
2656
+ current = {};
2657
+ }
2658
+ const merged = format.merge(current, templateDoc);
2659
+ const serialized = format.serialize(merged);
2660
+ const changed = serialized !== rawContent;
2661
+ if (changed && !context.dryRun) {
2662
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
2663
+ }
2664
+ return {
2665
+ outcome: {
2666
+ changed,
2667
+ effect: changed ? "write" : "none",
2668
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
2669
+ },
2670
+ details
2671
+ };
2672
+ }
2673
+
2674
+ // ../config-mutations/src/execution/run-mutations.ts
2675
+ async function runMutations(mutations, context, options) {
2676
+ const effects = [];
2677
+ let anyChanged = false;
2678
+ const resolverOptions = options ?? {};
2679
+ for (const mutation of mutations) {
2680
+ const { outcome } = await executeMutation(
2681
+ mutation,
2682
+ context,
2683
+ resolverOptions
2684
+ );
2685
+ effects.push(outcome);
2686
+ if (outcome.changed) {
2687
+ anyChanged = true;
2688
+ }
2689
+ }
2690
+ return {
2691
+ changed: anyChanged,
2692
+ effects
2693
+ };
2694
+ }
2695
+ async function executeMutation(mutation, context, options) {
2696
+ context.observers?.onStart?.({
2697
+ kind: mutation.kind,
2698
+ label: mutation.label ?? mutation.kind,
2699
+ targetPath: void 0
2700
+ // Will be resolved during apply
2701
+ });
2702
+ try {
2703
+ const { outcome, details } = await applyMutation(mutation, context, options);
2704
+ context.observers?.onComplete?.(details, outcome);
2705
+ return { outcome, details };
2706
+ } catch (error) {
2707
+ context.observers?.onError?.(
2708
+ {
2709
+ kind: mutation.kind,
2710
+ label: mutation.label ?? mutation.kind,
2711
+ targetPath: void 0
2712
+ },
2713
+ error
2714
+ );
2715
+ throw error;
2716
+ }
2717
+ }
2718
+
2719
+ // ../config-mutations/src/template/render.ts
2720
+ import Mustache2 from "mustache";
2721
+ var originalEscape = Mustache2.escape;
2722
+
2723
+ // ../agent-skill-config/src/templates.ts
2724
+ import { readFile } from "node:fs/promises";
2725
+
2726
+ // ../agent-skill-config/src/apply.ts
2727
+ var UnsupportedAgentError = class extends Error {
2728
+ constructor(agentId) {
2729
+ super(`Unsupported agent: ${agentId}`);
2730
+ this.name = "UnsupportedAgentError";
2731
+ }
2732
+ };
2733
+ function toHomeRelative(localSkillDir) {
2734
+ if (localSkillDir.startsWith("~/") || localSkillDir === "~") {
2735
+ return localSkillDir;
2736
+ }
2737
+ const normalized = localSkillDir.startsWith("./") ? localSkillDir.slice(2) : localSkillDir;
2738
+ return `~/${normalized}`;
2739
+ }
2740
+ var SKILL_TEMPLATE_ID = "__skill_content__";
2741
+ async function installSkill(agentId, skill, options) {
2742
+ const support = resolveAgentSupport(agentId);
2743
+ if (support.status !== "supported") {
2744
+ throw new UnsupportedAgentError(agentId);
2745
+ }
2746
+ const scope = options.scope ?? "local";
2747
+ const config = support.config;
2748
+ const skillDir = scope === "global" ? config.globalSkillDir : toHomeRelative(config.localSkillDir);
2749
+ const skillFolderPath = `${skillDir}/${skill.name}`;
2750
+ const skillFilePath = `${skillFolderPath}/SKILL.md`;
2751
+ const displayPath = `${scope === "global" ? config.globalSkillDir : config.localSkillDir}/${skill.name}/SKILL.md`;
2752
+ await runMutations(
2753
+ [
2754
+ fileMutation.ensureDirectory({
2755
+ path: skillFolderPath,
2756
+ label: `Ensure skill directory ${skill.name}`
2757
+ }),
2758
+ templateMutation.write({
2759
+ target: skillFilePath,
2760
+ templateId: SKILL_TEMPLATE_ID,
2761
+ label: `Write skill ${skill.name}`
2762
+ })
2763
+ ],
2764
+ {
2765
+ fs: options.fs,
2766
+ homeDir: scope === "global" ? options.homeDir : options.cwd,
2767
+ dryRun: options.dryRun,
2768
+ observers: options.observers,
2769
+ templates: async (templateId) => {
2770
+ if (templateId === SKILL_TEMPLATE_ID) {
2771
+ return skill.content;
2772
+ }
2773
+ throw new Error(`Unknown template: ${templateId}`);
2774
+ }
2775
+ }
2776
+ );
2777
+ return { skillPath: skillFilePath, displayPath };
2778
+ }
2779
+
2780
+ // src/commands/installer.ts
2781
+ import os2 from "node:os";
2782
+ import path3 from "node:path";
2783
+ import * as nodeFs from "node:fs/promises";
2784
+ import { readFile as readFile2 } from "node:fs/promises";
2785
+ var DEFAULT_INSTALL_AGENT = "claude-code";
2786
+ var DEFAULT_INSTALL_SCOPE = "local";
2787
+ var TERMINAL_PILOT_SKILL_NAME = "terminal-pilot";
2788
+ var installableAgents = supportedAgents;
2789
+ function isNotFoundError(error) {
2790
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2791
+ }
2792
+ function resolveInstallerServices(installer) {
2793
+ return {
2794
+ fs: installer?.fs ?? nodeFs,
2795
+ cwd: installer?.cwd ?? process.cwd(),
2796
+ homeDir: installer?.homeDir ?? os2.homedir(),
2797
+ platform: installer?.platform ?? process.platform
2798
+ };
2799
+ }
2800
+ function throwUnsupportedAgent(agent) {
2801
+ throw new UserError(`Unsupported agent: ${agent}`);
2802
+ }
2803
+ function resolveInstallableAgent(agent) {
2804
+ const skillSupport = resolveAgentSupport(agent);
2805
+ if (skillSupport.status !== "supported" || !skillSupport.id) {
2806
+ throwUnsupportedAgent(agent);
2807
+ }
2808
+ return skillSupport.id;
2809
+ }
2810
+ function resolveInstallScope(input) {
2811
+ if (input.local && input.global) {
2812
+ throw new UserError("Use either --local or --global, not both.");
2813
+ }
2814
+ if (input.local) {
2815
+ return "local";
2816
+ }
2817
+ if (input.global) {
2818
+ return "global";
2819
+ }
2820
+ return DEFAULT_INSTALL_SCOPE;
2821
+ }
2822
+ var terminalPilotTemplateCache;
2823
+ async function loadTerminalPilotTemplate() {
2824
+ if (terminalPilotTemplateCache !== void 0) {
2825
+ return terminalPilotTemplateCache;
2826
+ }
2827
+ const candidates = [
2828
+ new URL("./templates/terminal-pilot.md", import.meta.url),
2829
+ new URL("../templates/terminal-pilot.md", import.meta.url),
2830
+ new URL("../../../agent-skill-config/src/templates/terminal-pilot.md", import.meta.url)
2831
+ ];
2832
+ for (const candidate of candidates) {
2833
+ try {
2834
+ terminalPilotTemplateCache = await readFile2(candidate, "utf8");
2835
+ return terminalPilotTemplateCache;
2836
+ } catch (error) {
2837
+ if (!isNotFoundError(error)) {
2838
+ throw error;
2839
+ }
2840
+ }
2841
+ }
2842
+ throw new UserError("terminal-pilot skill template is missing.");
2843
+ }
2844
+ function resolveHomeRelativePath(targetPath, homeDir) {
2845
+ if (targetPath === "~") {
2846
+ return homeDir;
2847
+ }
2848
+ if (targetPath.startsWith("~/")) {
2849
+ return path3.join(homeDir, targetPath.slice(2));
2850
+ }
2851
+ return targetPath;
2852
+ }
2853
+ function getSkillFolderWithHome(agent, scope, cwd, homeDir) {
2854
+ const config = getAgentConfig(agent);
2855
+ if (!config) {
2856
+ throwUnsupportedAgent(agent);
2857
+ }
2858
+ return {
2859
+ displayPath: path3.join(
2860
+ scope === "global" ? config.globalSkillDir : config.localSkillDir,
2861
+ TERMINAL_PILOT_SKILL_NAME
2862
+ ),
2863
+ fullPath: path3.join(
2864
+ scope === "global" ? resolveHomeRelativePath(config.globalSkillDir, homeDir) : path3.resolve(cwd, config.localSkillDir),
2865
+ TERMINAL_PILOT_SKILL_NAME
2866
+ )
2867
+ };
2868
+ }
2869
+ async function removeSkillFolder(fs, folderPath) {
2870
+ try {
2871
+ await fs.stat(folderPath);
2872
+ } catch (error) {
2873
+ if (isNotFoundError(error)) {
2874
+ return false;
2875
+ }
2876
+ throw error;
2877
+ }
2878
+ if (typeof fs.rm !== "function") {
2879
+ throw new UserError("The configured filesystem does not support removing directories.");
2880
+ }
2881
+ await fs.rm(folderPath, { recursive: true, force: true });
2882
+ return true;
2883
+ }
2884
+
2885
+ // src/commands/install.ts
2886
+ var params5 = S.Object({
2887
+ agent: S.Enum(installableAgents, {
2888
+ description: "Agent to install terminal-pilot for",
2889
+ default: DEFAULT_INSTALL_AGENT
2890
+ }),
2891
+ local: S.Optional(S.Boolean({ description: "Install the skill in the current project" })),
2892
+ global: S.Optional(S.Boolean({ description: "Install the skill in the user home directory" }))
2893
+ });
2894
+ var install = defineCommand({
1453
2895
  name: "install",
1454
2896
  description: "Install the terminal-pilot CLI skill.",
1455
2897
  scope: ["cli"],
@@ -1482,9 +2924,8 @@ var install = defineCommand5({
1482
2924
  });
1483
2925
 
1484
2926
  // 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({
2927
+ var params6 = S.Object({});
2928
+ var listSessions = defineCommand({
1488
2929
  name: "list-sessions",
1489
2930
  description: "List active terminal sessions",
1490
2931
  scope: ["cli", "mcp", "sdk"],
@@ -1502,12 +2943,11 @@ var listSessions = defineCommand6({
1502
2943
  });
1503
2944
 
1504
2945
  // 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" }))
2946
+ var params7 = S.Object({
2947
+ key: S.String({ description: "Named key to press" }),
2948
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1509
2949
  });
1510
- var pressKey = defineCommand7({
2950
+ var pressKey = defineCommand({
1511
2951
  name: "press-key",
1512
2952
  description: "Send a named key press to an active terminal session",
1513
2953
  scope: ["cli", "mcp", "sdk"],
@@ -1521,12 +2961,11 @@ var pressKey = defineCommand7({
1521
2961
  });
1522
2962
 
1523
2963
  // 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" }))
2964
+ var params8 = S.Object({
2965
+ session: S.Optional(S.String({ short: "s", description: "Session name" })),
2966
+ last: S.Optional(S.Number({ short: "n", description: "Return only the last N lines" }))
1528
2967
  });
1529
- var readHistory = defineCommand8({
2968
+ var readHistory = defineCommand({
1530
2969
  name: "read-history",
1531
2970
  description: "Read terminal output history",
1532
2971
  scope: ["cli", "mcp", "sdk"],
@@ -1539,11 +2978,10 @@ var readHistory = defineCommand8({
1539
2978
  });
1540
2979
 
1541
2980
  // 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" }))
2981
+ var params9 = S.Object({
2982
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1545
2983
  });
1546
- var readScreen = defineCommand9({
2984
+ var readScreen = defineCommand({
1547
2985
  name: "read-screen",
1548
2986
  description: "Read the current visible terminal screen",
1549
2987
  scope: ["cli", "mcp", "sdk"],
@@ -1561,13 +2999,12 @@ var readScreen = defineCommand9({
1561
2999
  });
1562
3000
 
1563
3001
  // 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" }))
3002
+ var params10 = S.Object({
3003
+ cols: S.Number({ description: "Terminal width in columns" }),
3004
+ rows: S.Number({ description: "Terminal height in rows" }),
3005
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1569
3006
  });
1570
- var resize = defineCommand10({
3007
+ var resize = defineCommand({
1571
3008
  name: "resize",
1572
3009
  description: "Resize an active terminal session",
1573
3010
  scope: ["cli", "mcp", "sdk"],
@@ -1579,16 +3016,776 @@ var resize = defineCommand10({
1579
3016
  }
1580
3017
  });
1581
3018
 
3019
+ // ../terminal-png/src/index.ts
3020
+ import { writeFile } from "node:fs/promises";
3021
+
3022
+ // ../terminal-png/src/ansi-parser.ts
3023
+ var ESC2 = "\x1B";
3024
+ function createDefaultStyle() {
3025
+ return {
3026
+ fg: null,
3027
+ bg: null,
3028
+ bold: false,
3029
+ italic: false,
3030
+ underline: false,
3031
+ strikethrough: false,
3032
+ dim: false
3033
+ };
3034
+ }
3035
+ function cloneStyle(style) {
3036
+ return { ...style };
3037
+ }
3038
+ function stylesEqual(left, right) {
3039
+ 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);
3040
+ }
3041
+ function colorsEqual(left, right) {
3042
+ if (left === right) {
3043
+ return true;
3044
+ }
3045
+ if (!left || !right || left.type !== right.type) {
3046
+ return false;
3047
+ }
3048
+ if (left.type === "rgb" && right.type === "rgb") {
3049
+ return left.r === right.r && left.g === right.g && left.b === right.b;
3050
+ }
3051
+ if ((left.type === "ansi4" || left.type === "ansi8") && left.type === right.type) {
3052
+ return left.index === right.index;
3053
+ }
3054
+ return false;
3055
+ }
3056
+ function pushRun(runs, style, text) {
3057
+ if (text.length === 0) {
3058
+ return;
3059
+ }
3060
+ const previous = runs.at(-1);
3061
+ if (previous && text !== "\n" && previous.text !== "\n" && stylesEqual(previous, style)) {
3062
+ previous.text += text;
3063
+ return;
3064
+ }
3065
+ runs.push({
3066
+ text,
3067
+ fg: style.fg,
3068
+ bg: style.bg,
3069
+ bold: style.bold,
3070
+ italic: style.italic,
3071
+ underline: style.underline,
3072
+ strikethrough: style.strikethrough,
3073
+ dim: style.dim
3074
+ });
3075
+ }
3076
+ function parseCsi(input, start) {
3077
+ let index = start + 2;
3078
+ while (index < input.length) {
3079
+ const code = input.charCodeAt(index);
3080
+ if (code >= 64 && code <= 126) {
3081
+ return {
3082
+ end: index + 1,
3083
+ final: input[index],
3084
+ params: input.slice(start + 2, index)
3085
+ };
3086
+ }
3087
+ index += 1;
3088
+ }
3089
+ return {
3090
+ end: input.length,
3091
+ final: null,
3092
+ params: input.slice(start + 2)
3093
+ };
3094
+ }
3095
+ function toInteger(value) {
3096
+ if (!value || value.length === 0) {
3097
+ return null;
3098
+ }
3099
+ for (const char of value) {
3100
+ if (char < "0" || char > "9") {
3101
+ return null;
3102
+ }
3103
+ }
3104
+ return Number.parseInt(value, 10);
3105
+ }
3106
+ function clampByte(value) {
3107
+ if (value < 0) {
3108
+ return 0;
3109
+ }
3110
+ if (value > 255) {
3111
+ return 255;
3112
+ }
3113
+ return value;
3114
+ }
3115
+ function applyExtendedColor(params17, start) {
3116
+ const mode = params17[start];
3117
+ if (mode === 5) {
3118
+ const index = params17[start + 1];
3119
+ if (index === void 0) {
3120
+ return null;
3121
+ }
3122
+ return {
3123
+ color: { type: "ansi8", index: clampByte(index) },
3124
+ consumed: 2
3125
+ };
3126
+ }
3127
+ if (mode === 2) {
3128
+ const r = params17[start + 1];
3129
+ const g = params17[start + 2];
3130
+ const b = params17[start + 3];
3131
+ if (r === void 0 || g === void 0 || b === void 0) {
3132
+ return null;
3133
+ }
3134
+ return {
3135
+ color: { type: "rgb", r: clampByte(r), g: clampByte(g), b: clampByte(b) },
3136
+ consumed: 4
3137
+ };
3138
+ }
3139
+ return null;
3140
+ }
3141
+ function applySgr(style, paramsText) {
3142
+ const nextStyle = cloneStyle(style);
3143
+ const rawParams = paramsText.length === 0 ? ["0"] : paramsText.split(";");
3144
+ const params17 = [];
3145
+ for (const rawParam of rawParams) {
3146
+ const value = toInteger(rawParam);
3147
+ if (value === null) {
3148
+ return nextStyle;
3149
+ }
3150
+ params17.push(value);
3151
+ }
3152
+ for (let index = 0; index < params17.length; index += 1) {
3153
+ const value = params17[index];
3154
+ if (value === 0) {
3155
+ Object.assign(nextStyle, createDefaultStyle());
3156
+ continue;
3157
+ }
3158
+ if (value === 1) {
3159
+ nextStyle.bold = true;
3160
+ continue;
3161
+ }
3162
+ if (value === 2) {
3163
+ nextStyle.dim = true;
3164
+ continue;
3165
+ }
3166
+ if (value === 22) {
3167
+ nextStyle.bold = false;
3168
+ nextStyle.dim = false;
3169
+ continue;
3170
+ }
3171
+ if (value === 3) {
3172
+ nextStyle.italic = true;
3173
+ continue;
3174
+ }
3175
+ if (value === 23) {
3176
+ nextStyle.italic = false;
3177
+ continue;
3178
+ }
3179
+ if (value === 4) {
3180
+ nextStyle.underline = true;
3181
+ continue;
3182
+ }
3183
+ if (value === 24) {
3184
+ nextStyle.underline = false;
3185
+ continue;
3186
+ }
3187
+ if (value === 9) {
3188
+ nextStyle.strikethrough = true;
3189
+ continue;
3190
+ }
3191
+ if (value === 29) {
3192
+ nextStyle.strikethrough = false;
3193
+ continue;
3194
+ }
3195
+ if (value === 39) {
3196
+ nextStyle.fg = null;
3197
+ continue;
3198
+ }
3199
+ if (value === 49) {
3200
+ nextStyle.bg = null;
3201
+ continue;
3202
+ }
3203
+ if (value >= 30 && value <= 37) {
3204
+ nextStyle.fg = { type: "ansi4", index: value - 30 };
3205
+ continue;
3206
+ }
3207
+ if (value >= 90 && value <= 97) {
3208
+ nextStyle.fg = { type: "ansi4", index: value - 90 + 8 };
3209
+ continue;
3210
+ }
3211
+ if (value >= 40 && value <= 47) {
3212
+ nextStyle.bg = { type: "ansi4", index: value - 40 };
3213
+ continue;
3214
+ }
3215
+ if (value >= 100 && value <= 107) {
3216
+ nextStyle.bg = { type: "ansi4", index: value - 100 + 8 };
3217
+ continue;
3218
+ }
3219
+ if (value === 38 || value === 48) {
3220
+ const extended = applyExtendedColor(params17, index + 1);
3221
+ if (!extended) {
3222
+ continue;
3223
+ }
3224
+ if (value === 38) {
3225
+ nextStyle.fg = extended.color;
3226
+ } else {
3227
+ nextStyle.bg = extended.color;
3228
+ }
3229
+ index += extended.consumed;
3230
+ }
3231
+ }
3232
+ return nextStyle;
3233
+ }
3234
+ function parseAnsi(input) {
3235
+ const runs = [];
3236
+ let style = createDefaultStyle();
3237
+ let textStart = 0;
3238
+ let index = 0;
3239
+ while (index < input.length) {
3240
+ const char = input[index];
3241
+ if (char === "\n") {
3242
+ pushRun(runs, style, input.slice(textStart, index));
3243
+ pushRun(runs, style, "\n");
3244
+ index += 1;
3245
+ textStart = index;
3246
+ continue;
3247
+ }
3248
+ if (char === ESC2 && input[index + 1] === "[") {
3249
+ pushRun(runs, style, input.slice(textStart, index));
3250
+ const sequence = parseCsi(input, index);
3251
+ if (sequence.final === "m") {
3252
+ style = applySgr(style, sequence.params);
3253
+ }
3254
+ index = sequence.end;
3255
+ textStart = index;
3256
+ continue;
3257
+ }
3258
+ index += 1;
3259
+ }
3260
+ pushRun(runs, style, input.slice(textStart));
3261
+ return runs;
3262
+ }
3263
+
3264
+ // ../terminal-png/src/png-renderer.ts
3265
+ import { Resvg } from "@resvg/resvg-js";
3266
+
3267
+ // ../terminal-png/src/font.ts
3268
+ import { readFileSync } from "node:fs";
3269
+ import { createRequire } from "node:module";
3270
+ import { dirname, join } from "node:path";
3271
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
3272
+ var require2 = createRequire(import.meta.url);
3273
+ var fontPackageRoot = dirname(require2.resolve("jetbrains-mono/package.json"));
3274
+ var webfontRoot = join(fontPackageRoot, "fonts/webfonts");
3275
+ function readWebfontBase64(filename) {
3276
+ return readFileSync(join(webfontRoot, filename)).toString("base64");
3277
+ }
3278
+ function resolveAssetPath(filename) {
3279
+ return fileURLToPath2(new URL(`../assets/${filename}`, import.meta.url));
3280
+ }
3281
+ function createFontFace(base64, weight, style) {
3282
+ return `@font-face {
3283
+ font-family: 'JetBrains Mono';
3284
+ font-style: ${style};
3285
+ font-weight: ${weight};
3286
+ src: url('data:font/woff2;base64,${base64}') format('woff2');
3287
+ }`;
3288
+ }
3289
+ var JETBRAINS_MONO_BASE64 = readWebfontBase64("JetBrainsMono-Regular.woff2");
3290
+ var JETBRAINS_MONO_FONT_FILES = [
3291
+ resolveAssetPath("jetbrains-mono-400-normal.ttf"),
3292
+ resolveAssetPath("jetbrains-mono-700-normal.ttf"),
3293
+ resolveAssetPath("jetbrains-mono-400-italic.ttf"),
3294
+ resolveAssetPath("jetbrains-mono-700-italic.ttf")
3295
+ ];
3296
+ var JETBRAINS_MONO_TTF_PATH = JETBRAINS_MONO_FONT_FILES[0];
3297
+ var FONT_FACE_CSS = [
3298
+ createFontFace(JETBRAINS_MONO_BASE64, 400, "normal"),
3299
+ createFontFace(readWebfontBase64("JetBrainsMono-Bold.woff2"), 700, "normal"),
3300
+ createFontFace(readWebfontBase64("JetBrainsMono-Italic.woff2"), 400, "italic"),
3301
+ createFontFace(readWebfontBase64("JetBrainsMono-BoldItalic.woff2"), 700, "italic")
3302
+ ].join("\n");
3303
+
3304
+ // ../terminal-png/src/png-renderer.ts
3305
+ function renderPng(svg) {
3306
+ const resvg = new Resvg(svg, {
3307
+ font: {
3308
+ defaultFontFamily: "JetBrains Mono",
3309
+ fontFiles: [...JETBRAINS_MONO_FONT_FILES],
3310
+ loadSystemFonts: false,
3311
+ monospaceFamily: "JetBrains Mono"
3312
+ },
3313
+ fitTo: {
3314
+ mode: "zoom",
3315
+ value: 4
3316
+ }
3317
+ });
3318
+ const png = resvg.render();
3319
+ return Buffer.from(png.asPng());
3320
+ }
3321
+
3322
+ // ../terminal-png/src/svg-renderer.ts
3323
+ var ANSI_16_PALETTE = [
3324
+ "#282a2e",
3325
+ "#D74E6F",
3326
+ "#31BB71",
3327
+ "#D3E561",
3328
+ "#8056FF",
3329
+ "#ED61D7",
3330
+ "#04D7D7",
3331
+ "#C5C8C6",
3332
+ "#4B4B4B",
3333
+ "#FE5F86",
3334
+ "#00D787",
3335
+ "#EBFF71",
3336
+ "#8F69FF",
3337
+ "#FF7AEA",
3338
+ "#00FEFE",
3339
+ "#FFFFFF"
3340
+ ];
3341
+ var XTERM_256_PALETTE = [
3342
+ "#000000",
3343
+ "#800000",
3344
+ "#008000",
3345
+ "#808000",
3346
+ "#000080",
3347
+ "#800080",
3348
+ "#008080",
3349
+ "#c0c0c0",
3350
+ "#808080",
3351
+ "#ff0000",
3352
+ "#00ff00",
3353
+ "#ffff00",
3354
+ "#0000ff",
3355
+ "#ff00ff",
3356
+ "#00ffff",
3357
+ "#ffffff",
3358
+ "#000000",
3359
+ "#00005f",
3360
+ "#000087",
3361
+ "#0000af",
3362
+ "#0000d7",
3363
+ "#0000ff",
3364
+ "#005f00",
3365
+ "#005f5f",
3366
+ "#005f87",
3367
+ "#005faf",
3368
+ "#005fd7",
3369
+ "#005fff",
3370
+ "#008700",
3371
+ "#00875f",
3372
+ "#008787",
3373
+ "#0087af",
3374
+ "#0087d7",
3375
+ "#0087ff",
3376
+ "#00af00",
3377
+ "#00af5f",
3378
+ "#00af87",
3379
+ "#00afaf",
3380
+ "#00afd7",
3381
+ "#00afff",
3382
+ "#00d700",
3383
+ "#00d75f",
3384
+ "#00d787",
3385
+ "#00d7af",
3386
+ "#00d7d7",
3387
+ "#00d7ff",
3388
+ "#00ff00",
3389
+ "#00ff5f",
3390
+ "#00ff87",
3391
+ "#00ffaf",
3392
+ "#00ffd7",
3393
+ "#00ffff",
3394
+ "#5f0000",
3395
+ "#5f005f",
3396
+ "#5f0087",
3397
+ "#5f00af",
3398
+ "#5f00d7",
3399
+ "#5f00ff",
3400
+ "#5f5f00",
3401
+ "#5f5f5f",
3402
+ "#5f5f87",
3403
+ "#5f5faf",
3404
+ "#5f5fd7",
3405
+ "#5f5fff",
3406
+ "#5f8700",
3407
+ "#5f875f",
3408
+ "#5f8787",
3409
+ "#5f87af",
3410
+ "#5f87d7",
3411
+ "#5f87ff",
3412
+ "#5faf00",
3413
+ "#5faf5f",
3414
+ "#5faf87",
3415
+ "#5fafaf",
3416
+ "#5fafd7",
3417
+ "#5fafff",
3418
+ "#5fd700",
3419
+ "#5fd75f",
3420
+ "#5fd787",
3421
+ "#5fd7af",
3422
+ "#5fd7d7",
3423
+ "#5fd7ff",
3424
+ "#5fff00",
3425
+ "#5fff5f",
3426
+ "#5fff87",
3427
+ "#5fffaf",
3428
+ "#5fffd7",
3429
+ "#5fffff",
3430
+ "#870000",
3431
+ "#87005f",
3432
+ "#870087",
3433
+ "#8700af",
3434
+ "#8700d7",
3435
+ "#8700ff",
3436
+ "#875f00",
3437
+ "#875f5f",
3438
+ "#875f87",
3439
+ "#875faf",
3440
+ "#875fd7",
3441
+ "#875fff",
3442
+ "#878700",
3443
+ "#87875f",
3444
+ "#878787",
3445
+ "#8787af",
3446
+ "#8787d7",
3447
+ "#8787ff",
3448
+ "#87af00",
3449
+ "#87af5f",
3450
+ "#87af87",
3451
+ "#87afaf",
3452
+ "#87afd7",
3453
+ "#87afff",
3454
+ "#87d700",
3455
+ "#87d75f",
3456
+ "#87d787",
3457
+ "#87d7af",
3458
+ "#87d7d7",
3459
+ "#87d7ff",
3460
+ "#87ff00",
3461
+ "#87ff5f",
3462
+ "#87ff87",
3463
+ "#87ffaf",
3464
+ "#87ffd7",
3465
+ "#87ffff",
3466
+ "#af0000",
3467
+ "#af005f",
3468
+ "#af0087",
3469
+ "#af00af",
3470
+ "#af00d7",
3471
+ "#af00ff",
3472
+ "#af5f00",
3473
+ "#af5f5f",
3474
+ "#af5f87",
3475
+ "#af5faf",
3476
+ "#af5fd7",
3477
+ "#af5fff",
3478
+ "#af8700",
3479
+ "#af875f",
3480
+ "#af8787",
3481
+ "#af87af",
3482
+ "#af87d7",
3483
+ "#af87ff",
3484
+ "#afaf00",
3485
+ "#afaf5f",
3486
+ "#afaf87",
3487
+ "#afafaf",
3488
+ "#afafd7",
3489
+ "#afafff",
3490
+ "#afd700",
3491
+ "#afd75f",
3492
+ "#afd787",
3493
+ "#afd7af",
3494
+ "#afd7d7",
3495
+ "#afd7ff",
3496
+ "#afff00",
3497
+ "#afff5f",
3498
+ "#afff87",
3499
+ "#afffaf",
3500
+ "#afffd7",
3501
+ "#afffff",
3502
+ "#d70000",
3503
+ "#d7005f",
3504
+ "#d70087",
3505
+ "#d700af",
3506
+ "#d700d7",
3507
+ "#d700ff",
3508
+ "#d75f00",
3509
+ "#d75f5f",
3510
+ "#d75f87",
3511
+ "#d75faf",
3512
+ "#d75fd7",
3513
+ "#d75fff",
3514
+ "#d78700",
3515
+ "#d7875f",
3516
+ "#d78787",
3517
+ "#d787af",
3518
+ "#d787d7",
3519
+ "#d787ff",
3520
+ "#d7af00",
3521
+ "#d7af5f",
3522
+ "#d7af87",
3523
+ "#d7afaf",
3524
+ "#d7afd7",
3525
+ "#d7afff",
3526
+ "#d7d700",
3527
+ "#d7d75f",
3528
+ "#d7d787",
3529
+ "#d7d7af",
3530
+ "#d7d7d7",
3531
+ "#d7d7ff",
3532
+ "#d7ff00",
3533
+ "#d7ff5f",
3534
+ "#d7ff87",
3535
+ "#d7ffaf",
3536
+ "#d7ffd7",
3537
+ "#d7ffff",
3538
+ "#ff0000",
3539
+ "#ff005f",
3540
+ "#ff0087",
3541
+ "#ff00af",
3542
+ "#ff00d7",
3543
+ "#ff00ff",
3544
+ "#ff5f00",
3545
+ "#ff5f5f",
3546
+ "#ff5f87",
3547
+ "#ff5faf",
3548
+ "#ff5fd7",
3549
+ "#ff5fff",
3550
+ "#ff8700",
3551
+ "#ff875f",
3552
+ "#ff8787",
3553
+ "#ff87af",
3554
+ "#ff87d7",
3555
+ "#ff87ff",
3556
+ "#ffaf00",
3557
+ "#ffaf5f",
3558
+ "#ffaf87",
3559
+ "#ffafaf",
3560
+ "#ffafd7",
3561
+ "#ffafff",
3562
+ "#ffd700",
3563
+ "#ffd75f",
3564
+ "#ffd787",
3565
+ "#ffd7af",
3566
+ "#ffd7d7",
3567
+ "#ffd7ff",
3568
+ "#ffff00",
3569
+ "#ffff5f",
3570
+ "#ffff87",
3571
+ "#ffffaf",
3572
+ "#ffffd7",
3573
+ "#ffffff",
3574
+ "#080808",
3575
+ "#121212",
3576
+ "#1c1c1c",
3577
+ "#262626",
3578
+ "#303030",
3579
+ "#3a3a3a",
3580
+ "#444444",
3581
+ "#4e4e4e",
3582
+ "#585858",
3583
+ "#606060",
3584
+ "#666666",
3585
+ "#767676",
3586
+ "#808080",
3587
+ "#8a8a8a",
3588
+ "#949494",
3589
+ "#9e9e9e",
3590
+ "#a8a8a8",
3591
+ "#b2b2b2",
3592
+ "#bcbcbc",
3593
+ "#c6c6c6",
3594
+ "#d0d0d0",
3595
+ "#dadada",
3596
+ "#e4e4e4",
3597
+ "#eeeeee"
3598
+ ];
3599
+ var BACKGROUND = "#171717";
3600
+ var DEFAULT_FOREGROUND = "#c4c4c4";
3601
+ var FONT_FAMILY = "JetBrains Mono";
3602
+ var FONT_SIZE = 14;
3603
+ var LINE_HEIGHT = 1.2;
3604
+ var CHARACTER_WIDTH = 8.412666666666667;
3605
+ var TEXT_BOTTOM_PADDING = 11.6;
3606
+ var DEFAULT_PADDING = {
3607
+ top: 20,
3608
+ right: 40,
3609
+ bottom: 20,
3610
+ left: 20
3611
+ };
3612
+ var WINDOW_BAR_HEIGHT = 15;
3613
+ function renderSvg(runs, options = {}) {
3614
+ const padding = options.padding === void 0 ? DEFAULT_PADDING : {
3615
+ top: options.padding,
3616
+ right: options.padding,
3617
+ bottom: options.padding,
3618
+ left: options.padding
3619
+ };
3620
+ const showWindow = options.window ?? true;
3621
+ const titleBarHeight = showWindow ? WINDOW_BAR_HEIGHT : 0;
3622
+ const textStartX = padding.left;
3623
+ const lineHeightPx = FONT_SIZE * LINE_HEIGHT;
3624
+ const textStartY = titleBarHeight + padding.top + lineHeightPx;
3625
+ const lines = splitIntoLines(runs);
3626
+ const contentWidth = measureLines(lines);
3627
+ const width = padding.left + contentWidth + padding.right;
3628
+ const height = titleBarHeight + padding.top + lines.length * lineHeightPx + padding.bottom + TEXT_BOTTOM_PADDING;
3629
+ const svgWidth = formatNumber(width);
3630
+ const svgHeight = formatNumber(height);
3631
+ const viewBox = `0 0 ${svgWidth} ${svgHeight}`;
3632
+ const textElements = renderLines(lines, textStartX, textStartY, lineHeightPx);
3633
+ return [
3634
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${svgWidth}" height="${svgHeight}" viewBox="${viewBox}">`,
3635
+ "<defs>",
3636
+ "<style><![CDATA[",
3637
+ FONT_FACE_CSS,
3638
+ "]]></style>",
3639
+ "</defs>",
3640
+ `<rect x="0" y="0" width="${svgWidth}" height="${svgHeight}" fill="${BACKGROUND}" />`,
3641
+ showWindow ? renderWindowControls() : "",
3642
+ `<g font-family="${FONT_FAMILY}" font-size="${formatNumber(FONT_SIZE)}px" fill="${DEFAULT_FOREGROUND}">`,
3643
+ textElements,
3644
+ "</g>",
3645
+ "</svg>"
3646
+ ].join("");
3647
+ }
3648
+ function splitIntoLines(runs) {
3649
+ const lines = [[]];
3650
+ for (const run of runs) {
3651
+ if (run.text === "\n") {
3652
+ lines.push([]);
3653
+ continue;
3654
+ }
3655
+ lines[lines.length - 1]?.push(run);
3656
+ }
3657
+ return lines;
3658
+ }
3659
+ function measureLines(lines) {
3660
+ return Math.max(
3661
+ ...lines.map((line) => line.reduce((width, run) => width + displayWidth(run.text) * CHARACTER_WIDTH, 0)),
3662
+ 0
3663
+ );
3664
+ }
3665
+ function displayWidth(text) {
3666
+ const segmenter = new Intl.Segmenter();
3667
+ let width = 0;
3668
+ for (const { segment } of segmenter.segment(text)) {
3669
+ const cp = segment.codePointAt(0);
3670
+ width += cp !== void 0 && isWideCodePoint(cp) ? 2 : 1;
3671
+ }
3672
+ return width;
3673
+ }
3674
+ function isWideCodePoint(cp) {
3675
+ return cp >= 4352 && cp <= 4447 || // Hangul Jamo
3676
+ cp === 9001 || cp === 9002 || // Angle brackets
3677
+ cp >= 11904 && cp <= 12350 || // CJK Radicals, Kangxi
3678
+ cp >= 12353 && cp <= 13247 || // Hiragana, Katakana, CJK symbols
3679
+ cp >= 13312 && cp <= 19903 || // CJK Extension A
3680
+ cp >= 19968 && cp <= 42191 || // CJK Unified Ideographs
3681
+ cp >= 43360 && cp <= 43391 || // Hangul Jamo Extended-A
3682
+ cp >= 44032 && cp <= 55215 || // Hangul Syllables
3683
+ cp >= 63744 && cp <= 64255 || // CJK Compatibility Ideographs
3684
+ cp >= 65040 && cp <= 65049 || // Vertical Forms
3685
+ cp >= 65072 && cp <= 65135 || // CJK Compatibility Forms
3686
+ cp >= 65280 && cp <= 65376 || // Fullwidth Latin, Halfwidth Katakana
3687
+ cp >= 65504 && cp <= 65510 || // Fullwidth Signs
3688
+ cp >= 110592 && cp <= 110847 || // Kana Supplement
3689
+ cp >= 126980 && cp <= 126980 || // Mahjong tile
3690
+ cp >= 127183 && cp <= 127183 || // Playing card black joker
3691
+ cp >= 127488 && cp <= 131069 || // Enclosed CJK + Emoji
3692
+ cp >= 131072 && cp <= 196605 || // CJK Extension B–F
3693
+ cp >= 196608 && cp <= 262141;
3694
+ }
3695
+ function renderLines(lines, textStartX, textStartY, lineHeightPx) {
3696
+ return lines.map((line, index) => {
3697
+ const y = formatNumber(textStartY + index * lineHeightPx);
3698
+ if (line.length === 0) {
3699
+ return `<text x="${formatNumber(textStartX)}" y="${y}" xml:space="preserve"/>`;
3700
+ }
3701
+ return [
3702
+ `<text x="${formatNumber(textStartX)}" y="${y}" xml:space="preserve">`,
3703
+ line.map(renderRun).join(""),
3704
+ "</text>"
3705
+ ].join("");
3706
+ }).join("");
3707
+ }
3708
+ function renderRun(run) {
3709
+ const attributes = ['xml:space="preserve"'];
3710
+ const color = resolveColor(run.fg);
3711
+ const textDecorations = [];
3712
+ if (color !== DEFAULT_FOREGROUND) {
3713
+ attributes.push(`fill="${escapeXmlAttribute(color)}"`);
3714
+ }
3715
+ if (run.bold) {
3716
+ attributes.push('font-weight="bold"');
3717
+ }
3718
+ if (run.italic) {
3719
+ attributes.push('font-style="italic"');
3720
+ }
3721
+ if (run.underline) {
3722
+ textDecorations.push("underline");
3723
+ }
3724
+ if (run.strikethrough) {
3725
+ textDecorations.push("line-through");
3726
+ }
3727
+ if (textDecorations.length > 0) {
3728
+ attributes.push(`text-decoration="${textDecorations.join(" ")}"`);
3729
+ }
3730
+ if (run.dim) {
3731
+ attributes.push('opacity="0.7"');
3732
+ }
3733
+ return `<tspan ${attributes.join(" ")}>${escapeXmlText(run.text)}</tspan>`;
3734
+ }
3735
+ function renderWindowControls() {
3736
+ return [
3737
+ '<circle cx="13.5" cy="12" r="5.5" fill="#FF5A54" />',
3738
+ '<circle cx="32.5" cy="12" r="5.5" fill="#E6BF29" />',
3739
+ '<circle cx="51.5" cy="12" r="5.5" fill="#52C12B" />'
3740
+ ].join("");
3741
+ }
3742
+ function resolveColor(color) {
3743
+ if (color === null) {
3744
+ return DEFAULT_FOREGROUND;
3745
+ }
3746
+ if (color.type === "ansi4") {
3747
+ return ANSI_16_PALETTE[color.index] ?? DEFAULT_FOREGROUND;
3748
+ }
3749
+ if (color.type === "ansi8") {
3750
+ if (color.index < ANSI_16_PALETTE.length) {
3751
+ return ANSI_16_PALETTE[color.index] ?? DEFAULT_FOREGROUND;
3752
+ }
3753
+ return XTERM_256_PALETTE[color.index] ?? DEFAULT_FOREGROUND;
3754
+ }
3755
+ return `rgb(${color.r},${color.g},${color.b})`;
3756
+ }
3757
+ function escapeXmlText(value) {
3758
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
3759
+ }
3760
+ function escapeXmlAttribute(value) {
3761
+ return escapeXmlText(value).replaceAll('"', "&quot;").replaceAll("'", "&apos;");
3762
+ }
3763
+ function formatNumber(value) {
3764
+ return value.toFixed(2);
3765
+ }
3766
+
3767
+ // ../terminal-png/src/index.ts
3768
+ async function renderTerminalPng(ansiText, options = {}) {
3769
+ const runs = parseAnsi(ansiText);
3770
+ const svg = renderSvg(runs, {
3771
+ padding: options.padding,
3772
+ window: options.window
3773
+ });
3774
+ const png = renderPng(svg);
3775
+ if (options.output) {
3776
+ await writeFile(options.output, png);
3777
+ }
3778
+ return png;
3779
+ }
3780
+
1582
3781
  // 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" }))
3782
+ var params11 = S.Object({
3783
+ session: S.Optional(S.String({ short: "s", description: "Session name" })),
3784
+ output: S.String({ short: "o", description: "Path to the output PNG file" }),
3785
+ window: S.Optional(S.Boolean({ description: "Include terminal window chrome", default: true })),
3786
+ padding: S.Optional(S.Number({ short: "p", description: "Padding around terminal content" }))
1590
3787
  });
1591
- var screenshot = defineCommand11({
3788
+ var screenshot = defineCommand({
1592
3789
  name: "screenshot",
1593
3790
  description: "Capture the current terminal screen as a PNG image",
1594
3791
  scope: ["cli"],
@@ -1606,12 +3803,11 @@ var screenshot = defineCommand11({
1606
3803
  });
1607
3804
 
1608
3805
  // 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" }))
3806
+ var params12 = S.Object({
3807
+ signal: S.String({ description: "Signal to send to the session process" }),
3808
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1613
3809
  });
1614
- var sendSignal = defineCommand12({
3810
+ var sendSignal = defineCommand({
1615
3811
  name: "send-signal",
1616
3812
  description: "Send a process signal to an active terminal session",
1617
3813
  scope: ["cli", "mcp", "sdk"],
@@ -1625,12 +3821,11 @@ var sendSignal = defineCommand12({
1625
3821
  });
1626
3822
 
1627
3823
  // 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" }))
3824
+ var params13 = S.Object({
3825
+ text: S.String({ description: "Text to write to the session" }),
3826
+ session: S.Optional(S.String({ short: "s", description: "Session name" }))
1632
3827
  });
1633
- var type = defineCommand13({
3828
+ var type = defineCommand({
1634
3829
  name: "type",
1635
3830
  description: "Write text to an active terminal session character-by-character with delay",
1636
3831
  scope: ["cli", "mcp", "sdk"],
@@ -1644,14 +3839,13 @@ var type = defineCommand13({
1644
3839
  });
1645
3840
 
1646
3841
  // 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, {
3842
+ var params14 = S.Object({
3843
+ agent: S.Enum(installableAgents, {
1650
3844
  description: "Agent to uninstall terminal-pilot from",
1651
3845
  default: DEFAULT_INSTALL_AGENT
1652
3846
  })
1653
3847
  });
1654
- var uninstall = defineCommand14({
3848
+ var uninstall = defineCommand({
1655
3849
  name: "uninstall",
1656
3850
  description: "Remove the terminal-pilot CLI skill.",
1657
3851
  scope: ["cli"],
@@ -1687,19 +3881,18 @@ var uninstall = defineCommand14({
1687
3881
  });
1688
3882
 
1689
3883
  // 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({
3884
+ var params15 = S.Object({
3885
+ pattern: S.String({ description: "Regular expression pattern to wait for" }),
3886
+ session: S.Optional(S.String({ short: "s", description: "Session name" })),
3887
+ timeout: S.Optional(S.Number({ short: "t", description: "Maximum wait time in milliseconds" })),
3888
+ literal: S.Optional(
3889
+ S.Boolean({
1697
3890
  short: "l",
1698
3891
  description: "When true, treat pattern as a literal string instead of a regex"
1699
3892
  })
1700
3893
  )
1701
3894
  });
1702
- var waitFor = defineCommand15({
3895
+ var waitFor = defineCommand({
1703
3896
  name: "wait-for",
1704
3897
  description: "Wait for terminal output to match a pattern",
1705
3898
  scope: ["cli", "mcp", "sdk"],
@@ -1714,12 +3907,11 @@ var waitFor = defineCommand15({
1714
3907
  });
1715
3908
 
1716
3909
  // 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" }))
3910
+ var params16 = S.Object({
3911
+ session: S.Optional(S.String({ short: "s", description: "Session name" })),
3912
+ timeout: S.Optional(S.Number({ short: "t", description: "Maximum wait time in milliseconds" }))
1721
3913
  });
1722
- var waitForExit2 = defineCommand16({
3914
+ var waitForExit2 = defineCommand({
1723
3915
  name: "wait-for-exit",
1724
3916
  description: "Wait for a terminal session process to finish",
1725
3917
  scope: ["cli", "mcp", "sdk"],