synskill 0.1.5 → 0.1.7

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 (3) hide show
  1. package/README.md +69 -69
  2. package/bin/synskill.js +83 -16
  3. package/package.json +33 -33
package/README.md CHANGED
@@ -1,69 +1,69 @@
1
- # synskill
2
-
3
- `synskill` is a public npm CLI that installs skills from a private Git repository you already have access to.
4
-
5
- ## Usage
6
-
7
- ```bash
8
- npx synskill
9
- ```
10
-
11
- ## Install A Skill
12
-
13
- Install one skill directly into the current repo:
14
-
15
- ```bash
16
- npx synskill <skill-name> --project
17
- ```
18
-
19
- Examples:
20
-
21
- ```bash
22
- npx synskill pr-review --project
23
- npx synskill bootstrap-device-repo --project
24
- npx synskill skill-creator --global
25
- ```
26
-
27
- Omit `--project` for a global install. Add `--copy` if you want copies instead of symlinks.
28
-
29
- ## Requirements
30
-
31
- - Node.js 18 or newer
32
- - Git available in `PATH`
33
- - Access to the private repository that contains your skills
34
-
35
- ## Common Commands
36
-
37
- ```bash
38
- npx synskill
39
- npx synskill pr-review --project
40
- npx synskill install pr-review --project
41
- npx synskill add pr-review --project
42
- npx synskill list
43
- npx synskill skill-creator --global
44
- npx synskill install --skills my-skill --project
45
- npx synskill install --skills my-skill --project --copy
46
- npx synskill update
47
- ```
48
-
49
- ## Private Repo Override
50
-
51
- ```bash
52
- npx synskill --repo-url <git-url>
53
- ```
54
-
55
- or:
56
-
57
- ```bash
58
- SKILLS_REPO_URL=<git-url> npx synskill
59
- ```
60
-
61
- ## Notes
62
-
63
- - The CLI keeps a local cache of the private skills repository to make repeated runs fast.
64
- - You can install a specific skill directly with `npx synskill install <skill-name>` or `npx synskill <skill-name>`.
65
- - `skill-creator` is fetched on demand from `https://github.com/anthropics/skills/tree/main/skills/skill-creator` so it stays on the upstream latest version without being stored in this repo.
66
- - `skill-creator` is explicit-install only and does not appear in the default interactive skills list.
67
- - By default it symlinks each selected target directly to the source skill directory; use `--copy` to write independent copies instead.
68
- - `update` forces a cache refresh.
69
- - `--no-pull` uses the existing cache without refreshing it.
1
+ # synskill
2
+
3
+ `synskill` is a public npm CLI that installs skills from a private Git repository you already have access to.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npx synskill
9
+ ```
10
+
11
+ ## Install A Skill
12
+
13
+ Install one skill directly into the current repo:
14
+
15
+ ```bash
16
+ npx synskill <skill-name> --project
17
+ ```
18
+
19
+ Examples:
20
+
21
+ ```bash
22
+ npx synskill pr-review --project
23
+ npx synskill bootstrap-device-repo --project
24
+ npx synskill skill-creator --global
25
+ ```
26
+
27
+ Omit `--project` for a global install. Add `--copy` if you want copies instead of symlinks.
28
+
29
+ ## Requirements
30
+
31
+ - Node.js 18 or newer
32
+ - Git available in `PATH`
33
+ - Access to the private repository that contains your skills
34
+
35
+ ## Common Commands
36
+
37
+ ```bash
38
+ npx synskill
39
+ npx synskill pr-review --project
40
+ npx synskill install pr-review --project
41
+ npx synskill add pr-review --project
42
+ npx synskill list
43
+ npx synskill skill-creator --global
44
+ npx synskill install --skills my-skill --project
45
+ npx synskill install --skills my-skill --project --copy
46
+ npx synskill update
47
+ ```
48
+
49
+ ## Private Repo Override
50
+
51
+ ```bash
52
+ npx synskill --repo-url <git-url>
53
+ ```
54
+
55
+ or:
56
+
57
+ ```bash
58
+ SKILLS_REPO_URL=<git-url> npx synskill
59
+ ```
60
+
61
+ ## Notes
62
+
63
+ - The CLI keeps a local cache of the private skills repository to make repeated runs fast.
64
+ - You can install a specific skill directly with `npx synskill install <skill-name>` or `npx synskill <skill-name>`.
65
+ - `skill-creator` is fetched on demand from `https://github.com/anthropics/skills/tree/main/skills/skill-creator` so it stays on the upstream latest version without being stored in this repo.
66
+ - `skill-creator` is explicit-install only and does not appear in the default interactive skills list.
67
+ - By default it symlinks each selected target directly to the source skill directory; use `--copy` to write independent copies instead.
68
+ - `update` forces a cache refresh.
69
+ - `--no-pull` uses the existing cache without refreshing it.
package/bin/synskill.js CHANGED
@@ -180,6 +180,7 @@ var require_constants = __commonJS({
180
180
  folder: ".claude"
181
181
  }
182
182
  ];
