skilld 1.7.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/_chunks/add.mjs +66 -0
  2. package/dist/_chunks/add.mjs.map +1 -0
  3. package/dist/_chunks/agent-prompt.mjs +88 -0
  4. package/dist/_chunks/agent-prompt.mjs.map +1 -0
  5. package/dist/_chunks/agent.mjs +737 -619
  6. package/dist/_chunks/agent.mjs.map +1 -1
  7. package/dist/_chunks/args.mjs +42 -0
  8. package/dist/_chunks/args.mjs.map +1 -0
  9. package/dist/_chunks/assemble.mjs +11 -8
  10. package/dist/_chunks/assemble.mjs.map +1 -1
  11. package/dist/_chunks/author.mjs +77 -131
  12. package/dist/_chunks/author.mjs.map +1 -1
  13. package/dist/_chunks/cache.mjs +320 -54
  14. package/dist/_chunks/cache.mjs.map +1 -1
  15. package/dist/_chunks/cache2.mjs +7 -6
  16. package/dist/_chunks/cache2.mjs.map +1 -1
  17. package/dist/_chunks/client.mjs +117 -0
  18. package/dist/_chunks/client.mjs.map +1 -0
  19. package/dist/_chunks/core.mjs +7 -4
  20. package/dist/_chunks/detect.mjs +54 -44
  21. package/dist/_chunks/detect.mjs.map +1 -1
  22. package/dist/_chunks/eject.mjs +69 -0
  23. package/dist/_chunks/eject.mjs.map +1 -0
  24. package/dist/_chunks/embedding-cache2.mjs +2 -2
  25. package/dist/_chunks/env.mjs +19 -0
  26. package/dist/_chunks/env.mjs.map +1 -0
  27. package/dist/_chunks/install-many.mjs +376 -0
  28. package/dist/_chunks/install-many.mjs.map +1 -0
  29. package/dist/_chunks/install.mjs +86 -371
  30. package/dist/_chunks/install.mjs.map +1 -1
  31. package/dist/_chunks/intro.mjs +63 -0
  32. package/dist/_chunks/intro.mjs.map +1 -0
  33. package/dist/_chunks/list.mjs +2 -2
  34. package/dist/_chunks/list.mjs.map +1 -1
  35. package/dist/_chunks/lockfile.mjs +31 -7
  36. package/dist/_chunks/lockfile.mjs.map +1 -1
  37. package/dist/_chunks/login.mjs +233 -0
  38. package/dist/_chunks/login.mjs.map +1 -0
  39. package/dist/_chunks/logout.mjs +27 -0
  40. package/dist/_chunks/logout.mjs.map +1 -0
  41. package/dist/_chunks/map.mjs +11 -0
  42. package/dist/_chunks/map.mjs.map +1 -0
  43. package/dist/_chunks/markdown.mjs +79 -54
  44. package/dist/_chunks/markdown.mjs.map +1 -1
  45. package/dist/_chunks/menu.mjs +33 -0
  46. package/dist/_chunks/menu.mjs.map +1 -0
  47. package/dist/_chunks/model-picker.mjs +61 -0
  48. package/dist/_chunks/model-picker.mjs.map +1 -0
  49. package/dist/_chunks/monorepo.mjs +73 -0
  50. package/dist/_chunks/monorepo.mjs.map +1 -0
  51. package/dist/_chunks/package-json.mjs.map +1 -1
  52. package/dist/_chunks/paths.mjs +47 -0
  53. package/dist/_chunks/paths.mjs.map +1 -0
  54. package/dist/_chunks/pipeline.mjs +985 -0
  55. package/dist/_chunks/pipeline.mjs.map +1 -0
  56. package/dist/_chunks/pool2.mjs +2 -2
  57. package/dist/_chunks/portable.mjs +151 -0
  58. package/dist/_chunks/portable.mjs.map +1 -0
  59. package/dist/_chunks/prepare-hook.mjs +2 -0
  60. package/dist/_chunks/prepare-hook2.mjs +61 -0
  61. package/dist/_chunks/prepare-hook2.mjs.map +1 -0
  62. package/dist/_chunks/prepare.mjs +47 -3
  63. package/dist/_chunks/prepare.mjs.map +1 -1
  64. package/dist/_chunks/prepare2.mjs +9 -8
  65. package/dist/_chunks/prepare2.mjs.map +1 -1
  66. package/dist/_chunks/prompts.mjs +784 -26
  67. package/dist/_chunks/prompts.mjs.map +1 -1
  68. package/dist/_chunks/pull.mjs +219 -0
  69. package/dist/_chunks/pull.mjs.map +1 -0
  70. package/dist/_chunks/regex.mjs +19 -0
  71. package/dist/_chunks/regex.mjs.map +1 -0
  72. package/dist/_chunks/retriv.mjs +2 -171
  73. package/dist/_chunks/retriv2.mjs +159 -0
  74. package/dist/_chunks/retriv2.mjs.map +1 -0
  75. package/dist/_chunks/sanitize.mjs +12 -9
  76. package/dist/_chunks/sanitize.mjs.map +1 -1
  77. package/dist/_chunks/search-helpers.mjs +9 -8
  78. package/dist/_chunks/search-helpers.mjs.map +1 -1
  79. package/dist/_chunks/search-interactive.mjs +23 -20
  80. package/dist/_chunks/search-interactive.mjs.map +1 -1
  81. package/dist/_chunks/search.mjs +3 -4
  82. package/dist/_chunks/search.mjs.map +1 -1
  83. package/dist/_chunks/{sources.mjs → semver.mjs} +1128 -838
  84. package/dist/_chunks/semver.mjs.map +1 -0
  85. package/dist/_chunks/skill-installer.mjs +2 -0
  86. package/dist/_chunks/skill-installer2.mjs +154 -0
  87. package/dist/_chunks/skill-installer2.mjs.map +1 -0
  88. package/dist/_chunks/skills.mjs +12 -12
  89. package/dist/_chunks/skills.mjs.map +1 -1
  90. package/dist/_chunks/store.mjs +107 -0
  91. package/dist/_chunks/store.mjs.map +1 -0
  92. package/dist/_chunks/sync.mjs +761 -1349
  93. package/dist/_chunks/sync.mjs.map +1 -1
  94. package/dist/_chunks/sync2.mjs +2 -3
  95. package/dist/_chunks/telemetry.mjs +26 -0
  96. package/dist/_chunks/telemetry.mjs.map +1 -0
  97. package/dist/_chunks/uninstall.mjs +15 -13
  98. package/dist/_chunks/uninstall.mjs.map +1 -1
  99. package/dist/_chunks/update.mjs +171 -0
  100. package/dist/_chunks/update.mjs.map +1 -0
  101. package/dist/_chunks/upload.mjs +4 -4
  102. package/dist/_chunks/validate.mjs +1 -1
  103. package/dist/_chunks/version.mjs +16 -27
  104. package/dist/_chunks/version.mjs.map +1 -1
  105. package/dist/_chunks/whoami.mjs +21 -0
  106. package/dist/_chunks/whoami.mjs.map +1 -0
  107. package/dist/_chunks/wizard.mjs +2 -190
  108. package/dist/_chunks/wizard2.mjs +200 -0
  109. package/dist/_chunks/wizard2.mjs.map +1 -0
  110. package/dist/cli.mjs +77 -59
  111. package/dist/cli.mjs.map +1 -1
  112. package/dist/prepare.mjs +5 -4
  113. package/dist/prepare.mjs.map +1 -1
  114. package/dist/retriv/worker.d.mts +5 -1
  115. package/dist/retriv/worker.d.mts.map +1 -1
  116. package/dist/retriv/worker.mjs +1 -1
  117. package/package.json +20 -29
  118. package/dist/_chunks/author-group.mjs +0 -17
  119. package/dist/_chunks/author-group.mjs.map +0 -1
  120. package/dist/_chunks/cli-helpers.mjs +0 -335
  121. package/dist/_chunks/cli-helpers.mjs.map +0 -1
  122. package/dist/_chunks/cli-helpers2.mjs +0 -2
  123. package/dist/_chunks/config.mjs +0 -122
  124. package/dist/_chunks/config.mjs.map +0 -1
  125. package/dist/_chunks/index.d.mts +0 -151
  126. package/dist/_chunks/index.d.mts.map +0 -1
  127. package/dist/_chunks/index2.d.mts +0 -44
  128. package/dist/_chunks/index2.d.mts.map +0 -1
  129. package/dist/_chunks/index3.d.mts +0 -589
  130. package/dist/_chunks/index3.d.mts.map +0 -1
  131. package/dist/_chunks/prefix.mjs +0 -108
  132. package/dist/_chunks/prefix.mjs.map +0 -1
  133. package/dist/_chunks/retriv.mjs.map +0 -1
  134. package/dist/_chunks/setup.mjs +0 -17
  135. package/dist/_chunks/setup.mjs.map +0 -1
  136. package/dist/_chunks/shared.mjs +0 -503
  137. package/dist/_chunks/shared.mjs.map +0 -1
  138. package/dist/_chunks/skill.mjs +0 -329
  139. package/dist/_chunks/skill.mjs.map +0 -1
  140. package/dist/_chunks/sources.mjs.map +0 -1
  141. package/dist/_chunks/sync-registry.mjs +0 -59
  142. package/dist/_chunks/sync-registry.mjs.map +0 -1
  143. package/dist/_chunks/sync-shared.mjs +0 -2
  144. package/dist/_chunks/sync-shared2.mjs +0 -1020
  145. package/dist/_chunks/sync-shared2.mjs.map +0 -1
  146. package/dist/_chunks/types.d.mts +0 -88
  147. package/dist/_chunks/types.d.mts.map +0 -1
  148. package/dist/_chunks/wizard.mjs.map +0 -1
  149. package/dist/agent/index.d.mts +0 -346
  150. package/dist/agent/index.d.mts.map +0 -1
  151. package/dist/agent/index.mjs +0 -5
  152. package/dist/cache/index.d.mts +0 -2
  153. package/dist/cache/index.mjs +0 -4
  154. package/dist/index.d.mts +0 -5
  155. package/dist/index.mjs +0 -5
  156. package/dist/retriv/index.d.mts +0 -3
  157. package/dist/retriv/index.mjs +0 -2
  158. package/dist/sources/index.d.mts +0 -2
  159. package/dist/sources/index.mjs +0 -3
  160. package/dist/types.d.mts +0 -4
  161. package/dist/types.mjs +0 -1
@@ -1,62 +1,130 @@
1
- import { n as sanitizeMarkdown } from "./sanitize.mjs";
2
- import { _ as writeSections, g as readCachedSection } from "./cache.mjs";
3
- import { i as resolveSkilldCommand } from "./shared.mjs";
4
1
  import { a as targets, t as detectInstalledAgents } from "./detect.mjs";
5
- import { c as wrapSection, n as SECTION_OUTPUT_FILES, o as getSectionValidator, r as buildAllSectionPrompts, t as SECTION_MERGE_ORDER } from "./prompts.mjs";
6
- import "./skill.mjs";
7
- import { homedir } from "node:os";
8
- import { dirname, join } from "pathe";
9
- import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { A as resolveSkilldCommand, C as SECTION_OUTPUT_FILES, S as SECTION_MERGE_ORDER, _ as buildAllSectionPrompts, x as wrapSection, y as getSectionValidator } from "./prompts.mjs";
3
+ import { g as skillLogDir, i as LLM_CACHE_DIR, m as skillInternalDir, n as CACHE_DIR, o as PI_AI_AUTH_PATH } from "./paths.mjs";
4
+ import { d as SECTION_HEADING_RE, m as TRAILING_SLASH_RE, p as SOURCE_LINK_RE, t as API_CHANGE_BULLET_RE } from "./regex.mjs";
5
+ import { n as sanitizeMarkdown } from "./sanitize.mjs";
6
+ import { n as getSkillReferenceDirs, t as createReferenceCache } from "./cache.mjs";
7
+ import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
8
+ import { promisify, styleText } from "node:util";
9
+ import { join } from "pathe";
10
10
  import { exec, execFileSync, spawn } from "node:child_process";
