terminal-pilot 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/cli.js +5663 -1269
  2. package/dist/cli.js.map +4 -4
  3. package/dist/commands/close-session.js +268 -87
  4. package/dist/commands/close-session.js.map +4 -4
  5. package/dist/commands/create-session.js +268 -87
  6. package/dist/commands/create-session.js.map +4 -4
  7. package/dist/commands/fill.js +268 -87
  8. package/dist/commands/fill.js.map +4 -4
  9. package/dist/commands/get-session.js +268 -87
  10. package/dist/commands/get-session.js.map +4 -4
  11. package/dist/commands/index.js +2404 -279
  12. package/dist/commands/index.js.map +4 -4
  13. package/dist/commands/install.js +1372 -14
  14. package/dist/commands/install.js.map +4 -4
  15. package/dist/commands/installer.js +178 -15
  16. package/dist/commands/installer.js.map +4 -4
  17. package/dist/commands/list-sessions.js +268 -87
  18. package/dist/commands/list-sessions.js.map +4 -4
  19. package/dist/commands/press-key.js +268 -87
  20. package/dist/commands/press-key.js.map +4 -4
  21. package/dist/commands/read-history.js +268 -87
  22. package/dist/commands/read-history.js.map +4 -4
  23. package/dist/commands/read-screen.js +268 -87
  24. package/dist/commands/read-screen.js.map +4 -4
  25. package/dist/commands/resize.js +268 -87
  26. package/dist/commands/resize.js.map +4 -4
  27. package/dist/commands/runtime.js +27 -85
  28. package/dist/commands/runtime.js.map +4 -4
  29. package/dist/commands/screenshot.js +1033 -91
  30. package/dist/commands/screenshot.js.map +4 -4
  31. package/dist/commands/send-signal.js +268 -87
  32. package/dist/commands/send-signal.js.map +4 -4
  33. package/dist/commands/type.js +268 -87
  34. package/dist/commands/type.js.map +4 -4
  35. package/dist/commands/uninstall.js +419 -17
  36. package/dist/commands/uninstall.js.map +4 -4
  37. package/dist/commands/wait-for-exit.js +268 -87
  38. package/dist/commands/wait-for-exit.js.map +4 -4
  39. package/dist/commands/wait-for.js +268 -87
  40. package/dist/commands/wait-for.js.map +4 -4
  41. package/dist/index.js +19 -83
  42. package/dist/index.js.map +3 -3
  43. package/dist/terminal-pilot.js +19 -83
  44. package/dist/terminal-pilot.js.map +3 -3
  45. package/dist/terminal-session.js +19 -83
  46. package/dist/terminal-session.js.map +3 -3
  47. package/dist/testing/cli-repl.js +5598 -1203
  48. package/dist/testing/cli-repl.js.map +4 -4
  49. package/dist/testing/qa-cli.js +5601 -1206
  50. package/dist/testing/qa-cli.js.map +4 -4
  51. package/package.json +17 -8
@@ -1,22 +1,1380 @@
1
- // src/commands/install.ts
2
- import { installSkill } from "@poe-code/agent-skill-config";
3
- import { defineCommand, S } from "@poe-code/cmdkit";
4
-
5
- // src/commands/installer.ts
1
+ // ../agent-skill-config/src/configs.ts
6
2
  import os from "node:os";
7
3
  import path from "node:path";
