skillspp 1.2.0 → 1.3.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.
@@ -1,7 +1,7 @@
1
1
  // ../../packages/core/src/runtime/background-tasks.ts
2
- import fs15 from "node:fs";
2
+ import fs13 from "node:fs";
3
3
  import os7 from "node:os";
4
- import path16 from "node:path";
4
+ import path14 from "node:path";
5
5
 
6
6
  // ../../packages/core/src/runtime/agents.ts
7
7
  import fs from "node:fs";
@@ -12,264 +12,198 @@ var STANDARD_AGENTS = {
12
12
  displayName: "Universal",
13
13
  projectSkillsDir: ".agents/skills",
14
14
  globalSkillsDir: ".agents/skills",
15
- projectPluginsDir: ".agents/plugins/cache",
16
- globalPluginsDir: ".agents/plugins/cache",
17
15
  installMarkers: [".agents"]
18
16
  },
19
17
  adal: {
20
18
  displayName: "AdaL",
21
19
  projectSkillsDir: ".adal/skills",
22
20
  globalSkillsDir: ".adal/skills",
23
- projectPluginsDir: ".adal/plugins/cache",
24
- globalPluginsDir: ".adal/plugins/cache",
25
21
  installMarkers: [".adal"]
26
22
  },
27
23
  antigravity: {
28
24
  displayName: "Antigravity",
29
25
  projectSkillsDir: ".agent/skills",
30
26
  globalSkillsDir: ".gemini/antigravity/skills",
31
- projectPluginsDir: ".agent/plugins/cache",
32
- globalPluginsDir: ".gemini/antigravity/plugins/cache",
33
27
  installMarkers: [".gemini/antigravity"]
34
28
  },
35
29
  augment: {
36
30
  displayName: "Augment",
37
31
  projectSkillsDir: ".augment/skills",
38
32
  globalSkillsDir: ".augment/skills",
39
- projectPluginsDir: ".augment/plugins/cache",
40
- globalPluginsDir: ".augment/plugins/cache",
41
33
  installMarkers: [".augment"]
42
34
  },
43
35
  "claude-code": {
44
36
  displayName: "Claude Code",
45
37
  projectSkillsDir: ".claude/skills",
46
38
  globalSkillsDir: ".claude/skills",
47
- projectPluginsDir: ".claude/plugins/cache",
48
- globalPluginsDir: ".claude/plugins/cache",
49
39
  installMarkers: [".claude"]
50
40
  },
51
41
  "cortex-code": {
52
42
  displayName: "Cortex Code",
53
43
  projectSkillsDir: ".cortex/skills",
54
44
  globalSkillsDir: ".cortex/skills",
55
- projectPluginsDir: ".cortex/plugins/cache",
56
- globalPluginsDir: ".cortex/plugins/cache",
57
45
  installMarkers: [".cortex"]
58
46
  },
59
47
  crush: {
60
48
  displayName: "Crush",
61
49
  projectSkillsDir: ".crush/skills",
62
50
  globalSkillsDir: ".crush/skills",
63
- projectPluginsDir: ".crush/plugins/cache",
64
- globalPluginsDir: ".crush/plugins/cache",
65
51
  installMarkers: [".crush"]
66
52
  },
67
53
  droid: {
68
54
  displayName: "Droid",
69
55
  projectSkillsDir: ".factory/skills",
70
56
  globalSkillsDir: ".factory/skills",
71
- projectPluginsDir: ".factory/plugins/cache",
72
- globalPluginsDir: ".factory/plugins/cache",
73
57
  installMarkers: [".factory"]
74
58
  },
75
59
  goose: {
76
60
  displayName: "Goose",
77
61
  projectSkillsDir: ".goose/skills",
78
62
  globalSkillsDir: ".config/goose/skills",
79
- projectPluginsDir: ".goose/plugins/cache",
80
- globalPluginsDir: ".config/goose/plugins/cache",
81
63
  installMarkers: [".config/goose"]
82
64
  },
83
65
  "iflow-cli": {
84
66
  displayName: "iFlow CLI",
85
67
  projectSkillsDir: ".iflow/skills",
86
68
  globalSkillsDir: ".iflow/skills",
87
- projectPluginsDir: ".iflow/plugins/cache",
88
- globalPluginsDir: ".iflow/plugins/cache",
89
69
  installMarkers: [".iflow"]
90
70
  },
91
71
  junie: {
92
72
  displayName: "Junie",
93
73
  projectSkillsDir: ".junie/skills",
94
74
  globalSkillsDir: ".junie/skills",
95
- projectPluginsDir: ".junie/plugins/cache",
96
- globalPluginsDir: ".junie/plugins/cache",
97
75
  installMarkers: [".junie"]
98
76
  },
99
77
  "kiro-cli": {
100
78
  displayName: "Kiro CLI",
101
79
  projectSkillsDir: ".kiro/skills",
102
80
  globalSkillsDir: ".kiro/skills",
103
- projectPluginsDir: ".kiro/plugins/cache",
104
- globalPluginsDir: ".kiro/plugins/cache",
105
81
  installMarkers: [".kiro"]
106
82
  },
107
83
  kode: {
108
84
  displayName: "Kode",
109
85
  projectSkillsDir: ".kode/skills",
110
86
  globalSkillsDir: ".kode/skills",
111
- projectPluginsDir: ".kode/plugins/cache",
112
- globalPluginsDir: ".kode/plugins/cache",
113
87
  installMarkers: [".kode"]
114
88
  },
115
89
  openclaw: {
116
90
  displayName: "OpenClaw",
117
91
  projectSkillsDir: "skills",
118
92
  globalSkillsDir: ".openclaw/skills",
119
- projectPluginsDir: "plugins/cache",
120
- globalPluginsDir: ".openclaw/plugins/cache",
121
93
  installMarkers: [".openclaw"]
122
94
  },
123
95
  openhands: {
124
96
  displayName: "OpenHands",
125
97
  projectSkillsDir: ".openhands/skills",
126
98
  globalSkillsDir: ".openhands/skills",
127
- projectPluginsDir: ".openhands/plugins/cache",
128
- globalPluginsDir: ".openhands/plugins/cache",
129
99
  installMarkers: [".openhands"]
130
100
  },
131
101
  "mistral-vibe": {
132
102
  displayName: "Mistral Vibe",
133
103
  projectSkillsDir: ".vibe/skills",
134
104
  globalSkillsDir: ".vibe/skills",
135
- projectPluginsDir: ".vibe/plugins/cache",
136
- globalPluginsDir: ".vibe/plugins/cache",
137
105
  installMarkers: [".vibe"]
138
106
  },
139
107
  neovate: {
140
108
  displayName: "Neovate",
141
109
  projectSkillsDir: ".neovate/skills",
142
110
  globalSkillsDir: ".neovate/skills",
143
- projectPluginsDir: ".neovate/plugins/cache",
144
- globalPluginsDir: ".neovate/plugins/cache",
145
111
  installMarkers: [".neovate"]
146
112
  },
147
113
  pochi: {
148
114
  displayName: "Pochi",
149
115
  projectSkillsDir: ".pochi/skills",
150
116
  globalSkillsDir: ".pochi/skills",
151
- projectPluginsDir: ".pochi/plugins/cache",
152
- globalPluginsDir: ".pochi/plugins/cache",
153
117
  installMarkers: [".pochi"]
154
118
  },
155
119
  qoder: {
156
120
  displayName: "Qoder",
157
121
  projectSkillsDir: ".qoder/skills",
158
122
  globalSkillsDir: ".qoder/skills",
159
- projectPluginsDir: ".qoder/plugins/cache",
160
- globalPluginsDir: ".qoder/plugins/cache",
161
123
  installMarkers: [".qoder"]
162
124
  },
163
125
  "qwen-code": {
164
126
  displayName: "Qwen Code",
165
127
  projectSkillsDir: ".qwen/skills",
166
128
  globalSkillsDir: ".qwen/skills",
167
- projectPluginsDir: ".qwen/plugins/cache",
168
- globalPluginsDir: ".qwen/plugins/cache",
169
129
  installMarkers: [".qwen"]
170
130
  },
171
131
  roo: {
172
132
  displayName: "Roo Code",
173
133
  projectSkillsDir: ".roo/skills",
174
134
  globalSkillsDir: ".roo/skills",
175
- projectPluginsDir: ".roo/plugins/cache",
176
- globalPluginsDir: ".roo/plugins/cache",
177
135
  installMarkers: [".roo"]
178
136
  },
179
137
  trae: {
180
138
  displayName: "Trae",
181
139
  projectSkillsDir: ".trae/skills",
182
140
  globalSkillsDir: ".trae/skills",
183
- projectPluginsDir: ".trae/plugins/cache",
184
- globalPluginsDir: ".trae/plugins/cache",
185
141
  installMarkers: [".trae"]
186
142
  },
187
143
  "trae-cn": {
188
144
  displayName: "Trae CN",
189
145
  projectSkillsDir: ".trae/skills",
190
146
  globalSkillsDir: ".trae/skills",
191
- projectPluginsDir: ".trae/plugins/cache",
192
- globalPluginsDir: ".trae/plugins/cache",
193
147
  installMarkers: [".trae"]
194
148
  },
195
149
  windsurf: {
196
150
  displayName: "Windsurf",
197
151
  projectSkillsDir: ".windsurf/skills",
198
152
  globalSkillsDir: ".codeium/windsurf/skills",
199
- projectPluginsDir: ".windsurf/plugins/cache",
200
- globalPluginsDir: ".codeium/windsurf/plugins/cache",
201
153
  installMarkers: [".windsurf", ".codeium/windsurf"]
202
154
  },
203
155
  zencoder: {
204
156
  displayName: "Zencoder",
205
157
  projectSkillsDir: ".zencoder/skills",
206
158
  globalSkillsDir: ".zencoder/skills",
207
- projectPluginsDir: ".zencoder/plugins/cache",
208
- globalPluginsDir: ".zencoder/plugins/cache",
209
159
  installMarkers: [".zencoder"]
210
160
  },
211
161
  continue: {
212
162
  displayName: "Continue",
213
163
  projectSkillsDir: ".continue/skills",
214
164
  globalSkillsDir: ".continue/skills",
215
- projectPluginsDir: ".continue/plugins/cache",
216
- globalPluginsDir: ".continue/plugins/cache",
217
165
  installMarkers: [".continue"]
218
166
  },
219
167
  codebuddy: {
220
168
  displayName: "CodeBuddy",
221
169
  projectSkillsDir: ".codebuddy/skills",
222
170
  globalSkillsDir: ".codebuddy/skills",
223
- projectPluginsDir: ".codebuddy/plugins/cache",
224
- globalPluginsDir: ".codebuddy/plugins/cache",
225
171
  installMarkers: [".codebuddy"]
226
172
  },
227
173
  "command-code": {
228
174
  displayName: "Command Code",
229
175
  projectSkillsDir: ".commandcode/skills",
230
176
  globalSkillsDir: ".commandcode/skills",
231
- projectPluginsDir: ".commandcode/plugins/cache",
232
- globalPluginsDir: ".commandcode/plugins/cache",
233
177
  installMarkers: [".commandcode"]
234
178
  },
235
179
  kilo: {
236
180
  displayName: "Kilo Code",
237
181
  projectSkillsDir: ".kilocode/skills",
238
182
  globalSkillsDir: ".kilocode/skills",
239
- projectPluginsDir: ".kilocode/plugins/cache",
240
- globalPluginsDir: ".kilocode/plugins/cache",
241
183
  installMarkers: [".kilocode"]
242
184
  },
243
185
  mcpjam: {
244
186
  displayName: "MCPJam",
245
187
  projectSkillsDir: ".mcpjam/skills",
246
188
  globalSkillsDir: ".mcpjam/skills",
247
- projectPluginsDir: ".mcpjam/plugins/cache",
248
- globalPluginsDir: ".mcpjam/plugins/cache",
249
189
  installMarkers: [".mcpjam"]
250
190
  },
251
191
  mux: {
252
192
  displayName: "Mux",
253
193
  projectSkillsDir: ".mux/skills",
254
194
  globalSkillsDir: ".mux/skills",
255
- projectPluginsDir: ".mux/plugins/cache",
256
- globalPluginsDir: ".mux/plugins/cache",
257
195
  installMarkers: [".mux"]
258
196
  },
259
197
  pi: {
260
198
  displayName: "Pi",
261
199
  projectSkillsDir: ".pi/skills",
262
200
  globalSkillsDir: ".pi/agent/skills",
263
- projectPluginsDir: ".pi/plugins/cache",
264
- globalPluginsDir: ".pi/agent/plugins/cache",
265
201
  installMarkers: [".pi"]
266
202
  },
267
203
  replit: {
268
204
  displayName: "Replit",
269
205
  projectSkillsDir: ".agents/skills",
270
206
  globalSkillsDir: ".config/agents/skills",
271
- projectPluginsDir: ".agents/plugins/cache",
272
- globalPluginsDir: ".config/agents/plugins/cache",
273
207
  installMarkers: [".config/agents"]
274
208
  }
275
209
  };
@@ -279,64 +213,48 @@ var AGENTS = {
279
213
  displayName: "Codex",
280
214
  projectSkillsDir: ".agents/skills",
281
215
  globalSkillsDir: ".codex/skills",
282
- projectPluginsDir: ".agents/plugins/cache",
283
- globalPluginsDir: ".codex/plugins/cache",
284
216
  installMarkers: [".codex"]
285
217
  },
286
218
  cursor: {
287
219
  displayName: "Cursor",
288
220
  projectSkillsDir: ".agents/skills",
289
221
  globalSkillsDir: ".cursor/skills",
290
- projectPluginsDir: ".agents/plugins/cache",
291
- globalPluginsDir: ".cursor/plugins/cache",
292
222
  installMarkers: [".cursor"]
293
223
  },
294
224
  "gemini-cli": {
295
225
  displayName: "Gemini CLI",
296
226
  projectSkillsDir: ".agents/skills",
297
227
  globalSkillsDir: ".gemini/skills",
298
- projectPluginsDir: ".agents/plugins/cache",
299
- globalPluginsDir: ".gemini/plugins/cache",
300
228
  installMarkers: [".gemini"]
301
229
  },
302
230
  "github-copilot": {
303
231
  displayName: "GitHub Copilot",
304
232
  projectSkillsDir: ".agents/skills",
305
233
  globalSkillsDir: ".copilot/skills",
306
- projectPluginsDir: ".agents/plugins/cache",
307
- globalPluginsDir: ".copilot/plugins/cache",
308
234
  installMarkers: [".copilot"]
309
235
  },
310
236
  amp: {
311
237
  displayName: "Amp",
312
238
  projectSkillsDir: ".agents/skills",
313
239
  globalSkillsDir: ".config/agents/skills",
314
- projectPluginsDir: ".agents/plugins/cache",
315
- globalPluginsDir: ".config/agents/plugins/cache",
316
240
  installMarkers: [".config/agents"]
317
241
  },
318
242
  opencode: {
319
243
  displayName: "OpenCode",
320
244
  projectSkillsDir: ".agents/skills",
321
245
  globalSkillsDir: ".config/opencode/skills",
322
- projectPluginsDir: ".agents/plugins/cache",
323
- globalPluginsDir: ".config/opencode/plugins/cache",
324
246
  installMarkers: [".config/opencode"]
325
247
  },
326
248
  windsurf: {
327
249
  displayName: "Windsurf",
328
250
  projectSkillsDir: ".windsurf/skills",
329
251
  globalSkillsDir: ".codeium/windsurf/skills",
330
- projectPluginsDir: ".windsurf/plugins/cache",
331
- globalPluginsDir: ".codeium/windsurf/plugins/cache",
332
252
  installMarkers: [".windsurf", ".codeium/windsurf"]
333
253
  },
334
254
  cline: {
335
255
  displayName: "Cline",
336
256
  projectSkillsDir: ".cline/skills",
337
257
  globalSkillsDir: ".cline/skills",
338
- projectPluginsDir: ".cline/plugins/cache",
339
- globalPluginsDir: ".cline/plugins/cache",
340
258
  installMarkers: [".cline"]
341
259
  }
342
260
  };
@@ -369,11 +287,6 @@ function getAgentSkillsDir(agent, globalInstall, cwd) {
369
287
  const base = globalInstall ? os.homedir() : cwd;
370
288
  return path.join(base, relative);
371
289
  }
372
- function getAgentPluginsDir(agent, globalInstall, cwd) {
373
- const relative = globalInstall ? AGENTS[agent].globalPluginsDir : AGENTS[agent].projectPluginsDir;
374
- const base = globalInstall ? os.homedir() : cwd;
375
- return path.join(base, relative);
376
- }
377
290
  function detectInstalledAgents(cwd = process.cwd()) {
378
291
  const found = [];
379
292
  for (const agent of Object.keys(AGENTS)) {
@@ -909,27 +822,6 @@ var SKILL_CONFIG = {
909
822
  };
910
823
  }
911
824
  };
