@nano-step/skill-manager 4.0.0 → 5.0.1

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 (34) hide show
  1. package/README.md +79 -174
  2. package/dist/config.d.ts +5 -0
  3. package/dist/config.js +59 -0
  4. package/dist/index.js +147 -21
  5. package/dist/installer.d.ts +4 -0
  6. package/dist/installer.js +92 -0
  7. package/dist/registry.d.ts +3 -0
  8. package/dist/registry.js +63 -0
  9. package/dist/state.d.ts +4 -0
  10. package/dist/state.js +52 -0
  11. package/dist/utils.d.ts +25 -18
  12. package/dist/utils.js +7 -106
  13. package/package.json +4 -2
  14. package/skills/graphql-inspector/SKILL.md +170 -0
  15. package/skills/graphql-inspector/skill.json +9 -0
  16. package/{templates/skill → skills/skill-management}/SKILL.md +18 -18
  17. package/{templates/skill → skills/skill-management}/assets/tools-template.json +1 -1
  18. package/{templates/skill → skills/skill-management}/assets/workflow-schema.json +1 -1
  19. package/{templates/skill → skills/skill-management}/references/error-handling.md +7 -7
  20. package/{templates/skill → skills/skill-management}/references/tool-categories.md +4 -4
  21. package/{templates/skill → skills/skill-management}/references/tool-execution.md +7 -7
  22. package/{templates/skill → skills/skill-management}/references/workflows.md +9 -9
  23. package/{templates/command-refresh.md → skills/skill-management/skill-refresh.md} +8 -8
  24. package/skills/skill-management/skill.json +18 -0
  25. package/dist/install.d.ts +0 -1
  26. package/dist/install.js +0 -35
  27. package/dist/remove.d.ts +0 -1
  28. package/dist/remove.js +0 -32
  29. package/dist/update.d.ts +0 -1
  30. package/dist/update.js +0 -53
  31. package/templates/agent.json +0 -11
  32. package/templates/command-workflow.md +0 -188
  33. /package/{templates/skill → skills/skill-management}/assets/workflow-templates.json +0 -0
  34. /package/{templates/skill → skills/skill-management}/references/result-handling.md +0 -0
package/README.md CHANGED
@@ -1,211 +1,116 @@
1
1
  # @nano-step/skill-manager
2
2
 
3
- CLI tool that installs and manages **AI agent skills** into [OpenCode](https://github.com/sst/opencode) projects. Reduces token usage by **80-95%** by isolating MCP tool definitions in a dedicated subagent context.
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
- ## Why?
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
- npx @nano-step/skill-manager
18
- ```
8
+ # List available skills
9
+ npx @nano-step/skill-manager list
19
10
 
20
- This will:
21
- - Detect your OpenCode config directory (`.opencode/` or `~/.config/opencode/`)
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
- ## Usage
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
- ### Update (to latest version)
18
+ ## Commands
35
19
 
36
- ```bash
37
- npx @nano-step/skill-manager --update
38
- ```
39
-
40
- Creates timestamped backups of customized files before updating.
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
- ### Remove
29
+ ## Available Skills
43
30
 
44
- ```bash
45
- npx @nano-step/skill-manager --remove
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
- | Artifact | Location | Purpose |
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
- /agent-skill-refresh
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
- The tool looks for OpenCode configuration in this order:
45
+ Config directory is detected automatically: `.opencode/` (project-level, preferred) or `~/.config/opencode/` (global).
72
46
 
73
- 1. **Project-level**: `.opencode/` in current directory (preferred)
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
- ### Tool Categories
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
- Any tool name containing `__` executes directly without category lookup.
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
- ### Batch Operations
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
- ### Test Locally
184
-
185
- ```bash
186
- # After building
187
- node bin/cli.js # Install
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/ # TypeScript source
197
- │ ├── index.ts # CLI entry point
198
- │ ├── install.ts # Installation logic
199
- │ ├── update.ts # Update with backup
200
- │ ├── remove.ts # Clean removal
201
- └── utils.ts # Shared utilities
202
- ├── templates/ # Files to install
203
- ├── agent.json # mcp-manager agent config
204
- │ ├── command-refresh.md # /agent-skill-refresh command
205
- └── skill/ # mcp-management skill
206
- ├── bin/cli.js # Executable entry
207
- └── dist/ # Compiled output
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
 
@@ -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 install_1 = require("./install");
10
- const update_1 = require("./update");
11
- const remove_1 = require("./remove");
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 and configurations")
17
- .option("--update", "Update existing agent skill installation")
18
- .option("--remove", "Remove agent skill installation")
19
- .parse(process.argv);
20
- const options = program.opts();
21
- if (options.update && options.remove) {
22
- console.error(chalk_1.default.red("Cannot use --update and --remove together."));
23
- process.exit(1);
24
- }
25
- if (options.remove) {
26
- await (0, remove_1.remove)();
27
- return;
28
- }
29
- if (options.update) {
30
- await (0, update_1.update)();
31
- return;
32
- }
33
- await (0, install_1.install)();
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
+ }
@@ -0,0 +1,3 @@
1
+ import { SkillManifest } from "./utils";
2
+ export declare function loadCatalog(packageSkillsDir: string): Promise<SkillManifest[]>;
3
+ export declare function getSkillManifest(packageSkillsDir: string, name: string): Promise<SkillManifest | null>;