4
+
5
+ // ../agent-defs/src/agents/claude-code.ts
6
+ var claudeCodeAgent = {
7
+ id: "claude-code",
8
+ name: "claude-code",
9
+ label: "Claude Code",
10
+ summary: "Configure Claude Code to route through Poe.",
11
+ aliases: ["claude"],
12
+ binaryName: "claude",
13
+ configPath: "~/.claude/settings.json",
14
+ branding: {
15
+ colors: {
16
+ dark: "#C15F3C",
17
+ light: "#C15F3C"
18
+ }
19
+ }
20
+ };
21
+
22
+ // ../agent-defs/src/agents/claude-desktop.ts
23
+ var claudeDesktopAgent = {
24
+ id: "claude-desktop",
25
+ name: "claude-desktop",
26
+ label: "Claude Desktop",
27
+ summary: "Anthropic's official desktop application for Claude",
28
+ configPath: "~/.claude/settings.json",
29
+ branding: {
30
+ colors: {
31
+ dark: "#D97757",
32
+ light: "#D97757"
33
+ }
34
+ }
35
+ };
36
+
37
+ // ../agent-defs/src/agents/codex.ts
38
+ var codexAgent = {
39
+ id: "codex",
40
+ name: "codex",
41
+ label: "Codex",
42
+ summary: "Configure Codex to use Poe as the model provider.",
43
+ binaryName: "codex",
44
+ configPath: "~/.codex/config.toml",
45
+ branding: {
46
+ colors: {
47
+ dark: "#D5D9DF",
48
+ light: "#7A7F86"
49
+ }
50
+ }
51
+ };
52
+
53
+ // ../agent-defs/src/agents/opencode.ts
54
+ var openCodeAgent = {
55
+ id: "opencode",
56
+ name: "opencode",
57
+ label: "OpenCode CLI",
58
+ summary: "Configure OpenCode CLI to use the Poe API.",
59
+ binaryName: "opencode",
60
+ configPath: "~/.config/opencode/config.json",
61
+ branding: {
62
+ colors: {
63
+ dark: "#4A4F55",
64
+ light: "#2F3338"
65
+ }
66
+ }
67
+ };
68
+
69
+ // ../agent-defs/src/agents/kimi.ts
70
+ var kimiAgent = {
71
+ id: "kimi",
72
+ name: "kimi",
73
+ label: "Kimi",
74
+ summary: "Configure Kimi CLI to use Poe API",
75
+ aliases: ["kimi-cli"],
76
+ binaryName: "kimi",
77
+ configPath: "~/.kimi/config.toml",
78
+ branding: {
79
+ colors: {
80
+ dark: "#7B68EE",
81
+ light: "#6A5ACD"
82
+ }
83
+ }
84
+ };
85
+
86
+ // ../agent-defs/src/registry.ts
87
+ var allAgents = [
88
+ claudeCodeAgent,
89
+ claudeDesktopAgent,
90
+ codexAgent,
91
+ openCodeAgent,
92
+ kimiAgent
93
+ ];
94
+ var lookup = /* @__PURE__ */ new Map();
95
+ for (const agent of allAgents) {
96
+ const values = [agent.id, agent.name, ...agent.aliases ?? []];
97
+ for (const value of values) {
98
+ const normalized = value.toLowerCase();
99
+ if (!lookup.has(normalized)) {
100
+ lookup.set(normalized, agent.id);
101
+ }
102
+ }
103
+ }
104
+ function resolveAgentId(input) {
105
+ if (!input) {
106
+ return void 0;
107
+ }
108
+ return lookup.get(input.toLowerCase());
109
+ }
110
+
111
+ // ../agent-skill-config/src/configs.ts
112
+ var agentSkillConfigs = {
113
+ "claude-code": {
114
+ globalSkillDir: "~/.claude/skills",
115
+ localSkillDir: ".claude/skills"
116
+ },
117
+ codex: {
118
+ globalSkillDir: "~/.codex/skills",
119
+ localSkillDir: ".codex/skills"
120
+ },
121
+ opencode: {
122
+ globalSkillDir: "~/.config/opencode/skills",
123
+ localSkillDir: ".opencode/skills"
124
+ }
125
+ };
126
+ var supportedAgents = Object.keys(agentSkillConfigs);
127
+ function resolveAgentSupport(input, registry = agentSkillConfigs) {
128
+ const resolvedId = resolveAgentId(input);
129
+ if (!resolvedId) {
130
+ return { status: "unknown", input };
131
+ }
132
+ const config = registry[resolvedId];
133
+ if (!config) {
134
+ return { status: "unsupported", input, id: resolvedId };
135
+ }
136
+ return { status: "supported", input, id: resolvedId, config };
137
+ }
138
+
139
+ // ../config-mutations/src/mutations/file-mutation.ts
140
+ function ensureDirectory(options) {
141
+ return {
142
+ kind: "ensureDirectory",
143
+ path: options.path,
144
+ label: options.label
145
+ };
146
+ }
147
+ function remove(options) {
148
+ return {
149
+ kind: "removeFile",
150
+ target: options.target,
151
+ whenEmpty: options.whenEmpty,
152
+ whenContentMatches: options.whenContentMatches,
153
+ label: options.label
154
+ };
155
+ }
156
+ function removeDirectory(options) {
157
+ return {
158
+ kind: "removeDirectory",
159
+ path: options.path,
160
+ force: options.force,
161
+ label: options.label
162
+ };
163
+ }
164
+ function chmod(options) {
165
+ return {
166
+ kind: "chmod",
167
+ target: options.target,
168
+ mode: options.mode,
169
+ label: options.label
170
+ };
171
+ }
172
+ function backup(options) {
173
+ return {
174
+ kind: "backup",
175
+ target: options.target,
176
+ label: options.label
177
+ };
178
+ }
179
+ var fileMutation = {
180
+ ensureDirectory,
181
+ remove,
182
+ removeDirectory,
183
+ chmod,
184
+ backup
185
+ };
186
+
187
+ // ../config-mutations/src/mutations/template-mutation.ts
188
+ function write(options) {
189
+ return {
190
+ kind: "templateWrite",
191
+ target: options.target,
192
+ templateId: options.templateId,
193
+ context: options.context,
194
+ label: options.label
195
+ };
196
+ }
197
+ function mergeToml(options) {
198
+ return {
199
+ kind: "templateMergeToml",
200
+ target: options.target,
201
+ templateId: options.templateId,
202
+ context: options.context,
203
+ label: options.label
204
+ };
205
+ }
206
+ function mergeJson(options) {
207
+ return {
208
+ kind: "templateMergeJson",
209
+ target: options.target,
210
+ templateId: options.templateId,
211
+ context: options.context,
212
+ label: options.label
213
+ };
214
+ }
215
+ var templateMutation = {
216
+ write,
217
+ mergeToml,
218
+ mergeJson
219
+ };
220
+
221
+ // ../config-mutations/src/execution/apply-mutation.ts
222
+ import Mustache from "mustache";
223
+
224
+ // ../config-mutations/src/formats/json.ts
225
+ import * as jsonc from "jsonc-parser";
226
+ function isConfigObject(value) {
227
+ return typeof value === "object" && value !== null && !Array.isArray(value);
228
+ }
229
+ function parse2(content) {
230
+ if (!content || content.trim() === "") {
231
+ return {};
232
+ }
233
+ const errors = [];
234
+ const parsed = jsonc.parse(content, errors, {
235
+ allowTrailingComma: true,
236
+ disallowComments: false
237
+ });
238
+ if (errors.length > 0) {
239
+ throw new Error(`JSON parse error: ${jsonc.printParseErrorCode(errors[0].error)}`);
240
+ }
241
+ if (parsed === null || parsed === void 0) {
242
+ return {};
243
+ }
244
+ if (!isConfigObject(parsed)) {
245
+ throw new Error("Expected JSON object.");
246
+ }
247
+ return parsed;
248
+ }
249
+ function serialize(obj) {
250
+ return `${JSON.stringify(obj, null, 2)}
251
+ `;
252
+ }
253
+ function merge(base, patch) {
254
+ const result = { ...base };
255
+ for (const [key, value] of Object.entries(patch)) {
256
+ if (value === void 0) {
257
+ continue;
258
+ }
259
+ const existing = result[key];
260
+ if (isConfigObject(existing) && isConfigObject(value)) {
261
+ result[key] = merge(existing, value);
262
+ continue;
263
+ }
264
+ result[key] = value;
265
+ }
266
+ return result;
267
+ }
268
+ function prune(obj, shape) {
269
+ let changed = false;
270
+ const result = { ...obj };
271
+ for (const [key, pattern] of Object.entries(shape)) {
272
+ if (!(key in result)) {
273
+ continue;
274
+ }
275
+ const current = result[key];
276
+ if (isConfigObject(pattern) && Object.keys(pattern).length === 0) {
277
+ delete result[key];
278
+ changed = true;
279
+ continue;
280
+ }
281
+ if (isConfigObject(pattern) && isConfigObject(current)) {
282
+ const { changed: childChanged, result: childResult } = prune(
283
+ current,
284
+ pattern
285
+ );
286
+ if (childChanged) {
287
+ changed = true;
288
+ }
289
+ if (Object.keys(childResult).length === 0) {
290
+ delete result[key];
291
+ } else {
292
+ result[key] = childResult;
293
+ }
294
+ continue;
295
+ }
296
+ delete result[key];
297
+ changed = true;
298
+ }
299
+ return { changed, result };
300
+ }
301
+ var jsonFormat = {
302
+ parse: parse2,
303
+ serialize,
304
+ merge,
305
+ prune
306
+ };
307
+
308
+ // ../config-mutations/src/formats/toml.ts
309
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
310
+ function isConfigObject2(value) {
311
+ return typeof value === "object" && value !== null && !Array.isArray(value);
312
+ }
313
+ function parse3(content) {
314
+ if (!content || content.trim() === "") {
315
+ return {};
316
+ }
317
+ const parsed = parseToml(content);
318
+ if (!isConfigObject2(parsed)) {
319
+ throw new Error("Expected TOML document to be a table.");
320
+ }
321
+ return parsed;
322
+ }
323
+ function serialize2(obj) {
324
+ const serialized = stringifyToml(obj);
325
+ return serialized.endsWith("\n") ? serialized : `${serialized}
326
+ `;
327
+ }
328
+ function merge2(base, patch) {
329
+ const result = { ...base };
330
+ for (const [key, value] of Object.entries(patch)) {
331
+ if (value === void 0) {
332
+ continue;
333
+ }
334
+ const existing = result[key];
335
+ if (isConfigObject2(existing) && isConfigObject2(value)) {
336
+ result[key] = merge2(existing, value);
337
+ continue;
338
+ }
339
+ result[key] = value;
340
+ }
341
+ return result;
342
+ }
343
+ function prune2(obj, shape) {
344
+ let changed = false;
345
+ const result = { ...obj };
346
+ for (const [key, pattern] of Object.entries(shape)) {
347
+ if (!(key in result)) {
348
+ continue;
349
+ }
350
+ const current = result[key];
351
+ if (isConfigObject2(pattern) && Object.keys(pattern).length === 0) {
352
+ delete result[key];
353
+ changed = true;
354
+ continue;
355
+ }
356
+ if (isConfigObject2(pattern) && isConfigObject2(current)) {
357
+ const { changed: childChanged, result: childResult } = prune2(
358
+ current,
359
+ pattern
360
+ );
361
+ if (childChanged) {
362
+ changed = true;
363
+ }
364
+ if (Object.keys(childResult).length === 0) {
365
+ delete result[key];
366
+ } else {
367
+ result[key] = childResult;
368
+ }
369
+ continue;
370
+ }
371
+ delete result[key];
372
+ changed = true;
373
+ }
374
+ return { changed, result };
375
+ }
376
+ var tomlFormat = {
377
+ parse: parse3,
378
+ serialize: serialize2,
379
+ merge: merge2,
380
+ prune: prune2
381
+ };
382
+
383
+ // ../config-mutations/src/formats/index.ts
384
+ var formatRegistry = {
385
+ json: jsonFormat,
386
+ toml: tomlFormat
387
+ };
388
+ var extensionMap = {
389
+ ".json": "json",
390
+ ".toml": "toml"
391
+ };
392
+ function getConfigFormat(pathOrFormat) {
393
+ if (pathOrFormat in formatRegistry) {
394
+ return formatRegistry[pathOrFormat];
395
+ }
396
+ const ext = getExtension(pathOrFormat);
397
+ const formatName = extensionMap[ext];
398
+ if (!formatName) {
399
+ throw new Error(
400
+ `Unsupported config format. Cannot detect format from "${pathOrFormat}". Supported extensions: ${Object.keys(extensionMap).join(", ")}. Supported format names: ${Object.keys(formatRegistry).join(", ")}.`
401
+ );
402
+ }
403
+ return formatRegistry[formatName];
404
+ }
405
+ function detectFormat(path4) {
406
+ const ext = getExtension(path4);
407
+ return extensionMap[ext];
408
+ }
409
+ function getExtension(path4) {
410
+ const lastDot = path4.lastIndexOf(".");
411
+ if (lastDot === -1) {
412
+ return "";
413
+ }
414
+ return path4.slice(lastDot).toLowerCase();
415
+ }
416
+
417
+ // ../config-mutations/src/execution/path-utils.ts
418
+ import path2 from "node:path";
419
+ function expandHome(targetPath, homeDir) {
420
+ if (!targetPath?.startsWith("~")) {
421
+ return targetPath;
422
+ }
423
+ if (targetPath.startsWith("~./")) {
424
+ targetPath = `~/.${targetPath.slice(3)}`;
425
+ }
426
+ let remainder = targetPath.slice(1);
427
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
428
+ remainder = remainder.slice(1);
429
+ } else if (remainder.startsWith(".")) {
430
+ remainder = remainder.slice(1);
431
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
432
+ remainder = remainder.slice(1);
433
+ }
434
+ }
435
+ return remainder.length === 0 ? homeDir : path2.join(homeDir, remainder);
436
+ }
437
+ function validateHomePath(targetPath) {
438
+ if (typeof targetPath !== "string" || targetPath.length === 0) {
439
+ throw new Error("Target path must be a non-empty string.");
440
+ }
441
+ if (!targetPath.startsWith("~")) {
442
+ throw new Error(
443
+ `All target paths must be home-relative (start with ~). Received: "${targetPath}"`
444
+ );
445
+ }
446
+ }
447
+ function resolvePath(rawPath, homeDir, pathMapper) {
448
+ validateHomePath(rawPath);
449
+ const expanded = expandHome(rawPath, homeDir);
450
+ if (!pathMapper) {
451
+ return expanded;
452
+ }
453
+ const rawDirectory = path2.dirname(expanded);
454
+ const mappedDirectory = pathMapper.mapTargetDirectory({
455
+ targetDirectory: rawDirectory
456
+ });
457
+ const filename = path2.basename(expanded);
458
+ return filename.length === 0 ? mappedDirectory : path2.join(mappedDirectory, filename);
459
+ }
460
+
461
+ // ../config-mutations/src/fs-utils.ts
462
+ function isNotFound(error) {
463
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
464
+ }
465
+ async function readFileIfExists(fs, target) {
466
+ try {
467
+ return await fs.readFile(target, "utf8");
468
+ } catch (error) {
469
+ if (isNotFound(error)) {
470
+ return null;
471
+ }
472
+ throw error;
473
+ }
474
+ }
475
+ async function pathExists(fs, target) {
476
+ try {
477
+ await fs.stat(target);
478
+ return true;
479
+ } catch (error) {
480
+ if (isNotFound(error)) {
481
+ return false;
482
+ }
483
+ throw error;
484
+ }
485
+ }
486
+ function createTimestamp() {
487
+ return (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-");
488
+ }
489
+
490
+ // ../config-mutations/src/execution/apply-mutation.ts
491
+ function resolveValue(resolver, options) {
492
+ if (typeof resolver === "function") {
493
+ return resolver(options);
494
+ }
495
+ return resolver;
496
+ }
497
+ function createInvalidDocumentBackupPath(targetPath) {
498
+ const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
499
+ return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
500
+ }
501
+ async function backupInvalidDocument(fs, targetPath, content) {
502
+ const backupPath = createInvalidDocumentBackupPath(targetPath);
503
+ await fs.writeFile(backupPath, content, { encoding: "utf8" });
504
+ }
505
+ function describeMutation(kind, targetPath) {
506
+ const displayPath = targetPath ?? "target";
507
+ switch (kind) {
508
+ case "ensureDirectory":
509
+ return `Create ${displayPath}`;
510
+ case "removeDirectory":
511
+ return `Remove directory ${displayPath}`;
512
+ case "backup":
513
+ return `Backup ${displayPath}`;
514
+ case "templateWrite":
515
+ return `Write ${displayPath}`;
516
+ case "chmod":
517
+ return `Set permissions on ${displayPath}`;
518
+ case "removeFile":
519
+ return `Remove ${displayPath}`;
520
+ case "configMerge":
521
+ case "configPrune":
522
+ case "configTransform":
523
+ case "templateMergeToml":
524
+ case "templateMergeJson":
525
+ return `Update ${displayPath}`;
526
+ default:
527
+ return "Operation";
528
+ }
529
+ }
530
+ function pruneKeysByPrefix(table, prefix) {
531
+ const result = {};
532
+ for (const [key, value] of Object.entries(table)) {
533
+ if (!key.startsWith(prefix)) {
534
+ result[key] = value;
535
+ }
536
+ }
537
+ return result;
538
+ }
539
+ function isConfigObject3(value) {
540
+ return typeof value === "object" && value !== null && !Array.isArray(value);
541
+ }
542
+ function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
543
+ const result = { ...base };
544
+ const prefixMap = pruneByPrefix ?? {};
545
+ for (const [key, value] of Object.entries(patch)) {
546
+ const current = result[key];
547
+ const prefix = prefixMap[key];
548
+ if (isConfigObject3(current) && isConfigObject3(value)) {
549
+ if (prefix) {
550
+ const pruned = pruneKeysByPrefix(current, prefix);
551
+ result[key] = { ...pruned, ...value };
552
+ } else {
553
+ result[key] = mergeWithPruneByPrefix(
554
+ current,
555
+ value,
556
+ prefixMap
557
+ );
558
+ }
559
+ continue;
560
+ }
561
+ result[key] = value;
562
+ }
563
+ return result;
564
+ }
565
+ async function applyMutation(mutation, context, options) {
566
+ switch (mutation.kind) {
567
+ case "ensureDirectory":
568
+ return applyEnsureDirectory(mutation, context, options);
569
+ case "removeDirectory":
570
+ return applyRemoveDirectory(mutation, context, options);
571
+ case "removeFile":
572
+ return applyRemoveFile(mutation, context, options);
573
+ case "chmod":
574
+ return applyChmod(mutation, context, options);
575
+ case "backup":
576
+ return applyBackup(mutation, context, options);
577
+ case "configMerge":
578
+ return applyConfigMerge(mutation, context, options);
579
+ case "configPrune":
580
+ return applyConfigPrune(mutation, context, options);
581
+ case "configTransform":
582
+ return applyConfigTransform(mutation, context, options);
583
+ case "templateWrite":
584
+ return applyTemplateWrite(mutation, context, options);
585
+ case "templateMergeToml":
586
+ return applyTemplateMerge(mutation, context, options, "toml");
587
+ case "templateMergeJson":
588
+ return applyTemplateMerge(mutation, context, options, "json");
589
+ default: {
590
+ const never = mutation;
591
+ throw new Error(`Unknown mutation kind: ${never.kind}`);
592
+ }
593
+ }
594
+ }
595
+ async function applyEnsureDirectory(mutation, context, options) {
596
+ const rawPath = resolveValue(mutation.path, options);
597
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
598
+ const details = {
599
+ kind: mutation.kind,
600
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
601
+ targetPath
602
+ };
603
+ const existed = await pathExists(context.fs, targetPath);
604
+ if (!context.dryRun) {
605
+ await context.fs.mkdir(targetPath, { recursive: true });
606
+ }
607
+ return {
608
+ outcome: {
609
+ changed: !existed,
610
+ effect: "mkdir",
611
+ detail: existed ? "noop" : "create"
612
+ },
613
+ details
614
+ };
615
+ }
616
+ async function applyRemoveDirectory(mutation, context, options) {
617
+ const rawPath = resolveValue(mutation.path, options);
618
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
619
+ const details = {
620
+ kind: mutation.kind,
621
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
622
+ targetPath
623
+ };
624
+ const existed = await pathExists(context.fs, targetPath);
625
+ if (!existed) {
626
+ return {
627
+ outcome: { changed: false, effect: "none", detail: "noop" },
628
+ details
629
+ };
630
+ }
631
+ if (typeof context.fs.rm !== "function") {
632
+ return {
633
+ outcome: { changed: false, effect: "none", detail: "noop" },
634
+ details
635
+ };
636
+ }
637
+ if (mutation.force) {
638
+ if (!context.dryRun) {
639
+ await context.fs.rm(targetPath, { recursive: true, force: true });
640
+ }
641
+ return {
642
+ outcome: { changed: true, effect: "delete", detail: "delete" },
643
+ details
644
+ };
645
+ }
646
+ const entries = await context.fs.readdir(targetPath);
647
+ if (entries.length > 0) {
648
+ return {
649
+ outcome: { changed: false, effect: "none", detail: "noop" },
650
+ details
651
+ };
652
+ }
653
+ if (!context.dryRun) {
654
+ await context.fs.rm(targetPath, { recursive: true, force: true });
655
+ }
656
+ return {
657
+ outcome: { changed: true, effect: "delete", detail: "delete" },
658
+ details
659
+ };
660
+ }
661
+ async function applyRemoveFile(mutation, context, options) {
662
+ const rawPath = resolveValue(mutation.target, options);
663
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
664
+ const details = {
665
+ kind: mutation.kind,
666
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
667
+ targetPath
668
+ };
669
+ try {
670
+ const content = await context.fs.readFile(targetPath, "utf8");
671
+ const trimmed = content.trim();
672
+ if (mutation.whenContentMatches && !mutation.whenContentMatches.test(trimmed)) {
673
+ return {
674
+ outcome: { changed: false, effect: "none", detail: "noop" },
675
+ details
676
+ };
677
+ }
678
+ if (mutation.whenEmpty && trimmed.length > 0) {
679
+ return {
680
+ outcome: { changed: false, effect: "none", detail: "noop" },
681
+ details
682
+ };
683
+ }
684
+ if (!context.dryRun) {
685
+ await context.fs.unlink(targetPath);
686
+ }
687
+ return {
688
+ outcome: { changed: true, effect: "delete", detail: "delete" },
689
+ details
690
+ };
691
+ } catch (error) {
692
+ if (isNotFound(error)) {
693
+ return {
694
+ outcome: { changed: false, effect: "none", detail: "noop" },
695
+ details
696
+ };
697
+ }
698
+ throw error;
699
+ }
700
+ }
701
+ async function applyChmod(mutation, context, options) {
702
+ const rawPath = resolveValue(mutation.target, options);
703
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
704
+ const details = {
705
+ kind: mutation.kind,
706
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
707
+ targetPath
708
+ };
709
+ if (typeof context.fs.chmod !== "function") {
710
+ return {
711
+ outcome: { changed: false, effect: "none", detail: "noop" },
712
+ details
713
+ };
714
+ }
715
+ try {
716
+ const stat = await context.fs.stat(targetPath);
717
+ const currentMode = typeof stat.mode === "number" ? stat.mode & 511 : null;
718
+ if (currentMode === mutation.mode) {
719
+ return {
720
+ outcome: { changed: false, effect: "none", detail: "noop" },
721
+ details
722
+ };
723
+ }
724
+ if (!context.dryRun) {
725
+ await context.fs.chmod(targetPath, mutation.mode);
726
+ }
727
+ return {
728
+ outcome: { changed: true, effect: "chmod", detail: "update" },
729
+ details
730
+ };
731
+ } catch (error) {
732
+ if (isNotFound(error)) {
733
+ return {
734
+ outcome: { changed: false, effect: "none", detail: "noop" },
735
+ details
736
+ };
737
+ }
738
+ throw error;
739
+ }
740
+ }
741
+ async function applyBackup(mutation, context, options) {
742
+ const rawPath = resolveValue(mutation.target, options);
743
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
744
+ const details = {
745
+ kind: mutation.kind,
746
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
747
+ targetPath
748
+ };
749
+ const content = await readFileIfExists(context.fs, targetPath);
750
+ if (content === null) {
751
+ return {
752
+ outcome: { changed: false, effect: "none", detail: "noop" },
753
+ details
754
+ };
755
+ }
756
+ if (!context.dryRun) {
757
+ const backupPath = `${targetPath}.backup-${createTimestamp()}`;
758
+ await context.fs.writeFile(backupPath, content, { encoding: "utf8" });
759
+ }
760
+ return {
761
+ outcome: { changed: true, effect: "copy", detail: "backup" },
762
+ details
763
+ };
764
+ }
765
+ async function applyConfigMerge(mutation, context, options) {
766
+ const rawPath = resolveValue(mutation.target, options);
767
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
768
+ const details = {
769
+ kind: mutation.kind,
770
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
771
+ targetPath
772
+ };
773
+ const formatName = mutation.format ?? detectFormat(rawPath);
774
+ if (!formatName) {
775
+ throw new Error(
776
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
777
+ );
778
+ }
779
+ const format = getConfigFormat(formatName);
780
+ const rawContent = await readFileIfExists(context.fs, targetPath);
781
+ let current;
782
+ try {
783
+ current = rawContent === null ? {} : format.parse(rawContent);
784
+ } catch {
785
+ if (rawContent !== null) {
786
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
787
+ }
788
+ current = {};
789
+ }
790
+ const value = resolveValue(mutation.value, options);
791
+ let merged;
792
+ if (mutation.pruneByPrefix) {
793
+ merged = mergeWithPruneByPrefix(current, value, mutation.pruneByPrefix);
794
+ } else {
795
+ merged = format.merge(current, value);
796
+ }
797
+ const serialized = format.serialize(merged);
798
+ const changed = serialized !== rawContent;
799
+ if (changed && !context.dryRun) {
800
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
801
+ }
802
+ return {
803
+ outcome: {
804
+ changed,
805
+ effect: changed ? "write" : "none",
806
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
807
+ },
808
+ details
809
+ };
810
+ }
811
+ async function applyConfigPrune(mutation, context, options) {
812
+ const rawPath = resolveValue(mutation.target, options);
813
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
814
+ const details = {
815
+ kind: mutation.kind,
816
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
817
+ targetPath
818
+ };
819
+ const rawContent = await readFileIfExists(context.fs, targetPath);
820
+ if (rawContent === null) {
821
+ return {
822
+ outcome: { changed: false, effect: "none", detail: "noop" },
823
+ details
824
+ };
825
+ }
826
+ const formatName = mutation.format ?? detectFormat(rawPath);
827
+ if (!formatName) {
828
+ throw new Error(
829
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
830
+ );
831
+ }
832
+ const format = getConfigFormat(formatName);
833
+ let current;
834
+ try {
835
+ current = format.parse(rawContent);
836
+ } catch {
837
+ return {
838
+ outcome: { changed: false, effect: "none", detail: "noop" },
839
+ details
840
+ };
841
+ }
842
+ if (mutation.onlyIf && !mutation.onlyIf(current, options)) {
843
+ return {
844
+ outcome: { changed: false, effect: "none", detail: "noop" },
845
+ details
846
+ };
847
+ }
848
+ const shape = resolveValue(mutation.shape, options);
849
+ const { changed, result } = format.prune(current, shape);
850
+ if (!changed) {
851
+ return {
852
+ outcome: { changed: false, effect: "none", detail: "noop" },
853
+ details
854
+ };
855
+ }
856
+ if (Object.keys(result).length === 0) {
857
+ if (!context.dryRun) {
858
+ await context.fs.unlink(targetPath);
859
+ }
860
+ return {
861
+ outcome: { changed: true, effect: "delete", detail: "delete" },
862
+ details
863
+ };
864
+ }
865
+ const serialized = format.serialize(result);
866
+ if (!context.dryRun) {
867
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
868
+ }
869
+ return {
870
+ outcome: { changed: true, effect: "write", detail: "update" },
871
+ details
872
+ };
873
+ }
874
+ async function applyConfigTransform(mutation, context, options) {
875
+ const rawPath = resolveValue(mutation.target, options);
876
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
877
+ const details = {
878
+ kind: mutation.kind,
879
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
880
+ targetPath
881
+ };
882
+ const formatName = mutation.format ?? detectFormat(rawPath);
883
+ if (!formatName) {
884
+ throw new Error(
885
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
886
+ );
887
+ }
888
+ const format = getConfigFormat(formatName);
889
+ const rawContent = await readFileIfExists(context.fs, targetPath);
890
+ let current;
891
+ try {
892
+ current = rawContent === null ? {} : format.parse(rawContent);
893
+ } catch {
894
+ if (rawContent !== null) {
895
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
896
+ }
897
+ current = {};
898
+ }
899
+ const { content: transformed, changed } = mutation.transform(current, options);
900
+ if (!changed) {
901
+ return {
902
+ outcome: { changed: false, effect: "none", detail: "noop" },
903
+ details
904
+ };
905
+ }
906
+ if (transformed === null) {
907
+ if (rawContent === null) {
908
+ return {
909
+ outcome: { changed: false, effect: "none", detail: "noop" },
910
+ details
911
+ };
912
+ }
913
+ if (!context.dryRun) {
914
+ await context.fs.unlink(targetPath);
915
+ }
916
+ return {
917
+ outcome: { changed: true, effect: "delete", detail: "delete" },
918
+ details
919
+ };
920
+ }
921
+ const serialized = format.serialize(transformed);
922
+ if (!context.dryRun) {
923
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
924
+ }
925
+ return {
926
+ outcome: {
927
+ changed: true,
928
+ effect: "write",
929
+ detail: rawContent === null ? "create" : "update"
930
+ },
931
+ details
932
+ };
933
+ }
934
+ async function applyTemplateWrite(mutation, context, options) {
935
+ if (!context.templates) {
936
+ throw new Error(
937
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
938
+ );
939
+ }
940
+ const rawPath = resolveValue(mutation.target, options);
941
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
942
+ const details = {
943
+ kind: mutation.kind,
944
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
945
+ targetPath
946
+ };
947
+ const template = await context.templates(mutation.templateId);
948
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
949
+ const rendered = Mustache.render(template, templateContext);
950
+ const existed = await pathExists(context.fs, targetPath);
951
+ if (!context.dryRun) {
952
+ await context.fs.writeFile(targetPath, rendered, { encoding: "utf8" });
953
+ }
954
+ return {
955
+ outcome: {
956
+ changed: true,
957
+ effect: "write",
958
+ detail: existed ? "update" : "create"
959
+ },
960
+ details
961
+ };
962
+ }
963
+ async function applyTemplateMerge(mutation, context, options, formatName) {
964
+ if (!context.templates) {
965
+ throw new Error(
966
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
967
+ );
968
+ }
969
+ const rawPath = resolveValue(mutation.target, options);
970
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
971
+ const details = {
972
+ kind: mutation.kind,
973
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
974
+ targetPath
975
+ };
976
+ const format = getConfigFormat(formatName);
977
+ const template = await context.templates(mutation.templateId);
978
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
979
+ const rendered = Mustache.render(template, templateContext);
980
+ let templateDoc;
981
+ try {
982
+ templateDoc = format.parse(rendered);
983
+ } catch (error) {
984
+ throw new Error(
985
+ `Failed to parse rendered template "${mutation.templateId}" as ${formatName.toUpperCase()}: ${error}`,
986
+ { cause: error }
987
+ );
988
+ }
989
+ const rawContent = await readFileIfExists(context.fs, targetPath);
990
+ let current;
991
+ try {
992
+ current = rawContent === null ? {} : format.parse(rawContent);
993
+ } catch {
994
+ if (rawContent !== null) {
995
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
996
+ }
997
+ current = {};
998
+ }
999
+ const merged = format.merge(current, templateDoc);
1000
+ const serialized = format.serialize(merged);
1001
+ const changed = serialized !== rawContent;
1002
+ if (changed && !context.dryRun) {
1003
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
1004
+ }
1005
+ return {
1006
+ outcome: {
1007
+ changed,
1008
+ effect: changed ? "write" : "none",
1009
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
1010
+ },
1011
+ details
1012
+ };
1013
+ }
1014
+
1015
+ // ../config-mutations/src/execution/run-mutations.ts
1016
+ async function runMutations(mutations, context, options) {
1017
+ const effects = [];
1018
+ let anyChanged = false;
1019
+ const resolverOptions = options ?? {};
1020
+ for (const mutation of mutations) {
1021
+ const { outcome } = await executeMutation(
1022
+ mutation,
1023
+ context,
1024
+ resolverOptions
1025
+ );
1026
+ effects.push(outcome);
1027
+ if (outcome.changed) {
1028
+ anyChanged = true;
1029
+ }
1030
+ }
1031
+ return {
1032
+ changed: anyChanged,
1033
+ effects
1034
+ };
1035
+ }
1036
+ async function executeMutation(mutation, context, options) {
1037
+ context.observers?.onStart?.({
1038
+ kind: mutation.kind,
1039
+ label: mutation.label ?? mutation.kind,
1040
+ targetPath: void 0
1041
+ // Will be resolved during apply
1042
+ });
1043
+ try {
1044
+ const { outcome, details } = await applyMutation(mutation, context, options);
1045
+ context.observers?.onComplete?.(details, outcome);
1046
+ return { outcome, details };
1047
+ } catch (error) {
1048
+ context.observers?.onError?.(
1049
+ {
1050
+ kind: mutation.kind,
1051
+ label: mutation.label ?? mutation.kind,
1052
+ targetPath: void 0
1053
+ },
1054
+ error
1055
+ );
1056
+ throw error;
1057
+ }
1058
+ }
1059
+
1060
+ // ../config-mutations/src/template/render.ts
1061
+ import Mustache2 from "mustache";
1062
+ var originalEscape = Mustache2.escape;
1063
+
1064
+ // ../agent-skill-config/src/apply.ts
1065
+ var UnsupportedAgentError = class extends Error {
1066
+ constructor(agentId) {
1067
+ super(`Unsupported agent: ${agentId}`);
1068
+ this.name = "UnsupportedAgentError";
1069
+ }
1070
+ };
1071
+ function toHomeRelative(localSkillDir) {
1072
+ if (localSkillDir.startsWith("~/") || localSkillDir === "~") {
1073
+ return localSkillDir;
1074
+ }
1075
+ const normalized = localSkillDir.startsWith("./") ? localSkillDir.slice(2) : localSkillDir;
1076
+ return `~/${normalized}`;
1077
+ }
1078
+ var SKILL_TEMPLATE_ID = "__skill_content__";
1079
+ async function installSkill(agentId, skill, options) {
1080
+ const support = resolveAgentSupport(agentId);
1081
+ if (support.status !== "supported") {
1082
+ throw new UnsupportedAgentError(agentId);
1083
+ }
1084
+ const scope = options.scope ?? "local";
1085
+ const config = support.config;
1086
+ const skillDir = scope === "global" ? config.globalSkillDir : toHomeRelative(config.localSkillDir);
1087
+ const skillFolderPath = `${skillDir}/${skill.name}`;
1088
+ const skillFilePath = `${skillFolderPath}/SKILL.md`;
1089
+ const displayPath = `${scope === "global" ? config.globalSkillDir : config.localSkillDir}/${skill.name}/SKILL.md`;
1090
+ await runMutations(
1091
+ [
1092
+ fileMutation.ensureDirectory({
1093
+ path: skillFolderPath,
1094
+ label: `Ensure skill directory ${skill.name}`
1095
+ }),
1096
+ templateMutation.write({
1097
+ target: skillFilePath,
1098
+ templateId: SKILL_TEMPLATE_ID,
1099
+ label: `Write skill ${skill.name}`
1100
+ })
1101
+ ],
1102
+ {
1103
+ fs: options.fs,
1104
+ homeDir: scope === "global" ? options.homeDir : options.cwd,
1105
+ dryRun: options.dryRun,
1106
+ observers: options.observers,
1107
+ templates: async (templateId) => {
1108
+ if (templateId === SKILL_TEMPLATE_ID) {
1109
+ return skill.content;
1110
+ }
1111
+ throw new Error(`Unknown template: ${templateId}`);
1112
+ }
1113
+ }
1114
+ );
1115
+ return { skillPath: skillFilePath, displayPath };
1116
+ }
1117
+
1118
+ // ../cmdkit/src/index.ts
1119
+ import { fileURLToPath } from "node:url";
1120
+
1121
+ // ../cmdkit-schema/src/index.ts
1122
+ function assertValidEnumValues(values) {
1123
+ if (values.length === 0) {
1124
+ throw new Error("Enum schema requires at least one value");
1125
+ }
1126
+ const uniqueValues = new Set(values);
1127
+ if (uniqueValues.size !== values.length) {
1128
+ throw new Error("Enum schema values must be unique");
1129
+ }
1130
+ }
1131
+ var S = {
1132
+ String(options = {}) {
1133
+ return {
1134
+ kind: "string",
1135
+ ...options
1136
+ };
1137
+ },
1138
+ Number(options = {}) {
1139
+ return {
1140
+ kind: "number",
1141
+ ...options
1142
+ };
1143
+ },
1144
+ Boolean(options = {}) {
1145
+ return {
1146
+ kind: "boolean",
1147
+ ...options
1148
+ };
1149
+ },
1150
+ Enum(values, options = {}) {
1151
+ assertValidEnumValues(values);
1152
+ return {
1153
+ kind: "enum",
1154
+ values,
1155
+ ...options
1156
+ };
1157
+ },
1158
+ Array(item, options = {}) {
1159
+ return {
1160
+ kind: "array",
1161
+ item,
1162
+ ...options
1163
+ };
1164
+ },
1165
+ Object(shape) {
1166
+ return {
1167
+ kind: "object",
1168
+ shape
1169
+ };
1170
+ },
1171
+ Optional(inner) {
1172
+ return {
1173
+ kind: "optional",
1174
+ inner
1175
+ };
1176
+ }
1177
+ };
1178
+
1179
+ // ../cmdkit/src/index.ts
1180
+ var commandConfigSymbol = /* @__PURE__ */ Symbol("cmdkit.command.config");
1181
+ var commandSourcePathSymbol = /* @__PURE__ */ Symbol("cmdkit.command.sourcePath");
1182
+ var UserError = class extends Error {
1183
+ constructor(message) {
1184
+ super(message);
1185
+ this.name = "UserError";
1186
+ }
1187
+ };
1188
+ function cloneScope(scope) {
1189
+ return scope === void 0 ? void 0 : [...scope];
1190
+ }
1191
+ function cloneSecretDefinition(secret) {
1192
+ return {
1193
+ env: secret.env,
1194
+ description: secret.description,
1195
+ optional: secret.optional
1196
+ };
1197
+ }
1198
+ function cloneSecrets(secrets) {
1199
+ if (secrets === void 0) {
1200
+ return {};
1201
+ }
1202
+ return Object.fromEntries(
1203
+ Object.entries(secrets).map(([key, secret]) => [key, cloneSecretDefinition(secret)])
1204
+ );
1205
+ }
1206
+ function cloneRequires(requires) {
1207
+ if (requires === void 0) {
1208
+ return void 0;
1209
+ }
1210
+ return {
1211
+ auth: requires.auth,
1212
+ apiVersion: requires.apiVersion,
1213
+ check: requires.check
1214
+ };
1215
+ }
1216
+ function parseStackPath(candidate) {
1217
+ if (candidate.startsWith("file://")) {
1218
+ try {
1219
+ return fileURLToPath(candidate);
1220
+ } catch {
1221
+ return void 0;
1222
+ }
1223
+ }
1224
+ if (candidate.startsWith("/")) {
1225
+ return candidate;
1226
+ }
1227
+ return void 0;
1228
+ }
1229
+ function extractStackPath(line) {
1230
+ const trimmed = line.trim();
1231
+ const fileIndex = trimmed.indexOf("file://");
1232
+ if (fileIndex >= 0) {
1233
+ const location2 = trimmed.slice(fileIndex);
1234
+ const separatorIndex2 = location2.lastIndexOf(":");
1235
+ const previousSeparatorIndex2 = separatorIndex2 >= 0 ? location2.lastIndexOf(":", separatorIndex2 - 1) : -1;
1236
+ const candidate2 = separatorIndex2 >= 0 && previousSeparatorIndex2 >= 0 ? location2.slice(0, previousSeparatorIndex2) : location2;
1237
+ return parseStackPath(candidate2);
1238
+ }
1239
+ const slashIndex = trimmed.indexOf("/");
1240
+ if (slashIndex < 0) {
1241
+ return void 0;
1242
+ }
1243
+ const location = trimmed.slice(slashIndex);
1244
+ const separatorIndex = location.lastIndexOf(":");
1245
+ const previousSeparatorIndex = separatorIndex >= 0 ? location.lastIndexOf(":", separatorIndex - 1) : -1;
1246
+ const candidate = separatorIndex >= 0 && previousSeparatorIndex >= 0 ? location.slice(0, previousSeparatorIndex) : location;
1247
+ return parseStackPath(candidate);
1248
+ }
1249
+ function inferCommandSourcePath() {
1250
+ const stack = new Error().stack;
1251
+ if (typeof stack !== "string") {
1252
+ return void 0;
1253
+ }
1254
+ for (const line of stack.split("\n").slice(1)) {
1255
+ const candidate = extractStackPath(line);
1256
+ if (candidate === void 0) {
1257
+ continue;
1258
+ }
1259
+ if (candidate.includes("/packages/cmdkit/src/index.ts") || candidate.includes("/packages/cmdkit/dist/index.js")) {
1260
+ continue;
1261
+ }
1262
+ return candidate;
1263
+ }
1264
+ return void 0;
1265
+ }
1266
+ function composeChecks(parentCheck, childCheck) {
1267
+ if (parentCheck === void 0) {
1268
+ return childCheck;
1269
+ }
1270
+ if (childCheck === void 0) {
1271
+ return parentCheck;
1272
+ }
1273
+ return async (ctx) => {
1274
+ const parentResult = await parentCheck(ctx);
1275
+ if (!parentResult.ok) {
1276
+ return parentResult;
1277
+ }
1278
+ return childCheck(ctx);
1279
+ };
1280
+ }
1281
+ function mergeRequires(parent, child) {
1282
+ if (parent === void 0 && child === void 0) {
1283
+ return void 0;
1284
+ }
1285
+ const merged = {
1286
+ auth: child?.auth ?? parent?.auth,
1287
+ apiVersion: child?.apiVersion ?? parent?.apiVersion,
1288
+ check: composeChecks(parent?.check, child?.check)
1289
+ };
1290
+ if (merged.auth === void 0 && merged.apiVersion === void 0 && merged.check === void 0) {
1291
+ return void 0;
1292
+ }
1293
+ return merged;
1294
+ }
1295
+ function mergeSecrets(parent, child) {
1296
+ return cloneSecrets({
1297
+ ...parent,
1298
+ ...child
1299
+ });
1300
+ }
1301
+ function resolveCommandScope(ownScope, inheritedScope) {
1302
+ return cloneScope(ownScope ?? inheritedScope) ?? ["cli", "sdk"];
1303
+ }
1304
+ function createBaseCommand(config) {
1305
+ const command = {
1306
+ kind: "command",
1307
+ name: config.name,
1308
+ description: config.description,
1309
+ aliases: [...config.aliases ?? []],
1310
+ positional: [...config.positional ?? []],
1311
+ params: config.params,
1312
+ secrets: cloneSecrets(config.secrets),
1313
+ scope: resolveCommandScope(config.scope, void 0),
1314
+ confirm: config.confirm ?? false,
1315
+ requires: cloneRequires(config.requires),
1316
+ handler: config.handler,
1317
+ render: config.render
1318
+ };
1319
+ Object.defineProperty(command, commandConfigSymbol, {
1320
+ value: {
1321
+ scope: cloneScope(config.scope),
1322
+ secrets: cloneSecrets(config.secrets),
1323
+ requires: cloneRequires(config.requires),
1324
+ sourcePath: inferCommandSourcePath()
1325
+ }
1326
+ });
1327
+ return command;
1328
+ }
1329
+ function getInternalCommandConfig(command) {
1330
+ return command[commandConfigSymbol];
1331
+ }
1332
+ function materializeCommand(command, inherited) {
1333
+ const internal = getInternalCommandConfig(command);
1334
+ const materialized = {
1335
+ kind: "command",
1336
+ name: command.name,
1337
+ description: command.description,
1338
+ aliases: [...command.aliases],
1339
+ positional: [...command.positional],
1340
+ params: command.params,
1341
+ secrets: mergeSecrets(inherited.secrets, internal.secrets),
1342
+ scope: resolveCommandScope(internal.scope, inherited.scope),
1343
+ confirm: command.confirm,
1344
+ requires: mergeRequires(inherited.requires, internal.requires),
1345
+ handler: command.handler,
1346
+ render: command.render
1347
+ };
1348
+ Object.defineProperty(materialized, commandConfigSymbol, {
1349
+ value: {
1350
+ scope: cloneScope(internal.scope),
1351
+ secrets: cloneSecrets(internal.secrets),
1352
+ requires: cloneRequires(internal.requires),
1353
+ sourcePath: internal.sourcePath
1354
+ }
1355
+ });
1356
+ Object.defineProperty(materialized, commandSourcePathSymbol, {
1357
+ value: internal.sourcePath
1358
+ });
1359
+ return materialized;
1360
+ }
1361
+ function defineCommand(config) {
1362
+ return materializeCommand(createBaseCommand(config), {
1363
+ scope: void 0,
1364
+ secrets: {},
1365
+ requires: void 0
1366
+ });
1367
+ }
1368
+
1369
+ // src/commands/installer.ts
1370
+ import os2 from "node:os";
1371
+ import path3 from "node:path";
8
1372
  import * as nodeFs from "node:fs/promises";