183
+ var DEFAULT_TARGET_KEYS2 = INSTALL_TARGETS2.map((target) => target.key);
183
184
  function userHome() {
184
185
  return process.env.USERPROFILE || os.homedir();
185
186
  }
@@ -203,6 +204,7 @@ var require_constants = __commonJS({
203
204
  CLI_NAME: CLI_NAME2,
204
205
  DEFAULT_BRANCH: DEFAULT_BRANCH2,
205
206
  DEFAULT_REPO_URL: DEFAULT_REPO_URL2,
207
+ DEFAULT_TARGET_KEYS: DEFAULT_TARGET_KEYS2,
206
208
  INSTALL_TARGETS: INSTALL_TARGETS2,
207
209
  LEGACY_CACHE_DIR_NAMES,
208
210
  LEGACY_CACHE_METADATA_NAMES,
@@ -419,6 +421,30 @@ var require_git = __commonJS({
419
421
  });
420
422
  }
421
423
  }
424
+ function sshToHttpsUrl(url) {
425
+ if (!url || typeof url !== "string") {
426
+ return null;
427
+ }
428
+ const scpMatch = url.match(/^git@([^:]+):(.+)$/);
429
+ if (scpMatch) {
430
+ const host = scpMatch[1];
431
+ const repoPath = scpMatch[2].replace(/\.git$/, "");
432
+ return `https://${host}/${repoPath}.git`;
433
+ }
434
+ const sshMatch = url.match(/^ssh:\/\/git@([^/]+)\/(.+)$/);
435
+ if (sshMatch) {
436
+ const host = sshMatch[1];
437
+ const repoPath = sshMatch[2].replace(/\.git$/, "");
438
+ return `https://${host}/${repoPath}.git`;
439
+ }
440
+ return null;
441
+ }
442
+ function isSshUrl(url) {
443
+ if (!url || typeof url !== "string") {
444
+ return false;
445
+ }
446
+ return url.startsWith("git@") || url.startsWith("ssh://");
447
+ }
422
448
  function formatGitAccessError2(repoUrl, error) {
423
449
  const repoLabel = repoUrl && repoUrl !== DEFAULT_REPO_URL2 ? repoUrl : "the private repository";
424
450
  if (!(error instanceof GitCommandError)) {
@@ -451,7 +477,9 @@ ${error.message}`;
451
477
  GitCommandError,
452
478
  formatGitAccessError: formatGitAccessError2,
453
479
  getInheritedGitConfigArgs,
480
+ isSshUrl,
454
481
  runGit,
482
+ sshToHttpsUrl,
455
483
  syncInheritedGitConfig
456
484
  };
457
485
  }
@@ -1443,7 +1471,7 @@ var require_search_multiselect = __commonJS({
1443
1471
  var require_prompts = __commonJS({
1444
1472
  "src/prompts.js"(exports2, module2) {
1445
1473
  "use strict";
1446
- var { INSTALL_TARGETS: INSTALL_TARGETS2 } = require_constants();
1474
+ var { DEFAULT_TARGET_KEYS: DEFAULT_TARGET_KEYS2, INSTALL_TARGETS: INSTALL_TARGETS2 } = require_constants();
1447
1475
  var { splitRepositoryField } = require_skill_catalog();
1448
1476
  var {
1449
1477
  cancelSymbol: cancelSymbol2,
@@ -1476,7 +1504,7 @@ var require_prompts = __commonJS({
1476
1504
  label: target.name,
1477
1505
  hint: `${target.folder}/skills`
1478
1506
  })),
1479
- initialValues: INSTALL_TARGETS2.filter((target) => target.key !== "claude").map((target) => target.key),
1507
+ initialValues: [...DEFAULT_TARGET_KEYS2],
1480
1508
  required: true,
1481
1509
  instructions: "Use arrows to move, space to toggle, a for all, then press enter to confirm."
1482
1510
  });
@@ -1618,7 +1646,7 @@ var require_repo_cache = __commonJS({
1618
1646
  LEGACY_REPO_PACKAGE_NAMES,
1619
1647
  PRIVATE_REPO_ENV_VAR: PRIVATE_REPO_ENV_VAR2
1620
1648
  } = require_constants();
1621
- var { GitCommandError, formatGitAccessError: formatGitAccessError2, runGit, syncInheritedGitConfig } = require_git();
1649
+ var { GitCommandError, formatGitAccessError: formatGitAccessError2, runGit, syncInheritedGitConfig, isSshUrl, sshToHttpsUrl } = require_git();
1622
1650
  function getConfiguredRepoUrl2(options = {}, env = process.env) {
1623
1651
  return options.repoUrl || env[PRIVATE_REPO_ENV_VAR2] || env[LEGACY_PRIVATE_REPO_ENV_VAR] || DEFAULT_REPO_URL2;
1624
1652
  }
@@ -1792,27 +1820,65 @@ Using the existing local cache at ${cacheDir}.`
1792
1820
  };
1793
1821
  }