912
- var PLUGIN_CONFIG = {
913
- kind: "plugins",
914
- displayLabel: "well-known plugins",
915
- indexPath: "/.well-known/plugins/index.json",
916
- entryLabel: "plugin",
917
- requireDescription: false,
918
- missingManifestMessage: (name) => `Well-known plugin '${name}' is missing plugin.json`,
919
- hasRequiredManifest(filePath) {
920
- return filePath.split("/").at(-1)?.toLowerCase() === "plugin.json";
921
- },
922
- buildRemoteResult({ entry, files, sourceUrl }) {
923
- return {
924
- name: entry.name,
925
- description: entry.description || "",
926
- installName: entry.name,
927
- sourceUrl,
928
- sourceType: "well-known",
929
- files
930
- };
931
- }
932
- };
933
825
  var SecureWellKnownProvider = class {
934
826
  id = "well-known";
935
827
  displayName = "Secure Well-Known Skills";
@@ -955,9 +847,6 @@ var SecureWellKnownProvider = class {
955
847
  async fetchAllSkills(url, options = {}) {
956
848
  return this.fetchAllResources(url, options, SKILL_CONFIG);
957
849
  }
958
- async fetchAllPlugins(url, options = {}) {
959
- return this.fetchAllResources(url, options, PLUGIN_CONFIG);
960
- }
961
850
  async fetchAllResources(url, options, config) {
962
851
  const normalized = this.normalizeOptions(options);
963
852
  const budget = { remaining: normalized.maxDownloadBytes };
@@ -1249,33 +1138,6 @@ var HttpCatalogProvider = class {
1249
1138
  }
1250
1139
  });
1251
1140
  }
1252
- async fetchAllPlugins(url, options = {}) {
1253
- return this.fetchAllResources(url, options, {
1254
- kind: "plugins",
1255
- indexLabel: "catalog plugins",
1256
- resolveIndexUrl(parsed) {
1257
- return parsed.pathname.endsWith(".json") ? parsed.toString() : new URL(
1258
- "plugins/index.json",
1259
- parsed.toString().endsWith("/") ? parsed.toString() : `${parsed.toString()}/`
1260
- ).toString();
1261
- },
1262
- requireDescription: false,
1263
- missingManifestMessage: (name) => `Catalog plugin '${name}' is missing plugin.json`,
1264
- hasRequiredManifest(filePath) {
1265
- return filePath.split("/").at(-1)?.toLowerCase() === "plugin.json";
1266
- },
1267
- buildRemoteResult({ entry, files, sourceUrl }) {
1268
- return {
1269
- name: entry.name,
1270
- description: entry.description || "",
1271
- installName: entry.name,
1272
- sourceUrl,
1273
- sourceType: "catalog",
1274
- files
1275
- };
1276
- }
1277
- });
1278
- }
1279
1141
  async fetchAllResources(url, options, config) {
1280
1142
  const parsed = new URL(url);
1281
1143
  if (parsed.protocol !== "https:") {
@@ -1450,33 +1312,6 @@ async function resolveCatalogSkills(sourceUrl, options) {
1450
1312
  maxDownloadBytes: options.maxDownloadBytes
1451
1313
  });
1452
1314
  }
1453
- async function resolveWellKnownPlugins(sourceUrl, options) {
1454
- initializeProviders();
1455
- const provider = getProviderById("well-known");
1456
- if (!provider) {
1457
- throw new Error("Well-known provider is not registered");
1458
- }
1459
- const wellKnown = provider;
1460
- return wellKnown.fetchAllPlugins(sourceUrl, {
1461
- allowHosts: options.allowHost,
1462
- denyHosts: options.denyHost,
1463
- maxDownloadBytes: options.maxDownloadBytes
1464
- });
1465
- }
1466
- async function resolveCatalogPlugins(sourceUrl, options) {
1467
- assertExperimentalFeatureEnabled("catalog", Boolean(options.experimental));
1468
- initializeProviders();
1469
- const provider = getProviderById("catalog");
1470
- if (!provider) {
1471
- throw new Error("Catalog provider is not registered");
1472
- }
1473
- const catalog = provider;
1474
- return catalog.fetchAllPlugins(sourceUrl, {
1475
- allowHosts: options.allowHost,
1476
- denyHosts: options.denyHost,
1477
- maxDownloadBytes: options.maxDownloadBytes
1478
- });
1479
- }
1480
1315
 
1481
1316
  // ../../packages/core/src/runtime/hash.ts
1482
1317
  import fs4 from "node:fs";