9
1373
  import { readFile } from "node:fs/promises";
10
- import { UserError } from "@poe-code/cmdkit";
11
- import {
12
- getAgentConfig,
13
- resolveAgentSupport as resolveSkillAgentSupport,
14
- supportedAgents as skillSupportedAgents
15
- } from "@poe-code/agent-skill-config";
16
1374
  var DEFAULT_INSTALL_AGENT = "claude-code";
17
1375
  var DEFAULT_INSTALL_SCOPE = "local";
18
1376
  var TERMINAL_PILOT_SKILL_NAME = "terminal-pilot";
19
- var installableAgents = skillSupportedAgents;
1377
+ var installableAgents = supportedAgents;
20
1378
  function isNotFoundError(error) {
21
1379
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
22
1380
  }
@@ -24,7 +1382,7 @@ function resolveInstallerServices(installer) {
24
1382
  return {
25
1383
  fs: installer?.fs ?? nodeFs,
26
1384
  cwd: installer?.cwd ?? process.cwd(),
27
- homeDir: installer?.homeDir ?? os.homedir(),
1385
+ homeDir: installer?.homeDir ?? os2.homedir(),
28
1386
  platform: installer?.platform ?? process.platform
29
1387
  };
30
1388
  }
@@ -32,7 +1390,7 @@ function throwUnsupportedAgent(agent) {
32
1390
  throw new UserError(`Unsupported agent: ${agent}`);
33
1391
  }
34
1392
  function resolveInstallableAgent(agent) {
35
- const skillSupport = resolveSkillAgentSupport(agent);
1393
+ const skillSupport = resolveAgentSupport(agent);
36
1394
  if (skillSupport.status !== "supported" || !skillSupport.id) {
37
1395
  throwUnsupportedAgent(agent);
38
1396
  }