@tc9011/skills-manager 0.1.0

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/README.md ADDED
@@ -0,0 +1,231 @@
1
+ # skills-manager
2
+
3
+ A CLI companion to [vercel-labs/skills](https://github.com/vercel-labs/skills) that backs up your AI agent skills to GitHub and restores them with automatic agent symlink creation.
4
+
5
+ ## Why
6
+
7
+ `vercel-labs/skills` installs and manages skills, but it doesn't handle:
8
+
9
+ - **Backup** — pushing your `~/.agents/` directory (skills + lock file) to a remote GitHub repo
10
+ - **Restore** — cloning skills back on a new machine
11
+ - **Agent linking** — reading `.skill-lock.json` and creating symlinks to every agent you use
12
+
13
+ `skills-manager` fills that gap. It backs up the entire `~/.agents/` directory (including `.skill-lock.json` and `skills/`), reads the lock file that `vercel-labs/skills` owns (never modifies it), and creates relative symlinks from each agent's skills directory back to the canonical `~/.agents/skills/` source.
14
+
15
+ ## Quick Start
16
+
17
+ ```bash
18
+ # 1. Backup skills to GitHub
19
+ skills-manager push
20
+
21
+ # 2. Restore skills on a new machine
22
+ skills-manager pull --repo owner/my-skills
23
+
24
+ # 3. Link skills to your agents (auto-runs after pull)
25
+ skills-manager link
26
+ ```
27
+
28
+ ## Commands
29
+
30
+ ### `push`
31
+
32
+ Commits and pushes the contents of `~/.agents/` (skills + `.skill-lock.json`) to its configured GitHub remote.
33
+
34
+ ```bash
35
+ skills-manager push
36
+ skills-manager push -m "add new debugging skill"
37
+ ```
38
+
39
+ | Option | Description |
40
+ |--------|-------------|
41
+ | `-m, --message <msg>` | Custom commit message (default: auto-generated) |
42
+
43
+ **Prerequisites**: The `~/.agents/` directory must be a git repository with a remote configured.
44
+
45
+ ### `pull`
46
+
47
+ Clones or pulls the `~/.agents/` directory from GitHub, then automatically runs `link`.
48
+
49
+ ```bash
50
+ skills-manager pull --repo tc9011/my-skills
51
+ skills-manager pull # uses existing remote
52
+ skills-manager pull --skip-link # pull only, don't link
53
+ ```
54
+
55
+ | Option | Description |
56
+ |--------|-------------|
57
+ | `-r, --repo <owner/name>` | GitHub repo to pull from (default: existing remote) |
58
+ | `--skip-link` | Skip automatic agent linking after pull |
59
+
60
+ ### `link`
61
+
62
+ Reads `~/.agents/.skill-lock.json` (owned by `vercel-labs/skills`) and creates relative symlinks from each agent's global skills directory to the canonical `~/.agents/skills/`.
63
+
64
+ ```bash
65
+ skills-manager link
66
+ skills-manager link --agents cursor opencode claude-code
67
+ ```
68
+
69
+ | Option | Description |
70
+ |--------|-------------|
71
+ | `-a, --agents <ids...>` | Agent IDs to link (default: `lastSelectedAgents` from lock file) |
72
+
73
+ **Interactive**: Presents a multiselect prompt with all `lastSelectedAgents` pre-selected. You can deselect agents you don't want to link.
74
+
75
+ **Symlink model**:
76
+
77
+ ```
78
+ ~/.config/opencode/skills/my-skill → ../../../.agents/skills/my-skill (relative)
79
+ ~/.claude/skills/my-skill → ../../.agents/skills/my-skill (relative)
80
+ ```
81
+
82
+ ## Authentication
83
+
84
+ Token resolution order:
85
+
86
+ 1. `gh auth token` — GitHub CLI (preferred)
87
+ 2. `$GITHUB_TOKEN` environment variable
88
+ 3. `$GH_TOKEN` environment variable
89
+
90
+ If none found, `push` and `pull` will fail with a clear error message. `link` does not require authentication.
91
+
92
+ ## Supported Agents
93
+
94
+ ### Universal Agents (share `.agents/skills`)
95
+
96
+ | Agent | ID | Global Path |
97
+ |-------|----|-------------|
98
+ | Amp | `amp` | `~/.config/agents/skills` |
99
+ | Cline | `cline` | `~/.agents/skills` |
100
+ | Codex | `codex` | `$CODEX_HOME/skills` |
101
+ | Cursor | `cursor` | `~/.cursor/skills` |
102
+ | Gemini CLI | `gemini-cli` | `~/.gemini/skills` |
103
+ | GitHub Copilot | `github-copilot` | `~/.copilot/skills` |
104
+ | Kimi Code CLI | `kimi-cli` | `~/.config/agents/skills` |
105
+ | OpenCode | `opencode` | `$XDG_CONFIG_HOME/opencode/skills` |
106
+ | Replit | `replit` | `~/.config/agents/skills` |
107
+ | Universal | `universal` | `~/.config/agents/skills` |
108
+
109
+ ### Non-Universal Agents (agent-specific paths)
110
+
111
+ | Agent | ID | Global Path |
112
+ |-------|----|-------------|
113
+ | AdaL | `adal` | `~/.adal/skills` |
114
+ | Antigravity | `antigravity` | `~/.gemini/antigravity/skills` |
115
+ | Augment | `augment` | `~/.augment/skills` |
116
+ | Claude Code | `claude-code` | `$CLAUDE_CONFIG_DIR/skills` |
117
+ | CodeBuddy | `codebuddy` | `~/.codebuddy/skills` |
118
+ | Command Code | `command-code` | `~/.commandcode/skills` |
119
+ | Continue | `continue` | `~/.continue/skills` |
120
+ | Cortex Code | `cortex` | `~/.snowflake/cortex/skills` |
121
+ | Crush | `crush` | `~/.config/crush/skills` |
122
+ | Droid | `droid` | `~/.factory/skills` |
123
+ | Goose | `goose` | `~/.config/goose/skills` |
124
+ | iFlow CLI | `iflow-cli` | `~/.iflow/skills` |
125
+ | Junie | `junie` | `~/.junie/skills` |
126
+ | Kilo Code | `kilo` | `~/.kilocode/skills` |
127
+ | Kiro CLI | `kiro-cli` | `~/.kiro/skills` |
128
+ | Kode | `kode` | `~/.kode/skills` |
129
+ | MCPJam | `mcpjam` | `~/.mcpjam/skills` |
130
+ | Mistral Vibe | `mistral-vibe` | `~/.vibe/skills` |
131
+ | Mux | `mux` | `~/.mux/skills` |
132
+ | Neovate | `neovate` | `~/.neovate/skills` |
133
+ | OpenClaw | `openclaw` | `~/.openclaw/skills` |
134
+ | OpenHands | `openhands` | `~/.openhands/skills` |
135
+ | Pi | `pi` | `~/.pi/agent/skills` |
136
+ | Pochi | `pochi` | `~/.pochi/skills` |
137
+ | Qoder | `qoder` | `~/.qoder/skills` |
138
+ | Qwen Code | `qwen-code` | `~/.qwen/skills` |
139
+ | Replit | `replit` | `~/.config/agents/skills` |
140
+ | Roo Code | `roo` | `~/.roo/skills` |
141
+ | Trae | `trae` | `~/.trae/skills` |
142
+ | Trae CN | `trae-cn` | `~/.trae-cn/skills` |
143
+ | Windsurf | `windsurf` | `~/.codeium/windsurf/skills` |
144
+ | Zencoder | `zencoder` | `~/.zencoder/skills` |
145
+
146
+ **41 agents total** — matching the full [vercel-labs/skills](https://github.com/vercel-labs/skills) agent registry.
147
+
148
+ ## Environment Variables
149
+
150
+ | Variable | Used By | Default |
151
+ |----------|---------|---------|
152
+ | `$GITHUB_TOKEN` | `push`, `pull` | — |
153
+ | `$GH_TOKEN` | `push`, `pull` | — |
154
+ | `$XDG_CONFIG_HOME` | `opencode`, `amp`, `kimi-cli`, `replit`, `universal` | `~/.config` |
155
+ | `$CODEX_HOME` | `codex` | `~/.codex` |
156
+ | `$CLAUDE_CONFIG_DIR` | `claude-code` | `~/.claude` |
157
+
158
+ ## Development
159
+
160
+ ### Prerequisites
161
+
162
+ - Node.js ≥ 20
163
+ - GitHub CLI (`gh`) for authentication (recommended)
164
+
165
+ ### Local Debugging
166
+
167
+ ```bash
168
+ # Run any command directly with tsx (no build step needed)
169
+ npx tsx src/index.ts push
170
+ npx tsx src/index.ts pull --repo owner/my-skills
171
+ npx tsx src/index.ts link
172
+ npx tsx src/index.ts link --agents cursor opencode
173
+
174
+ # Or use the dev script (same thing)
175
+ npm run dev -- push
176
+ npm run dev -- link -- --agents cursor
177
+ ```
178
+
179
+ To simulate a real global install:
180
+
181
+ ```bash
182
+ npm link # register skills-manager globally
183
+ skills-manager push # use it like an end user
184
+ npm unlink -g skills-manager # clean up when done
185
+ ```
186
+
187
+ ### Scripts
188
+
189
+ ```bash
190
+ npm install # install dependencies
191
+ npm run dev # run via tsx (development)
192
+ npm test # run tests (vitest)
193
+ npm run test:watch # watch mode
194
+ npm run build # compile TypeScript to dist/
195
+ ```
196
+
197
+ ### Tech Stack
198
+
199
+ - **Runtime**: Node.js + TypeScript (ESM)
200
+ - **CLI framework**: [Commander.js](https://github.com/tj/commander.js)
201
+ - **Git operations**: [simple-git](https://github.com/steveukx/git-js)
202
+ - **Interactive prompts**: [@clack/prompts](https://github.com/bombshell-dev/clack)
203
+ - **Testing**: [Vitest](https://vitest.dev/)
204
+ - **Dev runner**: [tsx](https://github.com/privatenumber/tsx)
205
+
206
+ ### Project Structure
207
+
208
+ ```
209
+ src/
210
+ ├── index.ts # CLI entry point
211
+ ├── agents.ts # 41-agent registry with path resolution
212
+ ├── auth.ts # GitHub token resolution (gh CLI → env vars)
213
+ ├── lockfile.ts # Read-only .skill-lock.json parser
214
+ ├── linker.ts # Relative symlink creation
215
+ ├── git-ops.ts # Git push/pull via simple-git
216
+ └── commands/
217
+ ├── push.ts # push command handler
218
+ ├── pull.ts # pull command handler (auto-links)
219
+ └── link.ts # link command handler (interactive)
220
+ ```
221
+
222
+ ### Adding a New Agent
223
+
224
+ 1. Add the agent ID to the `AgentId` union type in `src/agents.ts`
225
+ 2. Add the agent entry to `agentRegistry` with `displayName`, `projectPath`, `globalPath`, and `universal` flag
226
+ 3. If the agent uses an environment variable for path resolution, add it to `getAgentGlobalPath()`
227
+ 4. Add a test case in `src/agents.test.ts`
228
+
229
+ ## License
230
+
231
+ MIT
@@ -0,0 +1,19 @@
1
+ export type AgentId = 'amp' | 'antigravity' | 'augment' | 'claude-code' | 'openclaw' | 'cline' | 'codebuddy' | 'codex' | 'command-code' | 'continue' | 'cortex' | 'crush' | 'cursor' | 'droid' | 'gemini-cli' | 'github-copilot' | 'goose' | 'junie' | 'iflow-cli' | 'kilo' | 'kimi-cli' | 'kiro-cli' | 'kode' | 'mcpjam' | 'mistral-vibe' | 'mux' | 'opencode' | 'openhands' | 'pi' | 'qoder' | 'qwen-code' | 'replit' | 'roo' | 'trae' | 'trae-cn' | 'windsurf' | 'zencoder' | 'neovate' | 'pochi' | 'adal' | 'universal';
2
+ export interface AgentPaths {
3
+ displayName: string;
4
+ projectPath: string;
5
+ globalPath: string;
6
+ universal: boolean;
7
+ }
8
+ export declare const agentRegistry: Record<AgentId, AgentPaths>;
9
+ /**
10
+ * Resolve an agent's global skills path to an absolute filesystem path.
11
+ * Handles ~, $XDG_CONFIG_HOME, $CODEX_HOME, $CLAUDE_CONFIG_DIR.
12
+ */
13
+ export declare function getAgentGlobalPath(agentId: AgentId): string;
14
+ /** Canonical skills directory — the source of truth. */
15
+ export declare const CANONICAL_SKILLS_DIR: string;
16
+ /** The git-managed agents directory — root of the backup repo. */
17
+ export declare const AGENTS_DIR: string;
18
+ /** Lock file path — READ ONLY, owned by vercel-labs/skills. */
19
+ export declare const SKILL_LOCK_PATH: string;
package/dist/agents.js ADDED
@@ -0,0 +1,80 @@
1
+ // src/agents.ts
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ export const agentRegistry = {
5
+ // UNIVERSAL GROUP (.agents/skills)
6
+ amp: { displayName: 'Amp', projectPath: '.agents/skills', globalPath: '~/.config/agents/skills', universal: true },
7
+ cline: { displayName: 'Cline', projectPath: '.agents/skills', globalPath: '~/.agents/skills', universal: true },
8
+ codex: { displayName: 'Codex', projectPath: '.agents/skills', globalPath: '$CODEX_HOME/skills', universal: true },
9
+ cursor: { displayName: 'Cursor', projectPath: '.agents/skills', globalPath: '~/.cursor/skills', universal: true },
10
+ 'gemini-cli': { displayName: 'Gemini CLI', projectPath: '.agents/skills', globalPath: '~/.gemini/skills', universal: true },
11
+ 'github-copilot': { displayName: 'GitHub Copilot', projectPath: '.agents/skills', globalPath: '~/.copilot/skills', universal: true },
12
+ 'kimi-cli': { displayName: 'Kimi Code CLI', projectPath: '.agents/skills', globalPath: '~/.config/agents/skills', universal: true },
13
+ opencode: { displayName: 'OpenCode', projectPath: '.agents/skills', globalPath: '$XDG_CONFIG_HOME/opencode/skills', universal: true },
14
+ replit: { displayName: 'Replit', projectPath: '.agents/skills', globalPath: '~/.config/agents/skills', universal: true },
15
+ universal: { displayName: 'Universal', projectPath: '.agents/skills', globalPath: '~/.config/agents/skills', universal: true },
16
+ // NON-UNIVERSAL (agent-specific paths)
17
+ antigravity: { displayName: 'Antigravity', projectPath: '.agent/skills', globalPath: '~/.gemini/antigravity/skills', universal: false },
18
+ augment: { displayName: 'Augment', projectPath: '.augment/skills', globalPath: '~/.augment/skills', universal: false },
19
+ 'claude-code': { displayName: 'Claude Code', projectPath: '.claude/skills', globalPath: '$CLAUDE_CONFIG_DIR/skills', universal: false },
20
+ openclaw: { displayName: 'OpenClaw', projectPath: 'skills', globalPath: '~/.openclaw/skills', universal: false },
21
+ codebuddy: { displayName: 'CodeBuddy', projectPath: '.codebuddy/skills', globalPath: '~/.codebuddy/skills', universal: false },
22
+ 'command-code': { displayName: 'Command Code', projectPath: '.commandcode/skills', globalPath: '~/.commandcode/skills', universal: false },
23
+ continue: { displayName: 'Continue', projectPath: '.continue/skills', globalPath: '~/.continue/skills', universal: false },
24
+ cortex: { displayName: 'Cortex Code', projectPath: '.cortex/skills', globalPath: '~/.snowflake/cortex/skills', universal: false },
25
+ crush: { displayName: 'Crush', projectPath: '.crush/skills', globalPath: '~/.config/crush/skills', universal: false },
26
+ droid: { displayName: 'Droid', projectPath: '.factory/skills', globalPath: '~/.factory/skills', universal: false },
27
+ goose: { displayName: 'Goose', projectPath: '.goose/skills', globalPath: '~/.config/goose/skills', universal: false },
28
+ junie: { displayName: 'Junie', projectPath: '.junie/skills', globalPath: '~/.junie/skills', universal: false },
29
+ 'iflow-cli': { displayName: 'iFlow CLI', projectPath: '.iflow/skills', globalPath: '~/.iflow/skills', universal: false },
30
+ kilo: { displayName: 'Kilo Code', projectPath: '.kilocode/skills', globalPath: '~/.kilocode/skills', universal: false },
31
+ 'kiro-cli': { displayName: 'Kiro CLI', projectPath: '.kiro/skills', globalPath: '~/.kiro/skills', universal: false },
32
+ kode: { displayName: 'Kode', projectPath: '.kode/skills', globalPath: '~/.kode/skills', universal: false },
33
+ mcpjam: { displayName: 'MCPJam', projectPath: '.mcpjam/skills', globalPath: '~/.mcpjam/skills', universal: false },
34
+ 'mistral-vibe': { displayName: 'Mistral Vibe', projectPath: '.vibe/skills', globalPath: '~/.vibe/skills', universal: false },
35
+ mux: { displayName: 'Mux', projectPath: '.mux/skills', globalPath: '~/.mux/skills', universal: false },
36
+ openhands: { displayName: 'OpenHands', projectPath: '.openhands/skills', globalPath: '~/.openhands/skills', universal: false },
37
+ pi: { displayName: 'Pi', projectPath: '.pi/skills', globalPath: '~/.pi/agent/skills', universal: false },
38
+ qoder: { displayName: 'Qoder', projectPath: '.qoder/skills', globalPath: '~/.qoder/skills', universal: false },
39
+ 'qwen-code': { displayName: 'Qwen Code', projectPath: '.qwen/skills', globalPath: '~/.qwen/skills', universal: false },
40
+ roo: { displayName: 'Roo Code', projectPath: '.roo/skills', globalPath: '~/.roo/skills', universal: false },
41
+ trae: { displayName: 'Trae', projectPath: '.trae/skills', globalPath: '~/.trae/skills', universal: false },
42
+ 'trae-cn': { displayName: 'Trae CN', projectPath: '.trae/skills', globalPath: '~/.trae-cn/skills', universal: false },
43
+ windsurf: { displayName: 'Windsurf', projectPath: '.windsurf/skills', globalPath: '~/.codeium/windsurf/skills', universal: false },
44
+ zencoder: { displayName: 'Zencoder', projectPath: '.zencoder/skills', globalPath: '~/.zencoder/skills', universal: false },
45
+ neovate: { displayName: 'Neovate', projectPath: '.neovate/skills', globalPath: '~/.neovate/skills', universal: false },
46
+ pochi: { displayName: 'Pochi', projectPath: '.pochi/skills', globalPath: '~/.pochi/skills', universal: false },
47
+ adal: { displayName: 'AdaL', projectPath: '.adal/skills', globalPath: '~/.adal/skills', universal: false },
48
+ };
49
+ /**
50
+ * Resolve an agent's global skills path to an absolute filesystem path.
51
+ * Handles ~, $XDG_CONFIG_HOME, $CODEX_HOME, $CLAUDE_CONFIG_DIR.
52
+ */
53
+ export function getAgentGlobalPath(agentId) {
54
+ const home = homedir();
55
+ let p = agentRegistry[agentId].globalPath;
56
+ // Resolve env-var-based paths first
57
+ if (p.startsWith('$XDG_CONFIG_HOME')) {
58
+ const xdg = process.env.XDG_CONFIG_HOME ?? join(home, '.config');
59
+ p = p.replace('$XDG_CONFIG_HOME', xdg);
60
+ }
61
+ else if (p.startsWith('$CODEX_HOME')) {
62
+ const codex = process.env.CODEX_HOME ?? join(home, '.codex');
63
+ p = p.replace('$CODEX_HOME', codex);
64
+ }
65
+ else if (p.startsWith('$CLAUDE_CONFIG_DIR')) {
66
+ const claude = process.env.CLAUDE_CONFIG_DIR ?? join(home, '.claude');
67
+ p = p.replace('$CLAUDE_CONFIG_DIR', claude);
68
+ }
69
+ else if (p.startsWith('~')) {
70
+ p = p.replace('~', home);
71
+ }
72
+ return p;
73
+ }
74
+ /** Canonical skills directory — the source of truth. */
75
+ export const CANONICAL_SKILLS_DIR = join(homedir(), '.agents', 'skills');
76
+ /** The git-managed agents directory — root of the backup repo. */
77
+ export const AGENTS_DIR = join(homedir(), '.agents');
78
+ /** Lock file path — READ ONLY, owned by vercel-labs/skills. */
79
+ export const SKILL_LOCK_PATH = join(homedir(), '.agents', '.skill-lock.json');
80
+ //# sourceMappingURL=agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"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"}
package/dist/auth.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Get GitHub token. Priority: gh auth token > GITHUB_TOKEN > GH_TOKEN.
3
+ * Returns null if none available.
4
+ */
5
+ export declare function getGitHubToken(): string | null;
6
+ /**
7
+ * Ensure a GitHub token is available. If not, interactively guide the user
8
+ * through authentication. Returns token or throws CliError.
9
+ */
10
+ export declare function ensureGitHubToken(): Promise<string>;
package/dist/auth.js ADDED
@@ -0,0 +1,70 @@
1
+ // src/auth.ts
2
+ import { execSync, spawnSync } from 'node:child_process';
3
+ import * as p from '@clack/prompts';
4
+ import { CliError } from './errors.js';
5
+ /**
6
+ * Get GitHub token. Priority: gh auth token > GITHUB_TOKEN > GH_TOKEN.
7
+ * Returns null if none available.
8
+ */
9
+ export function getGitHubToken() {
10
+ // Try gh CLI first
11
+ try {
12
+ const token = execSync('gh auth token', {
13
+ encoding: 'utf-8',
14
+ stdio: ['pipe', 'pipe', 'pipe'],
15
+ }).trim();
16
+ if (token)
17
+ return token;
18
+ }
19
+ catch {
20
+ // gh not installed or not authenticated
21
+ }
22
+ // Fallback to env vars
23
+ if (process.env.GITHUB_TOKEN)
24
+ return process.env.GITHUB_TOKEN;
25
+ if (process.env.GH_TOKEN)
26
+ return process.env.GH_TOKEN;
27
+ return null;
28
+ }
29
+ /**
30
+ * Ensure a GitHub token is available. If not, interactively guide the user
31
+ * through authentication. Returns token or throws CliError.
32
+ */
33
+ export async function ensureGitHubToken() {
34
+ const existing = getGitHubToken();
35
+ if (existing)
36
+ return existing;
37
+ const method = await p.select({
38
+ message: 'No GitHub authentication found. How would you like to authenticate?',
39
+ options: [
40
+ { value: 'gh', label: 'Run gh auth login', hint: 'recommended — opens GitHub CLI login flow' },
41
+ { value: 'env', label: 'Set GITHUB_TOKEN manually', hint: 'export GITHUB_TOKEN=ghp_...' },
42
+ ],
43
+ });
44
+ if (p.isCancel(method)) {
45
+ p.cancel('Authentication cancelled.');
46
+ throw new CliError('Authentication cancelled.');
47
+ }
48
+ if (method === 'gh') {
49
+ p.log.step('Starting gh auth login...');
50
+ const result = spawnSync('gh', ['auth', 'login'], {
51
+ stdio: 'inherit',
52
+ });
53
+ if (result.status !== 0) {
54
+ p.cancel('gh auth login failed. Please try again.');
55
+ throw new CliError('gh auth login failed.');
56
+ }
57
+ const token = getGitHubToken();
58
+ if (!token) {
59
+ p.cancel('Still no token after gh auth login. Please try again.');
60
+ throw new CliError('No token after gh auth login.');
61
+ }
62
+ return token;
63
+ }
64
+ // method === 'env'
65
+ p.note('Run one of the following, then re-run your command:\n\n' +
66
+ ' export GITHUB_TOKEN=ghp_your_token_here\n' +
67
+ ' export GH_TOKEN=ghp_your_token_here', 'Set environment variable');
68
+ throw new CliError('Please set GITHUB_TOKEN and try again.');
69
+ }
70
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,cAAc;AACd,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC5B,mBAAmB;IACnB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,eAAe,EAAE;YACtC,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;IAC1C,CAAC;IAED,uBAAuB;IACvB,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAC9D,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IAEtD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAClC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC;QAC5B,OAAO,EAAE,qEAAqE;QAC9E,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,2CAA2C,EAAE;YAC9F,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,2BAA2B,EAAE,IAAI,EAAE,6BAA6B,EAAE;SAC1F;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACvB,CAAC,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC;QACtC,MAAM,IAAI,QAAQ,CAAC,2BAA2B,CAAC,CAAC;IAClD,CAAC;IAED,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;YAChD,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,CAAC,CAAC,MAAM,CAAC,yCAAyC,CAAC,CAAC;YACpD,MAAM,IAAI,QAAQ,CAAC,uBAAuB,CAAC,CAAC;QAC9C,CAAC;QAED,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,CAAC,CAAC,MAAM,CAAC,uDAAuD,CAAC,CAAC;YAClE,MAAM,IAAI,QAAQ,CAAC,+BAA+B,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,mBAAmB;IACnB,CAAC,CAAC,IAAI,CACJ,yDAAyD;QACzD,6CAA6C;QAC7C,uCAAuC,EACvC,0BAA0B,CAC3B,CAAC;IACF,MAAM,IAAI,QAAQ,CAAC,wCAAwC,CAAC,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function linkCommand(options: {
2
+ agents?: string[];
3
+ }): Promise<void>;
@@ -0,0 +1,109 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { CANONICAL_SKILLS_DIR, SKILL_LOCK_PATH, agentRegistry, getAgentGlobalPath } from '../agents.js';
3
+ import { CliError } from '../errors.js';
4
+ import { readConfig, writeConfig } from '../config.js';
5
+ import { getLastSelectedAgents } from '../lockfile.js';
6
+ import { createSkillSymlinks, listCanonicalSkills } from '../linker.js';
7
+ import * as p from '@clack/prompts';
8
+ export async function linkCommand(options) {
9
+ p.intro('skills-manager link');
10
+ // 1. Read lock file for lastSelectedAgents
11
+ let agents;
12
+ if (options.agents?.length) {
13
+ const validIds = new Set(Object.keys(agentRegistry));
14
+ const invalid = options.agents.filter(id => !validIds.has(id));
15
+ if (invalid.length > 0) {
16
+ p.cancel(`Unknown agent ID(s): ${invalid.join(', ')}. Run with no --agents to see available IDs.`);
17
+ throw new CliError(`Unknown agent ID(s): ${invalid.join(', ')}`);
18
+ }
19
+ agents = options.agents;
20
+ }
21
+ else {
22
+ agents = await getLastSelectedAgents(SKILL_LOCK_PATH);
23
+ if (agents.length === 0) {
24
+ p.cancel('No lastSelectedAgents found in .skill-lock.json. Use --agents to specify.');
25
+ throw new CliError('No lastSelectedAgents found in .skill-lock.json.');
26
+ }
27
+ }
28
+ // 2. Interactive confirmation
29
+ const agentChoices = agents.map(id => {
30
+ const globalPath = getAgentGlobalPath(id);
31
+ const dirExists = existsSync(globalPath);
32
+ return {
33
+ value: id,
34
+ label: `${agentRegistry[id].displayName} (${id})`,
35
+ hint: dirExists ? globalPath : `${globalPath} — directory will be created`,
36
+ };
37
+ });
38
+ const selected = await p.multiselect({
39
+ message: 'Select agents to link skills to:',
40
+ options: agentChoices,
41
+ initialValues: computeInitialValues(agents, agentChoices),
42
+ required: false,
43
+ });
44
+ if (p.isCancel(selected) || !selected.length) {
45
+ p.cancel('No agents selected.');
46
+ return;
47
+ }
48
+ // 3. Get skill list
49
+ const skills = await listCanonicalSkills(CANONICAL_SKILLS_DIR);
50
+ if (skills.length === 0) {
51
+ p.cancel(`No skills found in ${CANONICAL_SKILLS_DIR}.`);
52
+ throw new CliError(`No skills found in ${CANONICAL_SKILLS_DIR}.`);
53
+ }
54
+ // 4. Create symlinks for each agent
55
+ const linkedAgents = [];
56
+ const skippedAgents = [];
57
+ const validSelected = new Set(Object.keys(agentRegistry));
58
+ const selectedAgents = selected.filter((id) => validSelected.has(id));
59
+ for (const agentId of selectedAgents) {
60
+ const globalPath = getAgentGlobalPath(agentId);
61
+ const spinner = p.spinner();
62
+ spinner.start(`Linking ${agentRegistry[agentId].displayName}...`);
63
+ try {
64
+ const results = await createSkillSymlinks(CANONICAL_SKILLS_DIR, globalPath, skills);
65
+ const created = results.filter(r => r.status === 'created').length;
66
+ const recreated = results.filter(r => r.status === 'recreated').length;
67
+ const existed = results.filter(r => r.status === 'exists').length;
68
+ const skipped = results.filter(r => r.status === 'skipped').length;
69
+ const parts = [`${created} linked`];
70
+ if (recreated > 0)
71
+ parts.push(`${recreated} recreated`);
72
+ parts.push(`${existed} existing`, `${skipped} skipped`);
73
+ spinner.stop(`${agentRegistry[agentId].displayName}: ${parts.join(', ')}`);
74
+ linkedAgents.push(agentId);
75
+ }
76
+ catch (err) {
77
+ spinner.stop(`${agentRegistry[agentId].displayName}: failed`);
78
+ skippedAgents.push({ id: agentId, reason: String(err) });
79
+ }
80
+ }
81
+ // 5. Save selection for next time
82
+ if (linkedAgents.length > 0) {
83
+ writeConfig({ lastLinkedAgents: selectedAgents });
84
+ }
85
+ // 6. Summary
86
+ p.note([
87
+ linkedAgents.length > 0 ? `✓ Linked: ${linkedAgents.join(', ')}` : '',
88
+ skippedAgents.length > 0 ? `✗ Skipped: ${skippedAgents.map(s => `${s.id} (${s.reason})`).join(', ')}` : '',
89
+ ].filter(Boolean).join('\n'), 'Summary');
90
+ p.outro('Done!');
91
+ }
92
+ /**
93
+ * Determine which agents should be pre-selected in the multiselect.
94
+ * Priority: saved lastLinkedAgents (if any match current list) > existsSync fallback.
95
+ */
96
+ function computeInitialValues(agents, choices) {
97
+ const config = readConfig();
98
+ const saved = config.lastLinkedAgents;
99
+ if (saved && saved.length > 0) {
100
+ const agentSet = new Set(agents);
101
+ const matching = saved.filter(id => agentSet.has(id));
102
+ if (matching.length > 0) {
103
+ return matching;
104
+ }
105
+ }
106
+ // Fallback: pre-select agents whose directories exist locally
107
+ return choices.filter(c => existsSync(getAgentGlobalPath(c.value))).map(c => c.value);
108
+ }
109
+ //# sourceMappingURL=link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.js","sourceRoot":"","sources":["../../src/commands/link.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function pullCommand(options: {
2
+ repo?: string;
3
+ skipLink?: boolean;
4
+ }): Promise<void>;
@@ -0,0 +1,57 @@
1
+ import { AGENTS_DIR } from '../agents.js';
2
+ import { ensureGitHubToken } from '../auth.js';
3
+ import { CliError } from '../errors.js';
4
+ import { pullSkills, buildRemoteUrl, getRepoRemoteUrl, ensureGitRepo, ensureRemote, } from '../git-ops.js';
5
+ import { linkCommand } from './link.js';
6
+ import * as p from '@clack/prompts';
7
+ export async function pullCommand(options) {
8
+ p.intro('skills-manager pull');
9
+ const token = await ensureGitHubToken();
10
+ // Determine remote URL
11
+ let remoteUrl;
12
+ if (options.repo) {
13
+ remoteUrl = buildRemoteUrl(options.repo);
14
+ }
15
+ else {
16
+ const existing = await getRepoRemoteUrl(AGENTS_DIR);
17
+ if (existing) {
18
+ remoteUrl = existing;
19
+ }
20
+ else {
21
+ // No --repo flag and no existing remote — prompt interactively
22
+ await ensureGitRepo(AGENTS_DIR);
23
+ const repo = await p.text({
24
+ message: 'Enter GitHub repo to pull from (owner/name):',
25
+ placeholder: 'e.g. tc9011/my-skills',
26
+ });
27
+ if (p.isCancel(repo)) {
28
+ p.cancel('Pull cancelled.');
29
+ throw new CliError('Pull cancelled by user.');
30
+ }
31
+ const result = await ensureRemote(AGENTS_DIR, repo);
32
+ remoteUrl = result.remoteUrl;
33
+ }
34
+ }
35
+ // Pull
36
+ const spinner = p.spinner();
37
+ spinner.start('Pulling skills from GitHub...');
38
+ try {
39
+ const result = await pullSkills(AGENTS_DIR, remoteUrl, token);
40
+ spinner.stop(result.cloned
41
+ ? 'Skills cloned successfully!'
42
+ : 'Skills updated from remote.');
43
+ }
44
+ catch (err) {
45
+ spinner.stop('Pull failed.');
46
+ p.cancel(String(err));
47
+ throw new CliError(String(err));
48
+ }
49
+ // Auto-run link unless skipped
50
+ if (!options.skipLink) {
51
+ await linkCommand({});
52
+ }
53
+ else {
54
+ p.outro('Pull complete. Run `skills-manager link` to create agent symlinks.');
55
+ }
56
+ }
57
+ //# sourceMappingURL=pull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.js","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EACL,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,YAAY,GACb,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AAEpC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAA8C;IAC9E,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAE/B,MAAM,KAAK,GAAG,MAAM,iBAAiB,EAAE,CAAC;IAExC,uBAAuB;IACvB,IAAI,SAAiB,CAAC;IACtB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;SAAM,CAAC;QACN,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,QAAQ,EAAE,CAAC;YACb,SAAS,GAAG,QAAQ,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,+DAA+D;YAC/D,MAAM,aAAa,CAAC,UAAU,CAAC,CAAC;YAEhC,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC;gBACxB,OAAO,EAAE,8CAA8C;gBACvD,WAAW,EAAE,uBAAuB;aACrC,CAAC,CAAC;YAEH,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrB,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;gBAC5B,MAAM,IAAI,QAAQ,CAAC,yBAAyB,CAAC,CAAC;YAChD,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;YACpD,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,OAAO;IACP,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IAC5B,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAE/C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CACV,MAAM,CAAC,MAAM;YACX,CAAC,CAAC,6BAA6B;YAC/B,CAAC,CAAC,6BAA6B,CAClC,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC7B,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACtB,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,+BAA+B;IAC/B,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtB,MAAM,WAAW,CAAC,EAAE,CAAC,CAAC;IACxB,CAAC;SAAM,CAAC;QACN,CAAC,CAAC,KAAK,CAAC,oEAAoE,CAAC,CAAC;IAChF,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function pushCommand(options: {
2
+ message?: string;
3
+ }): Promise<void>;