@@ -1637,10 +1472,10 @@ function writePerSkillLockfile(canonicalDir, entry, format) {
1637
1472
  function isSkillDirEntry(entry) {
1638
1473
  return entry.isDirectory() || entry.isSymbolicLink();
1639
1474
  }
1640
- function listInstalledResourceDirs(kind, globalInstall, cwd) {
1475
+ function listInstalledResourceDirs(_kind, globalInstall, cwd) {
1641
1476
  const out = /* @__PURE__ */ new Set();
1642
1477
  for (const agent of Object.keys(AGENTS)) {
1643
- const resourceRoot = kind === "plugin" ? getAgentPluginsDir(agent, globalInstall, cwd) : getAgentSkillsDir(agent, globalInstall, cwd);
1478
+ const resourceRoot = getAgentSkillsDir(agent, globalInstall, cwd);
1644
1479
  if (!fs5.existsSync(resourceRoot) || !fs5.statSync(resourceRoot).isDirectory()) {
1645
1480
  continue;
1646
1481
  }
@@ -2028,693 +1863,203 @@ async function assessLockEntries(options, cwd, behavior = { keepResolved: false
2028
1863
  }
2029
1864
  }
2030
1865
 
2031
- // ../../packages/core/src/runtime/plugin-check-analysis.ts
1866
+ // ../../packages/core/src/runtime/validate-analysis.ts
1867
+ import fs9 from "node:fs";
1868
+ import path10 from "node:path";
1869
+ import os5 from "node:os";
1870
+ import matter2 from "gray-matter";
1871
+
1872
+ // ../../packages/core/src/runtime/skill-installer.ts
1873
+ import { spawnSync as spawnSync2 } from "node:child_process";
2032
1874
  import fs8 from "node:fs";
1875
+ import os4 from "node:os";
2033
1876
  import path9 from "node:path";
1877
+ import YAML2 from "yaml";
1878
+ import { z } from "zod";
2034
1879
 
2035
- // ../../packages/core/src/sources/plugins.ts
1880
+ // ../../packages/core/src/runtime/installer-security.ts
2036
1881
  import fs7 from "node:fs";
2037
1882
  import path8 from "node:path";
2038
- var SKIP_DIRS3 = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "__pycache__"]);
2039
- async function resolvePluginsRootAsync(basePath) {
2040
- let stat;
2041
- try {
2042
- stat = await fs7.promises.stat(basePath);
2043
- } catch {
2044
- throw new Error(`Local source not found: ${basePath}`);
2045
- }
2046
- if (stat.isDirectory() && path8.basename(basePath) === "plugins") {
2047
- return basePath;
1883
+ function isInsideRoot(rootDir, candidatePath) {
1884
+ const relative = path8.relative(rootDir, candidatePath);
1885
+ return Boolean(relative) && !relative.startsWith("..") && !path8.isAbsolute(relative);
1886
+ }
1887
+ function toEscapeViolation(source) {
1888
+ return {
1889
+ rule: "installer-local-dependency-path-escape",
1890
+ message: `local dependency escapes source root: ${source}`,
1891
+ severity: "error",
1892
+ blocking: true
1893
+ };
1894
+ }
1895
+ function evaluateInstallerLocalDependency(input, _options = {}) {
1896
+ if (path8.isAbsolute(input.source)) {
1897
+ return {
1898
+ ok: false,
1899
+ violation: {
1900
+ rule: "installer-local-dependency-absolute-path",
1901
+ message: `absolute local dependency paths are not allowed: ${input.source}`,
1902
+ severity: "error",
1903
+ blocking: true
1904
+ }
1905
+ };
2048
1906
  }
2049
- const pluginsDir = path8.join(basePath, "plugins");
2050
- try {
2051
- const pluginsStat = await fs7.promises.stat(pluginsDir);
2052
- if (pluginsStat.isDirectory()) {
2053
- return pluginsDir;
2054
- }
2055
- } catch {
1907
+ const resolvedRoot = path8.resolve(input.sourceRoot);
1908
+ const resolvedSourcePath = path8.resolve(resolvedRoot, input.source);
1909
+ if (!isInsideRoot(resolvedRoot, resolvedSourcePath)) {
1910
+ return {
1911
+ ok: false,
1912
+ violation: toEscapeViolation(input.source)
1913
+ };
2056
1914
  }
2057
- throw new Error("No plugins directory found in source");
2058
- }
2059
- async function collectPluginJsonFilesRecursiveAsync(dir, pluginRoot, out) {
2060
- const entries = await fs7.promises.readdir(dir, { withFileTypes: true });
2061
- for (const entry of entries) {
2062
- if (SKIP_DIRS3.has(entry.name)) {
2063
- continue;
2064
- }
2065
- const fullPath = path8.join(dir, entry.name);
2066
- if (entry.isDirectory()) {
2067
- await collectPluginJsonFilesRecursiveAsync(fullPath, pluginRoot, out);
2068
- continue;
2069
- }
2070
- if (entry.isFile() && path8.basename(fullPath).toLowerCase() === "plugin.json") {
2071
- out.push(path8.relative(pluginRoot, fullPath));
1915
+ if (fs7.existsSync(resolvedSourcePath)) {
1916
+ const realRoot = fs7.realpathSync.native ? fs7.realpathSync.native(resolvedRoot) : fs7.realpathSync(resolvedRoot);
1917
+ const realSource = fs7.realpathSync.native ? fs7.realpathSync.native(resolvedSourcePath) : fs7.realpathSync(resolvedSourcePath);
1918
+ if (!isInsideRoot(realRoot, realSource)) {
1919
+ return {
1920
+ ok: false,
1921
+ violation: toEscapeViolation(input.source)
1922
+ };
2072
1923
  }
2073
1924
  }
1925
+ return {
1926
+ ok: true,
1927
+ resolvedPath: resolvedSourcePath
1928
+ };
2074
1929
  }
2075
- async function parsePluginManifestAsync(manifestPath, pluginName) {
2076
- let parsed;
2077
- try {
2078
- parsed = JSON.parse(await fs7.promises.readFile(manifestPath, "utf8"));
2079
- } catch {
2080
- throw new Error(`Plugin '${pluginName}' has invalid plugin.json`);
2081
- }
2082
- if (!parsed || typeof parsed !== "object") {
2083
- throw new Error(`Plugin '${pluginName}' has invalid plugin.json`);
2084
- }
2085
- const data = parsed;
2086
- if (typeof data.name !== "string" || data.name.trim().length === 0) {
2087
- throw new Error(`Plugin '${pluginName}' plugin.json is missing name`);
1930
+
1931
+ // ../../packages/core/src/runtime/policy.ts
1932
+ function applyMode(result, mode) {
1933
+ if (mode === "enforce" || result.ok) {
1934
+ return result;
2088
1935
  }
2089
- if (data.name !== pluginName) {
2090
- throw new Error(`Plugin '${pluginName}' plugin.json name must match plugin folder name`);
1936
+ const violation = "violation" in result ? result.violation : void 0;
1937
+ if (!violation) {
1938
+ return result;
2091
1939
  }
2092
1940
  return {
2093
- name: data.name,
2094
- description: typeof data.description === "string" ? data.description : void 0
1941
+ ok: false,
1942
+ violation: {
1943
+ ...violation,
1944
+ severity: "warning",
1945
+ blocking: false
1946
+ }
2095
1947
  };
2096
1948
  }
2097
- function sortManifestCandidates(a, b) {
2098
- const depthA = a.split(path8.sep).length;
2099
- const depthB = b.split(path8.sep).length;
2100
- if (depthA !== depthB) {
2101
- return depthA - depthB;
2102
- }
2103
- return a.localeCompare(b);
1949
+ function evaluateInstallerLocalDependencyPolicy(input, mode) {
1950
+ return applyMode(evaluateInstallerLocalDependency(input, { policyMode: "fixed" }), mode);
2104
1951
  }
2105
- async function inspectPluginFolderAsync(pluginDir) {
2106
- const pluginName = path8.basename(pluginDir);
2107
- const pluginJsonRelativePaths = [];
2108
- await collectPluginJsonFilesRecursiveAsync(pluginDir, pluginDir, pluginJsonRelativePaths);
2109
- pluginJsonRelativePaths.sort(sortManifestCandidates);
2110
- if (pluginJsonRelativePaths.length === 0) {
2111
- return {
2112
- pluginName,
2113
- pluginDir,
2114
- error: new Error(`Plugin '${pluginName}' is missing plugin.json`)
2115
- };
1952
+ function evaluateHookTrustPolicy(input) {
1953
+ if (input.sourceType !== "well-known") {
1954
+ return { allowed: true };
2116
1955
  }
2117
- const manifests = [];
2118
- try {
2119
- for (const relativePath of pluginJsonRelativePaths) {
2120
- const manifest = await parsePluginManifestAsync(
2121
- path8.join(pluginDir, relativePath),
2122
- pluginName
2123
- );
2124
- manifests.push({
2125
- description: manifest.description || "",
2126
- relativePath
2127
- });
2128
- }
2129
- } catch (error) {
1956
+ if (input.trustWellKnown) {
1957
+ return { allowed: true };
1958
+ }
1959
+ if (input.mode === "warn") {
2130
1960
  return {
2131
- pluginName,
2132
- pluginDir,
2133
- error: error instanceof Error ? error : new Error(String(error))
1961
+ allowed: true,
1962
+ violation: {
1963
+ rule: "hook-trust-required",
1964
+ message: "Well-known hook commands are untrusted. Proceeding due to warning policy mode.",
1965
+ severity: "warning",
1966
+ blocking: false
1967
+ }
2134
1968
  };
2135
1969
  }
2136
- const selectedManifest = manifests[0];
2137
1970
  return {
2138
- pluginName,
2139
- pluginDir,
2140
- plugin: {
2141
- name: pluginName,
2142
- description: selectedManifest?.description || "",
2143
- path: pluginDir
1971
+ allowed: false,
1972
+ violation: {
1973
+ rule: "hook-trust-required",
1974
+ message: "Blocked skill-installer hook commands for well-known source. Trust this source explicitly or use warning policy mode.",
1975
+ severity: "error",
1976
+ blocking: true
2144
1977
  }
2145
1978
  };
2146
1979
  }
2147
- function filterRequestedPluginErrors(requestedPlugins, inspections) {
2148
- if (!requestedPlugins || requestedPlugins.length === 0) {
2149
- return;
2150
- }
2151
- if (requestedPlugins.includes("*")) {
2152
- return;
2153
- }
2154
- const inspectionByName = new Map(
2155
- inspections.map((inspection) => [inspection.pluginName, inspection])
2156
- );
2157
- for (const requestedPlugin of requestedPlugins) {
2158
- const inspection = inspectionByName.get(requestedPlugin);
2159
- if (inspection?.error) {
2160
- throw inspection.error;
2161
- }
1980
+
1981
+ // ../../packages/core/src/runtime/skill-installer.ts
1982
+ var InstallerSecurityError = class extends Error {
1983
+ violation;
1984
+ constructor(violation) {
1985
+ super(violation.message);
1986
+ this.name = "InstallerSecurityError";
1987
+ this.violation = violation;
2162
1988
  }
1989
+ };
1990
+ function isInstallerSecurityError(error) {
1991
+ return error instanceof InstallerSecurityError;
2163
1992
  }
2164
- async function discoverPluginsAsync(basePath, requestedPlugins) {
2165
- const pluginsRoot = await resolvePluginsRootAsync(basePath);
2166
- const entries = await fs7.promises.readdir(pluginsRoot, {
2167
- withFileTypes: true
2168
- });
2169
- const inspections = await Promise.all(
2170
- entries.filter((entry) => entry.isDirectory()).map((entry) => inspectPluginFolderAsync(path8.join(pluginsRoot, entry.name)))
2171
- );
2172
- filterRequestedPluginErrors(requestedPlugins, inspections);
2173
- return filterPluginsByName(
2174
- inspections.map((inspection) => inspection.plugin).filter((plugin) => Boolean(plugin)),
2175
- requestedPlugins
2176
- );
2177
- }
2178
- function filterPluginsByName(plugins, requested) {
2179
- if (!requested || requested.length === 0) {
2180
- return plugins;
2181
- }
2182
- if (requested.includes("*")) {
2183
- return plugins;
1993
+ var InstallerPolicyError = class extends Error {
1994
+ violation;
1995
+ constructor(violation) {
1996
+ super(violation.message);
1997
+ this.name = "InstallerPolicyError";
1998
+ this.violation = violation;
2184
1999
  }
2185
- const wanted = new Set(requested.map((item) => item.toLowerCase()));
2186
- return plugins.filter((plugin) => wanted.has(plugin.name.toLowerCase()));
2000
+ };
2001
+ function isInstallerPolicyError(error) {
2002
+ return error instanceof InstallerPolicyError;
2187
2003
  }
2188
- function stageRemotePluginFilesToTempDir(pluginName, files) {
2189
- const prefixed = /* @__PURE__ */ new Map();
2190
- for (const [relativePath, content] of files.entries()) {
2191
- prefixed.set(path8.join("plugins", pluginName, relativePath), content);
2004
+ var dependencyObjectSchema = z.object({
2005
+ source: z.string().min(1),
2006
+ path: z.string().min(1)
2007
+ }).strict();
2008
+ var installerConfigSchema = z.object({
2009
+ schemaVersion: z.literal(1),
2010
+ "pre-install": z.array(z.string().min(1)).optional().default([]),
2011
+ dependencies: z.array(z.union([z.string().min(1), dependencyObjectSchema])).optional().default([]),
2012
+ "post-install": z.array(z.string().min(1)).optional().default([])
2013
+ }).strict();
2014
+ function ensureInsideRoot(rootDir, relativeTarget) {
2015
+ const resolved = path9.resolve(rootDir, relativeTarget);
2016
+ const relative = path9.relative(rootDir, resolved);
2017
+ if (!relative || relative.startsWith("..") || path9.isAbsolute(relative)) {
2018
+ throw new Error(`Unsafe destination path: ${relativeTarget}`);
2192
2019
  }
2193
- return stageRemoteSkillFilesToTempDir(prefixed, {
2194
- prefix: "skillspp-remote-plugin-"
2195
- });
2020
+ return resolved;
2196
2021
  }
2197
-
2198
- // ../../packages/core/src/runtime/plugin-check-analysis.ts
2199
- function includeByPlugin(entry, selected) {
2200
- if (!selected || selected.length === 0 || selected.includes("*")) {
2201
- return true;
2022
+ function sourceLooksLikeUrl(source) {
2023
+ try {
2024
+ const parsed = new URL(source);
2025
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
2026
+ } catch {
2027
+ return false;
2202
2028
  }
2203
- return selected.includes(entry.skillName);
2204
2029
  }
2205
- function migrateHint2(pluginName) {
2206
- return `pluginspp update ${pluginName} --migrate <new-plugin-source>`;
2030
+ function sourceLooksLikeRepoShorthand(source) {
2031
+ const trimmed = source.trim().replace(/^https?:\/\//, "");
2032
+ return /^(github\.com|gitlab\.com)\/[^/]+\/[^/]+(?:\.git)?\/?$/.test(trimmed);
2207
2033
  }
2208
- function resolveSourceMetadataIssue2(entry) {
2209
- if (!entry.source.canonical) {
2210
- return `source canonical metadata missing; run ${migrateHint2(entry.skillName)}`;
2034
+ function parseRepoSource(source) {
2035
+ const withoutProtocol = source.trim().replace(/^https?:\/\//, "").replace(/\/+$/, "");
2036
+ const match = withoutProtocol.match(/^(github\.com|gitlab\.com)\/([^/]+)\/([^/]+?)(?:\.git)?$/);
2037
+ if (!match) {
2038
+ throw new Error(`Unsupported repository dependency source: ${source}`);
2211
2039
  }
2212
- if (entry.source.type === "local") {
2213
- if (!entry.source.resolvedPath) {
2214
- return `local source metadata missing; run ${migrateHint2(entry.skillName)}`;
2215
- }
2216
- if (!fs8.existsSync(entry.source.canonical)) {
2217
- return `local source path not found; run ${migrateHint2(entry.skillName)}`;
2040
+ const host = match[1];
2041
+ const owner = match[2];
2042
+ const repo = match[3];
2043
+ return {
2044
+ repoUrl: `https://${host}/${owner}/${repo}.git`,
2045
+ repoName: repo
2046
+ };
2047
+ }
2048
+ function deriveDestinationNameFromSource(source) {
2049
+ if (sourceLooksLikeUrl(source)) {
2050
+ const parsed = new URL(source);
2051
+ const parts = parsed.pathname.split("/").filter(Boolean);
2052
+ const leaf2 = parts[parts.length - 1];
2053
+ if (!leaf2) {
2054
+ throw new Error(`Cannot derive dependency name from URL source: ${source}`);
2218
2055
  }
2219
- return null;
2220
- }
2221
- if (!entry.source.pinnedRef) {
2222
- return `remote pin metadata missing; run ${migrateHint2(entry.skillName)}`;
2223
- }
2224
- return null;
2225
- }
2226
- function createRetainedCleanup2(cleanup) {
2227
- let cleaned = false;
2228
- let refCount = 0;
2229
- const runCleanup = () => {
2230
- if (cleaned) {
2231
- return;
2232
- }
2233
- cleaned = true;
2234
- cleanup?.();
2235
- };
2236
- const cleanupNow = () => {
2237
- if (refCount === 0) {
2238
- runCleanup();
2239
- }
2240
- };
2241
- const retainCleanup = () => {
2242
- refCount += 1;
2243
- let released = false;
2244
- return () => {
2245
- if (released) {
2246
- return;
2247
- }
2248
- released = true;
2249
- refCount -= 1;
2250
- if (refCount === 0) {
2251
- runCleanup();
2252
- }
2253
- };
2254
- };
2255
- return {
2256
- cleanupNow,
2257
- retainCleanup
2258
- };
2259
- }
2260
- function toAsyncResult2(promise) {
2261
- return promise.then(
2262
- (value) => ({ ok: true, value }),
2263
- (error) => ({ ok: false, error })
2264
- );
2265
- }
2266
- function unwrapAsyncResult2(result) {
2267
- if (result.ok) {
2268
- return result.value;
2269
- }
2270
- throw result.error;
2271
- }
2272
- async function loadCachedSource2(entry, options) {
2273
- const sourceInput = resolveSourceLoadInput(entry.source);
2274
- const parsed = parseSource(sourceInput);
2275
- if (parsed.type === "well-known" || parsed.type === "catalog") {
2276
- const remotePlugins = parsed.type === "well-known" ? await resolveWellKnownPlugins(parsed.url, options) : await resolveCatalogPlugins(parsed.url, options);
2277
- return {
2278
- kind: "remote",
2279
- remotePlugins
2280
- };
2281
- }
2282
- const prepared = await prepareSourceDirAsync(
2283
- parsed
2284
- );
2285
- const retainedCleanup = createRetainedCleanup2(prepared.cleanup);
2286
- try {
2287
- const plugins = await discoverPluginsAsync(prepared.basePath);
2288
- return {
2289
- kind: "prepared",
2290
- basePath: prepared.basePath,
2291
- plugins,
2292
- cleanupNow: retainedCleanup.cleanupNow,
2293
- retainCleanup: retainedCleanup.retainCleanup
2294
- };
2295
- } catch (error) {
2296
- retainedCleanup.cleanupNow();
2297
- throw error;
2298
- }
2299
- }
2300
- function resolveSafeRealPath2(inputPath) {
2301
- try {
2302
- return fs8.realpathSync(inputPath);
2303
- } catch {
2304
- return path9.resolve(inputPath);
2305
- }
2306
- }
2307
- function isLocalSymlinkSource2(localPath) {
2308
- try {
2309
- if (!fs8.existsSync(localPath)) {
2310
- return false;
2311
- }
2312
- return fs8.lstatSync(localPath).isSymbolicLink();
2313
- } catch {
2314
- return false;
2315
- }
2316
- }
2317
- async function buildRefreshedSourceMetadata2(entry, resolved, cachedSource, sourceHash) {
2318
- if (entry.source.type === "local") {
2319
- const canonical2 = entry.source.canonical || entry.source.input;
2320
- return {
2321
- canonical: canonical2,
2322
- resolvedPath: resolveSafeRealPath2(resolved.plugin.path),
2323
- isSymlinkSource: isLocalSymlinkSource2(canonical2),
2324
- sourcePluginPath: resolved.sourcePluginPath
2325
- };
2326
- }
2327
- if (entry.source.type === "git" || entry.source.type === "github") {
2328
- const nextPinnedRef = cachedSource.kind === "prepared" ? await resolveGitHeadRefAsync(cachedSource.basePath) : entry.source.pinnedRef;
2329
- return {
2330
- canonical: entry.source.canonical,
2331
- pinnedRef: nextPinnedRef,
2332
- sourcePluginPath: resolved.sourcePluginPath
2333
- };
2334
- }
2335
- const canonical = resolved.wellKnownSourceUrl || entry.source.canonical;
2336
- return {
2337
- canonical,
2338
- pinnedRef: sourceHash,
2339
- wellKnownSourceUrl: resolved.wellKnownSourceUrl
2340
- };
2341
- }
2342
- function resolveCandidateFromCachedSource2(entry, cachedSource, keepResolved) {
2343
- if (cachedSource.kind === "remote") {
2344
- const matched2 = cachedSource.remotePlugins.find(
2345
- (item) => item.installName === entry.source.selector.skillName
2346
- );
2347
- if (!matched2) {
2348
- throw new Error(`Plugin '${entry.source.selector.skillName}' not found in well-known source`);
2349
- }
2350
- const staged = stageRemotePluginFilesToTempDir(matched2.installName, matched2.files);
2351
- return {
2352
- plugin: {
2353
- name: matched2.installName,
2354
- description: matched2.description,
2355
- path: path9.join(staged.path, "plugins", matched2.installName)
2356
- },
2357
- wellKnownSourceUrl: matched2.sourceUrl,
2358
- cleanup: staged.cleanup
2359
- };
2360
- }
2361
- const matched = entry.source.selector.relativePath ? cachedSource.plugins.find(
2362
- (item) => path9.resolve(item.path) === path9.resolve(path9.join(cachedSource.basePath, entry.source.selector.relativePath))
2363
- ) : cachedSource.plugins.find((item) => item.name === entry.source.selector.skillName);
2364
- if (!matched) {
2365
- throw new Error(`Plugin '${entry.source.selector.skillName}' not found in source`);
2366
- }
2367
- return {
2368
- plugin: matched,
2369
- sourcePluginPath: path9.relative(cachedSource.basePath, matched.path) || ".",
2370
- cleanup: keepResolved ? cachedSource.retainCleanup() : void 0
2371
- };
2372
- }
2373
- async function assessPluginLockEntries(options, cwd, behavior = { keepResolved: false }) {
2374
- const lock = readResourceLockfile("plugin", Boolean(options.global), cwd);
2375
- const entries = lock.entries.filter((entry) => includeByPlugin(entry, options.plugin));
2376
- const drift = [];
2377
- const assessments = [];
2378
- const sourceOptions = {
2379
- global: options.global,
2380
- allowHost: options.allowHost,
2381
- denyHost: options.denyHost,
2382
- maxDownloadBytes: options.maxDownloadBytes,
2383
- policyMode: options.policyMode,
2384
- experimental: options.experimental
2385
- };
2386
- const sourceCache = /* @__PURE__ */ new Map();
2387
- const getCachedSource = (entry) => {
2388
- const key = buildSourceLoadCacheKey(entry.source);
2389
- const existing = sourceCache.get(key);
2390
- if (existing) {
2391
- return existing;
2392
- }
2393
- const created = loadCachedSource2(entry, sourceOptions);
2394
- sourceCache.set(key, created);
2395
- return created;
2396
- };
2397
- try {
2398
- for (const entry of entries) {
2399
- const assessment = {
2400
- entry,
2401
- drift: []
2402
- };
2403
- if (!fs8.existsSync(entry.canonicalDir) || !fs8.statSync(entry.canonicalDir).isDirectory()) {
2404
- const row = {
2405
- skillName: entry.skillName,
2406
- kind: "local-modified",
2407
- detail: "canonical directory is missing"
2408
- };
2409
- drift.push(row);
2410
- assessment.drift.push(row);
2411
- assessments.push(assessment);
2412
- continue;
2413
- }
2414
- const installedHash = unwrapAsyncResult2(
2415
- await toAsyncResult2(hashDirectoryAsync(entry.canonicalDir))
2416
- );
2417
- if (installedHash !== entry.installedHash) {
2418
- const row = {
2419
- skillName: entry.skillName,
2420
- kind: "local-modified",
2421
- detail: "installed content differs from lockfile hash"
2422
- };
2423
- drift.push(row);
2424
- assessment.drift.push(row);
2425
- }
2426
- const metadataIssue = resolveSourceMetadataIssue2(entry);
2427
- if (metadataIssue) {
2428
- const row = {
2429
- skillName: entry.skillName,
2430
- kind: "migrate-required",
2431
- detail: metadataIssue
2432
- };
2433
- drift.push(row);
2434
- assessment.drift.push(row);
2435
- assessments.push(assessment);
2436
- continue;
2437
- }
2438
- let resolved;
2439
- try {
2440
- const cachedSource = unwrapAsyncResult2(await toAsyncResult2(getCachedSource(entry)));
2441
- resolved = resolveCandidateFromCachedSource2(entry, cachedSource, behavior.keepResolved);
2442
- if (entry.source.type === "local") {
2443
- const currentResolvedPath = fs8.realpathSync(resolved.plugin.path);
2444
- if (currentResolvedPath !== path9.resolve(entry.source.resolvedPath)) {
2445
- const row = {
2446
- skillName: entry.skillName,
2447
- kind: "migrate-required",
2448
- detail: `local source identity changed; run ${migrateHint2(entry.skillName)}`
2449
- };
2450
- drift.push(row);
2451
- assessment.drift.push(row);
2452
- if (resolved.cleanup && !behavior.keepResolved) {
2453
- resolved.cleanup();
2454
- resolved = void 0;
2455
- }
2456
- assessments.push(assessment);
2457
- continue;
2458
- }
2459
- }
2460
- const sourceHash = await hashDirectoryAsync(resolved.plugin.path);
2461
- assessment.sourceHash = sourceHash;
2462
- assessment.refreshedSource = await buildRefreshedSourceMetadata2(
2463
- entry,
2464
- resolved,
2465
- cachedSource,
2466
- sourceHash
2467
- );
2468
- if (sourceHash !== entry.sourceHash) {
2469
- const row = {
2470
- skillName: entry.skillName,
2471
- kind: "changed-source",
2472
- detail: "source hash changed"
2473
- };
2474
- drift.push(row);
2475
- assessment.drift.push(row);
2476
- }
2477
- if (behavior.keepResolved) {
2478
- assessment.resolved = resolved;
2479
- }
2480
- } catch (error) {
2481
- const asText = error instanceof Error ? error.message : String(error);
2482
- const migrateRequired = entry.source.type === "local" && (asText.includes("Local source not found") || asText.includes("not found in source"));
2483
- const row = {
2484
- skillName: entry.skillName,
2485
- kind: migrateRequired ? "migrate-required" : "missing-source",
2486
- detail: migrateRequired ? `local source identity changed; run ${migrateHint2(entry.skillName)}` : asText
2487
- };
2488
- drift.push(row);
2489
- assessment.drift.push(row);
2490
- } finally {
2491
- if (resolved?.cleanup && !behavior.keepResolved) {
2492
- resolved.cleanup();
2493
- }
2494
- }
2495
- assessments.push(assessment);
2496
- }
2497
- const canonical = listCanonicalResourceDirs("plugin", Boolean(options.global), cwd);
2498
- const lockNames = new Set(lock.entries.map((item) => item.skillName));
2499
- for (const pluginName of canonical) {
2500
- if (!lockNames.has(pluginName) && includeByPlugin({ skillName: pluginName }, options.plugin)) {
2501
- drift.push({
2502
- skillName: pluginName,
2503
- kind: "lock-missing",
2504
- detail: "installed plugin is not tracked in lockfile"
2505
- });
2506
- }
2507
- }
2508
- return { drift, checked: entries.length, assessments };
2509
- } finally {
2510
- if (!behavior.keepResolved) {
2511
- for (const cachedSourcePromise of sourceCache.values()) {
2512
- const cachedSource = await cachedSourcePromise.catch(() => null);
2513
- if (cachedSource?.kind === "prepared") {
2514
- cachedSource.cleanupNow();
2515
- }
2516
- }
2517
- }
2518
- }
2519
- }
2520
-
2521
- // ../../packages/core/src/runtime/validate-analysis.ts
2522
- import fs11 from "node:fs";
2523
- import path12 from "node:path";
2524
- import os5 from "node:os";
2525
- import matter2 from "gray-matter";
2526
-
2527
- // ../../packages/core/src/runtime/skill-installer.ts
2528
- import { spawnSync as spawnSync2 } from "node:child_process";
2529
- import fs10 from "node:fs";
2530
- import os4 from "node:os";
2531
- import path11 from "node:path";
2532
- import YAML2 from "yaml";
2533
- import { z } from "zod";
2534
-
2535
- // ../../packages/core/src/runtime/installer-security.ts
2536
- import fs9 from "node:fs";
2537
- import path10 from "node:path";
2538
- function isInsideRoot(rootDir, candidatePath) {
2539
- const relative = path10.relative(rootDir, candidatePath);
2540
- return Boolean(relative) && !relative.startsWith("..") && !path10.isAbsolute(relative);
2541
- }
2542
- function toEscapeViolation(source) {
2543
- return {
2544
- rule: "installer-local-dependency-path-escape",
2545
- message: `local dependency escapes source root: ${source}`,
2546
- severity: "error",
2547
- blocking: true
2548
- };
2549
- }
2550
- function evaluateInstallerLocalDependency(input, _options = {}) {
2551
- if (path10.isAbsolute(input.source)) {
2552
- return {
2553
- ok: false,
2554
- violation: {
2555
- rule: "installer-local-dependency-absolute-path",
2556
- message: `absolute local dependency paths are not allowed: ${input.source}`,
2557
- severity: "error",
2558
- blocking: true
2559
- }
2560
- };
2561
- }
2562
- const resolvedRoot = path10.resolve(input.sourceRoot);
2563
- const resolvedSourcePath = path10.resolve(resolvedRoot, input.source);
2564
- if (!isInsideRoot(resolvedRoot, resolvedSourcePath)) {
2565
- return {
2566
- ok: false,
2567
- violation: toEscapeViolation(input.source)
2568
- };
2569
- }
2570
- if (fs9.existsSync(resolvedSourcePath)) {
2571
- const realRoot = fs9.realpathSync.native ? fs9.realpathSync.native(resolvedRoot) : fs9.realpathSync(resolvedRoot);
2572
- const realSource = fs9.realpathSync.native ? fs9.realpathSync.native(resolvedSourcePath) : fs9.realpathSync(resolvedSourcePath);
2573
- if (!isInsideRoot(realRoot, realSource)) {
2574
- return {
2575
- ok: false,
2576
- violation: toEscapeViolation(input.source)
2577
- };
2578
- }
2579
- }
2580
- return {
2581
- ok: true,
2582
- resolvedPath: resolvedSourcePath
2583
- };
2584
- }
2585
-
2586
- // ../../packages/core/src/runtime/policy.ts
2587
- function applyMode(result, mode) {
2588
- if (mode === "enforce" || result.ok) {
2589
- return result;
2590
- }
2591
- const violation = "violation" in result ? result.violation : void 0;
2592
- if (!violation) {
2593
- return result;
2594
- }
2595
- return {
2596
- ok: false,
2597
- violation: {
2598
- ...violation,
2599
- severity: "warning",
2600
- blocking: false
2601
- }
2602
- };
2603
- }
2604
- function evaluateInstallerLocalDependencyPolicy(input, mode) {
2605
- return applyMode(evaluateInstallerLocalDependency(input, { policyMode: "fixed" }), mode);
2606
- }
2607
- function evaluateHookTrustPolicy(input) {
2608
- if (input.sourceType !== "well-known") {
2609
- return { allowed: true };
2610
- }
2611
- if (input.trustWellKnown) {
2612
- return { allowed: true };
2613
- }
2614
- if (input.mode === "warn") {
2615
- return {
2616
- allowed: true,
2617
- violation: {
2618
- rule: "hook-trust-required",
2619
- message: "Well-known hook commands are untrusted. Proceeding due to warning policy mode.",
2620
- severity: "warning",
2621
- blocking: false
2622
- }
2623
- };
2624
- }
2625
- return {
2626
- allowed: false,
2627
- violation: {
2628
- rule: "hook-trust-required",
2629
- message: "Blocked skill-installer hook commands for well-known source. Trust this source explicitly or use warning policy mode.",
2630
- severity: "error",
2631
- blocking: true
2632
- }
2633
- };
2634
- }
2635
-
2636
- // ../../packages/core/src/runtime/skill-installer.ts
2637
- var InstallerSecurityError = class extends Error {
2638
- violation;
2639
- constructor(violation) {
2640
- super(violation.message);
2641
- this.name = "InstallerSecurityError";
2642
- this.violation = violation;
2643
- }
2644
- };
2645
- function isInstallerSecurityError(error) {
2646
- return error instanceof InstallerSecurityError;
2647
- }
2648
- var InstallerPolicyError = class extends Error {
2649
- violation;
2650
- constructor(violation) {
2651
- super(violation.message);
2652
- this.name = "InstallerPolicyError";
2653
- this.violation = violation;
2654
- }
2655
- };
2656
- function isInstallerPolicyError(error) {
2657
- return error instanceof InstallerPolicyError;
2658
- }
2659
- var dependencyObjectSchema = z.object({
2660
- source: z.string().min(1),
2661
- path: z.string().min(1)
2662
- }).strict();
2663
- var installerConfigSchema = z.object({
2664
- schemaVersion: z.literal(1),
2665
- "pre-install": z.array(z.string().min(1)).optional().default([]),
2666
- dependencies: z.array(z.union([z.string().min(1), dependencyObjectSchema])).optional().default([]),
2667
- "post-install": z.array(z.string().min(1)).optional().default([])
2668
- }).strict();
2669
- function ensureInsideRoot(rootDir, relativeTarget) {
2670
- const resolved = path11.resolve(rootDir, relativeTarget);
2671
- const relative = path11.relative(rootDir, resolved);
2672
- if (!relative || relative.startsWith("..") || path11.isAbsolute(relative)) {
2673
- throw new Error(`Unsafe destination path: ${relativeTarget}`);
2674
- }
2675
- return resolved;
2676
- }
2677
- function sourceLooksLikeUrl(source) {
2678
- try {
2679
- const parsed = new URL(source);
2680
- return parsed.protocol === "http:" || parsed.protocol === "https:";
2681
- } catch {
2682
- return false;
2683
- }
2684
- }
2685
- function sourceLooksLikeRepoShorthand(source) {
2686
- const trimmed = source.trim().replace(/^https?:\/\//, "");
2687
- return /^(github\.com|gitlab\.com)\/[^/]+\/[^/]+(?:\.git)?\/?$/.test(trimmed);
2688
- }
2689
- function parseRepoSource(source) {
2690
- const withoutProtocol = source.trim().replace(/^https?:\/\//, "").replace(/\/+$/, "");
2691
- const match = withoutProtocol.match(/^(github\.com|gitlab\.com)\/([^/]+)\/([^/]+?)(?:\.git)?$/);
2692
- if (!match) {
2693
- throw new Error(`Unsupported repository dependency source: ${source}`);
2694
- }
2695
- const host = match[1];
2696
- const owner = match[2];
2697
- const repo = match[3];
2698
- return {
2699
- repoUrl: `https://${host}/${owner}/${repo}.git`,
2700
- repoName: repo
2701
- };
2702
- }
2703
- function deriveDestinationNameFromSource(source) {
2704
- if (sourceLooksLikeUrl(source)) {
2705
- const parsed = new URL(source);
2706
- const parts = parsed.pathname.split("/").filter(Boolean);
2707
- const leaf2 = parts[parts.length - 1];
2708
- if (!leaf2) {
2709
- throw new Error(`Cannot derive dependency name from URL source: ${source}`);
2710
- }
2711
- return leaf2;
2056
+ return leaf2;
2712
2057
  }
2713
2058
  if (sourceLooksLikeRepoShorthand(source)) {
2714
2059
  return parseRepoSource(source).repoName;
2715
2060
  }
2716
- const leaf = path11.basename(source);
2717
- if (!leaf || leaf === "." || leaf === path11.sep) {
2061
+ const leaf = path9.basename(source);
2062
+ if (!leaf || leaf === "." || leaf === path9.sep) {
2718
2063
  throw new Error(`Cannot derive dependency name from source: ${source}`);
2719
2064
  }
2720
2065
  return leaf;
@@ -2770,11 +2115,11 @@ function parseConfigObject(raw) {
2770
2115
  };
2771
2116
  }
2772
2117
  function assertNoLegacyInstallerBlock(skillDir) {
2773
- const openAiYamlPath = path11.join(skillDir, "agents", "openai.yaml");
2774
- if (!fs10.existsSync(openAiYamlPath) || !fs10.statSync(openAiYamlPath).isFile()) {
2118
+ const openAiYamlPath = path9.join(skillDir, "agents", "openai.yaml");
2119
+ if (!fs8.existsSync(openAiYamlPath) || !fs8.statSync(openAiYamlPath).isFile()) {
2775
2120
  return;
2776
2121
  }
2777
- const parsed = YAML2.parse(fs10.readFileSync(openAiYamlPath, "utf8"));
2122
+ const parsed = YAML2.parse(fs8.readFileSync(openAiYamlPath, "utf8"));
2778
2123
  if (parsed && typeof parsed === "object" && typeof parsed["skill-installer"] !== "undefined") {
2779
2124
  throw new Error(
2780
2125
  "Legacy skill-installer config detected in agents/openai.yaml. Move it to skill-installer.yaml or skill-installer.json."
@@ -2783,10 +2128,10 @@ function assertNoLegacyInstallerBlock(skillDir) {
2783
2128
  }
2784
2129
  function loadInstallerConfig(skillDir) {
2785
2130
  assertNoLegacyInstallerBlock(skillDir);
2786
- const yamlPath = path11.join(skillDir, "skill-installer.yaml");
2787
- const jsonPath = path11.join(skillDir, "skill-installer.json");
2788
- const hasYaml = fs10.existsSync(yamlPath) && fs10.statSync(yamlPath).isFile();
2789
- const hasJson = fs10.existsSync(jsonPath) && fs10.statSync(jsonPath).isFile();
2131
+ const yamlPath = path9.join(skillDir, "skill-installer.yaml");
2132
+ const jsonPath = path9.join(skillDir, "skill-installer.json");
2133
+ const hasYaml = fs8.existsSync(yamlPath) && fs8.statSync(yamlPath).isFile();
2134
+ const hasJson = fs8.existsSync(jsonPath) && fs8.statSync(jsonPath).isFile();
2790
2135
  if (hasYaml && hasJson) {
2791
2136
  throw new Error(
2792
2137
  "Both skill-installer.yaml and skill-installer.json exist. Keep only one installer config file."
@@ -2801,10 +2146,10 @@ function loadInstallerConfig(skillDir) {
2801
2146
  };
2802
2147
  }
2803
2148
  if (hasYaml) {
2804
- const parsed = YAML2.parse(fs10.readFileSync(yamlPath, "utf8"));
2149
+ const parsed = YAML2.parse(fs8.readFileSync(yamlPath, "utf8"));
2805
2150
  return parseConfigObject(parsed);
2806
2151
  }
2807
- const raw = JSON.parse(fs10.readFileSync(jsonPath, "utf8"));
2152
+ const raw = JSON.parse(fs8.readFileSync(jsonPath, "utf8"));
2808
2153
  return parseConfigObject(raw);
2809
2154
  }
2810
2155
  function runHookPhase(phase, commands, cwd, events) {
@@ -2869,11 +2214,11 @@ async function prepareDependency(source, targetPath, index, sourceCwd, stagingDi
2869
2214
  message: `[skills] ${violation.message}`
2870
2215
  });
2871
2216
  }
2872
- const sourcePath = evaluation.ok ? evaluation.resolvedPath : path11.resolve(sourceCwd, source);
2873
- if (!fs10.existsSync(sourcePath)) {
2217
+ const sourcePath = evaluation.ok ? evaluation.resolvedPath : path9.resolve(sourceCwd, source);
2218
+ if (!fs8.existsSync(sourcePath)) {
2874
2219
  throw new Error(`Dependency[${index}] (local) source not found: ${source}`);
2875
2220
  }
2876
- fs10.accessSync(sourcePath, fs10.constants.R_OK);
2221
+ fs8.accessSync(sourcePath, fs8.constants.R_OK);
2877
2222
  events.push({
2878
2223
  level: "info",
2879
2224
  message: `[skills] dependency[${index}] preflight-validated: ${source}`
@@ -2889,7 +2234,7 @@ async function prepareDependency(source, targetPath, index, sourceCwd, stagingDi
2889
2234
  if (sourceKind === "repo") {
2890
2235
  const stagePath = ensureInsideRoot(
2891
2236
  stagingDir,
2892
- `repo-${index}-${path11.basename(targetPath).replace(/[^a-zA-Z0-9._-]/g, "_")}`
2237
+ `repo-${index}-${path9.basename(targetPath).replace(/[^a-zA-Z0-9._-]/g, "_")}`
2893
2238
  );
2894
2239
  const { repoUrl } = parseRepoSource(source);
2895
2240
  runGitClone(repoUrl, stagePath);
@@ -2917,10 +2262,10 @@ async function prepareDependency(source, targetPath, index, sourceCwd, stagingDi
2917
2262
  }
2918
2263
  const stagedFilePath = ensureInsideRoot(
2919
2264
  stagingDir,
2920
- `remote-${index}-${path11.basename(targetPath)}`
2265
+ `remote-${index}-${path9.basename(targetPath)}`
2921
2266
  );
2922
- fs10.mkdirSync(path11.dirname(stagedFilePath), { recursive: true });
2923
- fs10.writeFileSync(stagedFilePath, Buffer.from(await response.arrayBuffer()));
2267
+ fs8.mkdirSync(path9.dirname(stagedFilePath), { recursive: true });
2268
+ fs8.writeFileSync(stagedFilePath, Buffer.from(await response.arrayBuffer()));
2924
2269
  events.push({
2925
2270
  level: "info",
2926
2271
  message: `[skills] dependency[${index}] staged: ${source}`
@@ -2969,7 +2314,7 @@ async function prepareInstallerArtifacts(skillDir, sourceCwd, options = {}) {
2969
2314
  events
2970
2315
  };
2971
2316
  }
2972
- const stagingDir = fs10.mkdtempSync(path11.join(os4.tmpdir(), "skillspp-installer-stage-"));
2317
+ const stagingDir = fs8.mkdtempSync(path9.join(os4.tmpdir(), "skillspp-installer-stage-"));
2973
2318
  const preparedDependencies = [];
2974
2319
  const seenTargetPaths = /* @__PURE__ */ new Set();
2975
2320
  try {
@@ -2982,9 +2327,9 @@ async function prepareInstallerArtifacts(skillDir, sourceCwd, options = {}) {
2982
2327
  }
2983
2328
  seenTargetPaths.add(normalizedTarget);
2984
2329
  const destinationInSkill = ensureInsideRoot(skillDir, targetPath);
2985
- if (fs10.existsSync(destinationInSkill)) {
2330
+ if (fs8.existsSync(destinationInSkill)) {
2986
2331
  throw new Error(
2987
- `Dependency[${i}] destination already exists: ${path11.relative(
2332
+ `Dependency[${i}] destination already exists: ${path9.relative(
2988
2333
  skillDir,
2989
2334
  destinationInSkill
2990
2335
  )}`
@@ -3009,13 +2354,13 @@ async function prepareInstallerArtifacts(skillDir, sourceCwd, options = {}) {
3009
2354
  events
3010
2355
  };
3011
2356
  } catch (error) {
3012
- fs10.rmSync(stagingDir, { recursive: true, force: true });
2357
+ fs8.rmSync(stagingDir, { recursive: true, force: true });
3013
2358
  throw error;
3014
2359
  }
3015
2360
  }
3016
2361
  function cleanupPreparedInstallerArtifacts(prepared) {
3017
2362
  if (prepared.stagingDir) {
3018
- fs10.rmSync(prepared.stagingDir, { recursive: true, force: true });
2363
+ fs8.rmSync(prepared.stagingDir, { recursive: true, force: true });
3019
2364
  }
3020
2365
  }
3021
2366
  async function applyInstallerArtifacts(installedSkillDir, prepared) {
@@ -3025,37 +2370,37 @@ async function applyInstallerArtifacts(installedSkillDir, prepared) {
3025
2370
  runHookPhase("pre-install", prepared.preInstall, installedSkillDir, prepared.events);
3026
2371
  for (const dep of prepared.preparedDependencies) {
3027
2372
  const destinationPath = ensureInsideRoot(installedSkillDir, dep.targetPath);
3028
- if (fs10.existsSync(destinationPath)) {
2373
+ if (fs8.existsSync(destinationPath)) {
3029
2374
  throw new Error(
3030
- `Dependency[${dep.index}] destination already exists: ${path11.relative(
2375
+ `Dependency[${dep.index}] destination already exists: ${path9.relative(
3031
2376
  installedSkillDir,
3032
2377
  destinationPath
3033
2378
  )}`
3034
2379
  );
3035
2380
  }
3036
- fs10.mkdirSync(path11.dirname(destinationPath), { recursive: true });
2381
+ fs8.mkdirSync(path9.dirname(destinationPath), { recursive: true });
3037
2382
  if (dep.kind === "local") {
3038
- const stat = fs10.statSync(dep.sourcePath);
2383
+ const stat = fs8.statSync(dep.sourcePath);
3039
2384
  if (stat.isDirectory()) {
3040
- fs10.cpSync(dep.sourcePath, destinationPath, {
2385
+ fs8.cpSync(dep.sourcePath, destinationPath, {
3041
2386
  recursive: true,
3042
2387
  force: false,
3043
2388
  errorOnExist: true
3044
2389
  });
3045
2390
  } else {
3046
- fs10.copyFileSync(dep.sourcePath, destinationPath);
2391
+ fs8.copyFileSync(dep.sourcePath, destinationPath);
3047
2392
  }
3048
2393
  } else if (dep.kind === "remote-bytes") {
3049
- fs10.copyFileSync(dep.remoteBufferPath, destinationPath);
2394
+ fs8.copyFileSync(dep.remoteBufferPath, destinationPath);
3050
2395
  } else {
3051
2396
  try {
3052
- fs10.renameSync(dep.repoStagePath, destinationPath);
2397
+ fs8.renameSync(dep.repoStagePath, destinationPath);
3053
2398
  } catch (error) {
3054
2399
  const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "";
3055
2400
  if (code !== "EXDEV") {
3056
2401
  throw error;
3057
2402
  }
3058
- fs10.cpSync(dep.repoStagePath, destinationPath, {
2403
+ fs8.cpSync(dep.repoStagePath, destinationPath, {
3059
2404
  recursive: true,
3060
2405
  force: false,
3061
2406
  errorOnExist: true
@@ -3114,8 +2459,8 @@ var typeRules = [
3114
2459
  id: "framework-references-dir",
3115
2460
  when: (frontmatter) => frontmatter.type === "framework",
3116
2461
  validate: ({ skillDir, skillName, diagnostics }) => {
3117
- const referencesDir = path12.join(skillDir, "references");
3118
- if (!fs11.existsSync(referencesDir) || !fs11.statSync(referencesDir).isDirectory()) {
2462
+ const referencesDir = path10.join(skillDir, "references");
2463
+ if (!fs9.existsSync(referencesDir) || !fs9.statSync(referencesDir).isDirectory()) {
3119
2464
  addDiagnostic(diagnostics, {
3120
2465
  severity: "error",
3121
2466
  skill: skillName,
@@ -3128,11 +2473,11 @@ var typeRules = [
3128
2473
  }
3129
2474
  ];
3130
2475
  async function validateInstallerDependencies(skillDir, sourceRoot, diagnostics, skillName) {
3131
- const installerConfigPath = fs11.existsSync(path12.join(skillDir, "skill-installer.yaml")) ? path12.join(skillDir, "skill-installer.yaml") : path12.join(skillDir, "skill-installer.json");
2476
+ const installerConfigPath = fs9.existsSync(path10.join(skillDir, "skill-installer.yaml")) ? path10.join(skillDir, "skill-installer.yaml") : path10.join(skillDir, "skill-installer.json");
3132
2477
  try {
3133
2478
  const installer = loadInstallerConfig(skillDir);
3134
2479
  const fallbackRoots = Array.from(
3135
- /* @__PURE__ */ new Set([path12.resolve(sourceRoot), path12.resolve(process.cwd())])
2480
+ /* @__PURE__ */ new Set([path10.resolve(sourceRoot), path10.resolve(process.cwd())])
3136
2481
  );
3137
2482
  let hasSecurityViolation = false;
3138
2483
  for (const dep of installer.dependencies) {
@@ -3172,18 +2517,18 @@ async function validateInstallerDependencies(skillDir, sourceRoot, diagnostics,
3172
2517
  if (hasSecurityViolation) {
3173
2518
  return;
3174
2519
  }
3175
- const tempSkillDir = fs11.mkdtempSync(path12.join(os5.tmpdir(), "skillspp-validate-installer-"));
2520
+ const tempSkillDir = fs9.mkdtempSync(path10.join(os5.tmpdir(), "skillspp-validate-installer-"));
3176
2521
  try {
3177
2522
  const filesToCopy = ["SKILL.md", "skill-installer.yaml", "skill-installer.json"];
3178
2523
  for (const fileName of filesToCopy) {
3179
- const src = path12.join(skillDir, fileName);
3180
- if (fs11.existsSync(src) && fs11.statSync(src).isFile()) {
3181
- fs11.copyFileSync(src, path12.join(tempSkillDir, fileName));
2524
+ const src = path10.join(skillDir, fileName);
2525
+ if (fs9.existsSync(src) && fs9.statSync(src).isFile()) {
2526
+ fs9.copyFileSync(src, path10.join(tempSkillDir, fileName));
3182
2527
  }
3183
2528
  }
3184
- const agentsDir = path12.join(skillDir, "agents");
3185
- if (fs11.existsSync(agentsDir) && fs11.statSync(agentsDir).isDirectory()) {
3186
- fs11.cpSync(agentsDir, path12.join(tempSkillDir, "agents"), {
2529
+ const agentsDir = path10.join(skillDir, "agents");
2530
+ if (fs9.existsSync(agentsDir) && fs9.statSync(agentsDir).isDirectory()) {
2531
+ fs9.cpSync(agentsDir, path10.join(tempSkillDir, "agents"), {
3187
2532
  recursive: true,
3188
2533
  force: true
3189
2534
  });
@@ -3225,7 +2570,7 @@ async function validateInstallerDependencies(skillDir, sourceRoot, diagnostics,
3225
2570
  throw lastMissingSourceError;
3226
2571
  }
3227
2572
  } finally {
3228
- fs11.rmSync(tempSkillDir, { recursive: true, force: true });
2573
+ fs9.rmSync(tempSkillDir, { recursive: true, force: true });
3229
2574
  }
3230
2575
  } catch (error) {
3231
2576
  if (isInstallerSecurityError(error)) {
@@ -3258,9 +2603,9 @@ async function validateInstallerDependencies(skillDir, sourceRoot, diagnostics,
3258
2603
  }
3259
2604
  }
3260
2605
  async function validateSkillDir(skillDir, sourceRoot, diagnostics, strict, thresholds) {
3261
- const skillMd = path12.join(skillDir, "SKILL.md");
3262
- const skillName = path12.basename(skillDir);
3263
- if (!fs11.existsSync(skillMd)) {
2606
+ const skillMd = path10.join(skillDir, "SKILL.md");
2607
+ const skillName = path10.basename(skillDir);
2608
+ if (!fs9.existsSync(skillMd)) {
3264
2609
  addDiagnostic(diagnostics, {
3265
2610
  severity: "error",
3266
2611
  skill: skillName,
@@ -3270,7 +2615,7 @@ async function validateSkillDir(skillDir, sourceRoot, diagnostics, strict, thres
3270
2615
  });
3271
2616
  return;
3272
2617
  }
3273
- const content = fs11.readFileSync(skillMd, "utf8");
2618
+ const content = fs9.readFileSync(skillMd, "utf8");
3274
2619
  const lines = content.split(/\r?\n/);
3275
2620
  if (lines.length > thresholds.maxLines) {
3276
2621
  addDiagnostic(diagnostics, {
@@ -3336,12 +2681,12 @@ async function validateSkillDir(skillDir, sourceRoot, diagnostics, strict, thres
3336
2681
  }
3337
2682
  }
3338
2683
  for (const ref of discoverMarkdownReferences(content)) {
3339
- const resolved = path12.resolve(skillDir, ref);
3340
- const rel = path12.relative(skillDir, resolved);
3341
- if (!rel || rel.startsWith("..") || path12.isAbsolute(rel)) {
2684
+ const resolved = path10.resolve(skillDir, ref);
2685
+ const rel = path10.relative(skillDir, resolved);
2686
+ if (!rel || rel.startsWith("..") || path10.isAbsolute(rel)) {
3342
2687
  continue;
3343
2688
  }
3344
- if (!fs11.existsSync(resolved)) {
2689
+ if (!fs9.existsSync(resolved)) {
3345
2690
  addDiagnostic(diagnostics, {
3346
2691
  severity: "error",
3347
2692
  skill: skillName,
@@ -3359,11 +2704,11 @@ async function validateSkillDir(skillDir, sourceRoot, diagnostics, strict, thres
3359
2704
  await validateInstallerDependencies(skillDir, sourceRoot, diagnostics, skillName);
3360
2705
  }
3361
2706
  async function stageAndValidateLocalRoot(rootPath, dependencyRoot, seenSkillPaths, diagnostics, strict, thresholds, emitProgress) {
3362
- const resolvedRoot = path12.resolve(rootPath);
3363
- if (!fs11.existsSync(resolvedRoot) || !fs11.statSync(resolvedRoot).isDirectory()) {
2707
+ const resolvedRoot = path10.resolve(rootPath);
2708
+ if (!fs9.existsSync(resolvedRoot) || !fs9.statSync(resolvedRoot).isDirectory()) {
3364
2709
  addDiagnostic(diagnostics, {
3365
2710
  severity: "error",
3366
- skill: path12.basename(resolvedRoot),
2711
+ skill: path10.basename(resolvedRoot),
3367
2712
  file: resolvedRoot,
3368
2713
  rule: "missing-root",
3369
2714
  message: "validation root does not exist"
@@ -3375,7 +2720,7 @@ async function stageAndValidateLocalRoot(rootPath, dependencyRoot, seenSkillPath
3375
2720
  if (skills.length === 0) {
3376
2721
  addDiagnostic(diagnostics, {
3377
2722
  severity: "error",
3378
- skill: path12.basename(resolvedRoot),
2723
+ skill: path10.basename(resolvedRoot),
3379
2724
  file: resolvedRoot,
3380
2725
  rule: "no-skills-discovered",
3381
2726
  message: "no SKILL.md files discovered"
@@ -3383,7 +2728,7 @@ async function stageAndValidateLocalRoot(rootPath, dependencyRoot, seenSkillPath
3383
2728
  return;
3384
2729
  }
3385
2730
  for (const skill of skills) {
3386
- const resolvedSkillPath = path12.resolve(skill.path);
2731
+ const resolvedSkillPath = path10.resolve(skill.path);
3387
2732
  if (seenSkillPaths.has(resolvedSkillPath)) {
3388
2733
  continue;
3389
2734
  }
@@ -3495,45 +2840,45 @@ async function runValidateAnalysis(options, emitProgress) {
3495
2840
  }
3496
2841
 
3497
2842
  // ../../packages/core/src/runtime/installer.ts
3498
- import fs12 from "node:fs";
3499
- import path13 from "node:path";
2843
+ import fs10 from "node:fs";
2844
+ import path11 from "node:path";
3500
2845
  function sanitizeSkillName(name) {
3501
2846
  const sanitized = name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[.-]+|[.-]+$/g, "");
3502
2847
  return sanitized || "unnamed-skill";
3503
2848
  }
3504
2849
  function ensureSafeInside(baseDir, target) {
3505
- const resolvedBase = path13.resolve(baseDir);
3506
- const resolvedTarget = path13.resolve(target);
3507
- if (!(resolvedTarget === resolvedBase || resolvedTarget.startsWith(`${resolvedBase}${path13.sep}`))) {
2850
+ const resolvedBase = path11.resolve(baseDir);
2851
+ const resolvedTarget = path11.resolve(target);
2852
+ if (!(resolvedTarget === resolvedBase || resolvedTarget.startsWith(`${resolvedBase}${path11.sep}`))) {
3508
2853
  throw new Error(`Unsafe path detected: ${target}`);
3509
2854
  }
3510
2855
  }
3511
2856
  function symlinkRelative(target, linkPath) {
3512
- const parent = path13.dirname(linkPath);
3513
- const relative = path13.relative(parent, target);
2857
+ const parent = path11.dirname(linkPath);
2858
+ const relative = path11.relative(parent, target);
3514
2859
  const symlinkType = process.platform === "win32" ? "junction" : void 0;
3515
- fs12.symlinkSync(relative, linkPath, symlinkType);
2860
+ fs10.symlinkSync(relative, linkPath, symlinkType);
3516
2861
  }
3517
2862
  function installToAgent(itemName, canonicalDir, agent, mode, globalInstall, cwd, resolveAgentBaseDir) {
3518
2863
  const agentBase = resolveAgentBaseDir(agent, globalInstall, cwd);
3519
- const agentDir = path13.join(agentBase, itemName);
3520
- fs12.mkdirSync(agentBase, { recursive: true });
2864
+ const agentDir = path11.join(agentBase, itemName);
2865
+ fs10.mkdirSync(agentBase, { recursive: true });
3521
2866
  ensureSafeInside(agentBase, agentDir);
3522
- if (path13.resolve(agentDir) === path13.resolve(canonicalDir)) {
2867
+ if (path11.resolve(agentDir) === path11.resolve(canonicalDir)) {
3523
2868
  return { agent, path: canonicalDir, mode };
3524
2869
  }
3525
2870
  if (mode === "copy") {
3526
- fs12.rmSync(agentDir, { recursive: true, force: true });
3527
- fs12.cpSync(canonicalDir, agentDir, { recursive: true, force: true });
2871
+ fs10.rmSync(agentDir, { recursive: true, force: true });
2872
+ fs10.cpSync(canonicalDir, agentDir, { recursive: true, force: true });
3528
2873
  return { agent, path: agentDir, mode };
3529
2874
  }
3530
2875
  try {
3531
- fs12.rmSync(agentDir, { recursive: true, force: true });
2876
+ fs10.rmSync(agentDir, { recursive: true, force: true });
3532
2877
  symlinkRelative(canonicalDir, agentDir);
3533
2878
  return { agent, path: agentDir, mode: "symlink" };
3534
2879
  } catch {
3535
- fs12.rmSync(agentDir, { recursive: true, force: true });
3536
- fs12.cpSync(canonicalDir, agentDir, { recursive: true, force: true });
2880
+ fs10.rmSync(agentDir, { recursive: true, force: true });
2881
+ fs10.cpSync(canonicalDir, agentDir, { recursive: true, force: true });
3537
2882
  return { agent, path: agentDir, mode: "copy" };
3538
2883
  }
3539
2884
  }
@@ -3548,23 +2893,12 @@ function installSkillToAgent(skillName, canonicalDir, agent, mode, globalInstall
3548
2893
  getAgentSkillsDir
3549
2894
  );
3550
2895
  }
3551
- function installPluginToAgent(pluginName, canonicalDir, agent, mode, globalInstall, cwd) {
3552
- return installToAgent(
3553
- pluginName,
3554
- canonicalDir,
3555
- agent,
3556
- mode,
3557
- globalInstall,
3558
- cwd,
3559
- getAgentPluginsDir
3560
- );
3561
- }
3562
2896
  function installToCanonicalDir(sourcePath, itemName, canonicalBase) {
3563
- const canonicalDir = path13.join(canonicalBase, itemName);
3564
- fs12.mkdirSync(canonicalBase, { recursive: true });
2897
+ const canonicalDir = path11.join(canonicalBase, itemName);
2898
+ fs10.mkdirSync(canonicalBase, { recursive: true });
3565
2899
  ensureSafeInside(canonicalBase, canonicalDir);
3566
- fs12.rmSync(canonicalDir, { recursive: true, force: true });
3567
- fs12.cpSync(sourcePath, canonicalDir, { recursive: true, force: true });
2900
+ fs10.rmSync(canonicalDir, { recursive: true, force: true });
2901
+ fs10.cpSync(sourcePath, canonicalDir, { recursive: true, force: true });
3568
2902
  return canonicalDir;
3569
2903
  }
3570
2904
  function installSkill(skill, agents, options) {
@@ -3595,48 +2929,20 @@ function installSkill(skill, agents, options) {
3595
2929
  installedTo
3596
2930
  };
3597
2931
  }
3598
- function installPlugin(plugin, agents, options) {
3599
- if (agents.length === 0) {
3600
- throw new Error("At least one target agent is required for installation.");
3601
- }
3602
- const uniqueAgents = Array.from(new Set(agents));
3603
- const pluginName = sanitizeSkillName(plugin.name);
3604
- const canonicalAgent = uniqueAgents[0];
3605
- const canonicalBase = getAgentPluginsDir(canonicalAgent, options.globalInstall, options.cwd);
3606
- const canonicalDir = installToCanonicalDir(plugin.path, pluginName, canonicalBase);
3607
- const installedTo = [
3608
- { agent: canonicalAgent, path: canonicalDir, mode: options.mode },
3609
- ...uniqueAgents.slice(1).map(
3610
- (agent) => installPluginToAgent(
3611
- pluginName,
3612
- canonicalDir,
3613
- agent,
3614
- options.mode,
3615
- options.globalInstall,
3616
- options.cwd
3617
- )
3618
- )
3619
- ];
3620
- return {
3621
- skillName: pluginName,
3622
- canonicalDir,
3623
- installedTo
3624
- };
3625
- }
3626
2932
 
3627
2933
  // ../../packages/core/src/sources/scanner.ts
3628
- import fs13 from "node:fs";
2934
+ import fs11 from "node:fs";
3629
2935
  import os6 from "node:os";
3630
- import path14 from "node:path";
2936
+ import path12 from "node:path";
3631
2937
  function listDirs(dir) {
3632
- if (!fs13.existsSync(dir) || !fs13.statSync(dir).isDirectory()) {
2938
+ if (!fs11.existsSync(dir) || !fs11.statSync(dir).isDirectory()) {
3633
2939
  return [];
3634
2940
  }
3635
- return fs13.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
2941
+ return fs11.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
3636
2942
  }
3637
2943
  function detectLocalGlobalConflicts(cwd) {
3638
- const localDir = path14.join(cwd, ".agents", "skills");
3639
- const globalDir = path14.join(os6.homedir(), ".config", "agents", "skills");
2944
+ const localDir = path12.join(cwd, ".agents", "skills");
2945
+ const globalDir = path12.join(os6.homedir(), ".config", "agents", "skills");
3640
2946
  const local = new Set(listDirs(localDir));
3641
2947
  const global = new Set(listDirs(globalDir));
3642
2948
  const overlap = [...local].filter((name) => global.has(name));
@@ -3646,30 +2952,30 @@ function detectLocalGlobalConflicts(cwd) {
3646
2952
  }));
3647
2953
  }
3648
2954
  function readPackageMeta(packageDir) {
3649
- const packageJson = path14.join(packageDir, "package.json");
3650
- if (!fs13.existsSync(packageJson) || !fs13.statSync(packageJson).isFile()) {
2955
+ const packageJson = path12.join(packageDir, "package.json");
2956
+ if (!fs11.existsSync(packageJson) || !fs11.statSync(packageJson).isFile()) {
3651
2957
  return {
3652
- name: path14.basename(packageDir),
2958
+ name: path12.basename(packageDir),
3653
2959
  version: "0.0.0"
3654
2960
  };
3655
2961
  }
3656
- const parsed = JSON.parse(fs13.readFileSync(packageJson, "utf8"));
2962
+ const parsed = JSON.parse(fs11.readFileSync(packageJson, "utf8"));
3657
2963
  return {
3658
- name: parsed.name || path14.basename(packageDir),
2964
+ name: parsed.name || path12.basename(packageDir),
3659
2965
  version: parsed.version || "0.0.0"
3660
2966
  };
3661
2967
  }
3662
2968
  function collectSkillsFromPackage(packageDir, depth, out) {
3663
2969
  const meta = readPackageMeta(packageDir);
3664
- const skillsDir = path14.join(packageDir, "skills");
3665
- if (fs13.existsSync(skillsDir) && fs13.statSync(skillsDir).isDirectory()) {
3666
- for (const entry of fs13.readdirSync(skillsDir, { withFileTypes: true })) {
2970
+ const skillsDir = path12.join(packageDir, "skills");
2971
+ if (fs11.existsSync(skillsDir) && fs11.statSync(skillsDir).isDirectory()) {
2972
+ for (const entry of fs11.readdirSync(skillsDir, { withFileTypes: true })) {
3667
2973
  if (!entry.isDirectory()) {
3668
2974
  continue;
3669
2975
  }
3670
- const skillDir = path14.join(skillsDir, entry.name);
3671
- const skillMd = path14.join(skillDir, "SKILL.md");
3672
- if (!fs13.existsSync(skillMd) || !fs13.statSync(skillMd).isFile()) {
2976
+ const skillDir = path12.join(skillsDir, entry.name);
2977
+ const skillMd = path12.join(skillDir, "SKILL.md");
2978
+ if (!fs11.existsSync(skillMd) || !fs11.statSync(skillMd).isFile()) {
3673
2979
  continue;
3674
2980
  }
3675
2981
  out.push({
@@ -3683,42 +2989,42 @@ function collectSkillsFromPackage(packageDir, depth, out) {
3683
2989
  }
3684
2990
  }
3685
2991
  function walkNodeModules(nodeModulesDir, depth, maxDepth, seen, out) {
3686
- const resolvedNodeModules = path14.resolve(nodeModulesDir);
2992
+ const resolvedNodeModules = path12.resolve(nodeModulesDir);
3687
2993
  if (seen.has(resolvedNodeModules) || depth > maxDepth) {
3688
2994
  return;
3689
2995
  }
3690
2996
  seen.add(resolvedNodeModules);
3691
- if (!fs13.existsSync(resolvedNodeModules) || !fs13.statSync(resolvedNodeModules).isDirectory()) {
2997
+ if (!fs11.existsSync(resolvedNodeModules) || !fs11.statSync(resolvedNodeModules).isDirectory()) {
3692
2998
  return;
3693
2999
  }
3694
- for (const entry of fs13.readdirSync(resolvedNodeModules, {
3000
+ for (const entry of fs11.readdirSync(resolvedNodeModules, {
3695
3001
  withFileTypes: true
3696
3002
  })) {
3697
3003
  if (!entry.isDirectory()) {
3698
3004
  continue;
3699
3005
  }
3700
3006
  if (entry.name.startsWith("@")) {
3701
- const scopeDir = path14.join(resolvedNodeModules, entry.name);
3702
- for (const scoped of fs13.readdirSync(scopeDir, { withFileTypes: true })) {
3007
+ const scopeDir = path12.join(resolvedNodeModules, entry.name);
3008
+ for (const scoped of fs11.readdirSync(scopeDir, { withFileTypes: true })) {
3703
3009
  if (!scoped.isDirectory()) {
3704
3010
  continue;
3705
3011
  }
3706
- const packageDir2 = path14.join(scopeDir, scoped.name);
3012
+ const packageDir2 = path12.join(scopeDir, scoped.name);
3707
3013
  collectSkillsFromPackage(packageDir2, depth, out);
3708
- walkNodeModules(path14.join(packageDir2, "node_modules"), depth + 1, maxDepth, seen, out);
3014
+ walkNodeModules(path12.join(packageDir2, "node_modules"), depth + 1, maxDepth, seen, out);
3709
3015
  }
3710
3016
  continue;
3711
3017
  }
3712
- const packageDir = path14.join(resolvedNodeModules, entry.name);
3018
+ const packageDir = path12.join(resolvedNodeModules, entry.name);
3713
3019
  collectSkillsFromPackage(packageDir, depth, out);
3714
- walkNodeModules(path14.join(packageDir, "node_modules"), depth + 1, maxDepth, seen, out);
3020
+ walkNodeModules(path12.join(packageDir, "node_modules"), depth + 1, maxDepth, seen, out);
3715
3021
  }
3716
3022
  }
3717
3023
  function discoverTransitiveSkillCandidates(cwd, options = {}) {
3718
3024
  const maxDepth = options.maxDepth ?? 8;
3719
3025
  const out = [];
3720
3026
  const seen = /* @__PURE__ */ new Set();
3721
- walkNodeModules(path14.join(cwd, "node_modules"), 0, maxDepth, seen, out);
3027
+ walkNodeModules(path12.join(cwd, "node_modules"), 0, maxDepth, seen, out);
3722
3028
  return out.sort((a, b) => {
3723
3029
  if (a.skillName !== b.skillName) {
3724
3030
  return a.skillName.localeCompare(b.skillName);
@@ -3769,8 +3075,8 @@ function detectTransitiveSkillConflicts(candidates) {
3769
3075
  }
3770
3076
 
3771
3077
  // ../../packages/core/src/runtime/installer-scaffold.ts
3772
- import fs14 from "node:fs";
3773
- import path15 from "node:path";
3078
+ import fs12 from "node:fs";
3079
+ import path13 from "node:path";
3774
3080
  import YAML3 from "yaml";
3775
3081
  function installerConfigSkeleton() {
3776
3082
  return {
@@ -3781,11 +3087,11 @@ function installerConfigSkeleton() {
3781
3087
  };
3782
3088
  }
3783
3089
  function isFile(filePath) {
3784
- return fs14.existsSync(filePath) && fs14.statSync(filePath).isFile();
3090
+ return fs12.existsSync(filePath) && fs12.statSync(filePath).isFile();
3785
3091
  }
3786
3092
  function getInstallerConfigState(skillDir) {
3787
- const yamlPath = path15.join(skillDir, "skill-installer.yaml");
3788
- const jsonPath = path15.join(skillDir, "skill-installer.json");
3093
+ const yamlPath = path13.join(skillDir, "skill-installer.yaml");
3094
+ const jsonPath = path13.join(skillDir, "skill-installer.json");
3789
3095
  const hasYaml = isFile(yamlPath);
3790
3096
  const hasJson = isFile(jsonPath);
3791
3097
  if (hasYaml && hasJson) {
@@ -3811,7 +3117,7 @@ function scaffoldInstallerConfigFile(skillDir, format) {
3811
3117
  }
3812
3118
  const content = format === "yaml" ? YAML3.stringify(installerConfigSkeleton()) : JSON.stringify(installerConfigSkeleton(), null, 2);
3813
3119
  const destinationPath = format === "yaml" ? state.yamlPath : state.jsonPath;
3814
- fs14.writeFileSync(destinationPath, `${content}
3120
+ fs12.writeFileSync(destinationPath, `${content}
3815
3121
  `, "utf8");
3816
3122
  return { created: true, filePath: destinationPath };
3817
3123
  }
@@ -3873,19 +3179,19 @@ function canonicalSourceIdentity(options) {
3873
3179
  }
3874
3180
  return options.parsedSource.repoUrl;
3875
3181
  }
3876
- function resolveSafeRealPath3(inputPath) {
3182
+ function resolveSafeRealPath2(inputPath) {
3877
3183
  try {
3878
- return fs15.realpathSync(inputPath);
3184
+ return fs13.realpathSync(inputPath);
3879
3185
  } catch {
3880
- return path16.resolve(inputPath);
3186
+ return path14.resolve(inputPath);
3881
3187
  }
3882
3188
  }
3883
- function isLocalSymlinkSource3(localPath) {
3189
+ function isLocalSymlinkSource2(localPath) {
3884
3190
  try {
3885
- if (!fs15.existsSync(localPath)) {
3191
+ if (!fs13.existsSync(localPath)) {
3886
3192
  return false;
3887
3193
  }
3888
- return fs15.lstatSync(localPath).isSymbolicLink();
3194
+ return fs13.lstatSync(localPath).isSymbolicLink();
3889
3195
  } catch {
3890
3196
  return false;
3891
3197
  }
@@ -3899,18 +3205,18 @@ function staleLockfileNameForFormat(format) {
3899
3205
  function propagateLockfileVisibility(options) {
3900
3206
  const lockName = lockfileNameForFormat(options.lockFormat);
3901
3207
  const staleLockName = staleLockfileNameForFormat(options.lockFormat);
3902
- const canonicalLockPath = path16.join(options.canonicalDir, lockName);
3903
- const canonicalRealPath = fs15.existsSync(options.canonicalDir) ? fs15.realpathSync(options.canonicalDir) : path16.resolve(options.canonicalDir);
3208
+ const canonicalLockPath = path14.join(options.canonicalDir, lockName);
3209
+ const canonicalRealPath = fs13.existsSync(options.canonicalDir) ? fs13.realpathSync(options.canonicalDir) : path14.resolve(options.canonicalDir);
3904
3210
  for (const targetDir of options.targetDirs) {
3905
- const targetRealPath = fs15.existsSync(targetDir) ? fs15.realpathSync(targetDir) : path16.resolve(targetDir);
3211
+ const targetRealPath = fs13.existsSync(targetDir) ? fs13.realpathSync(targetDir) : path14.resolve(targetDir);
3906
3212
  if (targetRealPath === canonicalRealPath) {
3907
3213
  continue;
3908
3214
  }
3909
- const targetLockPath = path16.join(targetDir, lockName);
3910
- const staleTargetPath = path16.join(targetDir, staleLockName);
3911
- fs15.copyFileSync(canonicalLockPath, targetLockPath);
3912
- if (fs15.existsSync(staleTargetPath)) {
3913
- fs15.rmSync(staleTargetPath, { force: true });
3215
+ const targetLockPath = path14.join(targetDir, lockName);
3216
+ const staleTargetPath = path14.join(targetDir, staleLockName);
3217
+ fs13.copyFileSync(canonicalLockPath, targetLockPath);
3218
+ if (fs13.existsSync(staleTargetPath)) {
3219
+ fs13.rmSync(staleTargetPath, { force: true });
3914
3220
  }
3915
3221
  }
3916
3222
  }
@@ -3955,12 +3261,12 @@ function writeLockEntryAfterInstall(options) {
3955
3261
  targetDirs: options.outcome.installedTo.map((destination) => destination.path),
3956
3262
  lockFormat: selectedFormat
3957
3263
  });
3958
- const staleLockPath = path16.join(
3264
+ const staleLockPath = path14.join(
3959
3265
  options.outcome.canonicalDir,
3960
3266
  staleLockfileNameForFormat(selectedFormat)
3961
3267
  );
3962
- if (fs15.existsSync(staleLockPath)) {
3963
- fs15.rmSync(staleLockPath, { force: true });
3268
+ if (fs13.existsSync(staleLockPath)) {
3269
+ fs13.rmSync(staleLockPath, { force: true });
3964
3270
  }
3965
3271
  }
3966
3272
  function buildRemoteSkill(remote) {
@@ -3974,23 +3280,6 @@ function buildRemoteSkill(remote) {
3974
3280
  cleanup: staged.cleanup
3975
3281
  };
3976
3282
  }
3977
- async function buildRemotePlugin(remote) {
3978
- const staged = stageRemotePluginFilesToTempDir(remote.installName, remote.files);
3979
- try {
3980
- const plugins = await discoverPluginsAsync(staged.path, [remote.installName]);
3981
- const plugin = plugins[0];
3982
- if (!plugin) {
3983
- throw new Error(`Plugin '${remote.installName}' is missing plugin.json`);
3984
- }
3985
- return {
3986
- plugin,
3987
- cleanup: staged.cleanup
3988
- };
3989
- } catch (error) {
3990
- staged.cleanup();
3991
- throw error;
3992
- }
3993
- }
3994
3283
  async function runCheckScanTask(cwd, options, emitProgress) {
3995
3284
  await emitProgress("checking drift");
3996
3285
  const assessed = await assessLockEntries(options, cwd, {
@@ -4017,20 +3306,11 @@ async function runUpdateAssessTask(cwd, options, emitProgress) {
4017
3306
  assessments: serializeAssessments(assessed.assessments)
4018
3307
  };
4019
3308
  }
4020
- async function runPluginUpdateAssessTask(cwd, options, emitProgress) {
4021
- await emitProgress("assessing drift");
4022
- const assessed = await assessPluginLockEntries(options, cwd, {
4023
- keepResolved: false
4024
- });
4025
- return {
4026
- assessments: serializeAssessments(assessed.assessments)
4027
- };
4028
- }
4029
3309
  function createBackupDir(skillName, sourceDir) {
4030
- const backupRoot = fs15.mkdtempSync(path16.join(os7.tmpdir(), "skillspp-update-backup-"));
4031
- const backupDir = path16.join(backupRoot, skillName);
4032
- fs15.mkdirSync(backupDir, { recursive: true });
4033
- fs15.cpSync(sourceDir, backupDir, { recursive: true, force: true });
3310
+ const backupRoot = fs13.mkdtempSync(path14.join(os7.tmpdir(), "skillspp-update-backup-"));
3311
+ const backupDir = path14.join(backupRoot, skillName);
3312
+ fs13.mkdirSync(backupDir, { recursive: true });
3313
+ fs13.cpSync(sourceDir, backupDir, { recursive: true, force: true });
4034
3314
  return backupDir;
4035
3315
  }
4036
3316
  async function applyEntryUpdate(assessment, options, cwd) {
@@ -4090,70 +3370,7 @@ async function applyEntryUpdate(assessment, options, cwd) {
4090
3370
  });
4091
3371
  throw error;
4092
3372
  } finally {
4093
- fs15.rmSync(path16.dirname(backupDir), { recursive: true, force: true });
4094
- if (resolved.cleanup) {
4095
- resolved.cleanup();
4096
- }
4097
- }
4098
- }
4099
- async function applyPluginEntryUpdate(assessment, options, cwd) {
4100
- const { entry } = assessment;
4101
- if (!assessment.resolved || !assessment.sourceHash) {
4102
- throw new Error(`No resolved source available for ${entry.skillName}`);
4103
- }
4104
- const resolved = assessment.resolved;
4105
- const sourceHash = assessment.sourceHash;
4106
- const backupDir = createBackupDir(entry.skillName, entry.canonicalDir);
4107
- try {
4108
- const preparedInstaller = await prepareInstallerArtifacts(resolved.plugin.path, cwd, {
4109
- sourceType: entry.source.type,
4110
- policyMode: options.policyMode || "enforce",
4111
- trustWellKnown: Boolean(options.trustWellKnown)
4112
- });
4113
- const outcome = installPlugin(resolved.plugin, entry.agents, {
4114
- mode: entry.installMode,
4115
- globalInstall: entry.global,
4116
- cwd
4117
- });
4118
- try {
4119
- await applyInstallerArtifacts(outcome.canonicalDir, preparedInstaller);
4120
- } finally {
4121
- cleanupPreparedInstallerArtifacts(preparedInstaller);
4122
- }
4123
- const installedHash = await hashDirectoryAsync(outcome.canonicalDir);
4124
- return {
4125
- ...entry,
4126
- source: {
4127
- ...entry.source,
4128
- canonical: assessment.refreshedSource?.canonical ?? entry.source.canonical,
4129
- pinnedRef: assessment.refreshedSource?.pinnedRef ?? entry.source.pinnedRef,
4130
- resolvedPath: assessment.refreshedSource?.resolvedPath ?? entry.source.resolvedPath,
4131
- isSymlinkSource: assessment.refreshedSource?.isSymlinkSource ?? entry.source.isSymlinkSource,
4132
- selector: {
4133
- ...entry.source.selector,
4134
- relativePath: assessment.refreshedSource?.sourcePluginPath ?? entry.source.selector.relativePath,
4135
- wellKnownSourceUrl: assessment.refreshedSource?.wellKnownSourceUrl ?? entry.source.selector.wellKnownSourceUrl
4136
- }
4137
- },
4138
- sourceHash,
4139
- installedHash,
4140
- canonicalDir: outcome.canonicalDir,
4141
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
4142
- };
4143
- } catch (error) {
4144
- const rollbackPlugin = {
4145
- name: entry.skillName,
4146
- description: `Rollback for ${entry.skillName}`,
4147
- path: backupDir
4148
- };
4149
- installPlugin(rollbackPlugin, entry.agents, {
4150
- mode: entry.installMode,
4151
- globalInstall: entry.global,
4152
- cwd
4153
- });
4154
- throw error;
4155
- } finally {
4156
- fs15.rmSync(path16.dirname(backupDir), { recursive: true, force: true });
3373
+ fs13.rmSync(path14.dirname(backupDir), { recursive: true, force: true });
4157
3374
  if (resolved.cleanup) {
4158
3375
  resolved.cleanup();
4159
3376
  }
@@ -4164,61 +3381,8 @@ async function runUpdateApplyTask(payload, emitProgress) {
4164
3381
  ...payload.options,
4165
3382
  skill: payload.selectedSkillNames
4166
3383
  };
4167
- await emitProgress("assessing selected skills");
4168
- const assessed = await assessLockEntries(selectedOptions, payload.cwd, {
4169
- keepResolved: true
4170
- });
4171
- const candidateAssessments = assessed.assessments.filter(
4172
- (assessment) => !assessment.drift.some((item) => item.kind === "migrate-required") && assessment.drift.some(
4173
- (item) => item.kind === "changed-source" || item.kind === "local-modified"
4174
- )
4175
- );
4176
- let nextLock = readLockfile(Boolean(payload.options.global), payload.cwd);
4177
- const updatedEntries = [];
4178
- const ordered = [...candidateAssessments].sort(
4179
- (a, b) => a.entry.skillName.localeCompare(b.entry.skillName)
4180
- );
4181
- try {
4182
- for (const assessment of ordered) {
4183
- await emitProgress(`updating ${assessment.entry.skillName}`);
4184
- const updated = await applyEntryUpdate(assessment, payload.options, payload.cwd);
4185
- updatedEntries.push(updated);
4186
- nextLock = upsertLockEntry(nextLock, updated);
4187
- }
4188
- await emitProgress("writing lockfile");
4189
- writeLockfile(Boolean(payload.options.global), payload.cwd, nextLock, payload.lockFormat);
4190
- for (const updated of updatedEntries) {
4191
- const targetDirs = Array.from(
4192
- new Set(
4193
- updated.agents.map(
4194
- (agent) => path16.join(getAgentSkillsDir(agent, updated.global, payload.cwd), updated.skillName)
4195
- )
4196
- )
4197
- );
4198
- propagateLockfileVisibility({
4199
- canonicalDir: updated.canonicalDir,
4200
- targetDirs,
4201
- lockFormat: payload.lockFormat
4202
- });
4203
- }
4204
- return {
4205
- updatedSkillNames: ordered.map((assessment) => assessment.entry.skillName)
4206
- };
4207
- } finally {
4208
- for (const assessment of assessed.assessments) {
4209
- if (assessment.resolved?.cleanup) {
4210
- assessment.resolved.cleanup();
4211
- }
4212
- }
4213
- }
4214
- }
4215
- async function runPluginUpdateApplyTask(payload, emitProgress) {
4216
- const selectedOptions = {
4217
- ...payload.options,
4218
- skill: payload.selectedPluginNames
4219
- };
4220
- await emitProgress("assessing selected plugins");
4221
- const assessed = await assessPluginLockEntries(selectedOptions, payload.cwd, {
3384
+ await emitProgress("assessing selected skills");
3385
+ const assessed = await assessLockEntries(selectedOptions, payload.cwd, {
4222
3386
  keepResolved: true
4223
3387
  });
4224
3388
  const candidateAssessments = assessed.assessments.filter(
@@ -4226,7 +3390,7 @@ async function runPluginUpdateApplyTask(payload, emitProgress) {
4226
3390
  (item) => item.kind === "changed-source" || item.kind === "local-modified"
4227
3391
  )
4228
3392
  );
4229
- let nextLock = readResourceLockfile("plugin", Boolean(payload.options.global), payload.cwd);
3393
+ let nextLock = readLockfile(Boolean(payload.options.global), payload.cwd);
4230
3394
  const updatedEntries = [];
4231
3395
  const ordered = [...candidateAssessments].sort(
4232
3396
  (a, b) => a.entry.skillName.localeCompare(b.entry.skillName)
@@ -4234,23 +3398,17 @@ async function runPluginUpdateApplyTask(payload, emitProgress) {
4234
3398
  try {
4235
3399
  for (const assessment of ordered) {
4236
3400
  await emitProgress(`updating ${assessment.entry.skillName}`);
4237
- const updated = await applyPluginEntryUpdate(assessment, payload.options, payload.cwd);
3401
+ const updated = await applyEntryUpdate(assessment, payload.options, payload.cwd);
4238
3402
  updatedEntries.push(updated);
4239
- nextLock = upsertResourceLockEntry(nextLock, updated);
3403
+ nextLock = upsertLockEntry(nextLock, updated);
4240
3404
  }
4241
3405
  await emitProgress("writing lockfile");
4242
- writeResourceLockfile(
4243
- "plugin",
4244
- Boolean(payload.options.global),
4245
- payload.cwd,
4246
- nextLock,
4247
- payload.lockFormat
4248
- );
3406
+ writeLockfile(Boolean(payload.options.global), payload.cwd, nextLock, payload.lockFormat);
4249
3407
  for (const updated of updatedEntries) {
4250
3408
  const targetDirs = Array.from(
4251
3409
  new Set(
4252
3410
  updated.agents.map(
4253
- (agent) => path16.join(getAgentPluginsDir(agent, updated.global, payload.cwd), updated.skillName)
3411
+ (agent) => path14.join(getAgentSkillsDir(agent, updated.global, payload.cwd), updated.skillName)
4254
3412
  )
4255
3413
  )
4256
3414
  );
@@ -4261,7 +3419,7 @@ async function runPluginUpdateApplyTask(payload, emitProgress) {
4261
3419
  });
4262
3420
  }
4263
3421
  return {
4264
- updatedPluginNames: ordered.map((assessment) => assessment.entry.skillName)
3422
+ updatedSkillNames: ordered.map((assessment) => assessment.entry.skillName)
4265
3423
  };
4266
3424
  } finally {
4267
3425
  for (const assessment of assessed.assessments) {
@@ -4311,67 +3469,12 @@ async function resolveMigrationSource(options) {
4311
3469
  return {
4312
3470
  parsedSource,
4313
3471
  skill,
4314
- sourceSkillPath: path16.relative(prepared.basePath, skill.path) || ".",
4315
- sourceHash,
4316
- sourceCanonical: canonicalSourceIdentity({ parsedSource }),
4317
- sourcePinnedRef,
4318
- sourceResolvedPath: parsedSource.type === "local" ? resolveSafeRealPath3(skill.path) : void 0,
4319
- sourceIsSymlink: parsedSource.type === "local" ? isLocalSymlinkSource3(parsedSource.localPath) : void 0,
4320
- cleanup: prepared.cleanup
4321
- };
4322
- } catch (error) {
4323
- if (prepared.cleanup) {
4324
- prepared.cleanup();
4325
- }
4326
- throw error;
4327
- }
4328
- }
4329
- async function resolvePluginMigrationSource(options) {
4330
- const parsedSource = parseSource(options.sourceInput);
4331
- if (parsedSource.type === "well-known" || parsedSource.type === "catalog") {
4332
- const remotePlugins = parsedSource.type === "well-known" ? await resolveWellKnownPlugins(parsedSource.url, options.addOptions) : await resolveCatalogPlugins(parsedSource.url, options.addOptions);
4333
- const remote = remotePlugins.find((item) => item.installName === options.pluginName);
4334
- if (!remote) {
4335
- throw new Error(`Plugin '${options.pluginName}' not found in migration source`);
4336
- }
4337
- const staged = await buildRemotePlugin(remote);
4338
- const sourceHash = hashDirectory(staged.plugin.path);
4339
- return {
4340
- parsedSource,
4341
- plugin: staged.plugin,
4342
- sourceHash,
4343
- sourceCanonical: canonicalSourceIdentity({
4344
- parsedSource,
4345
- wellKnownSourceUrl: remote.sourceUrl
4346
- }),
4347
- sourcePinnedRef: sourceHash,
4348
- wellKnownSourceUrl: remote.sourceUrl,
4349
- cleanup: staged.cleanup
4350
- };
4351
- }
4352
- const prepared = await prepareSourceDirAsync(
4353
- parsedSource
4354
- );
4355
- try {
4356
- const plugins = await discoverPluginsAsync(prepared.basePath, [options.pluginName]);
4357
- const plugin = plugins.find((item) => item.name === options.pluginName);
4358
- if (!plugin) {
4359
- throw new Error(`Plugin '${options.pluginName}' not found in migration source`);
4360
- }
4361
- const sourceHash = sourceHashForInstalledSkill({
4362
- parsedSource,
4363
- skillPath: plugin.path
4364
- });
4365
- const sourcePinnedRef = parsedSource.type === "github" || parsedSource.type === "git" ? await resolveGitHeadRefAsync(prepared.basePath) : void 0;
4366
- return {
4367
- parsedSource,
4368
- plugin,
4369
- sourcePluginPath: path16.relative(prepared.basePath, plugin.path) || ".",
3472
+ sourceSkillPath: path14.relative(prepared.basePath, skill.path) || ".",
4370
3473
  sourceHash,
4371
3474
  sourceCanonical: canonicalSourceIdentity({ parsedSource }),
4372
3475
  sourcePinnedRef,
4373
- sourceResolvedPath: parsedSource.type === "local" ? resolveSafeRealPath3(plugin.path) : void 0,
4374
- sourceIsSymlink: parsedSource.type === "local" ? isLocalSymlinkSource3(parsedSource.localPath) : void 0,
3476
+ sourceResolvedPath: parsedSource.type === "local" ? resolveSafeRealPath2(skill.path) : void 0,
3477
+ sourceIsSymlink: parsedSource.type === "local" ? isLocalSymlinkSource2(parsedSource.localPath) : void 0,
4375
3478
  cleanup: prepared.cleanup
4376
3479
  };
4377
3480
  } catch (error) {
@@ -4442,82 +3545,13 @@ async function runUpdateMigrateTask(payload, emitProgress) {
4442
3545
  });
4443
3546
  throw error;
4444
3547
  } finally {
4445
- fs15.rmSync(path16.dirname(backupDir), { recursive: true, force: true });
3548
+ fs13.rmSync(path14.dirname(backupDir), { recursive: true, force: true });
4446
3549
  if (source.cleanup) {
4447
3550
  source.cleanup();
4448
3551
  }
4449
3552
  }
4450
3553
  return { skillName: entry.skillName };
4451
3554
  }
4452
- async function runPluginUpdateMigrateTask(payload, emitProgress) {
4453
- await emitProgress("resolving migration source");
4454
- const lock = readResourceLockfile("plugin", Boolean(payload.options.global), payload.cwd);
4455
- const entry = lock.entries.find((item) => item.skillName === payload.pluginName);
4456
- if (!entry) {
4457
- throw new Error(`Unknown plugin for migration: ${payload.pluginName}`);
4458
- }
4459
- const source = await resolvePluginMigrationSource({
4460
- sourceInput: payload.sourceInput,
4461
- pluginName: payload.pluginName,
4462
- addOptions: payload.options
4463
- });
4464
- const backupDir = createBackupDir(entry.skillName, entry.canonicalDir);
4465
- try {
4466
- await emitProgress(`migrating ${entry.skillName}`);
4467
- const preparedInstaller = await prepareInstallerArtifacts(source.plugin.path, payload.cwd, {
4468
- sourceType: source.parsedSource.type,
4469
- policyMode: payload.options.policyMode || "enforce",
4470
- trustWellKnown: Boolean(payload.options.trustWellKnown)
4471
- });
4472
- const outcome = installPlugin(source.plugin, entry.agents, {
4473
- mode: entry.installMode,
4474
- globalInstall: entry.global,
4475
- cwd: payload.cwd
4476
- });
4477
- try {
4478
- await applyInstallerArtifacts(outcome.canonicalDir, preparedInstaller);
4479
- await emitProgress("writing lockfile");
4480
- writeLockEntryAfterInstall({
4481
- resourceKind: "plugin",
4482
- globalInstall: entry.global,
4483
- cwd: payload.cwd,
4484
- sourceInput: payload.sourceInput,
4485
- sourceType: source.parsedSource.type,
4486
- sourceCanonical: source.sourceCanonical,
4487
- sourcePinnedRef: source.sourcePinnedRef,
4488
- sourceResolvedPath: source.sourceResolvedPath,
4489
- sourceIsSymlink: source.sourceIsSymlink,
4490
- sourceSkillName: source.plugin.name,
4491
- sourceSkillPath: source.sourcePluginPath,
4492
- wellKnownSourceUrl: source.wellKnownSourceUrl,
4493
- sourceHash: source.sourceHash,
4494
- outcome,
4495
- mode: entry.installMode,
4496
- lockFormat: payload.lockFormat
4497
- });
4498
- } finally {
4499
- cleanupPreparedInstallerArtifacts(preparedInstaller);
4500
- }
4501
- } catch (error) {
4502
- const rollbackPlugin = {
4503
- name: entry.skillName,
4504
- description: `Rollback for ${entry.skillName}`,
4505
- path: backupDir
4506
- };
4507
- installPlugin(rollbackPlugin, entry.agents, {
4508
- mode: entry.installMode,
4509
- globalInstall: entry.global,
4510
- cwd: payload.cwd
4511
- });
4512
- throw error;
4513
- } finally {
4514
- fs15.rmSync(path16.dirname(backupDir), { recursive: true, force: true });
4515
- if (source.cleanup) {
4516
- source.cleanup();
4517
- }
4518
- }
4519
- return { pluginName: entry.skillName };
4520
- }
4521
3555
  async function runListDetectAgentsTask(cwd, options, emitProgress) {
4522
3556
  await emitProgress("detecting installed agents");
4523
3557
  const agents = options.agent ? filterInstalledAgents(resolveAgents(options.agent), cwd) : detectInstalledAgents(cwd);
@@ -4528,17 +3562,17 @@ async function runListScanInventoryTask(payload, emitProgress) {
4528
3562
  for (const agent of payload.agents) {
4529
3563
  await emitProgress(`scanning installed skills (${AGENTS[agent].displayName})`);
4530
3564
  const dir = getAgentSkillsDir(agent, payload.globalInstall, payload.cwd);
4531
- if (!fs15.existsSync(dir) || !fs15.statSync(dir).isDirectory()) {
3565
+ if (!fs13.existsSync(dir) || !fs13.statSync(dir).isDirectory()) {
4532
3566
  continue;
4533
3567
  }
4534
- for (const entry of fs15.readdirSync(dir, { withFileTypes: true })) {
3568
+ for (const entry of fs13.readdirSync(dir, { withFileTypes: true })) {
4535
3569
  if (!entry.isDirectory() && !entry.isSymbolicLink()) {
4536
3570
  continue;
4537
3571
  }
4538
- const fullPath = path16.join(dir, entry.name);
3572
+ const fullPath = path14.join(dir, entry.name);
4539
3573
  let resolvedPath = fullPath;
4540
3574
  try {
4541
- resolvedPath = fs15.realpathSync(fullPath);
3575
+ resolvedPath = fs13.realpathSync(fullPath);
4542
3576
  } catch {
4543
3577
  resolvedPath = fullPath;
4544
3578
  }
@@ -4595,56 +3629,6 @@ async function resolveAddSourceSkills(sourceInput, options, emitProgress) {
4595
3629
  }
4596
3630
  }
4597
3631
  }
4598
- async function resolveAddSourcePlugins(sourceInput, options, emitProgress) {
4599
- const parsed = parseSource(sourceInput);
4600
- const requestedPlugins = options.skill;
4601
- if (parsed.type === "well-known" || parsed.type === "catalog") {
4602
- await emitProgress("fetching plugin index");
4603
- const remotePlugins = parsed.type === "well-known" ? await resolveWellKnownPlugins(parsed.url, options) : await resolveCatalogPlugins(parsed.url, options);
4604
- if (remotePlugins.length === 0) {
4605
- throw new Error("No plugins found at remote endpoint");
4606
- }
4607
- const selectedRemotePlugins = requestedPlugins && !requestedPlugins.includes("*") ? remotePlugins.filter((remote) => requestedPlugins.includes(remote.installName)) : remotePlugins;
4608
- const discoveredPlugins = [];
4609
- for (const remote of selectedRemotePlugins) {
4610
- const staged = await buildRemotePlugin(remote);
4611
- try {
4612
- discoveredPlugins.push(staged.plugin);
4613
- } finally {
4614
- staged.cleanup();
4615
- }
4616
- }
4617
- if (discoveredPlugins.length === 0) {
4618
- throw new Error("No plugins found in source");
4619
- }
4620
- return {
4621
- plugins: discoveredPlugins.map((plugin) => ({
4622
- name: plugin.name,
4623
- description: plugin.description
4624
- }))
4625
- };
4626
- }
4627
- await emitProgress("loading source");
4628
- const prepared = await prepareSourceDirAsync(
4629
- parsed
4630
- );
4631
- try {
4632
- const plugins = await discoverPluginsAsync(prepared.basePath, requestedPlugins);
4633
- if (plugins.length === 0) {
4634
- throw new Error("No plugins found in source");
4635
- }
4636
- return {
4637
- plugins: plugins.map((plugin) => ({
4638
- name: plugin.name,
4639
- description: plugin.description
4640
- }))
4641
- };
4642
- } finally {
4643
- if (prepared.cleanup) {
4644
- prepared.cleanup();
4645
- }
4646
- }
4647
- }
4648
3632
  async function installSelectedAddSkills(payload, emitProgress) {
4649
3633
  const parsedSource = parseSource(payload.sourceInput);
4650
3634
  const globalInstall = resolveAddGlobalInstall(payload.options);
@@ -4703,7 +3687,6 @@ async function installSelectedAddSkills(payload, emitProgress) {
4703
3687
  try {
4704
3688
  await applyInstallerArtifacts(outcome.canonicalDir, preparedInstaller);
4705
3689
  writeLockEntryAfterInstall({
4706
- resourceKind: "plugin",
4707
3690
  globalInstall,
4708
3691
  cwd: payload.cwd,
4709
3692
  sourceInput: payload.sourceInput,
@@ -4738,7 +3721,7 @@ async function installSelectedAddSkills(payload, emitProgress) {
4738
3721
  try {
4739
3722
  const sourceCanonical = canonicalSourceIdentity({ parsedSource });
4740
3723
  const sourcePinnedRef = parsedSource.type === "github" || parsedSource.type === "git" ? await resolveGitHeadRefAsync(prepared.basePath) : void 0;
4741
- const sourceIsSymlink = parsedSource.type === "local" ? isLocalSymlinkSource3(parsedSource.localPath) : void 0;
3724
+ const sourceIsSymlink = parsedSource.type === "local" ? isLocalSymlinkSource2(parsedSource.localPath) : void 0;
4742
3725
  const skills = await discoverSkillsAsync(prepared.basePath);
4743
3726
  const selected = skills.filter((skill) => payload.selectedSkillNames.includes(skill.name));
4744
3727
  if (selected.length === 0) {
@@ -4758,9 +3741,9 @@ async function installSelectedAddSkills(payload, emitProgress) {
4758
3741
  scaffoldInstallerConfigForSkills(missingInstallerSkillDirs, scaffoldFormat);
4759
3742
  }
4760
3743
  for (const skill of selected) {
4761
- const sourceResolvedPath = parsedSource.type === "local" ? resolveSafeRealPath3(skill.path) : void 0;
3744
+ const sourceResolvedPath = parsedSource.type === "local" ? resolveSafeRealPath2(skill.path) : void 0;
4762
3745
  await emitProgress(`installing ${skill.name}`);
4763
- const sourceSkillPath = path16.relative(prepared.basePath, skill.path) || ".";
3746
+ const sourceSkillPath = path14.relative(prepared.basePath, skill.path) || ".";
4764
3747
  const preparedInstaller = await prepareInstallerArtifacts(skill.path, payload.cwd, {
4765
3748
  sourceType: parsedSource.type,
4766
3749
  policyMode: payload.options.policyMode || "enforce",
@@ -4774,7 +3757,6 @@ async function installSelectedAddSkills(payload, emitProgress) {
4774
3757
  try {
4775
3758
  await applyInstallerArtifacts(outcome.canonicalDir, preparedInstaller);
4776
3759
  writeLockEntryAfterInstall({
4777
- resourceKind: "plugin",
4778
3760
  globalInstall,
4779
3761
  cwd: payload.cwd,
4780
3762
  sourceInput: payload.sourceInput,
@@ -4808,166 +3790,6 @@ async function installSelectedAddSkills(payload, emitProgress) {
4808
3790
  }
4809
3791
  }
4810
3792
  }
4811
- async function installSelectedAddPlugins(payload, emitProgress) {
4812
- const parsedSource = parseSource(payload.sourceInput);
4813
- const globalInstall = resolveAddGlobalInstall(payload.options);
4814
- const mode = resolveAddInstallMode(payload.options);
4815
- if (parsedSource.type === "well-known" || parsedSource.type === "catalog") {
4816
- await emitProgress("fetching selected plugins");
4817
- const remotePlugins = parsedSource.type === "well-known" ? await resolveWellKnownPlugins(parsedSource.url, payload.options) : await resolveCatalogPlugins(parsedSource.url, payload.options);
4818
- const remoteByName = new Map(remotePlugins.map((remote) => [remote.installName, remote]));
4819
- const tempCleanups = [];
4820
- const stagedSelected = [];
4821
- const sourceHashesBefore = /* @__PURE__ */ new Map();
4822
- try {
4823
- for (const pluginName of payload.selectedPluginNames) {
4824
- const remote = remoteByName.get(pluginName);
4825
- if (!remote) {
4826
- throw new Error("No matching plugins found in source");
4827
- }
4828
- const staged = await buildRemotePlugin(remote);
4829
- tempCleanups.push(staged.cleanup);
4830
- stagedSelected.push(staged.plugin);
4831
- sourceHashesBefore.set(staged.plugin.path, hashDirectory(staged.plugin.path));
4832
- }
4833
- const missingInstallerPluginDirs = listSkillsMissingInstallerConfig(
4834
- stagedSelected.map((item) => item.path)
4835
- );
4836
- const scaffoldFormat = resolveAddInstallerScaffoldFormat(
4837
- payload.options,
4838
- missingInstallerPluginDirs.length
4839
- );
4840
- if (scaffoldFormat) {
4841
- scaffoldInstallerConfigForSkills(missingInstallerPluginDirs, scaffoldFormat);
4842
- }
4843
- for (const localPlugin of stagedSelected) {
4844
- const remote = remoteByName.get(localPlugin.name);
4845
- if (!remote) {
4846
- throw new Error(
4847
- `Could not resolve remote metadata for selected plugin '${localPlugin.name}'`
4848
- );
4849
- }
4850
- const sourceHash = sourceHashesBefore.get(localPlugin.path) || hashDirectory(localPlugin.path);
4851
- const sourceCanonical = canonicalSourceIdentity({
4852
- parsedSource,
4853
- wellKnownSourceUrl: remote.sourceUrl
4854
- });
4855
- await emitProgress(`installing ${localPlugin.name}`);
4856
- const preparedInstaller = await prepareInstallerArtifacts(localPlugin.path, payload.cwd, {
4857
- sourceType: parsedSource.type,
4858
- policyMode: payload.options.policyMode || "enforce",
4859
- trustWellKnown: Boolean(payload.options.trustWellKnown)
4860
- });
4861
- const outcome = installPlugin(localPlugin, payload.agents, {
4862
- mode,
4863
- globalInstall,
4864
- cwd: payload.cwd
4865
- });
4866
- try {
4867
- await applyInstallerArtifacts(outcome.canonicalDir, preparedInstaller);
4868
- writeLockEntryAfterInstall({
4869
- globalInstall,
4870
- cwd: payload.cwd,
4871
- sourceInput: payload.sourceInput,
4872
- sourceType: parsedSource.type,
4873
- sourceCanonical,
4874
- sourcePinnedRef: sourceHash,
4875
- sourceSkillName: remote.installName,
4876
- sourceHash,
4877
- wellKnownSourceUrl: remote.sourceUrl,
4878
- outcome,
4879
- mode,
4880
- lockFormat: payload.options.lockFormat
4881
- });
4882
- } finally {
4883
- cleanupPreparedInstallerArtifacts(preparedInstaller);
4884
- }
4885
- }
4886
- } finally {
4887
- for (const cleanup of tempCleanups) {
4888
- cleanup();
4889
- }
4890
- }
4891
- return {
4892
- installedPluginNames: [...payload.selectedPluginNames],
4893
- agentCount: payload.agents.length
4894
- };
4895
- }
4896
- await emitProgress("loading source");
4897
- const prepared = await prepareSourceDirAsync(
4898
- parsedSource
4899
- );
4900
- try {
4901
- const sourceCanonical = canonicalSourceIdentity({ parsedSource });
4902
- const sourcePinnedRef = parsedSource.type === "github" || parsedSource.type === "git" ? await resolveGitHeadRefAsync(prepared.basePath) : void 0;
4903
- const sourceIsSymlink = parsedSource.type === "local" ? isLocalSymlinkSource3(parsedSource.localPath) : void 0;
4904
- const selected = await discoverPluginsAsync(prepared.basePath, payload.selectedPluginNames);
4905
- if (selected.length === 0) {
4906
- throw new Error("No matching plugins found in source");
4907
- }
4908
- const sourceHashesBefore = new Map(
4909
- selected.map((plugin) => [plugin.path, hashDirectory(plugin.path)])
4910
- );
4911
- const missingInstallerPluginDirs = listSkillsMissingInstallerConfig(
4912
- selected.map((plugin) => plugin.path)
4913
- );
4914
- const scaffoldFormat = resolveAddInstallerScaffoldFormat(
4915
- payload.options,
4916
- missingInstallerPluginDirs.length
4917
- );
4918
- if (scaffoldFormat) {
4919
- scaffoldInstallerConfigForSkills(missingInstallerPluginDirs, scaffoldFormat);
4920
- }
4921
- for (const plugin of selected) {
4922
- const sourceResolvedPath = parsedSource.type === "local" ? resolveSafeRealPath3(plugin.path) : void 0;
4923
- await emitProgress(`installing ${plugin.name}`);
4924
- const sourceSkillPath = path16.relative(prepared.basePath, plugin.path) || ".";
4925
- const preparedInstaller = await prepareInstallerArtifacts(plugin.path, payload.cwd, {
4926
- sourceType: parsedSource.type,
4927
- policyMode: payload.options.policyMode || "enforce",
4928
- trustWellKnown: Boolean(payload.options.trustWellKnown)
4929
- });
4930
- const outcome = installPlugin(plugin, payload.agents, {
4931
- mode,
4932
- globalInstall,
4933
- cwd: payload.cwd
4934
- });
4935
- try {
4936
- await applyInstallerArtifacts(outcome.canonicalDir, preparedInstaller);
4937
- writeLockEntryAfterInstall({
4938
- globalInstall,
4939
- cwd: payload.cwd,
4940
- sourceInput: payload.sourceInput,
4941
- sourceType: parsedSource.type,
4942
- sourceCanonical,
4943
- sourcePinnedRef,
4944
- sourceResolvedPath,
4945
- sourceIsSymlink,
4946
- sourceSkillName: plugin.name,
4947
- sourceSkillPath,
4948
- sourceHash: sourceHashForInstalledSkill({
4949
- parsedSource,
4950
- skillPath: plugin.path,
4951
- beforeHash: sourceHashesBefore.get(plugin.path)
4952
- }),
4953
- outcome,
4954
- mode,
4955
- lockFormat: payload.options.lockFormat
4956
- });
4957
- } finally {
4958
- cleanupPreparedInstallerArtifacts(preparedInstaller);
4959
- }
4960
- }
4961
- return {
4962
- installedPluginNames: selected.map((plugin) => plugin.name),
4963
- agentCount: payload.agents.length
4964
- };
4965
- } finally {
4966
- if (prepared.cleanup) {
4967
- prepared.cleanup();
4968
- }
4969
- }
4970
- }
4971
3793
  async function runFindFetchInventoryTask(sourceInput, options, emitProgress) {
4972
3794
  await emitProgress("parsing source");
4973
3795
  const parsedSource = parseSource(sourceInput);
@@ -5030,20 +3852,10 @@ async function executeBackgroundTask(request, emitProgress) {
5030
3852
  return await runCheckScanTask(request.payload.cwd, request.payload.options, emitProgress);
5031
3853
  case "update.assess":
5032
3854
  return await runUpdateAssessTask(request.payload.cwd, request.payload.options, emitProgress);
5033
- case "plugin.update.assess":
5034
- return await runPluginUpdateAssessTask(
5035
- request.payload.cwd,
5036
- request.payload.options,
5037
- emitProgress
5038
- );
5039
3855
  case "update.apply":
5040
3856
  return await runUpdateApplyTask(request.payload, emitProgress);
5041
- case "plugin.update.apply":
5042
- return await runPluginUpdateApplyTask(request.payload, emitProgress);
5043
3857
  case "update.migrate":
5044
3858
  return await runUpdateMigrateTask(request.payload, emitProgress);
5045
- case "plugin.update.migrate":
5046
- return await runPluginUpdateMigrateTask(request.payload, emitProgress);
5047
3859
  case "list.detectAgents":
5048
3860
  return await runListDetectAgentsTask(
5049
3861
  request.payload.cwd,
@@ -5060,14 +3872,6 @@ async function executeBackgroundTask(request, emitProgress) {
5060
3872
  );
5061
3873
  case "add.install":
5062
3874
  return await installSelectedAddSkills(request.payload, emitProgress);
5063
- case "plugin.add.fetchOrDiscover":
5064
- return await resolveAddSourcePlugins(
5065
- request.payload.sourceInput,
5066
- request.payload.options,
5067
- emitProgress
5068
- );
5069
- case "plugin.add.install":
5070
- return await installSelectedAddPlugins(request.payload, emitProgress);
5071
3875
  case "find.fetchInventory":
5072
3876
  return await runFindFetchInventoryTask(
5073
3877
  request.payload.sourceInput,