@nano-step/skill-manager 4.0.0 → 5.0.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 +79 -174
- package/dist/config.d.ts +5 -0
- package/dist/config.js +59 -0
- package/dist/index.js +147 -21
- package/dist/installer.d.ts +4 -0
- package/dist/installer.js +92 -0
- package/dist/registry.d.ts +3 -0
- package/dist/registry.js +63 -0
- package/dist/state.d.ts +4 -0
- package/dist/state.js +52 -0
- package/dist/utils.d.ts +25 -18
- package/dist/utils.js +7 -106
- package/package.json +2 -2
- package/skills/graphql-inspector/SKILL.md +170 -0
- package/skills/graphql-inspector/skill.json +9 -0
- package/{templates/skill → skills/skill-management}/SKILL.md +18 -18
- package/{templates/command-refresh.md → skills/skill-management/skill-refresh.md} +8 -8
- package/skills/skill-management/skill.json +18 -0
- package/templates/agent.json +0 -11
- package/templates/command-workflow.md +0 -188
- /package/{templates/skill → skills/skill-management}/assets/tools-template.json +0 -0
- /package/{templates/skill → skills/skill-management}/assets/workflow-schema.json +0 -0
- /package/{templates/skill → skills/skill-management}/assets/workflow-templates.json +0 -0
- /package/{templates/skill → skills/skill-management}/references/error-handling.md +0 -0
- /package/{templates/skill → skills/skill-management}/references/result-handling.md +0 -0
- /package/{templates/skill → skills/skill-management}/references/tool-categories.md +0 -0
- /package/{templates/skill → skills/skill-management}/references/tool-execution.md +0 -0
- /package/{templates/skill → skills/skill-management}/references/workflows.md +0 -0
package/README.md
CHANGED
|
@@ -1,211 +1,116 @@
|
|
|
1
1
|
# @nano-step/skill-manager
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Multi-skill registry CLI for [OpenCode](https://github.com/sst/opencode). Install, manage, and update AI agent skills from a built-in catalog.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
When using many MCP (Model Context Protocol) tools, the tool definitions consume thousands of tokens in your main agent's context window. This tool creates a lightweight routing layer that:
|
|
8
|
-
|
|
9
|
-
1. **Installs a skill** (`mcp-management`) with routing logic and documentation
|
|
10
|
-
2. **Configures a subagent** (`mcp-manager`) using a fast, cheap model
|
|
11
|
-
3. **Caches tool metadata** in `.opencode/mcp-tools.json` for instant routing
|
|
12
|
-
4. **Returns summarized results** — main agent stays lean
|
|
13
|
-
|
|
14
|
-
## Installation
|
|
5
|
+
## Quick Start
|
|
15
6
|
|
|
16
7
|
```bash
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
# List available skills
|
|
9
|
+
npx @nano-step/skill-manager list
|
|
19
10
|
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
- Install the `mcp-management` skill with routing logic
|
|
23
|
-
- Install the `/mcp-refresh` command
|
|
24
|
-
- Add the `mcp-manager` subagent to `oh-my-opencode.json`
|
|
11
|
+
# Install a skill
|
|
12
|
+
npx @nano-step/skill-manager install skill-management
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
### Install (first time)
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npx @nano-step/skill-manager
|
|
14
|
+
# Install all skills
|
|
15
|
+
npx @nano-step/skill-manager install --all
|
|
32
16
|
```
|
|
33
17
|
|
|
34
|
-
|
|
18
|
+
## Commands
|
|
35
19
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
20
|
+
| Command | Description |
|
|
21
|
+
|---------|-------------|
|
|
22
|
+
| `list` | Show available skills from the catalog |
|
|
23
|
+
| `install <name>` | Install a specific skill |
|
|
24
|
+
| `install --all` | Install all available skills |
|
|
25
|
+
| `remove <name>` | Remove an installed skill |
|
|
26
|
+
| `update [name]` | Update one or all installed skills |
|
|
27
|
+
| `installed` | Show currently installed skills |
|
|
41
28
|
|
|
42
|
-
|
|
29
|
+
## Available Skills
|
|
43
30
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Cleanly removes all installed artifacts.
|
|
31
|
+
| Skill | Description |
|
|
32
|
+
|-------|-------------|
|
|
33
|
+
| `skill-management` | AI skill routing — isolates tool definitions in subagent context to save 80-95% tokens |
|
|
34
|
+
| `graphql-inspector` | GraphQL schema inspection with progressive discovery workflow |
|
|
49
35
|
|
|
50
36
|
## What Gets Installed
|
|
51
37
|
|
|
52
|
-
|
|
53
|
-
|----------|----------|---------|
|
|
54
|
-
| Skill | `{config}/skills/mcp-management/` | Routing logic & documentation |
|
|
55
|
-
| Command | `{config}/command/agent-skill-refresh.md` | Re-index MCP tools |
|
|
56
|
-
| Agent | `{config}/oh-my-opencode.json` | `mcp-manager` subagent config |
|
|
57
|
-
| Version | `{config}/.agent-skill-version.json` | Track installed version |
|
|
58
|
-
|
|
59
|
-
## After Installation
|
|
60
|
-
|
|
61
|
-
Run the refresh command in OpenCode to create the initial tool cache:
|
|
38
|
+
When you install a skill, the manager:
|
|
62
39
|
|
|
63
|
-
|
|
64
|
-
/
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
This analyzes all available MCP tools and creates a semantic index at `.opencode/mcp-tools.json`.
|
|
68
|
-
|
|
69
|
-
## Config Detection
|
|
40
|
+
1. Copies skill files (SKILL.md, references, assets) to `{config}/skills/{name}/`
|
|
41
|
+
2. Copies command files to `{config}/command/` (if the skill has commands)
|
|
42
|
+
3. Merges agent config into `{config}/oh-my-opencode.json` (if the skill defines an agent)
|
|
43
|
+
4. Tracks installation state in `{config}/.skill-manager.json`
|
|
70
44
|
|
|
71
|
-
|
|
45
|
+
Config directory is detected automatically: `.opencode/` (project-level, preferred) or `~/.config/opencode/` (global).
|
|
72
46
|
|
|
73
|
-
|
|
74
|
-
2. **Global**: `~/.config/opencode/`
|
|
75
|
-
|
|
76
|
-
## How It Works
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
User request → Main agent (Opus/Sonnet)
|
|
80
|
-
↓ delegates MCP task
|
|
81
|
-
mcp-manager subagent (Haiku — fast & cheap)
|
|
82
|
-
↓ reads .opencode/mcp-tools.json cache
|
|
83
|
-
↓ routes to correct MCP tool category
|
|
84
|
-
↓ executes tool, summarizes result
|
|
85
|
-
↓ returns to main agent
|
|
86
|
-
Main agent continues with minimal token cost
|
|
87
|
-
```
|
|
47
|
+
## Migrating from v4
|
|
88
48
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
| Category | Tools | Example |
|
|
92
|
-
|----------|-------|---------|
|
|
93
|
-
| browser | chrome-devtools__* | Screenshots, clicks, navigation |
|
|
94
|
-
| github | github-*__* | PRs, issues, repos |
|
|
95
|
-
| graphql | graphql-tools__* | Schema inspection, queries |
|
|
96
|
-
| docs | context7__* | Documentation lookup |
|
|
97
|
-
| reasoning | Sequential-Thinking__* | Complex analysis |
|
|
98
|
-
|
|
99
|
-
## Advanced Features (v4.0.0)
|
|
100
|
-
|
|
101
|
-
### Direct Passthrough
|
|
102
|
-
|
|
103
|
-
Skip routing when you know the exact tool name:
|
|
104
|
-
|
|
105
|
-
```
|
|
106
|
-
MetaMCP_chrome-devtools__take_screenshot
|
|
107
|
-
```
|
|
49
|
+
v5.0.0 replaces the flag-based CLI with subcommands:
|
|
108
50
|
|
|
109
|
-
|
|
51
|
+
| v4 (old) | v5 (new) |
|
|
52
|
+
|----------|----------|
|
|
53
|
+
| `npx @nano-step/skill-manager` | `npx @nano-step/skill-manager install skill-management` |
|
|
54
|
+
| `npx @nano-step/skill-manager --update` | `npx @nano-step/skill-manager update` |
|
|
55
|
+
| `npx @nano-step/skill-manager --remove` | `npx @nano-step/skill-manager remove skill-management` |
|
|
110
56
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
Execute multiple tools in one request:
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
BATCH: [
|
|
117
|
-
{"tool": "screenshot", "params": {}},
|
|
118
|
-
{"tool": "get_title", "params": {}}
|
|
119
|
-
]
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
- Maximum 10 tools per batch
|
|
123
|
-
- Returns array of results in execution order
|
|
124
|
-
|
|
125
|
-
### Tool Chaining
|
|
126
|
-
|
|
127
|
-
Chain tools with output passing:
|
|
128
|
-
|
|
129
|
-
```
|
|
130
|
-
CHAIN: [
|
|
131
|
-
{"tool": "get_element", "params": {"selector": "#btn"}, "output_as": "el"},
|
|
132
|
-
{"tool": "click", "params": {"element": "$el"}}
|
|
133
|
-
]
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
- Use `$varname` to reference previous outputs
|
|
137
|
-
- Maximum 5 tools per chain
|
|
138
|
-
|
|
139
|
-
### Automatic Retry
|
|
140
|
-
|
|
141
|
-
All tool executions automatically retry on failure:
|
|
142
|
-
|
|
143
|
-
- Up to 3 attempts with delays: 0s → 1s → 2s
|
|
144
|
-
- Returns detailed failure report if all attempts fail
|
|
145
|
-
|
|
146
|
-
### Workflows
|
|
147
|
-
|
|
148
|
-
Define prerequisite steps that automatically execute before certain tools:
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
# Add a workflow from template
|
|
152
|
-
/agent-skill-workflow add --template database
|
|
153
|
-
|
|
154
|
-
# List active workflows
|
|
155
|
-
/agent-skill-workflow list
|
|
156
|
-
|
|
157
|
-
# Disable temporarily
|
|
158
|
-
/agent-skill-workflow disable database-safe-query
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
**Built-in templates:**
|
|
162
|
-
|
|
163
|
-
| Template | Description | Prerequisites |
|
|
164
|
-
|----------|-------------|---------------|
|
|
165
|
-
| `database` | Safe database queries | list_databases → list_tables → inspect_table |
|
|
166
|
-
| `browser` | Safe browser interaction | take_snapshot |
|
|
167
|
-
| `github-pr` | PR review workflow | get_pr → get_files → get_status |
|
|
168
|
-
|
|
169
|
-
**Workflow modes:**
|
|
170
|
-
- `enforce`: Auto-run prerequisites (default)
|
|
171
|
-
- `warn`: Show warning, allow skip
|
|
172
|
-
- `suggest`: Mention only, don't block
|
|
57
|
+
Existing v4 installations are automatically migrated on first run.
|
|
173
58
|
|
|
174
59
|
## Development
|
|
175
60
|
|
|
176
|
-
### Build
|
|
177
|
-
|
|
178
61
|
```bash
|
|
179
62
|
npm install
|
|
180
63
|
npm run build
|
|
181
|
-
```
|
|
182
64
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
node bin/cli.js
|
|
188
|
-
node bin/cli.js --update # Update
|
|
189
|
-
node bin/cli.js --remove # Remove
|
|
65
|
+
# Test locally
|
|
66
|
+
node bin/cli.js list
|
|
67
|
+
node bin/cli.js install skill-management
|
|
68
|
+
node bin/cli.js installed
|
|
69
|
+
node bin/cli.js remove skill-management
|
|
190
70
|
```
|
|
191
71
|
|
|
192
72
|
### Project Structure
|
|
193
73
|
|
|
194
74
|
```
|
|
195
75
|
skill-manager/
|
|
196
|
-
├── src/
|
|
197
|
-
│ ├── index.ts
|
|
198
|
-
│ ├──
|
|
199
|
-
│ ├──
|
|
200
|
-
│ ├──
|
|
201
|
-
│
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
│ ├──
|
|
205
|
-
│
|
|
206
|
-
├──
|
|
207
|
-
|
|
208
|
-
|
|
76
|
+
├── src/
|
|
77
|
+
│ ├── index.ts # CLI entry (commander subcommands)
|
|
78
|
+
│ ├── registry.ts # Scan skills/ dirs, load manifests
|
|
79
|
+
│ ├── installer.ts # Install/remove/update logic
|
|
80
|
+
│ ├── state.ts # Read/write .skill-manager.json
|
|
81
|
+
│ ├── config.ts # Merge agents/commands into oh-my-opencode.json
|
|
82
|
+
│ └── utils.ts # Path detection, file ops, shared types
|
|
83
|
+
├── skills/
|
|
84
|
+
│ ├── skill-management/
|
|
85
|
+
│ │ ├── skill.json # Skill manifest
|
|
86
|
+
│ │ ├── SKILL.md # Skill instructions
|
|
87
|
+
│ │ ├── skill-refresh.md
|
|
88
|
+
│ │ ├── references/ # Detailed documentation
|
|
89
|
+
│ │ └── assets/ # Templates and schemas
|
|
90
|
+
│ └── graphql-inspector/
|
|
91
|
+
│ ├── skill.json
|
|
92
|
+
│ └── SKILL.md
|
|
93
|
+
├── bin/cli.js
|
|
94
|
+
└── package.json
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Adding a New Skill
|
|
98
|
+
|
|
99
|
+
1. Create `skills/<name>/` directory
|
|
100
|
+
2. Add `skill.json` manifest:
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"name": "<name>",
|
|
104
|
+
"version": "1.0.0",
|
|
105
|
+
"description": "...",
|
|
106
|
+
"compatibility": "OpenCode with ...",
|
|
107
|
+
"agent": null,
|
|
108
|
+
"commands": [],
|
|
109
|
+
"tags": ["..."]
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
3. Add `SKILL.md` with skill instructions
|
|
113
|
+
4. Build and test: `npm run build && node bin/cli.js list`
|
|
209
114
|
|
|
210
115
|
## Related Projects
|
|
211
116
|
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { SkillManifest } from "./utils";
|
|
2
|
+
export declare function mergeAgentConfig(agentConfigPath: string, manifest: SkillManifest): Promise<void>;
|
|
3
|
+
export declare function removeAgentConfig(agentConfigPath: string, manifest: SkillManifest): Promise<void>;
|
|
4
|
+
export declare function copyCommands(manifest: SkillManifest, packageSkillDir: string, commandDir: string): Promise<void>;
|
|
5
|
+
export declare function removeCommands(manifest: SkillManifest, commandDir: string): Promise<void>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.mergeAgentConfig = mergeAgentConfig;
|
|
7
|
+
exports.removeAgentConfig = removeAgentConfig;
|
|
8
|
+
exports.copyCommands = copyCommands;
|
|
9
|
+
exports.removeCommands = removeCommands;
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
12
|
+
const utils_1 = require("./utils");
|
|
13
|
+
async function mergeAgentConfig(agentConfigPath, manifest) {
|
|
14
|
+
if (!manifest.agent)
|
|
15
|
+
return;
|
|
16
|
+
const config = await (0, utils_1.readJsonFile)(agentConfigPath, {});
|
|
17
|
+
const agents = (config.agents || {});
|
|
18
|
+
agents[manifest.agent.id] = manifest.agent.config;
|
|
19
|
+
config.agents = agents;
|
|
20
|
+
await (0, utils_1.writeJsonFile)(agentConfigPath, config);
|
|
21
|
+
}
|
|
22
|
+
async function removeAgentConfig(agentConfigPath, manifest) {
|
|
23
|
+
if (!manifest.agent)
|
|
24
|
+
return;
|
|
25
|
+
const exists = await fs_extra_1.default.pathExists(agentConfigPath);
|
|
26
|
+
if (!exists)
|
|
27
|
+
return;
|
|
28
|
+
const config = await (0, utils_1.readJsonFile)(agentConfigPath, {});
|
|
29
|
+
const agents = (config.agents || {});
|
|
30
|
+
if (Object.prototype.hasOwnProperty.call(agents, manifest.agent.id)) {
|
|
31
|
+
delete agents[manifest.agent.id];
|
|
32
|
+
config.agents = agents;
|
|
33
|
+
await (0, utils_1.writeJsonFile)(agentConfigPath, config);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function copyCommands(manifest, packageSkillDir, commandDir) {
|
|
37
|
+
if (!manifest.commands || manifest.commands.length === 0)
|
|
38
|
+
return;
|
|
39
|
+
await (0, utils_1.ensureDirExists)(commandDir);
|
|
40
|
+
for (const cmd of manifest.commands) {
|
|
41
|
+
const src = path_1.default.join(packageSkillDir, cmd);
|
|
42
|
+
const dest = path_1.default.join(commandDir, cmd);
|
|
43
|
+
const srcExists = await fs_extra_1.default.pathExists(src);
|
|
44
|
+
if (srcExists) {
|
|
45
|
+
await fs_extra_1.default.copy(src, dest, { overwrite: true });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function removeCommands(manifest, commandDir) {
|
|
50
|
+
if (!manifest.commands || manifest.commands.length === 0)
|
|
51
|
+
return;
|
|
52
|
+
for (const cmd of manifest.commands) {
|
|
53
|
+
const cmdPath = path_1.default.join(commandDir, cmd);
|
|
54
|
+
const exists = await fs_extra_1.default.pathExists(cmdPath);
|
|
55
|
+
if (exists) {
|
|
56
|
+
await fs_extra_1.default.remove(cmdPath);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -6,29 +6,155 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.run = run;
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
|
+
const registry_1 = require("./registry");
|
|
11
|
+
const state_1 = require("./state");
|
|
12
|
+
const installer_1 = require("./installer");
|
|
12
13
|
async function run() {
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
if (args.includes("--update") || args.includes("--remove")) {
|
|
16
|
+
console.log(chalk_1.default.yellow("\n⚠️ The --update and --remove flags were removed in v5.0.0."));
|
|
17
|
+
console.log(chalk_1.default.yellow("Use subcommands instead:"));
|
|
18
|
+
console.log(chalk_1.default.cyan(" skill-manager install <name> # Install a skill"));
|
|
19
|
+
console.log(chalk_1.default.cyan(" skill-manager remove <name> # Remove a skill"));
|
|
20
|
+
console.log(chalk_1.default.cyan(" skill-manager update [name] # Update skill(s)"));
|
|
21
|
+
console.log(chalk_1.default.cyan(" skill-manager list # Show available skills"));
|
|
22
|
+
console.log(chalk_1.default.cyan(" skill-manager installed # Show installed skills\n"));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
13
25
|
const program = new commander_1.Command();
|
|
14
26
|
program
|
|
15
27
|
.name("skill-manager")
|
|
16
|
-
.description("Install and manage AI agent skills
|
|
17
|
-
.
|
|
18
|
-
|
|
19
|
-
.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
.description("Install and manage AI agent skills for OpenCode")
|
|
29
|
+
.version(utils_1.MANAGER_VERSION);
|
|
30
|
+
program
|
|
31
|
+
.command("list")
|
|
32
|
+
.description("Show available skills from the catalog")
|
|
33
|
+
.action(async () => {
|
|
34
|
+
const paths = await (0, utils_1.detectOpenCodePaths)();
|
|
35
|
+
await (0, state_1.migrateV4State)(paths.configDir, paths.stateFilePath, paths.skillsDir);
|
|
36
|
+
const catalog = await (0, registry_1.loadCatalog)(paths.packageSkillsDir);
|
|
37
|
+
const state = await (0, state_1.loadState)(paths.stateFilePath);
|
|
38
|
+
if (catalog.length === 0) {
|
|
39
|
+
console.log(chalk_1.default.yellow("No skills found in catalog."));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
console.log(chalk_1.default.bold("\nAvailable Skills:\n"));
|
|
43
|
+
const nameWidth = 22;
|
|
44
|
+
const versionWidth = 10;
|
|
45
|
+
const statusWidth = 14;
|
|
46
|
+
console.log(chalk_1.default.gray(" " +
|
|
47
|
+
"Name".padEnd(nameWidth) +
|
|
48
|
+
"Version".padEnd(versionWidth) +
|
|
49
|
+
"Status".padEnd(statusWidth) +
|
|
50
|
+
"Description"));
|
|
51
|
+
console.log(chalk_1.default.gray(" " + "─".repeat(nameWidth + versionWidth + statusWidth + 30)));
|
|
52
|
+
for (const skill of catalog) {
|
|
53
|
+
const installed = state.skills[skill.name];
|
|
54
|
+
const status = installed ? chalk_1.default.green("installed") : chalk_1.default.gray("not installed");
|
|
55
|
+
console.log(" " +
|
|
56
|
+
chalk_1.default.cyan(skill.name.padEnd(nameWidth)) +
|
|
57
|
+
skill.version.padEnd(versionWidth) +
|
|
58
|
+
status.padEnd(statusWidth + 10) +
|
|
59
|
+
skill.description);
|
|
60
|
+
}
|
|
61
|
+
console.log("");
|
|
62
|
+
});
|
|
63
|
+
program
|
|
64
|
+
.command("install [name]")
|
|
65
|
+
.description("Install a skill from the catalog")
|
|
66
|
+
.option("--all", "Install all available skills")
|
|
67
|
+
.action(async (name, options) => {
|
|
68
|
+
const paths = await (0, utils_1.detectOpenCodePaths)();
|
|
69
|
+
await (0, state_1.migrateV4State)(paths.configDir, paths.stateFilePath, paths.skillsDir);
|
|
70
|
+
if (options.all) {
|
|
71
|
+
const catalog = await (0, registry_1.loadCatalog)(paths.packageSkillsDir);
|
|
72
|
+
if (catalog.length === 0) {
|
|
73
|
+
console.log(chalk_1.default.yellow("No skills found in catalog."));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
for (const skill of catalog) {
|
|
77
|
+
await (0, installer_1.installSkill)(skill.name, paths);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if (name) {
|
|
81
|
+
await (0, installer_1.installSkill)(name, paths);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.error(chalk_1.default.red("Please specify a skill name or use --all."));
|
|
85
|
+
console.error(chalk_1.default.yellow("Run 'skill-manager list' to see available skills."));
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
program
|
|
90
|
+
.command("remove <name>")
|
|
91
|
+
.description("Remove an installed skill")
|
|
92
|
+
.action(async (name) => {
|
|
93
|
+
const paths = await (0, utils_1.detectOpenCodePaths)();
|
|
94
|
+
await (0, state_1.migrateV4State)(paths.configDir, paths.stateFilePath, paths.skillsDir);
|
|
95
|
+
await (0, installer_1.removeSkill)(name, paths);
|
|
96
|
+
});
|
|
97
|
+
program
|
|
98
|
+
.command("update [name]")
|
|
99
|
+
.description("Update installed skill(s) to latest catalog version")
|
|
100
|
+
.action(async (name) => {
|
|
101
|
+
const paths = await (0, utils_1.detectOpenCodePaths)();
|
|
102
|
+
await (0, state_1.migrateV4State)(paths.configDir, paths.stateFilePath, paths.skillsDir);
|
|
103
|
+
if (name) {
|
|
104
|
+
await (0, installer_1.updateSkill)(name, paths);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const state = await (0, state_1.loadState)(paths.stateFilePath);
|
|
108
|
+
const installed = Object.keys(state.skills);
|
|
109
|
+
if (installed.length === 0) {
|
|
110
|
+
console.log(chalk_1.default.yellow("No skills installed. Use 'skill-manager install <name>' first."));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
for (const skillName of installed) {
|
|
114
|
+
await (0, installer_1.updateSkill)(skillName, paths);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
program
|
|
119
|
+
.command("installed")
|
|
120
|
+
.description("Show currently installed skills")
|
|
121
|
+
.action(async () => {
|
|
122
|
+
const paths = await (0, utils_1.detectOpenCodePaths)();
|
|
123
|
+
await (0, state_1.migrateV4State)(paths.configDir, paths.stateFilePath, paths.skillsDir);
|
|
124
|
+
const state = await (0, state_1.loadState)(paths.stateFilePath);
|
|
125
|
+
const catalog = await (0, registry_1.loadCatalog)(paths.packageSkillsDir);
|
|
126
|
+
const installedNames = Object.keys(state.skills);
|
|
127
|
+
if (installedNames.length === 0) {
|
|
128
|
+
console.log(chalk_1.default.yellow("No skills installed."));
|
|
129
|
+
console.log(chalk_1.default.gray("Run 'skill-manager list' to see available skills."));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
console.log(chalk_1.default.bold("\nInstalled Skills:\n"));
|
|
133
|
+
const nameWidth = 22;
|
|
134
|
+
const versionWidth = 12;
|
|
135
|
+
const catalogWidth = 12;
|
|
136
|
+
console.log(chalk_1.default.gray(" " +
|
|
137
|
+
"Name".padEnd(nameWidth) +
|
|
138
|
+
"Installed".padEnd(versionWidth) +
|
|
139
|
+
"Catalog".padEnd(catalogWidth) +
|
|
140
|
+
"Installed At"));
|
|
141
|
+
console.log(chalk_1.default.gray(" " + "─".repeat(nameWidth + versionWidth + catalogWidth + 24)));
|
|
142
|
+
for (const name of installedNames.sort()) {
|
|
143
|
+
const info = state.skills[name];
|
|
144
|
+
const catalogSkill = catalog.find((s) => s.name === name);
|
|
145
|
+
const catalogVersion = catalogSkill ? catalogSkill.version : "?";
|
|
146
|
+
const needsUpdate = catalogSkill && catalogSkill.version !== info.version;
|
|
147
|
+
const catalogDisplay = needsUpdate
|
|
148
|
+
? chalk_1.default.yellow(catalogVersion)
|
|
149
|
+
: chalk_1.default.green(catalogVersion);
|
|
150
|
+
const date = new Date(info.installedAt).toLocaleDateString();
|
|
151
|
+
console.log(" " +
|
|
152
|
+
chalk_1.default.cyan(name.padEnd(nameWidth)) +
|
|
153
|
+
info.version.padEnd(versionWidth) +
|
|
154
|
+
catalogDisplay.padEnd(catalogWidth + 10) +
|
|
155
|
+
date);
|
|
156
|
+
}
|
|
157
|
+
console.log("");
|
|
158
|
+
});
|
|
159
|
+
await program.parseAsync(process.argv);
|
|
34
160
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { OpenCodePaths } from "./utils";
|
|
2
|
+
export declare function installSkill(name: string, paths: OpenCodePaths): Promise<void>;
|
|
3
|
+
export declare function removeSkill(name: string, paths: OpenCodePaths): Promise<void>;
|
|
4
|
+
export declare function updateSkill(name: string, paths: OpenCodePaths): Promise<void>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.installSkill = installSkill;
|
|
7
|
+
exports.removeSkill = removeSkill;
|
|
8
|
+
exports.updateSkill = updateSkill;
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
12
|
+
const utils_1 = require("./utils");
|
|
13
|
+
const registry_1 = require("./registry");
|
|
14
|
+
const state_1 = require("./state");
|
|
15
|
+
const config_1 = require("./config");
|
|
16
|
+
async function installSkill(name, paths) {
|
|
17
|
+
const manifest = await (0, registry_1.getSkillManifest)(paths.packageSkillsDir, name);
|
|
18
|
+
if (!manifest) {
|
|
19
|
+
const catalog = await (0, registry_1.loadCatalog)(paths.packageSkillsDir);
|
|
20
|
+
const available = catalog.map((s) => s.name).join(", ");
|
|
21
|
+
console.error(chalk_1.default.red(`Skill "${name}" not found in catalog.`));
|
|
22
|
+
console.error(chalk_1.default.yellow(`Available skills: ${available || "none"}`));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const state = await (0, state_1.loadState)(paths.stateFilePath);
|
|
26
|
+
const existing = state.skills[name];
|
|
27
|
+
if (existing && existing.version === manifest.version) {
|
|
28
|
+
console.log(chalk_1.default.yellow(`Skill "${name}" is already installed at v${manifest.version}.`));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
await (0, utils_1.ensureDirExists)(paths.skillsDir);
|
|
32
|
+
const packageSkillDir = path_1.default.join(paths.packageSkillsDir, name);
|
|
33
|
+
const targetSkillDir = path_1.default.join(paths.skillsDir, name);
|
|
34
|
+
await fs_extra_1.default.copy(packageSkillDir, targetSkillDir, { overwrite: true });
|
|
35
|
+
await (0, config_1.copyCommands)(manifest, packageSkillDir, paths.commandDir);
|
|
36
|
+
await (0, config_1.mergeAgentConfig)(paths.agentConfigPath, manifest);
|
|
37
|
+
state.skills[name] = {
|
|
38
|
+
version: manifest.version,
|
|
39
|
+
installedAt: new Date().toISOString(),
|
|
40
|
+
location: paths.configDir.includes(".config/opencode") ? "global" : "project",
|
|
41
|
+
};
|
|
42
|
+
await (0, state_1.saveState)(paths.stateFilePath, state);
|
|
43
|
+
console.log(chalk_1.default.green(`✓ Installed ${name} v${manifest.version}`));
|
|
44
|
+
}
|
|
45
|
+
async function removeSkill(name, paths) {
|
|
46
|
+
const state = await (0, state_1.loadState)(paths.stateFilePath);
|
|
47
|
+
if (!state.skills[name]) {
|
|
48
|
+
console.log(chalk_1.default.yellow(`Skill "${name}" is not installed.`));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const manifest = await (0, registry_1.getSkillManifest)(paths.packageSkillsDir, name);
|
|
52
|
+
const targetSkillDir = path_1.default.join(paths.skillsDir, name);
|
|
53
|
+
if (await fs_extra_1.default.pathExists(targetSkillDir)) {
|
|
54
|
+
await fs_extra_1.default.remove(targetSkillDir);
|
|
55
|
+
}
|
|
56
|
+
if (manifest) {
|
|
57
|
+
await (0, config_1.removeCommands)(manifest, paths.commandDir);
|
|
58
|
+
await (0, config_1.removeAgentConfig)(paths.agentConfigPath, manifest);
|
|
59
|
+
}
|
|
60
|
+
delete state.skills[name];
|
|
61
|
+
await (0, state_1.saveState)(paths.stateFilePath, state);
|
|
62
|
+
console.log(chalk_1.default.green(`✓ Removed ${name}`));
|
|
63
|
+
}
|
|
64
|
+
async function updateSkill(name, paths) {
|
|
65
|
+
const state = await (0, state_1.loadState)(paths.stateFilePath);
|
|
66
|
+
if (!state.skills[name]) {
|
|
67
|
+
console.error(chalk_1.default.red(`Skill "${name}" is not installed. Use 'skill-manager install ${name}' first.`));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
const manifest = await (0, registry_1.getSkillManifest)(paths.packageSkillsDir, name);
|
|
71
|
+
if (!manifest) {
|
|
72
|
+
console.error(chalk_1.default.red(`Skill "${name}" not found in catalog. It may have been removed.`));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
const installed = state.skills[name];
|
|
76
|
+
if (installed.version === manifest.version) {
|
|
77
|
+
console.log(chalk_1.default.yellow(`Skill "${name}" is already at v${manifest.version} (up to date).`));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const packageSkillDir = path_1.default.join(paths.packageSkillsDir, name);
|
|
81
|
+
const targetSkillDir = path_1.default.join(paths.skillsDir, name);
|
|
82
|
+
await fs_extra_1.default.copy(packageSkillDir, targetSkillDir, { overwrite: true });
|
|
83
|
+
await (0, config_1.copyCommands)(manifest, packageSkillDir, paths.commandDir);
|
|
84
|
+
await (0, config_1.mergeAgentConfig)(paths.agentConfigPath, manifest);
|
|
85
|
+
state.skills[name] = {
|
|
86
|
+
version: manifest.version,
|
|
87
|
+
installedAt: new Date().toISOString(),
|
|
88
|
+
location: installed.location,
|
|
89
|
+
};
|
|
90
|
+
await (0, state_1.saveState)(paths.stateFilePath, state);
|
|
91
|
+
console.log(chalk_1.default.green(`✓ Updated ${name} from v${installed.version} to v${manifest.version}`));
|
|
92
|
+
}
|