11
11
  import { isWindows } from "std-env";
12
- import { glob } from "tinyglobby";
13
- import { findDynamicImports, findStaticImports } from "mlly";
14
- import { createHash } from "node:crypto";
15
- import { setTimeout as setTimeout$1 } from "node:timers/promises";
16
- import { promisify } from "node:util";
12
+ import { homedir } from "node:os";
13
+ import { getEnvApiKey, getEnvApiKey as getEnvApiKey$1, getModel, getModels, getProviders, streamSimple } from "@earendil-works/pi-ai";
14
+ import { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from "@earendil-works/pi-ai/oauth";
17
15
  import { resolve as resolve$1 } from "node:path";
18
- import { getEnvApiKey, getModel, getModels, getProviders, streamSimple } from "@mariozechner/pi-ai";
19
- import { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from "@mariozechner/pi-ai/oauth";
20
16
  import { Type } from "typebox";
21
- import { readFile } from "node:fs/promises";
17
+ import { glob, readFile } from "node:fs/promises";
22
18
  import { parseSync } from "oxc-parser";
23
- var __defProp = Object.defineProperty;
24
- var __exportAll = (all, no_symbols) => {
25
- let target = {};
26
- for (var name in all) __defProp(target, name, {
27
- get: all[name],
28
- enumerable: true
29
- });
30
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
31
- return target;
32
- };
33
- var claude_exports = /* @__PURE__ */ __exportAll({
34
- agentId: () => agentId$2,
35
- buildArgs: () => buildArgs$2,
36
- cli: () => cli$2,
37
- models: () => models$2,
38
- parseLine: () => parseLine$2
39
- });
40
- const cli$2 = "claude";
41
- const agentId$2 = "claude-code";
42
- const models$2 = {
43
- opus: {
44
- model: "opus",
45
- name: "Opus 4.6",
46
- hint: "Most capable for complex work"
19
+ import { setTimeout as setTimeout$1 } from "node:timers/promises";
20
+ import { createHash } from "node:crypto";
21
+ const STATIC_REGEX_1$6 = /-\d{8}$/;
22
+ const STATIC_REGEX_2$5 = /-\d{4}-\d{2}-\d{2}$/;
23
+ const STATIC_REGEX_3$3 = /-preview/;
24
+ function isStableId(id) {
25
+ if (STATIC_REGEX_1$6.test(id)) return false;
26
+ if (STATIC_REGEX_2$5.test(id)) return false;
27
+ if (STATIC_REGEX_3$3.test(id)) return false;
28
+ return true;
29
+ }
30
+ function compareVersions(a, b) {
31
+ const versionOf = (id) => {
32
+ return (id.match(/\d+(?:\.\d+)?/g) ?? []).flatMap((n) => n.split(".").map(Number));
33
+ };
34
+ const av = versionOf(a);
35
+ const bv = versionOf(b);
36
+ for (let i = 0; i < Math.max(av.length, bv.length); i++) {
37
+ const x = av[i] ?? 0;
38
+ const y = bv[i] ?? 0;
39
+ if (x !== y) return y - x;
40
+ }
41
+ return 0;
42
+ }
43
+ function resolve$2(opts) {
44
+ const { provider, prefix, contains, exclude } = opts;
45
+ return getModels(provider).filter((m) => isStableId(m.id)).filter((m) => m.id.startsWith(prefix)).filter((m) => !contains || m.id.includes(contains)).filter((m) => !exclude?.some((e) => m.id.includes(e))).sort((a, b) => compareVersions(a.id, b.id))[0];
46
+ }
47
+ function buildModelEntry(opts) {
48
+ const found = resolve$2(opts);
49
+ const piName = found?.name;
50
+ const transformed = piName && opts.nameTransform ? opts.nameTransform(piName) : piName;
51
+ const name = opts.name ?? transformed ?? opts.model ?? opts.prefix;
52
+ return {
53
+ model: opts.model ?? found?.id ?? opts.prefix,
54
+ name,
55
+ hint: opts.hint,
56
+ recommended: opts.recommended
57
+ };
58
+ }
59
+ function buildModels(entries) {
60
+ return Object.fromEntries(entries.map((e) => {
61
+ const entry = buildModelEntry(e);
62
+ return [entry.model, entry];
63
+ }));
64
+ }
65
+ const TOOL_NAMES = {
66
+ Read: {
67
+ canonical: "read",
68
+ verb: "Reading"
47
69
  },
48
- sonnet: {
49
- model: "sonnet",
50
- name: "Sonnet 4.6",
51
- hint: "Best for everyday tasks"
70
+ Glob: {
71
+ canonical: "search",
72
+ verb: "Searching"
52
73
  },
53
- haiku: {
54
- model: "haiku",
55
- name: "Haiku 4.5",
56
- hint: "Fastest for quick answers",
57
- recommended: true
74
+ Grep: {
75
+ canonical: "search",
76
+ verb: "Searching"
77
+ },
78
+ Write: {
79
+ canonical: "write",
80
+ verb: "Writing"
81
+ },
82
+ Bash: {
83
+ canonical: "shell",
84
+ verb: "Running"
85
+ },
86
+ read_file: {
87
+ canonical: "read",
88
+ verb: "Reading"
89
+ },
90
+ glob_tool: {
91
+ canonical: "search",
92
+ verb: "Searching"
93
+ },
94
+ write_file: {
95
+ canonical: "write",
96
+ verb: "Writing"
97
+ },
98
+ list_directory: {
99
+ canonical: "list",
100
+ verb: "Listing"
101
+ },
102
+ search_file_content: {
103
+ canonical: "search",
104
+ verb: "Searching"
105
+ },
106
+ run_shell_command: {
107
+ canonical: "shell",
108
+ verb: "Running"
58
109
  }
59
110
  };
111
+ function extractToolHint(input) {
112
+ if (!input) return void 0;
113
+ for (const k of [
114
+ "file_path",
115
+ "path",
116
+ "dir_path",
117
+ "pattern",
118
+ "query",
119
+ "command"
120
+ ]) {
121
+ const v = input[k];
122
+ if (typeof v === "string" && v) return v;
123
+ }
124
+ }
125
+ const STATIC_REGEX_1$5 = /^Claude\s+/;
126
+ const STATIC_REGEX_2$4 = /\s*\(latest\)\s*$/i;
127
+ const stripClaude = (n) => n.replace(STATIC_REGEX_1$5, "").replace(STATIC_REGEX_2$4, "");
60
128
  function buildArgs$2(model, skillDir, symlinkDirs) {
61
129
  return [
62
130
  "-p",
@@ -82,38 +150,36 @@ function buildArgs$2(model, skillDir, symlinkDirs) {
82
150
  "--no-session-persistence"
83
151
  ];
84
152
  }
85
- function parseLine$2(line) {
153
+ function parseEvent$2(line) {
86
154
  try {
87
155
  const obj = JSON.parse(line);
88
156
  if (obj.type === "stream_event") {
89
157
  const evt = obj.event;
90
- if (!evt) return {};
91
- if (evt.type === "content_block_delta" && evt.delta?.type === "text_delta") return { textDelta: evt.delta.text };
92
- return {};
158
+ if (evt?.type === "content_block_delta" && evt.delta?.type === "text_delta") return {
159
+ kind: "text",
160
+ delta: evt.delta.text
161
+ };
162
+ return { kind: "noop" };
93
163
  }
94
164
  if (obj.type === "assistant" && obj.message?.content) {
95
165
  const content = obj.message.content;
96
166
  const tools = content.filter((c) => c.type === "tool_use");
97
- if (tools.length) {
98
- const names = tools.map((t) => t.name);
99
- const hint = tools.map((t) => {
100
- const input = t.input || {};
101
- return input.file_path || input.path || input.pattern || input.query || input.command || "";
102
- }).filter(Boolean).join(", ");
103
- const writeTool = tools.find((t) => t.name === "Write" && t.input?.content);
104
- return {
105
- toolName: names.join(", "),
106
- toolHint: hint || void 0,
107
- writeContent: writeTool?.input?.content
108
- };
109
- }
167
+ if (tools.length) return {
168
+ kind: "tool-call",
169
+ tool: tools.map((t) => t.name).join(", "),
170
+ hint: tools.map((t) => extractToolHint(t.input) ?? "").filter(Boolean).join(", ") || void 0,
171
+ writeContent: tools.find((t) => t.name === "Write" && t.input?.content)?.input?.content
172
+ };
110
173
  const text = content.filter((c) => c.type === "text").map((c) => c.text).join("");
111
- if (text) return { fullText: text };
174
+ if (text) return {
175
+ kind: "text",
176
+ full: text
177
+ };
112
178
  }
113
179
  if (obj.type === "result") {
114
180
  const u = obj.usage;
115
181
  return {
116
- done: true,
182
+ kind: "done",
117
183
  usage: u ? {
118
184
  input: u.input_tokens ?? u.inputTokens ?? 0,
119
185
  output: u.output_tokens ?? u.outputTokens ?? 0
@@ -123,35 +189,40 @@ function parseLine$2(line) {
123
189
  };
124
190
  }
125
191
  } catch {}
126
- return {};
192
+ return { kind: "noop" };
127
193
  }
128
- var codex_exports = /* @__PURE__ */ __exportAll({
129
- agentId: () => agentId$1,
130
- buildArgs: () => buildArgs$1,
131
- cli: () => cli$1,
132
- models: () => models$1,
133
- parseLine: () => parseLine$1
134
- });
135
- const cli$1 = "codex";
136
- const agentId$1 = "codex";
137
- const models$1 = {
138
- "gpt-5.3-codex": {
139
- model: "gpt-5.3-codex",
140
- name: "GPT-5.3 Codex",
141
- hint: "Latest frontier Codex model"
142
- },
143
- "gpt-5.3-codex-spark": {
144
- model: "gpt-5.3-codex-spark",
145
- name: "GPT-5.3 Codex Spark",
146
- hint: "Faster GPT-5.3 Codex variant",
147
- recommended: true
148
- },
149
- "gpt-5.2-codex": {
150
- model: "gpt-5.2-codex",
151
- name: "GPT-5.2 Codex",
152
- hint: "Frontier agentic coding model"
153
- }
194
+ const adapter$2 = {
195
+ cli: "claude",
196
+ agentId: "claude-code",
197
+ providerName: "Anthropic",
198
+ models: buildModels([
199
+ {
200
+ model: "opus",
201
+ provider: "anthropic",
202
+ prefix: "claude-opus-",
203
+ nameTransform: stripClaude,
204
+ hint: "Most capable for complex work"
205
+ },
206
+ {
207
+ model: "sonnet",
208
+ provider: "anthropic",
209
+ prefix: "claude-sonnet-",
210
+ nameTransform: stripClaude,
211
+ hint: "Best for everyday tasks"
212
+ },
213
+ {
214
+ model: "haiku",
215
+ provider: "anthropic",
216
+ prefix: "claude-haiku-",
217
+ nameTransform: stripClaude,
218
+ hint: "Fastest for quick answers",
219
+ recommended: true
220
+ }
221
+ ]),
222
+ buildArgs: buildArgs$2,
223
+ parseEvent: parseEvent$2
154
224
  };
225
+ const STATIC_REGEX_1$4 = /^cat\s*>|>/;
155
226
  function buildArgs$1(model, _skillDir, _symlinkDirs) {
156
227
  return [
157
228
  "exec",
@@ -163,62 +234,82 @@ function buildArgs$1(model, _skillDir, _symlinkDirs) {
163
234
  "-"
164
235
  ];
165
236
  }
166
- function parseLine$1(line) {
237
+ function parseEvent$1(line) {
167
238
  try {
168
239
  const obj = JSON.parse(line);
169
240
  if (obj.type === "item.completed" && obj.item) {
170
241
  const item = obj.item;
171
- if (item.type === "agent_message" && item.text) return { fullText: item.text };
242
+ if (item.type === "agent_message" && item.text) return {
243
+ kind: "text",
244
+ full: item.text
245
+ };
172
246
  if (item.type === "command_execution" && item.aggregated_output) {
173
247
  const cmd = item.command || "";
174
- const writeContent = /^cat\s*>|>/.test(cmd) ? item.aggregated_output : void 0;
248
+ const writeContent = STATIC_REGEX_1$4.test(cmd) ? item.aggregated_output : void 0;
175
249
  return {
176
- toolName: "Bash",
177
- toolHint: `(${item.aggregated_output.length} chars output)`,
250
+ kind: "tool-call",
251
+ tool: "Bash",
252
+ hint: `(${item.aggregated_output.length} chars output)`,
178
253
  writeContent
179
254
  };
180
255
  }
181
256
  if (item.type === "file_change" && item.changes?.length) return {
182
- toolName: "Write",
183
- toolHint: item.changes.map((c) => c.path).join(", ")
257
+ kind: "tool-call",
258
+ tool: "Write",
259
+ hint: item.changes.map((c) => c.path).join(", ")
184
260
  };
185
261
  }
186
262
  if (obj.type === "item.started" && obj.item?.type === "command_execution") return {
187
- toolName: "Bash",
188
- toolHint: obj.item.command
263
+ kind: "tool-call",
264
+ tool: "Bash",
265
+ hint: obj.item.command
189
266
  };
190
267
  if (obj.type === "turn.completed" && obj.usage) return {
191
- done: true,
268
+ kind: "done",
192
269
  usage: {
193
270
  input: obj.usage.input_tokens ?? 0,
194
271
  output: obj.usage.output_tokens ?? 0
195
272
  }
196
273
  };
197
- if (obj.type === "turn.failed" || obj.type === "error") return { done: true };
274
+ if (obj.type === "turn.failed" || obj.type === "error") return {
275
+ kind: "error",
276
+ message: obj.message
277
+ };
198
278
  } catch {}
199
- return {};
279
+ return { kind: "noop" };
200
280
  }
201
- var gemini_exports = /* @__PURE__ */ __exportAll({
202
- agentId: () => agentId,
203
- buildArgs: () => buildArgs,
204
- cli: () => cli,
205
- models: () => models,
206
- parseLine: () => parseLine
207
- });
208
- const cli = "gemini";
209
- const agentId = "gemini-cli";
210
- const models = {
211
- "gemini-3.1-pro": {
212
- model: "gemini-3.1-pro",
213
- name: "Gemini 3.1 Pro",
214
- hint: "Most capable"
215
- },
216
- "gemini-3-flash": {
217
- model: "gemini-3-flash",
218
- name: "Gemini 3 Flash",
219
- hint: "Balanced",
220
- recommended: true
221
- }
281
+ const adapter$1 = {
282
+ cli: "codex",
283
+ agentId: "codex",
284
+ providerName: "OpenAI",
285
+ models: buildModels([
286
+ {
287
+ provider: "openai",
288
+ prefix: "gpt-",
289
+ contains: "codex",
290
+ exclude: [
291
+ "spark",
292
+ "mini",
293
+ "max"
294
+ ],
295
+ hint: "Latest frontier Codex model"
296
+ },
297
+ {
298
+ provider: "openai",
299
+ prefix: "gpt-",
300
+ contains: "codex-spark",
301
+ hint: "Faster Codex variant",
302
+ recommended: true
303
+ },
304
+ {
305
+ provider: "openai",
306
+ prefix: "gpt-",
307
+ contains: "codex-mini",
308
+ hint: "Cheapest Codex variant"
309
+ }
310
+ ]),
311
+ buildArgs: buildArgs$1,
312
+ parseEvent: parseEvent$1
222
313
  };
223
314
  function buildArgs(model, skillDir, symlinkDirs) {
224
315
  return [
@@ -233,28 +324,30 @@ function buildArgs(model, skillDir, symlinkDirs) {
233
324
  ...symlinkDirs.flatMap((d) => ["--include-directories", d])
234
325
  ];
235
326
  }
236
- function parseLine(line) {
327
+ function parseEvent(line) {
237
328
  try {
238
329
  const obj = JSON.parse(line);
239
- if (obj.type === "message" && obj.role === "assistant" && obj.content) return obj.delta ? { textDelta: obj.content } : { fullText: obj.content };
330
+ if (obj.type === "message" && obj.role === "assistant" && obj.content) return obj.delta ? {
331
+ kind: "text",
332
+ delta: obj.content
333
+ } : {
334
+ kind: "text",
335
+ full: obj.content
336
+ };
240
337
  if (obj.type === "tool_use" || obj.type === "tool_call") {
241
- const name = obj.tool_name || obj.name || obj.tool || "tool";
338
+ const tool = obj.tool_name || obj.name || obj.tool || "tool";
242
339
  const params = obj.parameters || obj.args || obj.input || {};
243
- const hint = params.file_path || params.path || params.dir_path || params.pattern || params.query || params.command || "";
244
- if (name === "write_file" && params.content) return {
245
- toolName: name,
246
- toolHint: hint || void 0,
247
- writeContent: params.content
248
- };
249
340
  return {
250
- toolName: name,
251
- toolHint: hint || void 0
341
+ kind: "tool-call",
342
+ tool,
343
+ hint: extractToolHint(params),
344
+ writeContent: tool === "write_file" && typeof params.content === "string" ? params.content : void 0
252
345
  };
253
346
  }
254
347
  if (obj.type === "result") {
255
348
  const s = obj.stats;
256
349
  return {
257
- done: true,
350
+ kind: "done",
258
351
  usage: s ? {
259
352
  input: s.input_tokens ?? s.input ?? 0,
260
353
  output: s.output_tokens ?? s.output ?? 0
@@ -263,21 +356,37 @@ function parseLine(line) {
263
356
  };
264
357
  }
265
358
  } catch {}
266
- return {};
267
- }
268
- function isPiAiModel(model) {
269
- return model.startsWith("pi:");
270
- }
271
- function parsePiAiModelId(model) {
272
- if (!model.startsWith("pi:")) return null;
273
- const rest = model.slice(3);
274
- const slashIdx = rest.indexOf("/");
275
- if (slashIdx === -1) return null;
276
- return {
277
- provider: rest.slice(0, slashIdx),
278
- modelId: rest.slice(slashIdx + 1)
279
- };
359
+ return { kind: "noop" };
280
360
  }
361
+ const adapter = {
362
+ cli: "gemini",
363
+ agentId: "gemini-cli",
364
+ providerName: "Google",
365
+ models: buildModels([{
366
+ provider: "google",
367
+ prefix: "gemini-",
368
+ contains: "pro",
369
+ exclude: [
370
+ "flash",
371
+ "live",
372
+ "gemma"
373
+ ],
374
+ hint: "Most capable"
375
+ }, {
376
+ provider: "google",
377
+ prefix: "gemini-",
378
+ contains: "flash",
379
+ exclude: [
380
+ "lite",
381
+ "live",
382
+ "preview"
383
+ ],
384
+ hint: "Balanced",
385
+ recommended: true
386
+ }]),
387
+ buildArgs,
388
+ parseEvent
389
+ };
281
390
  const BLOCKED_OAUTH_PROVIDERS = new Set([
282
391
  "google-antigravity",
283
392
  "google-gemini-cli",
@@ -286,7 +395,11 @@ const BLOCKED_OAUTH_PROVIDERS = new Set([
286
395
  "openai-codex"
287
396
  ]);
288
397
  const PI_AGENT_AUTH_PATH = join(process.env.PI_CODING_AGENT_DIR || join(homedir(), ".pi", "agent"), "auth.json");
289
- const SKILLD_AUTH_PATH = join(homedir(), ".skilld", "pi-ai-auth.json");
398
+ const SKILLD_AUTH_PATH = PI_AI_AUTH_PATH;
399
+ const OAUTH_PROVIDER_OVERRIDES = {
400
+ google: "google-gemini-cli",
401
+ openai: "openai-codex"
402
+ };
290
403
  function readAuthFile(path) {
291
404
  if (!existsSync(path)) return {};
292
405
  try {
@@ -303,16 +416,12 @@ function loadAuth() {
303
416
  };
304
417
  }
305
418
  function saveAuth(auth) {
306
- mkdirSync(join(homedir(), ".skilld"), {
419
+ mkdirSync(CACHE_DIR, {
307
420
  recursive: true,
308
421
  mode: 448
309
422
  });
310
423
  writeFileSync(SKILLD_AUTH_PATH, JSON.stringify(auth, null, 2), { mode: 384 });
311
424
  }
312
- const OAUTH_PROVIDER_OVERRIDES = {
313
- google: "google-gemini-cli",
314
- openai: "openai-codex"
315
- };
316
425
  function resolveOAuthProviderId(modelProvider) {
317
426
  const oauthId = OAUTH_PROVIDER_OVERRIDES[modelProvider] ?? modelProvider;
318
427
  if (BLOCKED_OAUTH_PROVIDERS.has(oauthId)) return null;
@@ -321,7 +430,7 @@ function resolveOAuthProviderId(modelProvider) {
321
430
  return null;
322
431
  }
323
432
  async function resolveApiKey(provider) {
324
- const envKey = getEnvApiKey(provider);
433
+ const envKey = getEnvApiKey$1(provider);
325
434
  if (envKey) return envKey;
326
435
  const oauthProviderId = resolveOAuthProviderId(provider);
327
436
  if (!oauthProviderId) return null;
@@ -366,6 +475,19 @@ function logoutOAuthProvider(providerId) {
366
475
  delete auth[providerId];
367
476
  saveAuth(auth);
368
477
  }
478
+ function isPiAiModel(model) {
479
+ return model.startsWith("pi:");
480
+ }
481
+ function parsePiAiModelId(model) {
482
+ if (!model.startsWith("pi:")) return null;
483
+ const rest = model.slice(3);
484
+ const slashIdx = rest.indexOf("/");
485
+ if (slashIdx === -1) return null;
486
+ return {
487
+ provider: rest.slice(0, slashIdx),
488
+ modelId: rest.slice(slashIdx + 1)
489
+ };
490
+ }
369
491
  const MIN_CONTEXT_WINDOW = 32e3;
370
492
  const LEGACY_MODEL_PATTERNS = [
371
493
  /^claude-3-/,
@@ -429,6 +551,10 @@ function getAvailablePiAiModels() {
429
551
  }
430
552
  return available;
431
553
  }
554
+ const STATIC_REGEX_1$3 = /^\.\/\.skilld\//;
555
+ const STATIC_REGEX_2$3 = /^\.skilld\//;
556
+ const STATIC_REGEX_3$2 = /^\.\//;
557
+ const STATIC_REGEX_5$2 = /\s+/;
432
558
  const TOOLS = [
433
559
  {
434
560
  name: "Read",
@@ -457,7 +583,6 @@ const TOOLS = [
457
583
  parameters: Type.Object({ command: Type.String({ description: "Shell command to run" }) })
458
584
  }
459
585
  ];
460
- const MAX_TOOL_TURNS = 30;
461
586
  const SAFE_COMMANDS = new Set([
462
587
  "skilld",
463
588
  "ls",
@@ -466,7 +591,7 @@ const SAFE_COMMANDS = new Set([
466
591
  ]);
467
592
  const SHELL_META_RE = /[;&|`$()<>]/;
468
593
  function resolveSandboxedPath(p, skilldDir) {
469
- const resolved = resolve$1(skilldDir, String(p).replace(/^\.\/\.skilld\//, "./").replace(/^\.skilld\//, "./").replace(/^\.\//, ""));
594
+ const resolved = resolve$1(skilldDir, String(p).replace(STATIC_REGEX_1$3, "./").replace(STATIC_REGEX_2$3, "./").replace(STATIC_REGEX_3$2, ""));
470
595
  if (!resolved.startsWith(`${skilldDir}/`) && resolved !== skilldDir) throw new Error(`Path traversal blocked: ${p}`);
471
596
  return resolved;
472
597
  }
@@ -522,7 +647,7 @@ function executeTool(toolCall, skilldDir) {
522
647
  return sanitizeMarkdown(readFileSync(filePath, "utf-8"));
523
648
  }
524
649
  case "Glob": {
525
- const pattern = String(args.pattern).replace(/^\.\/\.skilld\//, "./").replace(/^\.skilld\//, "./").replace(/^\.\//, "");
650
+ const pattern = String(args.pattern).replace(STATIC_REGEX_1$3, "./").replace(STATIC_REGEX_2$3, "./").replace(STATIC_REGEX_3$2, "");
526
651
  const results = [];
527
652
  const walkDir = (dir, prefix) => {
528
653
  if (!existsSync(dir)) return;
@@ -532,9 +657,9 @@ function executeTool(toolCall, skilldDir) {
532
657
  else results.push(`./.skilld/${relPath}`);
533
658
  }
534
659
  };
535
- const baseDir = pattern.split("*")[0]?.replace(/\/$/, "") ?? "";
660
+ const baseDir = pattern.split("*")[0]?.replace(TRAILING_SLASH_RE, "") ?? "";
536
661
  walkDir(join(skilldDir, baseDir), baseDir);
537
- const matched = results.filter((r) => globMatch(r.replace(/^\.\/\.skilld\//, ""), pattern));
662
+ const matched = results.filter((r) => globMatch(r.replace(STATIC_REGEX_1$3, ""), pattern));
538
663
  return matched.length > 0 ? matched.join("\n") : `No files matching: ${args.pattern}`;
539
664
  }
540
665
  case "Write":
@@ -542,7 +667,7 @@ function executeTool(toolCall, skilldDir) {
542
667
  return "File written successfully.";
543
668
  case "Bash": {
544
669
  const cmd = String(args.command).trim();
545
- const parts = cmd.split(/\s+/);
670
+ const parts = cmd.split(STATIC_REGEX_5$2);
546
671
  const bin = parts[0] ?? "";
547
672
  if (!SAFE_COMMANDS.has(bin) || SHELL_META_RE.test(cmd)) return `Error: command not allowed. Only skilld, ls, cat, find commands are permitted.`;
548
673
  try {
@@ -559,12 +684,13 @@ function executeTool(toolCall, skilldDir) {
559
684
  default: return `Unknown tool: ${toolCall.name}`;
560
685
  }
561
686
  }
687
+ const SYSTEM_PROMPT = "You are a technical documentation expert generating SKILL.md sections for AI agent skills. Follow the format instructions exactly. Use the provided tools to explore reference files in ./.skilld/ before writing your output.";
562
688
  async function optimizeSectionPiAi(opts) {
563
689
  const parsed = parsePiAiModelId(opts.model);
564
690
  if (!parsed) throw new Error(`Invalid pi-ai model ID: ${opts.model}. Expected format: pi:provider/model-id`);
565
691
  const model = getModel(parsed.provider, parsed.modelId);
566
692
  const apiKey = await resolveApiKey(parsed.provider);
567
- const skilldDir = join(opts.skillDir, ".skilld");
693
+ const skilldDir = skillInternalDir(opts.skillDir);
568
694
  const fullPrompt = opts.prompt;
569
695
  opts.onProgress?.({
570
696
  chunk: "[starting...]",
@@ -586,10 +712,10 @@ async function optimizeSectionPiAi(opts) {
586
712
  let totalUsage;
587
713
  let totalCost;
588
714
  let lastWriteContent = "";
589
- for (let turn = 0; turn < MAX_TOOL_TURNS; turn++) {
715
+ for (let turn = 0; turn < 30; turn++) {
590
716
  if (opts.signal?.aborted) throw new Error("pi-ai request timed out");
591
717
  const eventStream = streamSimple(model, {
592
- systemPrompt: "You are a technical documentation expert generating SKILL.md sections for AI agent skills. Follow the format instructions exactly. Use the provided tools to explore reference files in ./.skilld/ before writing your output.",
718
+ systemPrompt: SYSTEM_PROMPT,
593
719
  messages,
594
720
  tools: TOOLS
595
721
  }, {
@@ -664,7 +790,7 @@ async function optimizeSectionPiAi(opts) {
664
790
  });
665
791
  }
666
792
  }
667
- if (!completed) throw new Error(`pi-ai exceeded ${MAX_TOOL_TURNS} tool turns without completing`);
793
+ if (!completed) throw new Error(`pi-ai exceeded 30 tool turns without completing`);
668
794
  return {
669
795
  text: text || lastWriteContent,
670
796
  fullPrompt,
@@ -672,19 +798,51 @@ async function optimizeSectionPiAi(opts) {
672
798
  cost: totalCost
673
799
  };
674
800
  }
675
- const TOOL_VERBS = {
676
- Read: "Reading",
677
- Glob: "Searching",
678
- Grep: "Searching",
679
- Write: "Writing",
680
- Bash: "Running",
681
- read_file: "Reading",
682
- glob_tool: "Searching",
683
- write_file: "Writing",
684
- list_directory: "Listing",
685
- search_file_content: "Searching",
686
- run_shell_command: "Running"
687
- };
801
+ const STATIC_REGEX_1$2 = /^```(?:markdown|md)?[^\S\n]*\n([\s\S]+)\n```[^\S\n]*$/;
802
+ const STATIC_REGEX_2$2 = /^```(?:markdown|md)/;
803
+ const STATIC_REGEX_5$1 = /^-{3,}\n/;
804
+ const STATIC_REGEX_6 = /\n-{3,}/;
805
+ const STATIC_REGEX_7 = /^(##\s|- (?:BREAKING|DEPRECATED|NEW): )/m;
806
+ const STATIC_REGEX_8 = /^(## .+)\n/;
807
+ const STATIC_REGEX_9 = /^\.\/(?:\.skilld\/|references\/)/;
808
+ function cleanSectionOutput(content) {
809
+ let cleaned = content.trim();
810
+ const wrapMatch = cleaned.match(STATIC_REGEX_1$2);
811
+ if (wrapMatch) {
812
+ const inner = wrapMatch[1].trim();
813
+ if (STATIC_REGEX_2$2.test(cleaned) || SECTION_HEADING_RE.test(inner) || API_CHANGE_BULLET_RE.test(inner)) cleaned = inner;
814
+ }
815
+ cleaned = cleaned.replace(/^# (?!#)/gm, "## ");
816
+ const fmMatch = cleaned.match(STATIC_REGEX_5$1);
817
+ if (fmMatch) {
818
+ const afterOpen = fmMatch[0].length;
819
+ const closeMatch = cleaned.slice(afterOpen).match(STATIC_REGEX_6);
820
+ if (closeMatch) cleaned = cleaned.slice(afterOpen + closeMatch.index + closeMatch[0].length).trim();
821
+ else cleaned = cleaned.slice(afterOpen).trim();
822
+ }
823
+ const firstMarker = cleaned.match(STATIC_REGEX_7);
824
+ if (firstMarker?.index && firstMarker.index > 0) cleaned = cleaned.slice(firstMarker.index).trim();
825
+ const headingMatch = cleaned.match(STATIC_REGEX_8);
826
+ if (headingMatch) {
827
+ const heading = headingMatch[1];
828
+ const afterFirst = headingMatch[0].length;
829
+ const secondIdx = cleaned.indexOf(heading, afterFirst);
830
+ if (secondIdx !== -1) {
831
+ if (secondIdx - afterFirst < 200) cleaned = cleaned.slice(secondIdx).trim();
832
+ }
833
+ }
834
+ cleaned = cleaned.replace(/\(?\[`?\.\/(?:\.skilld\/|references\/)[^)\]]*\]\(([^)]+)\)\)?/g, (match, url) => {
835
+ if (STATIC_REGEX_9.test(url)) return `[source](${url})`;
836
+ return match;
837
+ });
838
+ cleaned = cleaned.replace(/\[source\]\(\.\/((docs|issues|discussions|releases|pkg|guide)\/)/g, "[source](./.skilld/$1");
839
+ cleaned = sanitizeMarkdown(cleaned);
840
+ if (!SECTION_HEADING_RE.test(cleaned) && !API_CHANGE_BULLET_RE.test(cleaned) && !SOURCE_LINK_RE.test(cleaned)) return "";
841
+ return cleaned;
842
+ }
843
+ const STATIC_REGEX_1$1 = /^\[(?:starting|retrying|cached)/;
844
+ const STATIC_REGEX_2$1 = /^\[([^:[\]]+)(?::\s(.+))?\]$/;
845
+ const STATIC_REGEX_3$1 = /skilld search\s+"([^"]+)"/;
688
846
  function createToolProgress(log) {
689
847
  let lastMsg = "";
690
848
  let repeatCount = 0;
@@ -693,7 +851,7 @@ function createToolProgress(log) {
693
851
  function emit(msg) {
694
852
  if (msg === lastMsg) {
695
853
  repeatCount++;
696
- log.message(`${msg} \x1B[90m(+${repeatCount})\x1B[0m`);
854
+ log.message(`${msg} ${styleText("gray", `(+${repeatCount})`)}`);
697
855
  } else {
698
856
  lastMsg = msg;
699
857
  repeatCount = 0;
@@ -706,57 +864,55 @@ function createToolProgress(log) {
706
864
  const now = Date.now();
707
865
  if (now - (lastTextEmit.get(key) ?? 0) < TEXT_THROTTLE_MS) return;
708
866
  lastTextEmit.set(key, now);
709
- const prefix = section ? `\x1B[90m[${section}]\x1B[0m ` : "";
867
+ const prefix = section ? `${styleText("gray", `[${section}]`)} ` : "";
710
868
  const items = text ? text.match(/^- (?:BREAKING|DEPRECATED|NEW|CHANGED|REMOVED|Use |Do |Set |Add |Avoid |Always |Never |Prefer |Check |Ensure )/gm)?.length ?? 0 : 0;
711
- emit(items > 0 ? `${prefix}Writing... \x1B[90m(${items} items)\x1B[0m` : `${prefix}Writing...`);
869
+ emit(items > 0 ? `${prefix}Writing... ${styleText("gray", `(${items} items)`)}` : `${prefix}Writing...`);
712
870
  return;
713
871
  }
714
872
  if (type !== "reasoning" || !chunk.startsWith("[")) return;
715
- if (/^\[(?:starting|retrying|cached)/.test(chunk)) {
716
- emit(`${section ? `\x1B[90m[${section}]\x1B[0m ` : ""}${chunk.slice(1, -1)}`);
873
+ if (STATIC_REGEX_1$1.test(chunk)) {
874
+ emit(`${section ? `${styleText("gray", `[${section}]`)} ` : ""}${chunk.slice(1, -1)}`);
717
875
  return;
718
876
  }
719
- const match = chunk.match(/^\[([^:[\]]+)(?::\s(.+))?\]$/);
877
+ const match = chunk.match(STATIC_REGEX_2$1);
720
878
  if (!match) return;
721
879
  const names = match[1].split(",").map((n) => n.trim());
722
880
  const hints = match[2]?.split(",").map((h) => h.trim()) ?? [];
723
881
  for (let i = 0; i < names.length; i++) {
724
882
  const rawName = names[i];
725
883
  const hint = hints[i] ?? hints[0] ?? "";
726
- const verb = TOOL_VERBS[rawName] ?? rawName;
727
- const prefix = section ? `\x1B[90m[${section}]\x1B[0m ` : "";
884
+ const verb = TOOL_NAMES[rawName]?.verb ?? rawName;
885
+ const prefix = section ? `${styleText("gray", `[${section}]`)} ` : "";
728
886
  if ((rawName === "Bash" || rawName === "run_shell_command") && hint) {
729
- const searchMatch = hint.match(/skilld search\s+"([^"]+)"/);
730
- if (searchMatch) emit(`${prefix}Searching \x1B[36m"${searchMatch[1]}"\x1B[0m`);
887
+ const searchMatch = hint.match(STATIC_REGEX_3$1);
888
+ if (searchMatch) emit(`${prefix}Searching ${styleText("cyan", `"${searchMatch[1]}"`)}`);
731
889
  else if (hint.includes("skilld validate")) emit(`${prefix}Validating...`);
732
890
  else {
733
891
  const shortened = shortenCommand(hint);
734
892
  emit(`${prefix}Running ${shortened.length > 50 ? `${shortened.slice(0, 47)}...` : shortened}`);
735
893
  }
736
- } else emit(`${prefix}${verb} \x1B[90m${shortenPath(hint || "...")}\x1B[0m`);
894
+ } else emit(`${prefix}${verb} ${styleText("gray", shortenPath(hint || "..."))}`);
737
895
  }
738
896
  };
739
897
  }
740
- const CLI_DEFS = [
741
- claude_exports,
742
- gemini_exports,
743
- codex_exports
744
- ];
745
- const CLI_BUILD_ARGS = {
746
- claude: buildArgs$2,
747
- gemini: buildArgs,
748
- codex: buildArgs$1
749
- };
750
- const CLI_PARSE_LINE = {
751
- claude: parseLine$2,
752
- gemini: parseLine,
753
- codex: parseLine$1
754
- };
755
- const CLI_PROVIDER_NAMES = {
756
- "claude-code": "Anthropic",
757
- "gemini-cli": "Google",
758
- "codex": "OpenAI"
898
+ function shortenPath(p) {
899
+ const refIdx = p.indexOf(".skilld/");
900
+ if (refIdx !== -1) return p.slice(refIdx + 8);
901
+ const parts = p.split("/");
902
+ return parts.length > 2 ? `.../${parts.slice(-2).join("/")}` : p;
903
+ }
904
+ function shortenCommand(cmd) {
905
+ return cmd.replace(/\/[^\s"']+/g, (match) => {
906
+ if (match.includes(".claude/") || match.includes(".skilld/") || match.includes("node_modules/")) return `.../${match.split("/").slice(-2).join("/")}`;
907
+ return match;
908
+ });
909
+ }
910
+ const CLI_ADAPTERS = {
911
+ claude: adapter$2,
912
+ gemini: adapter,
913
+ codex: adapter$1
759
914
  };
915
+ const CLI_PROVIDER_NAMES = Object.fromEntries(Object.values(CLI_ADAPTERS).map((a) => [a.agentId, a.providerName]));
760
916
  const PI_PROVIDER_NAMES = {
761
917
  "anthropic": "Anthropic",
762
918
  "google": "Google",
@@ -770,10 +926,10 @@ const PI_PROVIDER_NAMES = {
770
926
  "mistral": "Mistral",
771
927
  "xai": "xAI"
772
928
  };
773
- const CLI_MODELS = Object.fromEntries(CLI_DEFS.flatMap((def) => Object.entries(def.models).map(([id, entry]) => [id, {
929
+ const CLI_MODELS = Object.fromEntries(Object.values(CLI_ADAPTERS).flatMap((adapter) => Object.entries(adapter.models).map(([id, entry]) => [id, {
774
930
  ...entry,
775
- cli: def.cli,
776
- agentId: def.agentId
931
+ cli: adapter.cli,
932
+ agentId: adapter.agentId
777
933
  }])));
778
934
  function getModelName(id) {
779
935
  if (isPiAiModel(id)) return parsePiAiModelId(id)?.modelId ?? id;
@@ -834,135 +990,248 @@ async function getAvailableModels() {
834
990
  });
835
991
  return [...cliModels, ...piAiEntries];
836
992
  }
837
- function resolveReferenceDirs(skillDir) {
838
- const refsDir = join(skillDir, ".skilld");
839
- if (!existsSync(refsDir)) return [];
840
- const resolved = readdirSync(refsDir).map((entry) => join(refsDir, entry)).filter((p) => lstatSync(p).isSymbolicLink() && existsSync(p)).map((p) => realpathSync(p));
841
- const parents = /* @__PURE__ */ new Set();
842
- for (const p of resolved) {
843
- const parent = dirname(p);
844
- if (!resolved.includes(parent)) parents.add(parent);
993
+ const NUXT_CONFIG_FILES = [
994
+ "nuxt.config.ts",
995
+ "nuxt.config.js",
996
+ "nuxt.config.mjs"
997
+ ];
998
+ const NUXT_ECOSYSTEM = [
999
+ "vue",
1000
+ "nitro",
1001
+ "h3"
1002
+ ];
1003
+ async function findNuxtConfig(cwd) {
1004
+ for (const name of NUXT_CONFIG_FILES) {
1005
+ const path = join(cwd, name);
1006
+ const content = await readFile(path, "utf8").catch(() => null);
1007
+ if (content) return {
1008
+ path,
1009
+ content
1010
+ };
845
1011
  }
846
- return [...resolved, ...parents];
847
- }
848
- const CACHE_DIR = join(homedir(), ".skilld", "llm-cache");
849
- function normalizePromptForHash(prompt) {
850
- return prompt.replace(/\/[^\s`]*\.(?:claude|codex|gemini)\/skills\/[^\s/`]+/g, "<SKILL_DIR>");
1012
+ return null;
851
1013
  }
852
- function hashPrompt(prompt, model, section) {
853
- return createHash("sha256").update(`exec:${model}:${section}:${normalizePromptForHash(prompt)}`).digest("hex").slice(0, 16);
1014
+ function extractModuleStrings(node) {
1015
+ if (!node || typeof node !== "object") return [];
1016
+ if (node.type === "Property" && !node.computed && node.key?.type === "Identifier" && node.key.name === "modules" && node.value?.type === "ArrayExpression") return node.value.elements.filter((el) => el?.type === "Literal" && typeof el.value === "string").map((el) => el.value);
1017
+ const results = [];
1018
+ if (Array.isArray(node)) for (const child of node) results.push(...extractModuleStrings(child));
1019
+ else for (const key of Object.keys(node)) {
1020
+ if (key === "start" || key === "end" || key === "type") continue;
1021
+ const val = node[key];
1022
+ if (val && typeof val === "object") results.push(...extractModuleStrings(val));
1023
+ }
1024
+ return results;
854
1025
  }
855
- function getCached(prompt, model, section, maxAge = 10080 * 60 * 1e3) {
856
- const path = join(CACHE_DIR, `${hashPrompt(prompt, model, section)}.json`);
857
- if (!existsSync(path)) return null;
858
- try {
859
- const { text, timestamp } = JSON.parse(readFileSync(path, "utf-8"));
860
- return Date.now() - timestamp > maxAge ? null : text;
861
- } catch {
862
- return null;
1026
+ async function detectNuxtModules(cwd) {
1027
+ const config = await findNuxtConfig(cwd);
1028
+ if (!config) return [];
1029
+ const modules = extractModuleStrings(parseSync(config.path, config.content).program);
1030
+ const seen = /* @__PURE__ */ new Set();
1031
+ const packages = [];
1032
+ for (const mod of modules) if (!seen.has(mod)) {
1033
+ seen.add(mod);
1034
+ packages.push({
1035
+ name: mod,
1036
+ count: 0,
1037
+ source: "preset"
1038
+ });
1039
+ }
1040
+ for (const pkg of NUXT_ECOSYSTEM) if (!seen.has(pkg)) {
1041
+ seen.add(pkg);
1042
+ packages.push({
1043
+ name: pkg,
1044
+ count: 0,
1045
+ source: "preset"
1046
+ });
863
1047
  }
1048
+ return packages;
864
1049
  }
865
- function setCache(prompt, model, section, text) {
866
- mkdirSync(CACHE_DIR, {
867
- recursive: true,
868
- mode: 448
869
- });
870
- writeFileSync(join(CACHE_DIR, `${hashPrompt(prompt, model, section)}.json`), JSON.stringify({
871
- text,
872
- model,
873
- section,
874
- timestamp: Date.now()
875
- }), { mode: 384 });
1050
+ async function detectPresetPackages(cwd) {
1051
+ return detectNuxtModules(cwd);
876
1052
  }
877
- async function optimizeSectionViaPiAi(opts) {
878
- const { section, prompt, outputFile, skillDir, model, onProgress, timeout, debug } = opts;
879
- const skilldDir = join(skillDir, ".skilld");
880
- const outputPath = join(skilldDir, outputFile);
881
- const logsDir = join(skilldDir, "logs");
882
- const logName = section.toUpperCase().replace(/-/g, "_");
883
- if (existsSync(outputPath)) unlinkSync(outputPath);
884
- writeFileSync(join(skilldDir, `PROMPT_${section}.md`), prompt);
1053
+ const FROM_STATIC_IMPORT_RE = /\bfrom\s*['"]([^'"\n]+)['"]/g;
1054
+ const SIDE_EFFECT_IMPORT_RE = /\bimport\s*['"]([^'"\n]+)['"]/g;
1055
+ const DYNAMIC_IMPORT_RE = /\bimport\s*\(\s*['"]([^'"\n]+)['"]\s*\)/g;
1056
+ const PATTERNS = ["**/*.{ts,js,vue,mjs,cjs,tsx,jsx,mts,cts}"];
1057
+ const IGNORE_DIRS = [
1058
+ "node_modules",
1059
+ "dist",
1060
+ ".nuxt",
1061
+ ".output",
1062
+ "coverage"
1063
+ ];
1064
+ function addPackage(counts, specifier) {
1065
+ if (!specifier || specifier.startsWith(".") || specifier.startsWith("/")) return;
1066
+ const name = specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0];
1067
+ if (!isNodeBuiltin(name)) counts.set(name, (counts.get(name) || 0) + 1);
1068
+ }
1069
+ async function detectImportedPackages(cwd = process.cwd()) {
885
1070
  try {
886
- const ac = new AbortController();
887
- const timer = setTimeout(() => ac.abort(), timeout);
888
- const result = await optimizeSectionPiAi({
889
- section,
890
- prompt,
891
- skillDir,
892
- model,
893
- onProgress,
894
- signal: ac.signal
895
- }).finally(() => clearTimeout(timer));
896
- const raw = result.text.trim();
897
- if (debug) {
898
- mkdirSync(logsDir, { recursive: true });
899
- writeFileSync(join(skilldDir, `PROMPT_${section}.md`), result.fullPrompt);
900
- if (raw) writeFileSync(join(logsDir, `${logName}.md`), raw);
901
- }
902
- if (!raw) return {
903
- section,
904
- content: "",
905
- wasOptimized: false,
906
- error: "pi-ai returned empty response"
907
- };
908
- const content = cleanSectionOutput(raw);
909
- if (content) writeFileSync(outputPath, content);
910
- const validator = getSectionValidator(section);
911
- const warnings = (content && validator ? validator(content) : []).map((w) => ({
912
- section,
913
- warning: w.warning
1071
+ const counts = /* @__PURE__ */ new Map();
1072
+ const files = [];
1073
+ for await (const file of glob(PATTERNS, {
1074
+ cwd,
1075
+ exclude: (p) => IGNORE_DIRS.some((dir) => p === dir || p.endsWith(`/${dir}`))
1076
+ })) files.push(join(cwd, file));
1077
+ await Promise.all(files.map(async (file) => {
1078
+ const content = await readFile(file, "utf8");
1079
+ for (const m of content.matchAll(FROM_STATIC_IMPORT_RE)) addPackage(counts, m[1]);
1080
+ for (const m of content.matchAll(SIDE_EFFECT_IMPORT_RE)) addPackage(counts, m[1]);
1081
+ for (const m of content.matchAll(DYNAMIC_IMPORT_RE)) addPackage(counts, m[1]);
914
1082
  }));
915
- return {
916
- section,
917
- content,
918
- wasOptimized: !!content,
919
- warnings: warnings?.length ? warnings : void 0,
920
- usage: result.usage,
921
- cost: result.cost
922
- };
1083
+ const packages = Array.from(counts.entries(), ([name, count]) => ({
1084
+ name,
1085
+ count,
1086
+ source: "import"
1087
+ })).sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
1088
+ const presets = await detectPresetPackages(cwd);
1089
+ const importNames = new Set(packages.map((p) => p.name));
1090
+ for (const preset of presets) if (!importNames.has(preset.name)) packages.push(preset);
1091
+ return { packages };
923
1092
  } catch (err) {
924
- const errMsg = err.message;
925
- if (debug || errMsg) {
926
- mkdirSync(logsDir, { recursive: true });
927
- writeFileSync(join(logsDir, `${logName}.stderr.log`), errMsg);
928
- }
929
1093
  return {
930
- section,
931
- content: "",
932
- wasOptimized: false,
933
- error: errMsg
1094
+ packages: [],
1095
+ error: String(err)
934
1096
  };
935
1097
  }
936
1098
  }
937
- function optimizeSection(opts) {
938
- const { section, prompt, outputFile, skillDir, model, onProgress, timeout, debug, preExistingFiles } = opts;
939
- if (isPiAiModel(model)) return optimizeSectionViaPiAi({
940
- section,
941
- prompt,
942
- outputFile,
943
- skillDir,
944
- model,
945
- onProgress,
946
- timeout,
947
- debug
1099
+ const NODE_BUILTINS = new Set([
1100
+ "assert",
1101
+ "async_hooks",
1102
+ "buffer",
1103
+ "child_process",
1104
+ "cluster",
1105
+ "console",
1106
+ "constants",
1107
+ "crypto",
1108
+ "dgram",
1109
+ "diagnostics_channel",
1110
+ "dns",
1111
+ "domain",
1112
+ "events",
1113
+ "fs",
1114
+ "http",
1115
+ "http2",
1116
+ "https",
1117
+ "inspector",
1118
+ "module",
1119
+ "net",
1120
+ "os",
1121
+ "path",
1122
+ "perf_hooks",
1123
+ "process",
1124
+ "punycode",
1125
+ "querystring",
1126
+ "readline",
1127
+ "repl",
1128
+ "sea",
1129
+ "sqlite",
1130
+ "stream",
1131
+ "string_decoder",
1132
+ "sys",
1133
+ "test",
1134
+ "timers",
1135
+ "tls",
1136
+ "trace_events",
1137
+ "tty",
1138
+ "url",
1139
+ "util",
1140
+ "v8",
1141
+ "vm",
1142
+ "wasi",
1143
+ "worker_threads",
1144
+ "zlib"
1145
+ ]);
1146
+ function isNodeBuiltin(pkg) {
1147
+ const base = pkg.startsWith("node:") ? pkg.slice(5) : pkg;
1148
+ return NODE_BUILTINS.has(base.split("/")[0]);
1149
+ }
1150
+ const DEFAULT_MAX_AGE = 10080 * 60 * 1e3;
1151
+ function normalizePromptForHash(prompt) {
1152
+ return prompt.replace(/\/[^\s`]*\.(?:claude|codex|gemini)\/skills\/[^\s/`]+/g, "<SKILL_DIR>");
1153
+ }
1154
+ function hashPrompt(prompt, model, section) {
1155
+ return createHash("sha256").update(`exec:${model}:${section}:${normalizePromptForHash(prompt)}`).digest("hex").slice(0, 16);
1156
+ }
1157
+ function getCached(prompt, model, section, maxAge = DEFAULT_MAX_AGE) {
1158
+ const path = join(LLM_CACHE_DIR, `${hashPrompt(prompt, model, section)}.json`);
1159
+ if (!existsSync(path)) return null;
1160
+ try {
1161
+ const { text, timestamp } = JSON.parse(readFileSync(path, "utf-8"));
1162
+ return Date.now() - timestamp > maxAge ? null : text;
1163
+ } catch {
1164
+ return null;
1165
+ }
1166
+ }
1167
+ function setCache(prompt, model, section, text) {
1168
+ mkdirSync(LLM_CACHE_DIR, {
1169
+ recursive: true,
1170
+ mode: 448
948
1171
  });
949
- const cliConfig = CLI_MODELS[model];
950
- if (!cliConfig) return Promise.resolve({
1172
+ writeFileSync(join(LLM_CACHE_DIR, `${hashPrompt(prompt, model, section)}.json`), JSON.stringify({
1173
+ text,
1174
+ model,
1175
+ section,
1176
+ timestamp: Date.now()
1177
+ }), { mode: 384 });
1178
+ }
1179
+ function prepareSection(opts) {
1180
+ if (existsSync(opts.outputPath)) unlinkSync(opts.outputPath);
1181
+ writeFileSync(join(opts.skilldDir, `PROMPT_${opts.section}.md`), opts.prompt);
1182
+ }
1183
+ function finalizeSection(opts) {
1184
+ const { section, raw, outputFile, outputPath, skilldDir, debug, cliCleanup } = opts;
1185
+ if (cliCleanup) for (const entry of readdirSync(skilldDir)) {
1186
+ if (entry === outputFile || cliCleanup.preExistingFiles.has(entry)) continue;
1187
+ if (Object.values(SECTION_OUTPUT_FILES).includes(entry)) continue;
1188
+ if (entry.startsWith("PROMPT_") || entry === "logs") continue;
1189
+ try {
1190
+ unlinkSync(join(skilldDir, entry));
1191
+ } catch {}
1192
+ }
1193
+ const logsDir = join(skilldDir, "logs");
1194
+ const logName = section.toUpperCase().replace(/-/g, "_");
1195
+ const text = ((existsSync(outputPath) ? readFileSync(outputPath, "utf-8") : "") || raw.writeContent || raw.text).trim();
1196
+ const stderr = raw.stderr ?? "";
1197
+ const code = raw.exitCode ?? 0;
1198
+ if (debug || stderr && (!text || code !== 0)) {
1199
+ mkdirSync(logsDir, { recursive: true });
1200
+ if (stderr) writeFileSync(join(logsDir, `${logName}.stderr.log`), stderr);
1201
+ }
1202
+ if (debug) {
1203
+ mkdirSync(logsDir, { recursive: true });
1204
+ if (raw.rawLines?.length) writeFileSync(join(logsDir, `${logName}.jsonl`), raw.rawLines.join("\n"));
1205
+ if (text) writeFileSync(join(logsDir, `${logName}.md`), text);
1206
+ }
1207
+ if (!text && code !== 0) return {
951
1208
  section,
952
1209
  content: "",
953
1210
  wasOptimized: false,
954
- error: `No CLI mapping for model: ${model}`
955
- });
956
- const { cli, model: cliModel } = cliConfig;
957
- const symlinkDirs = resolveReferenceDirs(skillDir);
958
- const args = CLI_BUILD_ARGS[cli](cliModel, skillDir, symlinkDirs);
959
- const parseLine = CLI_PARSE_LINE[cli];
960
- const skilldDir = join(skillDir, ".skilld");
961
- const outputPath = join(skilldDir, outputFile);
962
- if (existsSync(outputPath)) unlinkSync(outputPath);
963
- writeFileSync(join(skilldDir, `PROMPT_${section}.md`), prompt);
1211
+ error: stderr.trim() || `CLI exited with code ${code}`
1212
+ };
1213
+ const content = text ? cleanSectionOutput(text) : "";
1214
+ if (content) writeFileSync(outputPath, content);
1215
+ const validator = getSectionValidator(section);
1216
+ const warnings = (content && validator ? validator(content) : []).map((w) => ({
1217
+ section,
1218
+ warning: w.warning
1219
+ }));
1220
+ return {
1221
+ section,
1222
+ content,
1223
+ wasOptimized: !!content,
1224
+ warnings: warnings.length ? warnings : void 0,
1225
+ usage: raw.usage,
1226
+ cost: raw.cost
1227
+ };
1228
+ }
1229
+ function spawnCliAndStream(opts) {
1230
+ const { adapter, cliModel, prompt, skillDir, skilldDir, symlinkDirs, timeout, debug, section, onProgress } = opts;
1231
+ const args = adapter.buildArgs(cliModel, skillDir, symlinkDirs);
1232
+ const parseEvent = adapter.parseEvent;
964
1233
  return new Promise((resolve) => {
965
- const proc = spawn(cli, args, {
1234
+ const proc = spawn(adapter.cli, args, {
966
1235
  cwd: skilldDir,
967
1236
  stdio: [
968
1237
  "pipe",
@@ -991,6 +1260,32 @@ function optimizeSection(opts) {
991
1260
  });
992
1261
  proc.stdin.write(prompt);
993
1262
  proc.stdin.end();
1263
+ function applyEvent(evt) {
1264
+ switch (evt.kind) {
1265
+ case "text":
1266
+ if (evt.delta) accumulatedText += evt.delta;
1267
+ if (evt.full !== void 0) accumulatedText = evt.full;
1268
+ break;
1269
+ case "tool-call": {
1270
+ if (evt.writeContent) lastWriteContent = evt.writeContent;
1271
+ const chunk = evt.hint ? `[${evt.tool}: ${evt.hint}]` : `[${evt.tool}]`;
1272
+ onProgress?.({
1273
+ chunk,
1274
+ type: "reasoning",
1275
+ text: "",
1276
+ reasoning: chunk,
1277
+ section
1278
+ });
1279
+ break;
1280
+ }
1281
+ case "done":
1282
+ if (evt.usage) usage = evt.usage;
1283
+ if (evt.cost != null) cost = evt.cost;
1284
+ break;
1285
+ case "error":
1286
+ case "noop": break;
1287
+ }
1288
+ }
994
1289
  proc.stdout.on("data", (chunk) => {
995
1290
  buffer += chunk.toString();
996
1291
  const lines = buffer.split("\n");
@@ -998,22 +1293,7 @@ function optimizeSection(opts) {
998
1293
  for (const line of lines) {
999
1294
  if (!line.trim()) continue;
1000
1295
  if (debug) rawLines.push(line);
1001
- const evt = parseLine(line);
1002
- if (evt.textDelta) accumulatedText += evt.textDelta;
1003
- if (evt.fullText) accumulatedText = evt.fullText;
1004
- if (evt.writeContent) lastWriteContent = evt.writeContent;
1005
- if (evt.toolName) {
1006
- const hint = evt.toolHint ? `[${evt.toolName}: ${evt.toolHint}]` : `[${evt.toolName}]`;
1007
- onProgress?.({
1008
- chunk: hint,
1009
- type: "reasoning",
1010
- text: "",
1011
- reasoning: hint,
1012
- section
1013
- });
1014
- }
1015
- if (evt.usage) usage = evt.usage;
1016
- if (evt.cost != null) cost = evt.cost;
1296
+ applyEvent(parseEvent(line));
1017
1297
  }
1018
1298
  });
1019
1299
  let stderr = "";
@@ -1021,70 +1301,118 @@ function optimizeSection(opts) {
1021
1301
  stderr += chunk.toString();
1022
1302
  });
1023
1303
  proc.on("close", (code) => {
1024
- if (buffer.trim()) {
1025
- const evt = parseLine(buffer);
1026
- if (evt.textDelta) accumulatedText += evt.textDelta;
1027
- if (evt.fullText) accumulatedText = evt.fullText;
1028
- if (evt.writeContent) lastWriteContent = evt.writeContent;
1029
- if (evt.usage) usage = evt.usage;
1030
- if (evt.cost != null) cost = evt.cost;
1031
- }
1032
- for (const entry of readdirSync(skilldDir)) if (entry !== outputFile && !preExistingFiles.has(entry)) {
1033
- if (Object.values(SECTION_OUTPUT_FILES).includes(entry)) continue;
1034
- if (entry.startsWith("PROMPT_") || entry === "logs") continue;
1035
- try {
1036
- unlinkSync(join(skilldDir, entry));
1037
- } catch {}
1038
- }
1039
- const raw = (existsSync(outputPath) ? readFileSync(outputPath, "utf-8") : lastWriteContent || accumulatedText).trim();
1040
- const logsDir = join(skilldDir, "logs");
1041
- const logName = section.toUpperCase().replace(/-/g, "_");
1042
- if (debug || stderr && (!raw || code !== 0)) {
1043
- mkdirSync(logsDir, { recursive: true });
1044
- if (stderr) writeFileSync(join(logsDir, `${logName}.stderr.log`), stderr);
1045
- }
1046
- if (debug) {
1047
- mkdirSync(logsDir, { recursive: true });
1048
- if (rawLines.length) writeFileSync(join(logsDir, `${logName}.jsonl`), rawLines.join("\n"));
1049
- if (raw) writeFileSync(join(logsDir, `${logName}.md`), raw);
1050
- }
1051
- if (!raw && code !== 0) {
1052
- resolve({
1053
- section,
1054
- content: "",
1055
- wasOptimized: false,
1056
- error: stderr.trim() || `CLI exited with code ${code}`
1057
- });
1058
- return;
1059
- }
1060
- const content = raw ? cleanSectionOutput(raw) : "";
1061
- if (content) writeFileSync(outputPath, content);
1062
- const validator = getSectionValidator(section);
1063
- const warnings = (content && validator ? validator(content) : []).map((w) => ({
1064
- section,
1065
- warning: w.warning
1066
- }));
1304
+ if (buffer.trim()) applyEvent(parseEvent(buffer));
1067
1305
  resolve({
1068
- section,
1069
- content,
1070
- wasOptimized: !!content,
1071
- warnings: warnings?.length ? warnings : void 0,
1306
+ text: accumulatedText,
1307
+ writeContent: lastWriteContent || void 0,
1072
1308
  usage,
1073
- cost
1309
+ cost,
1310
+ stderr,
1311
+ exitCode: code ?? 0,
1312
+ rawLines: debug ? rawLines : void 0
1074
1313
  });
1075
1314
  });
1076
1315
  proc.on("error", (err) => {
1077
1316
  resolve({
1078
- section,
1079
- content: "",
1080
- wasOptimized: false,
1081
- error: err.message
1317
+ text: "",
1318
+ stderr: err.message,
1319
+ exitCode: 1
1082
1320
  });
1083
1321
  });
1084
1322
  });
1085
1323
  }
1324
+ function cliExecutor(model) {
1325
+ const cliConfig = CLI_MODELS[model];
1326
+ if (!cliConfig) return { error: `No CLI mapping for model: ${model}` };
1327
+ const adapter = CLI_ADAPTERS[cliConfig.cli];
1328
+ return {
1329
+ cliCleanup: true,
1330
+ run: ({ section, prompt, skillDir, skilldDir, timeout, debug, onProgress }) => spawnCliAndStream({
1331
+ adapter,
1332
+ cliModel: cliConfig.model,
1333
+ prompt,
1334
+ skillDir,
1335
+ skilldDir,
1336
+ symlinkDirs: getSkillReferenceDirs(skillDir),
1337
+ timeout,
1338
+ debug,
1339
+ section,
1340
+ onProgress
1341
+ })
1342
+ };
1343
+ }
1344
+ function piAiExecutor(model) {
1345
+ if (!new Set(getAvailablePiAiModels().map((m) => m.id)).has(model)) return { error: `Pi model unavailable or not authenticated: ${model}` };
1346
+ return {
1347
+ cliCleanup: false,
1348
+ run: async ({ section, prompt, skillDir, timeout, onProgress }) => {
1349
+ const ac = new AbortController();
1350
+ const timer = setTimeout(() => ac.abort(), timeout);
1351
+ try {
1352
+ const result = await optimizeSectionPiAi({
1353
+ section,
1354
+ prompt,
1355
+ skillDir,
1356
+ model,
1357
+ onProgress,
1358
+ signal: ac.signal
1359
+ });
1360
+ return {
1361
+ text: result.text.trim(),
1362
+ usage: result.usage,
1363
+ cost: result.cost
1364
+ };
1365
+ } catch (err) {
1366
+ return {
1367
+ text: "",
1368
+ stderr: err.message,
1369
+ exitCode: 1
1370
+ };
1371
+ } finally {
1372
+ clearTimeout(timer);
1373
+ }
1374
+ }
1375
+ };
1376
+ }
1377
+ function selectExecutor(model) {
1378
+ return isPiAiModel(model) ? piAiExecutor(model) : cliExecutor(model);
1379
+ }
1380
+ const STATIC_REGEX_1 = /\b429\b/;
1381
+ const STATIC_REGEX_2 = /rate.?limit/i;
1382
+ const STATIC_REGEX_3 = /exhausted.*capacity/i;
1383
+ const STATIC_REGEX_4 = /quota.*reset/i;
1384
+ const STATIC_REGEX_5 = /reset\s+after\s+(\d+)s/i;
1385
+ async function optimizeSection(opts) {
1386
+ const { section, prompt, outputFile, skillDir, executor, onProgress, timeout, debug, preExistingFiles } = opts;
1387
+ const skilldDir = skillInternalDir(skillDir);
1388
+ const outputPath = join(skilldDir, outputFile);
1389
+ prepareSection({
1390
+ section,
1391
+ prompt,
1392
+ outputPath,
1393
+ skilldDir
1394
+ });
1395
+ return finalizeSection({
1396
+ section,
1397
+ raw: await executor.run({
1398
+ section,
1399
+ prompt,
1400
+ skillDir,
1401
+ skilldDir,
1402
+ timeout,
1403
+ debug,
1404
+ onProgress
1405
+ }),
1406
+ outputFile,
1407
+ outputPath,
1408
+ skilldDir,
1409
+ debug: !!debug,
1410
+ cliCleanup: executor.cliCleanup ? { preExistingFiles } : void 0
1411
+ });
1412
+ }
1086
1413
  async function optimizeDocs(opts) {
1087
1414
  const { packageName, skillDir, model = "sonnet", version, hasGithub, hasReleases, hasChangelog, docFiles, docsType, hasShippedDocs, onProgress, timeout = 18e4, debug, noCache, sections, customPrompt, features, pkgFiles, overheadLines } = opts;
1415
+ const cache = createReferenceCache(packageName, version);
1088
1416
  const sectionPrompts = buildAllSectionPrompts({
1089
1417
  packageName,
1090
1418
  skillDir,
@@ -1107,24 +1435,20 @@ async function optimizeDocs(opts) {
1107
1435
  wasOptimized: false,
1108
1436
  error: "No valid sections to generate"
1109
1437
  };
1110
- if (isPiAiModel(model)) {
1111
- if (!new Set(getAvailablePiAiModels().map((m) => m.id)).has(model)) return {
1112
- optimized: "",
1113
- wasOptimized: false,
1114
- error: `Pi model unavailable or not authenticated: ${model}`
1115
- };
1116
- } else if (!CLI_MODELS[model]) return {
1438
+ const executorOrError = selectExecutor(model);
1439
+ if ("error" in executorOrError) return {
1117
1440
  optimized: "",
1118
1441
  wasOptimized: false,
1119
- error: `No CLI mapping for model: ${model}`
1442
+ error: executorOrError.error
1120
1443
  };
1444
+ const executor = executorOrError;
1121
1445
  const cachedResults = [];
1122
1446
  const uncachedSections = [];
1123
1447
  for (const [section, prompt] of sectionPrompts) {
1124
1448
  if (!noCache) {
1125
1449
  if (version) {
1126
1450
  const outputFile = SECTION_OUTPUT_FILES[section];
1127
- const refCached = readCachedSection(packageName, version, outputFile);
1451
+ const refCached = cache.readSection(outputFile);
1128
1452
  if (refCached) {
1129
1453
  onProgress?.({
1130
1454
  chunk: `[${section}: cached]`,
@@ -1163,7 +1487,7 @@ async function optimizeDocs(opts) {
1163
1487
  prompt
1164
1488
  });
1165
1489
  }
1166
- const skilldDir = join(skillDir, ".skilld");
1490
+ const skilldDir = skillInternalDir(skillDir);
1167
1491
  mkdirSync(skilldDir, { recursive: true });
1168
1492
  for (const entry of readdirSync(skilldDir)) {
1169
1493
  const entryPath = join(skilldDir, entry);
@@ -1185,8 +1509,7 @@ async function optimizeDocs(opts) {
1185
1509
  prompt,
1186
1510
  outputFile,
1187
1511
  skillDir,
1188
- model,
1189
- packageName,
1512
+ executor,
1190
1513
  onProgress,
1191
1514
  timeout,
1192
1515
  debug,
@@ -1247,8 +1570,7 @@ async function optimizeDocs(opts) {
1247
1570
  prompt,
1248
1571
  outputFile: SECTION_OUTPUT_FILES[section],
1249
1572
  skillDir,
1250
- model,
1251
- packageName,
1573
+ executor,
1252
1574
  onProgress,
1253
1575
  timeout,
1254
1576
  debug,
@@ -1276,7 +1598,7 @@ async function optimizeDocs(opts) {
1276
1598
  file: SECTION_OUTPUT_FILES[r.section],
1277
1599
  content: r.content
1278
1600
  }));
1279
- if (sectionFiles.length > 0) writeSections(packageName, version, sectionFiles);
1601
+ if (sectionFiles.length > 0) cache.writeSections(sectionFiles);
1280
1602
  }
1281
1603
  const mergedParts = [];
1282
1604
  for (const section of SECTION_MERGE_ORDER) {
@@ -1292,7 +1614,7 @@ async function optimizeDocs(opts) {
1292
1614
  } : void 0;
1293
1615
  const errors = allResults.filter((r) => r.error).map((r) => `${r.section}: ${r.error}`);
1294
1616
  const warnings = allResults.flatMap((r) => r.warnings ?? []).map((w) => `${w.section}: ${w.warning}`);
1295
- const debugLogsDir = debug && uncachedSections.length > 0 ? join(skillDir, ".skilld", "logs") : void 0;
1617
+ const debugLogsDir = debug && uncachedSections.length > 0 ? skillLogDir(skillDir) : void 0;
1296
1618
  return {
1297
1619
  optimized,
1298
1620
  wasOptimized,
@@ -1306,221 +1628,17 @@ async function optimizeDocs(opts) {
1306
1628
  }
1307
1629
  function isRateLimitError(error) {
1308
1630
  if (!error) return false;
1309
- return /\b429\b/.test(error) || /rate.?limit/i.test(error) || /exhausted.*capacity/i.test(error) || /quota.*reset/i.test(error);
1631
+ return STATIC_REGEX_1.test(error) || STATIC_REGEX_2.test(error) || STATIC_REGEX_3.test(error) || STATIC_REGEX_4.test(error);
1310
1632
  }
1311
1633
  function parseRateLimitDelay(error) {
1312
1634
  if (!error || !isRateLimitError(error)) return void 0;
1313
- const match = error.match(/reset\s+after\s+(\d+)s/i);
1635
+ const match = error.match(STATIC_REGEX_5);
1314
1636
  return match ? Number(match[1]) : 10;
1315
1637
  }
1316
1638
  function getRetryError(result) {
1317
1639
  if (result.status === "rejected") return String(result.reason);
1318
1640
  return result.value.error;
1319
1641
  }
1320
- function shortenPath(p) {
1321
- const refIdx = p.indexOf(".skilld/");
1322
- if (refIdx !== -1) return p.slice(refIdx + 8);
1323
- const parts = p.split("/");
1324
- return parts.length > 2 ? `.../${parts.slice(-2).join("/")}` : p;
1325
- }
1326
- function shortenCommand(cmd) {
1327
- return cmd.replace(/\/[^\s"']+/g, (match) => {
1328
- if (match.includes(".claude/") || match.includes(".skilld/") || match.includes("node_modules/")) return `.../${match.split("/").slice(-2).join("/")}`;
1329
- return match;
1330
- });
1331
- }
1332
- function cleanSectionOutput(content) {
1333
- let cleaned = content.trim();
1334
- const wrapMatch = cleaned.match(/^```(?:markdown|md)?[^\S\n]*\n([\s\S]+)\n```[^\S\n]*$/);
1335
- if (wrapMatch) {
1336
- const inner = wrapMatch[1].trim();
1337
- if (/^```(?:markdown|md)/.test(cleaned) || /^##\s/m.test(inner) || /^- (?:BREAKING|DEPRECATED|NEW): /m.test(inner)) cleaned = inner;
1338
- }
1339
- cleaned = cleaned.replace(/^# (?!#)/gm, "## ");
1340
- const fmMatch = cleaned.match(/^-{3,}\n/);
1341
- if (fmMatch) {
1342
- const afterOpen = fmMatch[0].length;
1343
- const closeMatch = cleaned.slice(afterOpen).match(/\n-{3,}/);
1344
- if (closeMatch) cleaned = cleaned.slice(afterOpen + closeMatch.index + closeMatch[0].length).trim();
1345
- else cleaned = cleaned.slice(afterOpen).trim();
1346
- }
1347
- const firstMarker = cleaned.match(/^(##\s|- (?:BREAKING|DEPRECATED|NEW): )/m);
1348
- if (firstMarker?.index && firstMarker.index > 0) cleaned = cleaned.slice(firstMarker.index).trim();
1349
- const headingMatch = cleaned.match(/^(## .+)\n/);
1350
- if (headingMatch) {
1351
- const heading = headingMatch[1];
1352
- const afterFirst = headingMatch[0].length;
1353
- const secondIdx = cleaned.indexOf(heading, afterFirst);
1354
- if (secondIdx !== -1) {
1355
- if (secondIdx - afterFirst < 200) cleaned = cleaned.slice(secondIdx).trim();
1356
- }
1357
- }
1358
- cleaned = cleaned.replace(/\(?\[`?\.\/(?:\.skilld\/|references\/)[^)\]]*\]\(([^)]+)\)\)?/g, (match, url) => {
1359
- if (/^\.\/(?:\.skilld\/|references\/)/.test(url)) return `[source](${url})`;
1360
- return match;
1361
- });
1362
- cleaned = cleaned.replace(/\[source\]\(\.\/((docs|issues|discussions|releases|pkg|guide)\/)/g, "[source](./.skilld/$1");
1363
- cleaned = sanitizeMarkdown(cleaned);
1364
- if (!/^##\s/m.test(cleaned) && !/^- (?:BREAKING|DEPRECATED|NEW): /m.test(cleaned) && !/\[source\]/.test(cleaned)) return "";
1365
- return cleaned;
1366
- }
1367
- const NUXT_CONFIG_FILES = [
1368
- "nuxt.config.ts",
1369
- "nuxt.config.js",
1370
- "nuxt.config.mjs"
1371
- ];
1372
- const NUXT_ECOSYSTEM = [
1373
- "vue",
1374
- "nitro",
1375
- "h3"
1376
- ];
1377
- async function findNuxtConfig(cwd) {
1378
- for (const name of NUXT_CONFIG_FILES) {
1379
- const path = join(cwd, name);
1380
- const content = await readFile(path, "utf8").catch(() => null);
1381
- if (content) return {
1382
- path,
1383
- content
1384
- };
1385
- }
1386
- return null;
1387
- }
1388
- function extractModuleStrings(node) {
1389
- if (!node || typeof node !== "object") return [];
1390
- if (node.type === "Property" && !node.computed && node.key?.type === "Identifier" && node.key.name === "modules" && node.value?.type === "ArrayExpression") return node.value.elements.filter((el) => el?.type === "Literal" && typeof el.value === "string").map((el) => el.value);
1391
- const results = [];
1392
- if (Array.isArray(node)) for (const child of node) results.push(...extractModuleStrings(child));
1393
- else for (const key of Object.keys(node)) {
1394
- if (key === "start" || key === "end" || key === "type") continue;
1395
- const val = node[key];
1396
- if (val && typeof val === "object") results.push(...extractModuleStrings(val));
1397
- }
1398
- return results;
1399
- }
1400
- async function detectNuxtModules(cwd) {
1401
- const config = await findNuxtConfig(cwd);
1402
- if (!config) return [];
1403
- const modules = extractModuleStrings(parseSync(config.path, config.content).program);
1404
- const seen = /* @__PURE__ */ new Set();
1405
- const packages = [];
1406
- for (const mod of modules) if (!seen.has(mod)) {
1407
- seen.add(mod);
1408
- packages.push({
1409
- name: mod,
1410
- count: 0,
1411
- source: "preset"
1412
- });
1413
- }
1414
- for (const pkg of NUXT_ECOSYSTEM) if (!seen.has(pkg)) {
1415
- seen.add(pkg);
1416
- packages.push({
1417
- name: pkg,
1418
- count: 0,
1419
- source: "preset"
1420
- });
1421
- }
1422
- return packages;
1423
- }
1424
- async function detectPresetPackages(cwd) {
1425
- return detectNuxtModules(cwd);
1426
- }
1427
- const PATTERNS = ["**/*.{ts,js,vue,mjs,cjs,tsx,jsx,mts,cts}"];
1428
- const IGNORE = [
1429
- "**/node_modules/**",
1430
- "**/dist/**",
1431
- "**/.nuxt/**",
1432
- "**/.output/**",
1433
- "**/coverage/**"
1434
- ];
1435
- function addPackage(counts, specifier) {
1436
- if (!specifier || specifier.startsWith(".") || specifier.startsWith("/")) return;
1437
- const name = specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0];
1438
- if (!isNodeBuiltin(name)) counts.set(name, (counts.get(name) || 0) + 1);
1439
- }
1440
- async function detectImportedPackages(cwd = process.cwd()) {
1441
- try {
1442
- const counts = /* @__PURE__ */ new Map();
1443
- const files = await glob(PATTERNS, {
1444
- cwd,
1445
- ignore: IGNORE,
1446
- absolute: true,
1447
- expandDirectories: false
1448
- });
1449
- await Promise.all(files.map(async (file) => {
1450
- const content = await readFile(file, "utf8");
1451
- for (const imp of findStaticImports(content)) addPackage(counts, imp.specifier);
1452
- for (const imp of findDynamicImports(content)) {
1453
- const match = imp.expression.match(/^['"]([^'"]+)['"]$/);
1454
- if (match) addPackage(counts, match[1]);
1455
- }
1456
- }));
1457
- const packages = Array.from(counts.entries(), ([name, count]) => ({
1458
- name,
1459
- count,
1460
- source: "import"
1461
- })).sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
1462
- const presets = await detectPresetPackages(cwd);
1463
- const importNames = new Set(packages.map((p) => p.name));
1464
- for (const preset of presets) if (!importNames.has(preset.name)) packages.push(preset);
1465
- return { packages };
1466
- } catch (err) {
1467
- return {
1468
- packages: [],
1469
- error: String(err)
1470
- };
1471
- }
1472
- }
1473
- const NODE_BUILTINS = new Set([
1474
- "assert",
1475
- "async_hooks",
1476
- "buffer",
1477
- "child_process",
1478
- "cluster",
1479
- "console",
1480
- "constants",
1481
- "crypto",
1482
- "dgram",
1483
- "diagnostics_channel",
1484
- "dns",
1485
- "domain",
1486
- "events",
1487
- "fs",
1488
- "http",
1489
- "http2",
1490
- "https",
1491
- "inspector",
1492
- "module",
1493
- "net",
1494
- "os",
1495
- "path",
1496
- "perf_hooks",
1497
- "process",
1498
- "punycode",
1499
- "querystring",
1500
- "readline",
1501
- "repl",
1502
- "sea",
1503
- "sqlite",
1504
- "stream",
1505
- "string_decoder",
1506
- "sys",
1507
- "test",
1508
- "timers",
1509
- "tls",
1510
- "trace_events",
1511
- "tty",
1512
- "url",
1513
- "util",
1514
- "v8",
1515
- "vm",
1516
- "wasi",
1517
- "worker_threads",
1518
- "zlib"
1519
- ]);
1520
- function isNodeBuiltin(pkg) {
1521
- const base = pkg.startsWith("node:") ? pkg.slice(5) : pkg;
1522
- return NODE_BUILTINS.has(base.split("/")[0]);
1523
- }
1524
- export { getModelLabel as a, getOAuthProviderList as c, getAvailableModels as i, loginOAuthProvider as l, cleanSectionOutput as n, getModelName as o, createToolProgress as r, optimizeDocs as s, detectImportedPackages as t, logoutOAuthProvider as u };
1642
+ export { getModelName as a, getOAuthProviderList as c, getModelLabel as i, loginOAuthProvider as l, detectImportedPackages as n, createToolProgress as o, getAvailableModels as r, cleanSectionOutput as s, optimizeDocs as t, logoutOAuthProvider as u };
1525
1643
 
1526
1644
  //# sourceMappingURL=agent.mjs.map