@akshar5/skillsync 0.1.0 → 0.1.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 (3) hide show
  1. package/README.md +161 -47
  2. package/package.json +1 -1
  3. package/src/cli.js +39 -19
package/README.md CHANGED
@@ -2,7 +2,53 @@
2
2
 
3
3
  Local-first skill manager for AI agent skills.
4
4
 
5
- SkillSync uses a private GitHub repo as your cloud vault, then keeps selected skills linked into local agent skill folders on each device.
5
+ SkillSync uses a private GitHub repo as your cloud vault, then keeps selected skills linked into local agent skill folders on each device. Install it once with npm, connect it to your vault, and use the `skillsync` command anywhere.
6
+
7
+ ## Quick start
8
+
9
+ Install the CLI:
10
+
11
+ ```bash
12
+ npm install -g @akshar5/skillsync
13
+ ```
14
+
15
+ Connect this device to an existing private skills vault:
16
+
17
+ ```bash
18
+ skillsync setup --repo AksharP5/skills
19
+ ```
20
+
21
+ Add a local skill folder to the vault:
22
+
23
+ ```bash
24
+ skillsync add ~/path/to/my-skill --skill my-skill
25
+ ```
26
+
27
+ Add a skill from a GitHub repository:
28
+
29
+ ```bash
30
+ skillsync add https://github.com/example-org/example-skill --skill example-skill
31
+ ```
32
+
33
+ Install a vaulted skill into a local agent target:
34
+
35
+ ```bash
36
+ skillsync target add codex ~/.codex/skills
37
+ skillsync install my-skill --target codex
38
+ ```
39
+
40
+ Open the interactive UI:
41
+
42
+ ```bash
43
+ skillsync
44
+ ```
45
+
46
+ ## Requirements
47
+
48
+ - Node.js 20 or newer
49
+ - Git
50
+ - GitHub CLI (`gh`) authenticated with `gh auth login`
51
+ - A private GitHub repo for the skills vault
6
52
 
7
53
  ## What it manages
8
54
 