1794
1822
  function cloneRepo(cacheDir, repoUrl, branch, gitConfigContextCwd) {
1795
- runGit(["clone", "--depth", "1", "--branch", branch, repoUrl, cacheDir], {
1796
- repoUrl,
1797
- gitConfigContextCwd
1798
- });
1823
+ try {
1824
+ runGit(["clone", "--depth", "1", "--branch", branch, repoUrl, cacheDir], {
1825
+ repoUrl,
1826
+ gitConfigContextCwd
1827
+ });
1828
+ } catch (error) {
1829
+ if (error instanceof GitCommandError && error.isAuthError && isSshUrl(repoUrl)) {
1830
+ const httpsUrl = sshToHttpsUrl(repoUrl);
1831
+ if (httpsUrl) {
1832
+ runGit(["clone", "--depth", "1", "--branch", branch, httpsUrl, cacheDir], {
1833
+ repoUrl: httpsUrl,
1834
+ gitConfigContextCwd
1835
+ });
1836
+ syncInheritedGitConfig(cacheDir, gitConfigContextCwd);
1837
+ return;
1838
+ }
1839
+ }
1840
+ throw error;
1841
+ }
1799
1842
  syncInheritedGitConfig(cacheDir, gitConfigContextCwd);
1800
1843
  }
1801
1844
  function refreshRepo(cacheDir, repoUrl, branch, gitConfigContextCwd) {
1802
1845
  syncInheritedGitConfig(cacheDir, gitConfigContextCwd);
1803
- runGit(["fetch", "--depth", "1", "origin", branch], {
1804
- cwd: cacheDir,
1805
- repoUrl,
1806
- gitConfigContextCwd
1807
- });
1846
+ const currentRemote = readOriginUrl(cacheDir);
1847
+ const effectiveUrl = currentRemote || repoUrl;
1848
+ try {
1849
+ runGit(["fetch", "--depth", "1", "origin", branch], {
1850
+ cwd: cacheDir,
1851
+ repoUrl: effectiveUrl,
1852
+ gitConfigContextCwd
1853
+ });
1854
+ } catch (error) {
1855
+ if (error instanceof GitCommandError && error.isAuthError && isSshUrl(effectiveUrl)) {
1856
+ const httpsUrl = sshToHttpsUrl(effectiveUrl);
1857
+ if (httpsUrl) {
1858
+ try {
1859
+ runGit(["remote", "set-url", "origin", httpsUrl], { cwd: cacheDir });
1860
+ } catch {
1861
+ }
1862
+ runGit(["fetch", "--depth", "1", "origin", branch], {
1863
+ cwd: cacheDir,
1864
+ repoUrl: httpsUrl,
1865
+ gitConfigContextCwd
1866
+ });
1867
+ } else {
1868
+ throw error;
1869
+ }
1870
+ } else {
1871
+ throw error;
1872
+ }
1873
+ }
1808
1874
  runGit(["reset", "--hard", "FETCH_HEAD"], {
1809
1875
  cwd: cacheDir,
1810
- repoUrl,
1876
+ repoUrl: effectiveUrl,
1811
1877
  gitConfigContextCwd
1812
1878
  });
1813
1879
  runGit(["clean", "-fd", "-e", path2.basename(getCacheMetadataPath(cacheDir))], {
1814
1880
  cwd: cacheDir,
1815
- repoUrl,
1881
+ repoUrl: effectiveUrl,
1816
1882
  gitConfigContextCwd
1817
1883
  });
1818
1884
  }
