skillex 0.2.1 → 0.2.3

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.
package/dist/fs.d.ts CHANGED
@@ -48,6 +48,13 @@ export declare function writeText(filePath: string, value: string): Promise<void
48
48
  * @param targetPath - File system path to remove.
49
49
  */
50
50
  export declare function removePath(targetPath: string): Promise<void>;
51
+ /**
52
+ * Copies a file or directory recursively to a destination path.
53
+ *
54
+ * @param sourcePath - Existing source path.
55
+ * @param targetPath - Destination path.
56
+ */
57
+ export declare function copyPath(sourcePath: string, targetPath: string): Promise<void>;
51
58
  /**
52
59
  * Creates a relative symlink and reports whether the caller should fall back to copy mode.
53
60
  *
package/dist/fs.js CHANGED
@@ -89,6 +89,17 @@ export async function writeText(filePath, value) {
89
89
  export async function removePath(targetPath) {
90
90
  await fs.rm(targetPath, { recursive: true, force: true });
91
91
  }
92
+ /**
93
+ * Copies a file or directory recursively to a destination path.
94
+ *
95
+ * @param sourcePath - Existing source path.
96
+ * @param targetPath - Destination path.
97
+ */
98
+ export async function copyPath(sourcePath, targetPath) {
99
+ await ensureDir(path.dirname(targetPath));
100
+ await removePath(targetPath);
101
+ await fs.cp(sourcePath, targetPath, { recursive: true, force: true });
102
+ }
92
103
  /**
93
104
  * Creates a relative symlink and reports whether the caller should fall back to copy mode.
94
105
  *
@@ -98,14 +109,16 @@ export async function removePath(targetPath) {
98
109
  */
