wyvrnpm 2.11.0 → 2.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +1914 -1905
  2. package/bin/{wyvrn.js → wyvrnpm.js} +31 -0
  3. package/cmake/cpp.cmake +9 -9
  4. package/cmake/functions.cmake +224 -224
  5. package/cmake/macros.cmake +284 -284
  6. package/package.json +3 -2
  7. package/src/auth.js +66 -66
  8. package/src/binary-dir.js +95 -0
  9. package/src/bootstrap/cookbook.js +196 -196
  10. package/src/bootstrap/detect.js +150 -150
  11. package/src/bootstrap/index.js +220 -220
  12. package/src/bootstrap/version.js +72 -72
  13. package/src/build/cache.js +344 -344
  14. package/src/build/clone.js +170 -170
  15. package/src/build/cmake.js +342 -342
  16. package/src/build/index.js +299 -297
  17. package/src/build/msvc-env.js +260 -260
  18. package/src/build/recipe.js +188 -188
  19. package/src/commands/add.js +141 -141
  20. package/src/commands/bootstrap.js +96 -96
  21. package/src/commands/build.js +482 -452
  22. package/src/commands/cache.js +189 -189
  23. package/src/commands/clean.js +80 -80
  24. package/src/commands/configure.js +92 -92
  25. package/src/commands/init.js +70 -70
  26. package/src/commands/install-skill.js +115 -115
  27. package/src/commands/install.js +730 -674
  28. package/src/commands/link.js +320 -320
  29. package/src/commands/profile.js +237 -237
  30. package/src/commands/publish.js +584 -555
  31. package/src/commands/show.js +252 -252
  32. package/src/commands/version.js +187 -187
  33. package/src/compat.js +273 -273
  34. package/src/conf/index.js +415 -391
  35. package/src/conf/namespaces.js +94 -94
  36. package/src/context.js +230 -230
  37. package/src/http-fetch.js +53 -53
  38. package/src/ignore.js +118 -118
  39. package/src/logger.js +122 -122
  40. package/src/options.js +303 -303
  41. package/src/settings-overrides.js +152 -152
  42. package/src/toolchain/deps.js +164 -164
  43. package/src/toolchain/index.js +212 -212
  44. package/src/toolchain/presets.js +356 -340
  45. package/src/toolchain/template.cmake +77 -77
  46. package/src/upload-built.js +265 -265
  47. package/src/version-range.js +301 -301
  48. package/src/zip-safe.js +52 -52
  49. package/src/zip-stream.js +126 -0
