skillpm 0.0.5 → 0.0.6

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.
@@ -70,7 +70,7 @@ export async function wireSkills(cwd) {
70
70
  if (skill.configsDir) {
71
71
  log.info(`Copying config files from ${log.skill(skill.name, skill.version)}`);
72
72
  try {
73
- const copied = await copyConfigs(skill.configsDir, cwd, skill.name);
73
+ const copied = await copyConfigs(skill.configsDir, cwd, skill.name, skill.configPrefix);
74
74
  log.success(`Copied ${copied.length} config file(s) from ${skill.name}`);
75
75
  }
76
76
  catch (err) {
@@ -1,8 +1,20 @@
1
1
  /**
2
2
  * Copy all files from a skill's configs/ directory to the workspace,
3
- * auto-prefixing filenames with the package name.
3
+ * auto-prefixing filenames to avoid conflicts between installed skills.
4
+ *
5
+ * The prefix used is, in priority order:
6
+ * 1. `configPrefix` argument (from skillpm.configPrefix in package.json)
7
+ * 2. De-scoped package name (strips "@scope/" from scoped packages)
8
+ *
9
+ * Examples:
10
+ * packageName="@mcaps/spt-iq-consumption", configPrefix="consumption"
11
+ * → "consumption-briefing.md"
12
+ * packageName="@mcaps/spt-iq-consumption", no configPrefix
13
+ * → "spt-iq-consumption-briefing.md"
14
+ * packageName="my-skill", no configPrefix
15
+ * → "my-skill-briefing.md"
4
16
  */
5
- export declare function copyConfigs(configsDir: string, cwd: string, packageName: string): Promise<string[]>;
17
+ export declare function copyConfigs(configsDir: string, cwd: string, packageName: string, configPrefix?: string): Promise<string[]>;
6
18
  /**
7
19
  * Remove all config files for a package using the manifest.
8
20
  */
@@ -2,6 +2,10 @@ import { readdir, copyFile, mkdir, unlink, readFile, writeFile, stat } from 'nod
2
2
  import { join, relative, dirname, basename } from 'node:path';
3
3
  const MANIFEST_DIR = '.skillpm';
4
4
  const MANIFEST_FILE = 'manifest.json';
5
+ /** Normalize path separators to forward slashes for cross-platform consistency. */
6
+ function normalizePath(p) {
7
+ return p.replace(/\\/g, '/');
8
+ }
5
9
  async function readManifest(cwd) {
6
10
  try {
7
11
  const raw = await readFile(join(cwd, MANIFEST_DIR, MANIFEST_FILE), 'utf-8');
@@ -36,30 +40,55 @@ async function walkDir(dir, root) {
36
40
  files.push(...(await walkDir(full, root)));
37
41
  }
38
42
  else {
39
- files.push(relative(root, full));
43
+ files.push(normalizePath(relative(root, full)));
40
44
  }
41
45
  }
42
46
  return files;
43
47
  }
44
48
  /**
45
- * Auto-prefix a filename with the package name to avoid conflicts.
46
- * e.g. "reviewer.md" with package "my-skill" → "my-skill--reviewer.md"
49
+ * Strip npm scope from a package name.
50
+ * e.g. "@mcaps/spt-iq-consumption" → "spt-iq-consumption"
51
+ * "spt-iq-consumption" → "spt-iq-consumption"
52
+ */
53
+ function stripScope(packageName) {
54
+ if (packageName.startsWith('@')) {
55
+ const slash = packageName.indexOf('/');
56
+ return slash >= 0 ? packageName.slice(slash + 1) : packageName;
57
+ }
58
+ return packageName;
59
+ }
60
+ /**
61
+ * Auto-prefix a filename with the resolved prefix to avoid conflicts.
62
+ * e.g. "reviewer.md" with prefix "my-skill" → "my-skill-reviewer.md"
47
63
  */
48
- function prefixFilename(relPath, packageName) {
64
+ function prefixFilename(relPath, prefix) {
49
65
  const dir = dirname(relPath);
50
66
  const file = basename(relPath);
51
- const prefixed = `${packageName}--${file}`;
52
- return dir === '.' ? prefixed : join(dir, prefixed);
67
+ const prefixed = `${prefix}-${file}`;
68
+ return normalizePath(dir === '.' ? prefixed : join(dir, prefixed));
53
69
  }
54
70
  /**
55
71
  * Copy all files from a skill's configs/ directory to the workspace,
56
- * auto-prefixing filenames with the package name.
72
+ * auto-prefixing filenames to avoid conflicts between installed skills.
73
+ *
74
+ * The prefix used is, in priority order:
75
+ * 1. `configPrefix` argument (from skillpm.configPrefix in package.json)
76
+ * 2. De-scoped package name (strips "@scope/" from scoped packages)
77
+ *
78
+ * Examples:
79
+ * packageName="@mcaps/spt-iq-consumption", configPrefix="consumption"
80
+ * → "consumption-briefing.md"
81
+ * packageName="@mcaps/spt-iq-consumption", no configPrefix
82
+ * → "spt-iq-consumption-briefing.md"
83
+ * packageName="my-skill", no configPrefix
84
+ * → "my-skill-briefing.md"
57
85
  */
58
- export async function copyConfigs(configsDir, cwd, packageName) {
86
+ export async function copyConfigs(configsDir, cwd, packageName, configPrefix) {
87
+ const prefix = configPrefix ?? stripScope(packageName);
59
88
  const files = await walkDir(configsDir);
60
89
  const copied = [];
61
90
  for (const relPath of files) {
62
- const prefixed = prefixFilename(relPath, packageName);
91
+ const prefixed = prefixFilename(relPath, prefix);
63
92
  const src = join(configsDir, relPath);
64
93
  const dest = join(cwd, prefixed);
65
94
  await mkdir(dirname(dest), { recursive: true });
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  export declare const SkillpmFieldSchema: z.ZodOptional<z.ZodObject<{
3
3
  mcpServers: z.ZodOptional<z.ZodArray<z.ZodString>>;
4
+ configPrefix: z.ZodOptional<z.ZodString>;
4
5
  }, z.core.$strict>>;
5
6
  export type SkillpmField = z.infer<typeof SkillpmFieldSchema>;
6
7
  export interface SkillPackageJson {
@@ -21,4 +22,11 @@ export interface SkillInfo {
21
22
  legacy?: boolean;
22
23
  /** Path to configs/ directory if present (mirrors workspace layout) */
23
24
  configsDir?: string;
25
+ /**
26
+ * Optional prefix override for config file naming.
27
+ * When set, used instead of the (de-scoped) package name.
28
+ * e.g. configPrefix: "consumption" → "consumption--briefing.md"
29
+ * Declared via skillpm.configPrefix in package.json.
30
+ */
31
+ configPrefix?: string;
24
32
  }
@@ -2,6 +2,7 @@ import { z } from 'zod';
2
2
  export const SkillpmFieldSchema = z
3
3
  .object({
4
4
  mcpServers: z.array(z.string()).optional(),
5
+ configPrefix: z.string().optional(),
5
6
  })
6
7
  .strict()
7
8
  .optional();
@@ -85,6 +85,7 @@ async function tryReadSkill(pkgDir) {
85
85
  skillDir,
86
86
  mcpServers: skillpm?.mcpServers ?? [],
87
87
  configsDir: hasConfigs ? configsDir : undefined,
88
+ configPrefix: skillpm?.configPrefix,
88
89
  };
89
90
  }
90
91
  // Fallback: root SKILL.md (legacy format)
@@ -103,6 +104,7 @@ async function tryReadSkill(pkgDir) {
103
104
  mcpServers: skillpm?.mcpServers ?? [],
104
105
  legacy: true,
105
106
  configsDir: hasConfigs ? configsDir : undefined,
107
+ configPrefix: skillpm?.configPrefix,
106
108
  };
107
109
  }
108
110
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillpm",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Package manager for Agent Skills. Built on npm.",
5
5
  "type": "module",
6
6
  "bin": {