99
110
  export async function createSymlink(targetPath, linkPath) {
100
111
  const absoluteTarget = path.resolve(targetPath);
112
+ const relativeTarget = path.relative(path.dirname(linkPath), absoluteTarget) || ".";
101
113
  await ensureDir(path.dirname(linkPath));
102
114
  await removePath(linkPath);
103
115
  try {
104
- await fs.symlink(absoluteTarget, linkPath);
116
+ const stats = await fs.lstat(absoluteTarget);
117
+ await fs.symlink(relativeTarget, linkPath, stats.isDirectory() ? "dir" : "file");
105
118
  return {
106
119
  ok: true,
107
120
  fallback: false,
108
- relativeTarget: absoluteTarget,
121
+ relativeTarget,
109
122
  };
110
123
  }
111
124
  catch (error) {
@@ -116,7 +129,7 @@ export async function createSymlink(targetPath, linkPath) {
116
129
  return {
117
130
  ok: false,
118
131
  fallback: true,
119
- relativeTarget: absoluteTarget,
132
+ relativeTarget,
120
133
  };
121
134
  }
122
135
  throw error;
package/dist/install.d.ts CHANGED
@@ -15,7 +15,7 @@ interface InstallOptions extends ProjectOptions {
15
15
  */
16
16
  export declare function initProject(options?: ProjectOptions): Promise<InitProjectResult>;
17
17
  /**
18
- * Installs one or more catalog skills or direct GitHub skills into the local workspace state.
18
+ * Installs one or more catalog skills or direct GitHub skills into the selected Skillex state.
19
19
  *
20
20
  * @param requestedSkillIds - Requested skill ids or `owner/repo[@ref]` direct references.
21
21
  * @param options - Installation options.
@@ -33,7 +33,7 @@ export declare function installSkills(requestedSkillIds: string[], options?: Ins
33
33
  */
34
34
  export declare function updateInstalledSkills(requestedSkillIds: string[], options?: InstallOptions): Promise<UpdateInstalledSkillsResult>;
35
35
  /**
36
- * Removes installed skills from the local workspace state.
36
+ * Removes installed skills from the selected Skillex state.
37
37
  *
38
38
  * @param requestedSkillIds - Skill ids to remove.
39
39
  * @param options - Remove options.
@@ -46,11 +46,11 @@ export declare function removeSkills(requestedSkillIds: string[], options?: Proj
46
46
  *
47
47
  * @param options - Sync options.
48
48
  * @returns Sync command result.
49
- * @throws {InstallError} When workspace state is missing or invalid.
49
+ * @throws {InstallError} When the selected install scope is missing or invalid.
50
50
  */
51
51
  export declare function syncInstalledSkills(options?: ProjectOptions): Promise<SyncCommandResult>;
52
52
  /**
53
- * Reads the local workspace lockfile when it exists.
53
+ * Reads the selected Skillex lockfile when it exists.
54
54
  *
55
55
  * @param options - Project lookup options.
56
56
  * @returns Normalized lockfile state or `null` when no install exists.
package/dist/install.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as path from "node:path";
2
- import { DEFAULT_AGENT_SKILLS_DIR, DEFAULT_REF, DEFAULT_REPO, getStatePaths } from "./config.js";
2
+ import { DEFAULT_INSTALL_SCOPE, DEFAULT_REF, DEFAULT_REPO, getScopedStatePaths, } from "./config.js";
3
3
  import { confirmAction } from "./confirm.js";
4
4
  import { ensureDir, pathExists, readJson, removePath, writeJson, writeText } from "./fs.js";
5
5
  import { fetchOptionalJson, fetchOptionalText, fetchText } from "./http.js";
@@ -18,7 +18,7 @@ import { CliError, InstallError } from "./types.js";
18
18
  export async function initProject(options = {}) {
19
19
  try {
20
20
  const cwd = options.cwd || process.cwd();
21
- const statePaths = getStatePaths(cwd, options.agentSkillsDir || DEFAULT_AGENT_SKILLS_DIR);
21
+ const statePaths = resolveStatePathsForOptions(cwd, options);
22
22
  const now = getNow(options);
23
23
  await ensureDir(statePaths.stateDir);
24
24
  await ensureDir(statePaths.skillsDirPath);
@@ -41,10 +41,12 @@ export async function initProject(options = {}) {
41
41
  }
42
42
  lockfile.updatedAt = now();
43
43
  await writeJson(statePaths.lockfilePath, lockfile);
44
- // Create .gitignore for the state directory on first init
45
- const gitignorePath = path.join(statePaths.stateDir, ".gitignore");
46
- if (!(await pathExists(gitignorePath))) {
47
- await writeText(gitignorePath, ".cache/\n*.log\n");
44
+ // Create .gitignore for the local state directory on first init.
45
+ if (statePaths.scope === "local") {
46
+ const gitignorePath = path.join(statePaths.stateDir, ".gitignore");
47
+ if (!(await pathExists(gitignorePath))) {
48
+ await writeText(gitignorePath, ".cache/\n*.log\n");
49
+ }
48
50
  }
49
51
  return { created: !existing, statePaths, lockfile };
50
52
  }
@@ -53,7 +55,7 @@ export async function initProject(options = {}) {
53
55
  }
54
56
  }
55
57
  /**
56
- * Installs one or more catalog skills or direct GitHub skills into the local workspace state.
58
+ * Installs one or more catalog skills or direct GitHub skills into the selected Skillex state.
57
59
  *
58
60
  * @param requestedSkillIds - Requested skill ids or `owner/repo[@ref]` direct references.
59
61
  * @param options - Installation options.
@@ -63,7 +65,7 @@ export async function initProject(options = {}) {
63
65
  export async function installSkills(requestedSkillIds, options = {}) {
64
66
  try {
65
67
  const cwd = options.cwd || process.cwd();
66
- const statePaths = getStatePaths(cwd, options.agentSkillsDir || DEFAULT_AGENT_SKILLS_DIR);
68
+ const statePaths = resolveStatePathsForOptions(cwd, options);
67
69
  const now = getNow(options);
68
70
  const catalogLoader = options.catalogLoader || loadCatalog;
69
71
  const downloader = options.downloader || downloadSkill;
@@ -126,6 +128,7 @@ export async function installSkills(requestedSkillIds, options = {}) {
126
128
  await writeJson(statePaths.lockfilePath, lockfile);
127
129
  const autoSync = await maybeAutoSync(withAgentSkillsDir({
128
130
  cwd,
131
+ scope: options.scope,
129
132
  adapter: lockfile.adapters.active,
130
133
  enabled: lockfile.settings.autoSync,
131
134
  now,
@@ -154,13 +157,15 @@ export async function installSkills(requestedSkillIds, options = {}) {
154
157
  export async function updateInstalledSkills(requestedSkillIds, options = {}) {
155
158
  try {
156
159
  const cwd = options.cwd || process.cwd();
157
- const statePaths = getStatePaths(cwd, options.agentSkillsDir || DEFAULT_AGENT_SKILLS_DIR);
160
+ const statePaths = resolveStatePathsForOptions(cwd, options);
158
161
  const now = getNow(options);
159
162
  const catalogLoader = options.catalogLoader || loadCatalog;
160
163
  const downloader = options.downloader || downloadSkill;
161
164
  const existing = await readJson(statePaths.lockfilePath, null);
162
165
  if (!existing) {
163
- throw new InstallError("No local installation found. Run: skillex init", "LOCKFILE_MISSING");
166
+ throw new InstallError(statePaths.scope === "global"
167
+ ? "No global installation found. Run: skillex init --global --adapter <id>"
168
+ : "No local installation found. Run: skillex init", "LOCKFILE_MISSING");
164
169
  }
165
170
  const defaultSource = resolveSource(toCatalogSourceInput(options, resolvePrimarySourceOverride(options, existing)));
166
171
  const lockfile = normalizeLockfile(existing, defaultSource, now);
@@ -211,6 +216,7 @@ export async function updateInstalledSkills(requestedSkillIds, options = {}) {
211
216
  await writeJson(statePaths.lockfilePath, lockfile);
212
217
  const autoSync = await maybeAutoSync(withAgentSkillsDir({
213
218
  cwd,
219
+ scope: options.scope,
214
220
  adapter: lockfile.adapters.active,
215
221
  enabled: lockfile.settings.autoSync,
216
222
  now,
@@ -229,7 +235,7 @@ export async function updateInstalledSkills(requestedSkillIds, options = {}) {
229
235
  }
230
236
  }
231
237
  /**
232
- * Removes installed skills from the local workspace state.
238
+ * Removes installed skills from the selected Skillex state.
233
239
  *
234
240
  * @param requestedSkillIds - Skill ids to remove.
235
241
  * @param options - Remove options.
@@ -239,11 +245,13 @@ export async function updateInstalledSkills(requestedSkillIds, options = {}) {
239
245
  export async function removeSkills(requestedSkillIds, options = {}) {
240
246
  try {
241
247
  const cwd = options.cwd || process.cwd();
242
- const statePaths = getStatePaths(cwd, options.agentSkillsDir || DEFAULT_AGENT_SKILLS_DIR);
248
+ const statePaths = resolveStatePathsForOptions(cwd, options);
243
249
  const now = getNow(options);
244
250
  const existing = await readJson(statePaths.lockfilePath, null);
245
251
  if (!existing) {
246
- throw new InstallError("No local installation found. Run: skillex init", "LOCKFILE_MISSING");
252
+ throw new InstallError(statePaths.scope === "global"
253
+ ? "No global installation found. Run: skillex init --global --adapter <id>"
254
+ : "No local installation found. Run: skillex init", "LOCKFILE_MISSING");
247
255
  }
248
256
  if (!requestedSkillIds.length) {
249
257
  throw new InstallError("Provide at least one skill-id to remove.", "REMOVE_REQUIRES_SKILL");
@@ -258,7 +266,7 @@ export async function removeSkills(requestedSkillIds, options = {}) {
258
266
  missingSkills.push(skillId);
259
267
  continue;
260
268
  }
261
- await removePath(path.resolve(cwd, metadata.path));
269
+ await removePath(resolveInstalledSkillPath(cwd, metadata.path));
262
270
  delete lockfile.installed[skillId];
263
271
  removedSkills.push(skillId);
264
272
  }
@@ -266,6 +274,7 @@ export async function removeSkills(requestedSkillIds, options = {}) {
266
274
  await writeJson(statePaths.lockfilePath, lockfile);
267
275
  const autoSync = await maybeAutoSync(withAgentSkillsDir({
268
276
  cwd,
277
+ scope: options.scope,
269
278
  adapter: lockfile.adapters.active,
270
279
  enabled: lockfile.settings.autoSync,
271
280
  now,
@@ -288,16 +297,18 @@ export async function removeSkills(requestedSkillIds, options = {}) {
288
297
  *
289
298
  * @param options - Sync options.
290
299
  * @returns Sync command result.
291
- * @throws {InstallError} When workspace state is missing or invalid.
300
+ * @throws {InstallError} When the selected install scope is missing or invalid.
292
301
  */
293
302
  export async function syncInstalledSkills(options = {}) {
294
303
  try {
295
304
  const cwd = options.cwd || process.cwd();
296
- const statePaths = getStatePaths(cwd, options.agentSkillsDir || DEFAULT_AGENT_SKILLS_DIR);
305
+ const statePaths = resolveStatePathsForOptions(cwd, options);
297
306
  const now = getNow(options);
298
307
  const existing = await readJson(statePaths.lockfilePath, null);
299
308
  if (!existing) {
300
- throw new InstallError("No local installation found. Run: skillex init", "LOCKFILE_MISSING");
309
+ throw new InstallError(statePaths.scope === "global"
310
+ ? "No global installation found. Run: skillex init --global --adapter <id>"
311
+ : "No local installation found. Run: skillex init", "LOCKFILE_MISSING");
301
312
  }
302
313
  const defaultSource = resolveSource(toCatalogSourceInput(options, resolvePrimarySourceOverride(options, existing)));
303
314
  const lockfile = normalizeLockfile(existing, defaultSource, now);
@@ -311,9 +322,11 @@ export async function syncInstalledSkills(options = {}) {
311
322
  });
312
323
  const syncResult = await syncAdapterFiles({
313
324
  cwd,
325
+ scope: statePaths.scope,
314
326
  adapterId,
315
327
  statePaths,
316
328
  skills,
329
+ previousSkillIds: lockfile.sync?.skillIds || [],
317
330
  ...(options.mode ? { mode: options.mode } : {}),
318
331
  ...(options.dryRun !== undefined ? { dryRun: options.dryRun } : {}),
319
332
  });
@@ -335,6 +348,7 @@ export async function syncInstalledSkills(options = {}) {
335
348
  adapter: syncResult.adapter,
336
349
  targetPath: syncResult.targetPath,
337
350
  syncedAt: now(),
351
+ skillIds: skills.map((skill) => skill.id),
338
352
  };
339
353
  lockfile.syncMode = syncResult.syncMode;
340
354
  lockfile.updatedAt = now();
@@ -354,14 +368,14 @@ export async function syncInstalledSkills(options = {}) {
354
368
  }
355
369
  }
356
370
  /**
357
- * Reads the local workspace lockfile when it exists.
371
+ * Reads the selected Skillex lockfile when it exists.
358
372
  *
359
373
  * @param options - Project lookup options.
360
374
  * @returns Normalized lockfile state or `null` when no install exists.
361
375
  */
362
376
  export async function getInstalledSkills(options = {}) {
363
377
  const cwd = options.cwd || process.cwd();
364
- const statePaths = getStatePaths(cwd, options.agentSkillsDir || DEFAULT_AGENT_SKILLS_DIR);
378
+ const statePaths = resolveStatePathsForOptions(cwd, options);
365
379
  if (!(await pathExists(statePaths.lockfilePath))) {
366
380
  return null;
367
381
  }
@@ -380,7 +394,7 @@ export async function getInstalledSkills(options = {}) {
380
394
  */
381
395
  export async function resolveProjectSource(options = {}) {
382
396
  const cwd = options.cwd || process.cwd();
383
- const statePaths = getStatePaths(cwd, options.agentSkillsDir || DEFAULT_AGENT_SKILLS_DIR);
397
+ const statePaths = resolveStatePathsForOptions(cwd, options);
384
398
  const existing = await readJson(statePaths.lockfilePath, null);
385
399
  return resolveSource(toCatalogSourceInput(options, resolvePrimarySourceOverride(options, existing)));
386
400
  }
@@ -392,7 +406,7 @@ export async function resolveProjectSource(options = {}) {
392
406
  */
393
407
  export async function resolveProjectSources(options = {}) {
394
408
  const cwd = options.cwd || process.cwd();
395
- const statePaths = getStatePaths(cwd, options.agentSkillsDir || DEFAULT_AGENT_SKILLS_DIR);
409
+ const statePaths = resolveStatePathsForOptions(cwd, options);
396
410
  const existing = await readJson(statePaths.lockfilePath, null);
397
411
  if (options.repo) {
398
412
  return [toLockfileSource(resolveSource(toCatalogSourceInput(options, { repo: options.repo, ref: options.ref })))];
@@ -435,7 +449,7 @@ export async function loadProjectCatalogs(options = {}, catalogLoader = loadCata
435
449
  */
436
450
  export async function addProjectSource(sourceInput, options = {}) {
437
451
  const cwd = options.cwd || process.cwd();
438
- const statePaths = getStatePaths(cwd, options.agentSkillsDir || DEFAULT_AGENT_SKILLS_DIR);
452
+ const statePaths = resolveStatePathsForOptions(cwd, options);
439
453
  const now = getNow(options);
440
454
  await ensureDir(statePaths.stateDir);
441
455
  await ensureDir(statePaths.skillsDirPath);
@@ -461,11 +475,13 @@ export async function addProjectSource(sourceInput, options = {}) {
461
475
  */
462
476
  export async function removeProjectSource(repo, options = {}) {
463
477
  const cwd = options.cwd || process.cwd();
464
- const statePaths = getStatePaths(cwd, options.agentSkillsDir || DEFAULT_AGENT_SKILLS_DIR);
478
+ const statePaths = resolveStatePathsForOptions(cwd, options);
465
479
  const now = getNow(options);
466
480
  const existing = await readJson(statePaths.lockfilePath, null);
467
481
  if (!existing) {
468
- throw new InstallError("No local installation found. Run: skillex init", "LOCKFILE_MISSING");
482
+ throw new InstallError(statePaths.scope === "global"
483
+ ? "No global installation found. Run: skillex init --global --adapter <id>"
484
+ : "No local installation found. Run: skillex init", "LOCKFILE_MISSING");
469
485
  }
470
486
  const fallbackSource = resolveSource(toCatalogSourceInput(options, resolvePrimarySourceOverride(options, existing)));
471
487
  const lockfile = normalizeLockfile(existing, fallbackSource, now);
@@ -489,7 +505,7 @@ export async function removeProjectSource(repo, options = {}) {
489
505
  */
490
506
  export async function listProjectSources(options = {}) {
491
507
  const cwd = options.cwd || process.cwd();
492
- const statePaths = getStatePaths(cwd, options.agentSkillsDir || DEFAULT_AGENT_SKILLS_DIR);
508
+ const statePaths = resolveStatePathsForOptions(cwd, options);
493
509
  const existing = await readJson(statePaths.lockfilePath, null);
494
510
  const fallbackSource = resolveSource(toCatalogSourceInput(options, resolvePrimarySourceOverride(options, existing)));
495
511
  return getLockfileSources(existing, fallbackSource);
@@ -657,11 +673,14 @@ function normalizeLockfile(existing, source, now) {
657
673
  installed: existing.installed || {},
658
674
  };
659
675
  }
676
+ /** Repos that are known placeholder values written by older versions and must be ignored. */
677
+ const PLACEHOLDER_REPOS = new Set(["owner/repo"]);
660
678
  function getLockfileSources(existing, fallbackSource) {
661
679
  const legacyCatalog = getLegacyCatalog(existing);
662
680
  const configuredSources = Array.isArray(existing?.sources)
663
681
  ? existing.sources
664
682
  .filter((entry) => Boolean(entry?.repo))
683
+ .filter((entry) => !PLACEHOLDER_REPOS.has(entry.repo))
665
684
  .map((entry) => ({
666
685
  repo: entry.repo,
667
686
  ref: entry.ref || DEFAULT_REF,
@@ -671,7 +690,7 @@ function getLockfileSources(existing, fallbackSource) {
671
690
  if (configuredSources.length > 0) {
672
691
  return dedupeSources(configuredSources);
673
692
  }
674
- if (legacyCatalog?.repo) {
693
+ if (legacyCatalog?.repo && !PLACEHOLDER_REPOS.has(legacyCatalog.repo)) {
675
694
  return dedupeSources([
676
695
  {
677
696
  repo: legacyCatalog.repo,
@@ -781,7 +800,9 @@ function buildInstalledMetadata(skill, context) {
781
800
  return {
782
801
  name: skill.name,
783
802
  version: skill.version,
784
- path: toPosix(path.relative(context.cwd, path.join(context.statePaths.skillsDirPath, skill.id))),
803
+ path: context.statePaths.scope === "global"
804
+ ? toPosix(path.join(context.statePaths.skillsDirPath, skill.id))
805
+ : toPosix(path.relative(context.cwd, path.join(context.statePaths.skillsDirPath, skill.id))),
785
806
  installedAt: context.installedAt,
786
807
  compatibility: skill.compatibility,
787
808
  tags: skill.tags,
@@ -799,7 +820,7 @@ function resolveInstalledSkillIds(lockfile, requestedSkillIds) {
799
820
  const installedSet = new Set(installedIds);
800
821
  for (const skillId of requestedSkillIds) {
801
822
  if (!installedSet.has(skillId)) {
802
- throw new InstallError(`Skill "${skillId}" is not installed locally.`, "SKILL_NOT_INSTALLED");
823
+ throw new InstallError(`Skill "${skillId}" is not installed in the selected scope.`, "SKILL_NOT_INSTALLED");
803
824
  }
804
825
  }
805
826
  return requestedSkillIds;
@@ -816,6 +837,7 @@ async function maybeAutoSync(options) {
816
837
  }
817
838
  return syncInstalledSkills({
818
839
  cwd: options.cwd,
840
+ scope: options.scope || DEFAULT_INSTALL_SCOPE,
819
841
  ...(options.agentSkillsDir ? { agentSkillsDir: options.agentSkillsDir } : {}),
820
842
  ...(options.adapter ? { adapter: options.adapter } : {}),
821
843
  ...(options.mode ? { mode: options.mode } : {}),
@@ -853,6 +875,15 @@ function withAgentSkillsDir(options, agentSkillsDir) {
853
875
  ...(agentSkillsDir ? { agentSkillsDir } : {}),
854
876
  };
855
877
  }
878
+ function resolveStatePathsForOptions(cwd, options) {
879
+ return getScopedStatePaths(cwd, {
880
+ scope: options.scope || DEFAULT_INSTALL_SCOPE,
881
+ baseDir: options.agentSkillsDir,
882
+ });
883
+ }
884
+ function resolveInstalledSkillPath(cwd, skillPath) {
885
+ return path.isAbsolute(skillPath) ? skillPath : path.resolve(cwd, skillPath);
886
+ }
856
887
  async function confirmDirectInstall(skillRef, options) {
857
888
  const warning = `Warning: ${skillRef} will be installed directly from GitHub and has not been verified by the active catalog.`;
858
889
  (options.warn || console.error)(warning);
package/dist/runner.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import * as path from "node:path";
3
- import { DEFAULT_AGENT_SKILLS_DIR, getStatePaths } from "./config.js";
3
+ import { DEFAULT_INSTALL_SCOPE, getScopedStatePaths } from "./config.js";
4
4
  import { confirmAction } from "./confirm.js";
5
5
  import { readJson } from "./fs.js";
6
6
  import { CliError } from "./types.js";
@@ -32,16 +32,21 @@ export function parseSkillCommandReference(value) {
32
32
  */
33
33
  export async function runSkillScript(skillId, commandName, options = {}) {
34
34
  const cwd = options.cwd || process.cwd();
35
- const statePaths = getStatePaths(cwd, options.agentSkillsDir || DEFAULT_AGENT_SKILLS_DIR);
35
+ const statePaths = getScopedStatePaths(cwd, {
36
+ scope: options.scope || DEFAULT_INSTALL_SCOPE,
37
+ baseDir: options.agentSkillsDir,
38
+ });
36
39
  const lockfile = (await readJson(statePaths.lockfilePath, null)) || null;
37
40
  if (!lockfile) {
38
- throw new CliError("Nenhuma instalacao local encontrada. Rode: skillex init", "LOCKFILE_MISSING");
41
+ throw new CliError(statePaths.scope === "global"
42
+ ? "Nenhuma instalacao global encontrada. Rode: skillex init --global --adapter <id>"
43
+ : "Nenhuma instalacao local encontrada. Rode: skillex init", "LOCKFILE_MISSING");
39
44
  }
40
45
  const metadata = lockfile.installed?.[skillId];
41
46
  if (!metadata?.path) {
42
47
  throw new CliError(`Skill "${skillId}" nao esta instalada.`, "SKILL_NOT_INSTALLED");
43
48
  }
44
- const skillDir = path.resolve(cwd, metadata.path);
49
+ const skillDir = path.isAbsolute(metadata.path) ? metadata.path : path.resolve(cwd, metadata.path);
45
50
  const manifest = (await readJson(path.join(skillDir, "skill.json"), {})) || {};
46
51
  const scripts = manifest.scripts || {};
47
52
  const script = scripts[commandName];
package/dist/sync.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { InstalledSkillDocument, LockfileState, PreparedSyncResult, SyncOptions, SyncResult } from "./types.js";
2
2
  /**
3
- * Loads installed skill documents from the local workspace state directory.
3
+ * Loads installed skill documents from the selected Skillex state directory.
4
4
  *
5
5
  * @param context - Workspace root and lockfile context.
6
6
  * @returns Installed skill documents used for sync rendering.
@@ -10,7 +10,7 @@ export declare function loadInstalledSkillDocuments(context: {
10
10
  lockfile: LockfileState;
11
11
  }): Promise<InstalledSkillDocument[]>;
12
12
  /**
13
- * Synchronizes installed skills into the target file consumed by an adapter.
13
+ * Synchronizes installed skills into the target consumed by an adapter.
14
14
  *
15
15
  * @param options - Sync execution options.
16
16
  * @returns Final sync result.