@@ -1882,6 +1948,7 @@ var {
1882
1948
  CLI_NAME,
1883
1949
  DEFAULT_BRANCH,
1884
1950
  DEFAULT_REPO_URL,
1951
+ DEFAULT_TARGET_KEYS,
1885
1952
  INSTALL_TARGETS,
1886
1953
  PRIVATE_REPO_ENV_VAR
1887
1954
  } = require_constants();
@@ -1922,7 +1989,7 @@ function usage() {
1922
1989
  console.log(" --copy Copy files instead of symlinking targets to the source skill directory");
1923
1990
  console.log(" --force Overwrite existing skills");
1924
1991
  console.log(" --no-pull Use the local cache as-is");
1925
- console.log(" --yes, -y Skip prompts (defaults to all skills + global + .agents only)");
1992
+ console.log(" --yes, -y Skip prompts (defaults to all skills + global + all install targets)");
1926
1993
  console.log(" --filter <text> Filter skills by name, description, or repository");
1927
1994
  console.log(" --details Show full descriptions");
1928
1995
  console.log(" --repo-url <git-url> Override the private skills repo URL");
@@ -2125,7 +2192,7 @@ async function resolveTargetKeys(options, interactive) {
2125
2192
  return [];
2126
2193
  }
2127
2194
  if (options.yes || !interactive) {
2128
- return ["agents"];
2195
+ return [...DEFAULT_TARGET_KEYS];
2129
2196
  }
2130
2197
  const selected = await promptForTargets();
2131
2198
  if (selected === cancelSymbol) {
package/package.json CHANGED
@@ -1,33 +1,33 @@
1
- {
2
- "name": "synskill",
3
- "version": "0.1.5",
4
- "private": false,
5
- "description": "CLI installer for private agent skills.",
6
- "license": "UNLICENSED",
7
- "files": [
8
- "bin",
9
- "README.md"
10
- ],
11
- "scripts": {
12
- "build": "node scripts/bundle-cli.js",
13
- "synskill": "node src/synskill.js",
14
- "test": "node --test tests/*.test.js",
15
- "prepack": "node scripts/manage-pack-readme.js prepack",
16
- "postpack": "node scripts/manage-pack-readme.js postpack",
17
- "prepublishOnly": "npm run build && npm test"
18
- },
19
- "bin": {
20
- "synskill": "bin/synskill.js"
21
- },
22
- "engines": {
23
- "node": ">=18"
24
- },
25
- "devDependencies": {
26
- "@clack/prompts": "^0.11.0",
27
- "esbuild": "^0.20.2",
28
- "picocolors": "^1.1.1"
29
- },
30
- "dependencies": {
31
- "markdown-it": "^14.1.1"
32
- }
33
- }
1
+ {
2
+ "name": "synskill",
3
+ "version": "0.1.7",
4
+ "private": false,
5
+ "description": "CLI installer for private agent skills.",
6
+ "license": "UNLICENSED",
7
+ "files": [
8
+ "bin",
9
+ "README.md"
10
+ ],
11
+ "scripts": {
12
+ "build": "node scripts/bundle-cli.js",
13
+ "synskill": "node src/synskill.js",
14
+ "test": "node scripts/run-tests.js",
15
+ "prepack": "node scripts/manage-pack-readme.js prepack",
16
+ "postpack": "node scripts/manage-pack-readme.js postpack",
17
+ "prepublishOnly": "npm run build && npm test"
18
+ },
19
+ "bin": {
20
+ "synskill": "bin/synskill.js"
21
+ },
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "devDependencies": {
26
+ "@clack/prompts": "^0.11.0",
27
+ "esbuild": "^0.20.2",
28
+ "picocolors": "^1.1.1"
29
+ },
30
+ "dependencies": {
31
+ "markdown-it": "^14.1.1"
32
+ }
33
+ }