@@ -1,92 +1,92 @@
1
- 'use strict';
2
-
3
- const { readConfig, writeConfig, getConfigPath } = require('../config');
4
- const log = require('../logger');
5
-
6
- /** wyvrnpm configure list */
7
- function list() {
8
- const config = readConfig();
9
-
10
- console.log(`Config: ${getConfigPath()}\n`);
11
-
12
- printSection('Install Sources', config.installSources);
13
- console.log('');
14
- printSection('Publish Sources', config.publishSources);
15
- }
16
-
17
- function printSection(title, sources) {
18
- console.log(`${title}:`);
19
- if (sources.length === 0) {
20
- console.log(' (none)');
21
- return;
22
- }
23
- for (const src of sources) {
24
- const auth = authLabel(src);
25
- console.log(` ${src.name.padEnd(20)} ${src.url}${auth ? ' ' + auth : ''}`);
26
- }
27
- }
28
-
29
- function authLabel(src) {
30
- if (src.profile) return `[profile: ${src.profile}]`;
31
- if (src.tokenEnv) return `[token-env: ${src.tokenEnv}]`;
32
- if (src.token) return '[token: ***]';
33
- return '';
34
- }
35
-
36
- /**
37
- * wyvrnpm configure add-source --kind install|publish --name <name> --url <url>
38
- * [--profile <profile>] [--token <token>]
39
- */
40
- function addSource(argv) {
41
- const { kind, name, url, profile, token } = argv;
42
- // yargs delivers --token-env as argv['token-env']; accept both shapes
43
- // so programmatic callers with camelCase also work.
44
- const tokenEnv = argv.tokenEnv ?? argv['token-env'];
45
-
46
- if (token && tokenEnv) {
47
- log.error('Pass --token OR --token-env, not both. --token-env is preferred for CI and shared setups (EVALUATION.md S8).');
48
- process.exit(1);
49
- }
50
-
51
- const config = readConfig();
52
- const key = kind === 'install' ? 'installSources' : 'publishSources';
53
-
54
- const entry = { name, url };
55
- if (profile) entry.profile = profile;
56
- if (token) entry.token = token;
57
- if (tokenEnv) entry.tokenEnv = tokenEnv;
58
-
59
- const idx = config[key].findIndex((s) => s.name === name);
60
- if (idx !== -1) {
61
- config[key][idx] = entry;
62
- log.info(`Updated ${kind} source "${name}"`);
63
- } else {
64
- config[key].push(entry);
65
- log.info(`Added ${kind} source "${name}"`);
66
- }
67
-
68
- writeConfig(config);
69
- log.info(`Saved to ${getConfigPath()}`);
70
- }
71
-
72
- /**
73
- * wyvrnpm configure remove-source --kind install|publish --name <name>
74
- */
75
- function removeSource(argv) {
76
- const { kind, name } = argv;
77
- const config = readConfig();
78
- const key = kind === 'install' ? 'installSources' : 'publishSources';
79
-
80
- const before = config[key].length;
81
- config[key] = config[key].filter((s) => s.name !== name);
82
-
83
- if (config[key].length === before) {
84
- log.error(`No ${kind} source named "${name}"`);
85
- process.exit(1);
86
- }
87
-
88
- writeConfig(config);
89
- log.info(`Removed ${kind} source "${name}"`);
90
- }
91
-
92
- module.exports = { list, addSource, removeSource };
1
+ 'use strict';
2
+
3
+ const { readConfig, writeConfig, getConfigPath } = require('../config');
4
+ const log = require('../logger');
5
+
6
+ /** wyvrnpm configure list */
7
+ function list() {
8
+ const config = readConfig();
9
+
10
+ console.log(`Config: ${getConfigPath()}\n`);
11
+
12
+ printSection('Install Sources', config.installSources);
13
+ console.log('');
14
+ printSection('Publish Sources', config.publishSources);
15
+ }
16
+
17
+ function printSection(title, sources) {
18
+ console.log(`${title}:`);
19
+ if (sources.length === 0) {
20
+ console.log(' (none)');
21
+ return;
22
+ }
23
+ for (const src of sources) {
24
+ const auth = authLabel(src);
25
+ console.log(` ${src.name.padEnd(20)} ${src.url}${auth ? ' ' + auth : ''}`);
26
+ }
27
+ }
28
+
29
+ function authLabel(src) {
30
+ if (src.profile) return `[profile: ${src.profile}]`;
31
+ if (src.tokenEnv) return `[token-env: ${src.tokenEnv}]`;
32
+ if (src.token) return '[token: ***]';
33
+ return '';
34
+ }
35
+
36
+ /**
37
+ * wyvrnpm configure add-source --kind install|publish --name <name> --url <url>
38
+ * [--profile <profile>] [--token <token>]
39
+ */
40
+ function addSource(argv) {
41
+ const { kind, name, url, profile, token } = argv;
42
+ // yargs delivers --token-env as argv['token-env']; accept both shapes
43
+ // so programmatic callers with camelCase also work.
44
+ const tokenEnv = argv.tokenEnv ?? argv['token-env'];
45
+
46
+ if (token && tokenEnv) {
47
+ log.error('Pass --token OR --token-env, not both. --token-env is preferred for CI and shared setups (EVALUATION.md S8).');
48
+ process.exit(1);
49
+ }
50
+
51
+ const config = readConfig();
52
+ const key = kind === 'install' ? 'installSources' : 'publishSources';
53
+
54
+ const entry = { name, url };
55
+ if (profile) entry.profile = profile;
56
+ if (token) entry.token = token;
57
+ if (tokenEnv) entry.tokenEnv = tokenEnv;
58
+
59
+ const idx = config[key].findIndex((s) => s.name === name);
60
+ if (idx !== -1) {
61
+ config[key][idx] = entry;
62
+ log.info(`Updated ${kind} source "${name}"`);
63
+ } else {
64
+ config[key].push(entry);
65
+ log.info(`Added ${kind} source "${name}"`);
66
+ }
67
+
68
+ writeConfig(config);
69
+ log.info(`Saved to ${getConfigPath()}`);
70
+ }
71
+
72
+ /**
73
+ * wyvrnpm configure remove-source --kind install|publish --name <name>
74
+ */
75
+ function removeSource(argv) {
76
+ const { kind, name } = argv;
77
+ const config = readConfig();
78
+ const key = kind === 'install' ? 'installSources' : 'publishSources';
79
+
80
+ const before = config[key].length;
81
+ config[key] = config[key].filter((s) => s.name !== name);
82
+
83
+ if (config[key].length === before) {
84
+ log.error(`No ${kind} source named "${name}"`);
85
+ process.exit(1);
86
+ }
87
+
88
+ writeConfig(config);
89
+ log.info(`Removed ${kind} source "${name}"`);
90
+ }
91
+
92
+ module.exports = { list, addSource, removeSource };
@@ -1,70 +1,70 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const { defaultManifest, writeManifest } = require('../manifest');
6
- const { LOCAL_OVERLAY_FILENAME } = require('../conf');
7
- const log = require('../logger');
8
-
9
- // The wyvrn.local.json overlay must never be committed — it is by design
10
- // per-developer state. init writes a gitignore entry; existing projects
11
- // get a one-line nudge from install (see install.js) when the file is
12
- // present but not ignored.
13
- const GITIGNORE_MARKER_LINE = '# wyvrnpm — per-developer conf overlay (never commit)';
14
-
15
- function ensureGitignoreEntry(rootDir) {
16
- const gitignorePath = path.join(rootDir, '.gitignore');
17
- const desiredLines = [GITIGNORE_MARKER_LINE, LOCAL_OVERLAY_FILENAME];
18
-
19
- let existing = '';
20
- if (fs.existsSync(gitignorePath)) {
21
- existing = fs.readFileSync(gitignorePath, 'utf8');
22
- const hasEntry = existing.split(/\r?\n/).some((l) => l.trim() === LOCAL_OVERLAY_FILENAME);
23
- if (hasEntry) return { action: 'already-present', path: gitignorePath };
24
- }
25
-
26
- const needsLeadingNewline = existing.length > 0 && !existing.endsWith('\n');
27
- const block = (needsLeadingNewline ? '\n' : '') + desiredLines.join('\n') + '\n';
28
- fs.writeFileSync(gitignorePath, existing + block, 'utf8');
29
- return {
30
- action: existing.length > 0 ? 'appended' : 'created',
31
- path: gitignorePath,
32
- };
33
- }
34
-
35
- /**
36
- * Initialises a new wyvrn.json manifest in the project root.
37
- * If a manifest already exists the command exits early without overwriting it.
38
- *
39
- * @param {object} argv - Parsed yargs arguments.
40
- * @param {string} argv.manifest - Path to the manifest file.
41
- * @param {string} argv.root - Project root directory.
42
- * @returns {Promise<void>}
43
- */
44
- async function init(argv) {
45
- const manifestPath = path.resolve(argv.manifest);
46
- const rootDir = path.resolve(argv.root);
47
-
48
- if (fs.existsSync(manifestPath)) {
49
- log.info(`Manifest already exists at ${manifestPath}`);
50
- return;
51
- }
52
-
53
- const projectName = path.basename(rootDir);
54
- const manifest = defaultManifest(projectName);
55
-
56
- writeManifest(manifestPath, manifest);
57
-
58
- const gitignore = ensureGitignoreEntry(rootDir);
59
- if (gitignore.action === 'created') {
60
- log.info(`Created .gitignore at ${gitignore.path} with ${LOCAL_OVERLAY_FILENAME} exclusion`);
61
- } else if (gitignore.action === 'appended') {
62
- log.info(`Added ${LOCAL_OVERLAY_FILENAME} to ${gitignore.path}`);
63
- }
64
-
65
- log.success(`Created manifest at ${manifestPath}`);
66
- }
67
-
68
- module.exports = init;
69
- module.exports.ensureGitignoreEntry = ensureGitignoreEntry;
70
- module.exports.GITIGNORE_MARKER_LINE = GITIGNORE_MARKER_LINE;
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { defaultManifest, writeManifest } = require('../manifest');
6
+ const { LOCAL_OVERLAY_FILENAME } = require('../conf');
7
+ const log = require('../logger');
8
+
9
+ // The wyvrn.local.json overlay must never be committed — it is by design
10
+ // per-developer state. init writes a gitignore entry; existing projects
11
+ // get a one-line nudge from install (see install.js) when the file is
12
+ // present but not ignored.
13
+ const GITIGNORE_MARKER_LINE = '# wyvrnpm — per-developer conf overlay (never commit)';
14
+
15
+ function ensureGitignoreEntry(rootDir) {
16
+ const gitignorePath = path.join(rootDir, '.gitignore');
17
+ const desiredLines = [GITIGNORE_MARKER_LINE, LOCAL_OVERLAY_FILENAME];
18
+
19
+ let existing = '';
20
+ if (fs.existsSync(gitignorePath)) {
21
+ existing = fs.readFileSync(gitignorePath, 'utf8');
22
+ const hasEntry = existing.split(/\r?\n/).some((l) => l.trim() === LOCAL_OVERLAY_FILENAME);
23
+ if (hasEntry) return { action: 'already-present', path: gitignorePath };
24
+ }
25
+
26
+ const needsLeadingNewline = existing.length > 0 && !existing.endsWith('\n');
27
+ const block = (needsLeadingNewline ? '\n' : '') + desiredLines.join('\n') + '\n';
28
+ fs.writeFileSync(gitignorePath, existing + block, 'utf8');
29
+ return {
30
+ action: existing.length > 0 ? 'appended' : 'created',
31
+ path: gitignorePath,
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Initialises a new wyvrn.json manifest in the project root.
37
+ * If a manifest already exists the command exits early without overwriting it.
38
+ *
39
+ * @param {object} argv - Parsed yargs arguments.
40
+ * @param {string} argv.manifest - Path to the manifest file.
41
+ * @param {string} argv.root - Project root directory.
42
+ * @returns {Promise<void>}
43
+ */
44
+ async function init(argv) {
45
+ const manifestPath = path.resolve(argv.manifest);
46
+ const rootDir = path.resolve(argv.root);
47
+
48
+ if (fs.existsSync(manifestPath)) {
49
+ log.info(`Manifest already exists at ${manifestPath}`);
50
+ return;
51
+ }
52
+
53
+ const projectName = path.basename(rootDir);
54
+ const manifest = defaultManifest(projectName);
55
+
56
+ writeManifest(manifestPath, manifest);
57
+
58
+ const gitignore = ensureGitignoreEntry(rootDir);
59
+ if (gitignore.action === 'created') {
60
+ log.info(`Created .gitignore at ${gitignore.path} with ${LOCAL_OVERLAY_FILENAME} exclusion`);
61
+ } else if (gitignore.action === 'appended') {
62
+ log.info(`Added ${LOCAL_OVERLAY_FILENAME} to ${gitignore.path}`);
63
+ }
64
+
65
+ log.success(`Created manifest at ${manifestPath}`);
66
+ }
67
+
68
+ module.exports = init;
69
+ module.exports.ensureGitignoreEntry = ensureGitignoreEntry;
70
+ module.exports.GITIGNORE_MARKER_LINE = GITIGNORE_MARKER_LINE;
@@ -1,115 +1,115 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const os = require('os');
5
- const path = require('path');
6
- const AdmZip = require('adm-zip');
7
-
8
- const { assertAllSafeZipEntryNames } = require('../zip-safe');
9
- const log = require('../logger');
10
-
11
- // ---------------------------------------------------------------------------
12
- // `wyvrnpm install-skill --claude`
13
- //
14
- // Copies the bundled wyvrnpm Agent Skill (claude/skills/wyvrnpm.skill) into
15
- // the user's Claude Code skills directory so Claude picks it up automatically.
16
- //
17
- // Works for both install shapes:
18
- // - `npm i -g wyvrnpm` → skill zip ships under <pkg>/claude/skills/
19
- // - `git clone` of this repo → same path, just not from node_modules
20
- //
21
- // Destination, per target:
22
- // --claude → %USERPROFILE%\.claude\skills\wyvrnpm\ (Windows)
23
- // ~/.claude/skills/wyvrnpm/ (macOS / Linux)
24
- // ---------------------------------------------------------------------------
25
-
26
- const SKILL_NAME = 'wyvrnpm';
27
-
28
- function bundledSkillPath() {
29
- // __dirname = <pkg>/src/commands; the .skill zip lives at
30
- // <pkg>/claude/skills/wyvrnpm.skill.
31
- return path.resolve(__dirname, '..', '..', 'claude', 'skills', `${SKILL_NAME}.skill`);
32
- }
33
-
34
- function claudeSkillsDir() {
35
- const home = os.homedir();
36
- if (!home) throw new Error('cannot resolve user home directory');
37
- return path.join(home, '.claude', 'skills');
38
- }
39
-
40
- function rmrf(target) {
41
- if (!fs.existsSync(target)) return;
42
- fs.rmSync(target, { recursive: true, force: true });
43
- }
44
-
45
- function installForClaude({ force, dryRun }) {
46
- const zipPath = bundledSkillPath();
47
- if (!fs.existsSync(zipPath)) {
48
- log.error(`bundled skill not found at ${zipPath}`);
49
- log.error('this is a packaging bug — please file an issue');
50
- process.exit(1);
51
- }
52
-
53
- const skillsDir = claudeSkillsDir();
54
- const destDir = path.join(skillsDir, SKILL_NAME);
55
- const destZip = path.join(skillsDir, `${SKILL_NAME}.skill`);
56
-
57
- log.info(`Source : ${zipPath}`);
58
- log.info(`Destination : ${destDir}`);
59
-
60
- if (fs.existsSync(destDir) && !force) {
61
- log.warn(`${destDir} already exists`);
62
- log.warn('re-run with --force to overwrite');
63
- process.exit(1);
64
- }
65
-
66
- if (dryRun) {
67
- log.info('(--dry-run) would remove existing skill dir and zip, then extract fresh copy');
68
- return;
69
- }
70
-
71
- fs.mkdirSync(skillsDir, { recursive: true });
72
- rmrf(destDir);
73
- rmrf(destZip);
74
-
75
- const zip = new AdmZip(zipPath);
76
- // Zip Slip defense-in-depth (EVALUATION.md S2). The shipped .skill zip
77
- // is trusted-by-provenance, but validating consistently keeps the
78
- // attack surface flat across all extraction call sites.
79
- assertAllSafeZipEntryNames(
80
- zip.getEntries().map((e) => e.entryName),
81
- `${SKILL_NAME}.skill`,
82
- );
83
- zip.extractAllTo(skillsDir, /* overwrite */ true);
84
- fs.copyFileSync(zipPath, destZip);
85
-
86
- if (!fs.existsSync(path.join(destDir, 'SKILL.md'))) {
87
- log.error(`extraction produced no SKILL.md under ${destDir}`);
88
- log.error('the bundled .skill zip may be malformed');
89
- process.exit(1);
90
- }
91
-
92
- log.success(`Installed ${SKILL_NAME} skill to ${destDir}`);
93
- log.info('Claude Code will pick it up on next session start.');
94
- }
95
-
96
- async function installSkill(argv) {
97
- const targets = [];
98
- if (argv.claude) targets.push('claude');
99
-
100
- if (targets.length === 0) {
101
- log.error('no target specified — pass --claude');
102
- log.error('(more targets may be added in future releases)');
103
- process.exit(1);
104
- }
105
-
106
- for (const target of targets) {
107
- if (target === 'claude') {
108
- installForClaude({ force: !!argv.force, dryRun: !!argv['dry-run'] });
109
- }
110
- }
111
- }
112
-
113
- module.exports = installSkill;
114
- module.exports.bundledSkillPath = bundledSkillPath;
115
- module.exports.claudeSkillsDir = claudeSkillsDir;
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+ const AdmZip = require('adm-zip');
7
+
8
+ const { assertAllSafeZipEntryNames } = require('../zip-safe');
9
+ const log = require('../logger');
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // `wyvrnpm install-skill --claude`
13
+ //
14
+ // Copies the bundled wyvrnpm Agent Skill (claude/skills/wyvrnpm.skill) into
15
+ // the user's Claude Code skills directory so Claude picks it up automatically.
16
+ //
17
+ // Works for both install shapes:
18
+ // - `npm i -g wyvrnpm` → skill zip ships under <pkg>/claude/skills/
19
+ // - `git clone` of this repo → same path, just not from node_modules
20
+ //
21
+ // Destination, per target:
22
+ // --claude → %USERPROFILE%\.claude\skills\wyvrnpm\ (Windows)
23
+ // ~/.claude/skills/wyvrnpm/ (macOS / Linux)
24
+ // ---------------------------------------------------------------------------
25
+
26
+ const SKILL_NAME = 'wyvrnpm';
27
+
28
+ function bundledSkillPath() {
29
+ // __dirname = <pkg>/src/commands; the .skill zip lives at
30
+ // <pkg>/claude/skills/wyvrnpm.skill.
31
+ return path.resolve(__dirname, '..', '..', 'claude', 'skills', `${SKILL_NAME}.skill`);
32
+ }
33
+
34
+ function claudeSkillsDir() {
35
+ const home = os.homedir();
36
+ if (!home) throw new Error('cannot resolve user home directory');
37
+ return path.join(home, '.claude', 'skills');
38
+ }
39
+
40
+ function rmrf(target) {
41
+ if (!fs.existsSync(target)) return;
42
+ fs.rmSync(target, { recursive: true, force: true });
43
+ }
44
+
45
+ function installForClaude({ force, dryRun }) {
46
+ const zipPath = bundledSkillPath();
47
+ if (!fs.existsSync(zipPath)) {
48
+ log.error(`bundled skill not found at ${zipPath}`);
49
+ log.error('this is a packaging bug — please file an issue');
50
+ process.exit(1);
51
+ }
52
+
53
+ const skillsDir = claudeSkillsDir();
54
+ const destDir = path.join(skillsDir, SKILL_NAME);
55
+ const destZip = path.join(skillsDir, `${SKILL_NAME}.skill`);
56
+
57
+ log.info(`Source : ${zipPath}`);
58
+ log.info(`Destination : ${destDir}`);
59
+
60
+ if (fs.existsSync(destDir) && !force) {
61
+ log.warn(`${destDir} already exists`);
62
+ log.warn('re-run with --force to overwrite');
63
+ process.exit(1);
64
+ }
65
+
66
+ if (dryRun) {
67
+ log.info('(--dry-run) would remove existing skill dir and zip, then extract fresh copy');
68
+ return;
69
+ }
70
+
71
+ fs.mkdirSync(skillsDir, { recursive: true });
72
+ rmrf(destDir);
73
+ rmrf(destZip);
74
+
75
+ const zip = new AdmZip(zipPath);
76
+ // Zip Slip defense-in-depth (EVALUATION.md S2). The shipped .skill zip
77
+ // is trusted-by-provenance, but validating consistently keeps the
78
+ // attack surface flat across all extraction call sites.
79
+ assertAllSafeZipEntryNames(
80
+ zip.getEntries().map((e) => e.entryName),
81
+ `${SKILL_NAME}.skill`,
82
+ );
83
+ zip.extractAllTo(skillsDir, /* overwrite */ true);
84
+ fs.copyFileSync(zipPath, destZip);
85
+
86
+ if (!fs.existsSync(path.join(destDir, 'SKILL.md'))) {
87
+ log.error(`extraction produced no SKILL.md under ${destDir}`);
88
+ log.error('the bundled .skill zip may be malformed');
89
+ process.exit(1);
90
+ }
91
+
92
+ log.success(`Installed ${SKILL_NAME} skill to ${destDir}`);
93
+ log.info('Claude Code will pick it up on next session start.');
94
+ }
95
+
96
+ async function installSkill(argv) {
97
+ const targets = [];
98
+ if (argv.claude) targets.push('claude');
99
+
100
+ if (targets.length === 0) {
101
+ log.error('no target specified — pass --claude');
102
+ log.error('(more targets may be added in future releases)');
103
+ process.exit(1);
104
+ }
105
+
106
+ for (const target of targets) {
107
+ if (target === 'claude') {
108
+ installForClaude({ force: !!argv.force, dryRun: !!argv['dry-run'] });
109
+ }
110
+ }
111
+ }
112
+
113
+ module.exports = installSkill;
114
+ module.exports.bundledSkillPath = bundledSkillPath;
115
+ module.exports.claudeSkillsDir = claudeSkillsDir;