@@ -18,56 +64,129 @@ devices/*.json # generated, do not edit
18
64
 
19
65
  User-owned files are the skill folders under `skills/`. SkillSync owns `registry.json` and `devices/*.json`.
20
66
 
21
- ## Install on another device
67
+ Local target folders are per-device. For example, one laptop can install a skill into `~/.codex/skills`, while another can install the same vault skill into a different agent folder.
68
+
69
+ ## Install on a new device
22
70
 
23
- SkillSync is an npm-style CLI package. Once published, the nice path will be:
71
+ Install prerequisites on macOS:
24
72
 
25
73
  ```bash
26
- npx @akshar5/skillsync setup --repo AksharP5/skills
27
- npx @akshar5/skillsync add https://github.com/raroque/vibe-security-skill --skill vibe-security
74
+ brew install gh git node
75
+ gh auth login
28
76
  ```
29
77
 
30
- It is not published to the public npm registry yet. Install it from the private GitHub repo for now:
78
+ SkillSync clones vault repos over HTTPS using your GitHub CLI authentication, so a GitHub SSH key is not required.
79
+
80
+ Install SkillSync:
31
81
 
32
82
  ```bash
33
- # macOS
34
- brew install gh git node
83
+ npm install -g @akshar5/skillsync
84
+ ```
35
85
 
36
- gh auth login
37
- npm install -g git+ssh://git@github.com/AksharP5/skillsync.git
86
+ Connect to an existing vault:
87
+
88
+ ```bash
89
+ skillsync setup --repo AksharP5/skills
38
90
  ```
39
91
 
40
- If SSH is not set up on that device yet, use the clone/link fallback:
92
+ Or create/select a vault repo interactively:
41
93
 
42
94
  ```bash
43
- mkdir -p ~/projects
44
- gh repo clone AksharP5/skillsync ~/projects/skillsync
45
- cd ~/projects/skillsync
46
- npm install
47
- npm link
95
+ skillsync setup
48
96
  ```
49
97
 
50
- Connect to an existing vault:
98
+ If you run plain `skillsync setup` in an interactive terminal, it first asks which vault type to use:
99
+
100
+ - Choose `Use an existing GitHub repo`, then enter a repo like `AksharP5/skills`.
101
+ - Choose `Create or use OWNER/<name>`, then enter a repo name like `skills`.
102
+
103
+ `setup --name skills` is the non-interactive form of the second option. It creates `OWNER/skills` as a private GitHub repo if it does not exist. If it exists, SkillSync verifies it is private before using it.
104
+
105
+ You can also run commands without a global install:
106
+
107
+ ```bash
108
+ npx @akshar5/skillsync setup --repo AksharP5/skills
109
+ npx @akshar5/skillsync add https://github.com/example-org/example-skill --skill example-skill
110
+ ```
111
+
112
+ If an older SkillSync version failed with `git@github.com: Permission denied (publickey)`, update the CLI and rerun setup:
51
113
 
52
114
  ```bash
115
+ npm install -g @akshar5/skillsync@latest
53
116
  skillsync setup --repo AksharP5/skills
54
117
  ```
55
118
 
56
- Or create/select a different private vault repo name:
119
+ ## Common workflows
120
+
121
+ List available skills:
122
+
123
+ ```bash
124
+ skillsync list
125
+ ```
126
+
127
+ Check current vault/device state:
57
128
 
58
129
  ```bash
59
- skillsync setup --name my-skills
130
+ skillsync status
60
131
  ```
61
132
 
62
- If you run plain `skillsync setup` in an interactive terminal, it asks for the repo name and defaults to `skills`. `setup --name` creates `OWNER/my-skills` as a private GitHub repo if it does not exist. If it exists, SkillSync verifies it is private before using it.
133
+ Add local agent targets:
134
+
135
+ ```bash
136
+ skillsync target add codex ~/.codex/skills
137
+ skillsync target add claude ~/.claude/skills
138
+ skillsync target add hermes ~/.hermes/skills/personal --scan-path ~/.hermes/skills
139
+ ```
140
+
141
+ Add a skill folder to the vault:
142
+
143
+ ```bash
144
+ skillsync add ~/Developer/skills/my-skill --skill my-skill
145
+ ```
146
+
147
+ Add from a GitHub repo:
148
+
149
+ ```bash
150
+ skillsync add https://github.com/example-org/example-skill --skill example-skill
151
+ ```
152
+
153
+ If the source repo contains multiple skills, omit `--skill` in an interactive terminal and SkillSync will ask which ones to add. Add `--target codex` or `--target '*'` to install immediately after importing:
154
+
155
+ ```bash
156
+ skillsync add https://github.com/example-org/example-skill --target codex
157
+ skillsync add https://github.com/example-org/example-skill --target '*'
158
+ ```
159
+
160
+ Install or uninstall a vaulted skill on this device:
161
+
162
+ ```bash
163
+ skillsync install my-skill --target codex
164
+ skillsync uninstall my-skill
165
+ ```
166
+
167
+ Sync the vault and reapply local links:
168
+
169
+ ```bash
170
+ skillsync sync
171
+ ```
172
+
173
+ Scan configured target folders for already-installed local skills:
174
+
175
+ ```bash
176
+ skillsync scan
177
+ ```
63
178
 
64
179
  ## Commands
65
180
 
66
181
  ```bash
67
182
  skillsync setup
183
+ skillsync setup --repo owner/repo
184
+ skillsync setup --name skills
68
185
  skillsync
186
+ skillsync status
187
+ skillsync list
69
188
  skillsync add <skill-folder-or-git-url> --skill <name>
70
- skillsync add https://github.com/raroque/vibe-security-skill --skill vibe-security
189
+ skillsync add https://github.com/example-org/example-skill --skill example-skill
71
190
  skillsync import hermes
72
191
  skillsync install <skill> --target codex
73
192
  skillsync uninstall <skill>
@@ -77,6 +196,7 @@ skillsync target add hermes ~/.hermes/skills/personal --scan-path ~/.hermes/skil
77
196
  skillsync scan
78
197
  skillsync sync
79
198
  skillsync service install
199
+ skillsync daemon
80
200
  ```
81
201
 
82
202
  ## Removal model
@@ -84,32 +204,6 @@ skillsync service install
84
204
  - `skillsync uninstall <skill>` removes the skill from the current device only.
85
205
  - `skillsync delete <skill>` removes the skill from the vault and all device manifests.
86
206
 
87
- ## Add from GitHub
88
-
89
- You can import a skill directly from a GitHub repo into your private vault:
90
-
91
- ```bash
92
- skillsync add https://github.com/raroque/vibe-security-skill --skill vibe-security
93
- ```
94
-
95
- If the repo has multiple skills, omit `--skill` in an interactive terminal and SkillSync will ask which ones to add. Add `--target codex` or `--target '*'` to install it on the current device immediately after importing.
96
-
97
- ## Publishing to npm
98
-
99
- Clone the project on the machine where your npm account is configured:
100
-
101
- ```bash
102
- gh repo clone AksharP5/skillsync ~/projects/skillsync
103
- cd ~/projects/skillsync
104
- npm install
105
- npm test
106
- npm pack --dry-run
107
- npm login
108
- npm publish --access public
109
- ```
110
-
111
- The package name is `@akshar5/skillsync` because `skillsync` is already taken on npm. The installed command is still `skillsync`.
112
-
113
207
  ## Detected versus managed skills
114
208
 
115
209
  `installed` skills are SkillSync-managed projections into a target folder. `detected` skills are already present in a local agent's skill tree, such as bundled Hermes skills under `~/.hermes/skills`.
@@ -129,3 +223,23 @@ skillsync scan
129
223
  - Linux: systemd user service
130
224
 
131
225
  The service periodically pulls/pushes the GitHub vault and reapplies symlinks.
226
+
227
+ ## Development
228
+
229
+ Clone and test locally:
230
+
231
+ ```bash
232
+ git clone https://github.com/AksharP5/skillsync.git
233
+ cd skillsync
234
+ npm install
235
+ npm test
236
+ npm pack --dry-run
237
+ ```
238
+
239
+ The npm package name is `@akshar5/skillsync` because `skillsync` is already taken on npm. The installed command is still `skillsync`.
240
+
241
+ Future releases are managed by Release Please and GitHub Actions. Use conventional commits:
242
+
243
+ - `fix:` creates a patch release.
244
+ - `feat:` creates a minor release.
245
+ - `feat!:` or `BREAKING CHANGE:` creates a major release.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akshar5/skillsync",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Local-first GitHub-backed skill vault sync client for AI agent skills.",
5
5
  "type": "module",
6
6
  "repository": {
package/src/cli.js CHANGED
@@ -97,10 +97,29 @@ async function resolveRepoCloneUrl(repo) {
97
97
  if (/^(git@|https?:\/\/|ssh:\/\/)/.test(repo)) return repo;
98
98
  if (!repo.includes('/')) return repo;
99
99
  if (!await commandExists('gh')) return repo;
100
- const { stdout } = await gh(['repo', 'view', repo, '--json', 'isPrivate,sshUrl', '--jq', '.']);
100
+ return verifiedRepoCloneUrl(repo);
101
+ }
102
+
103
+ async function verifiedRepoCloneUrl(repo) {
104
+ const { stdout } = await gh(['repo', 'view', repo, '--json', 'isPrivate,url', '--jq', '.']);
101
105
  const view = JSON.parse(stdout);
102
106
  if (!view.isPrivate) throw new Error(`${repo} exists but is not private. Make it private before using it as a skill vault.`);
103
- return view.sshUrl;
107
+ return view.url;
108
+ }
109
+
110
+ async function ensureOwnedVaultRepo(owner, name) {
111
+ const repo = `${owner}/${name}`;
112
+ let repoExists = true;
113
+ try {
114
+ await gh(['repo', 'view', repo]);
115
+ } catch {
116
+ repoExists = false;
117
+ }
118
+ if (!repoExists) {
119
+ console.log(`Creating private GitHub repo ${repo}...`);
120
+ await gh(['repo', 'create', repo, '--private', '--description', 'Private AI agent skills vault'], undefined, { inherit: true });
121
+ }
122
+ return verifiedRepoCloneUrl(repo);
104
123
  }
105
124
 
106
125
  async function setup(rest) {
@@ -119,24 +138,25 @@ async function setup(rest) {
119
138
  if (!repo) {
120
139
  const { stdout: ownerOut } = await gh(['api', 'user', '--jq', '.login']);
121
140
  const owner = ownerOut.trim();
122
- const name = flagValue(rest, '--name') || (yes || !process.stdin.isTTY
123
- ? 'skills'
124
- : await input({ message: 'GitHub skills vault repo name:', default: 'skills' }));
125
- repo = `${owner}/${name}`;
126
- let repoExists = true;
127
- try {
128
- await gh(['repo', 'view', repo]);
129
- } catch {
130
- repoExists = false;
131
- }
132
- if (!repoExists) {
133
- console.log(`Creating private GitHub repo ${repo}...`);
134
- await gh(['repo', 'create', repo, '--private', '--description', 'Private AI agent skills vault'], undefined, { inherit: true });
141
+ const nameArg = flagValue(rest, '--name');
142
+ if (nameArg || yes || !process.stdin.isTTY) {
143
+ repo = await ensureOwnedVaultRepo(owner, nameArg || 'skills');
144
+ } else {
145
+ const setupMode = await select({
146
+ message: 'Which skills vault do you want to use?',
147
+ choices: [
148
+ { name: 'Use an existing GitHub repo', value: 'existing' },
149
+ { name: `Create or use ${owner}/<name>`, value: 'owned' },
150
+ ],
151
+ });
152
+ if (setupMode === 'existing') {
153
+ const existingRepo = await input({ message: 'Existing vault repo (owner/repo or URL):', default: `${owner}/skills` });
154
+ repo = await resolveRepoCloneUrl(existingRepo);
155
+ } else {
156
+ const name = await input({ message: `Vault repo name under ${owner}:`, default: 'skills' });
157
+ repo = await ensureOwnedVaultRepo(owner, name);
158
+ }
135
159
  }
136
- const { stdout: viewOut } = await gh(['repo', 'view', repo, '--json', 'isPrivate,sshUrl', '--jq', '.']);
137
- const view = JSON.parse(viewOut);
138
- if (!view.isPrivate) throw new Error(`${repo} exists but is not private. Make it private before using it as a skill vault.`);
139
- repo = view.sshUrl;
140
160
  } else {
141
161
  repo = await resolveRepoCloneUrl(repo);
142
162
  }