@lyupro/skillforge-mcp 1.1.1 → 1.3.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/.claude-plugin/marketplace.json +36 -0
- package/.claude-plugin/plugin.json +28 -0
- package/CHANGELOG.md +43 -0
- package/README.md +94 -12
- package/dist/cli/dispatcher.d.ts.map +1 -1
- package/dist/cli/dispatcher.js +24 -2
- package/dist/cli/dispatcher.js.map +1 -1
- package/dist/cli/folders-format.d.ts +10 -0
- package/dist/cli/folders-format.d.ts.map +1 -0
- package/dist/cli/folders-format.js +41 -0
- package/dist/cli/folders-format.js.map +1 -0
- package/dist/cli/folders-handlers.d.ts +16 -0
- package/dist/cli/folders-handlers.d.ts.map +1 -0
- package/dist/cli/folders-handlers.js +195 -0
- package/dist/cli/folders-handlers.js.map +1 -0
- package/dist/cli/folders-shared.d.ts +29 -0
- package/dist/cli/folders-shared.d.ts.map +1 -0
- package/dist/cli/folders-shared.js +82 -0
- package/dist/cli/folders-shared.js.map +1 -0
- package/dist/cli/folders.d.ts +41 -0
- package/dist/cli/folders.d.ts.map +1 -0
- package/dist/cli/folders.js +100 -0
- package/dist/cli/folders.js.map +1 -0
- package/dist/cli/install.d.ts +2 -0
- package/dist/cli/install.d.ts.map +1 -1
- package/dist/cli/install.js +47 -9
- package/dist/cli/install.js.map +1 -1
- package/dist/cli/tools.d.ts +49 -0
- package/dist/cli/tools.d.ts.map +1 -0
- package/dist/cli/tools.js +177 -0
- package/dist/cli/tools.js.map +1 -0
- package/dist/config/config-schema.d.ts +8 -0
- package/dist/config/config-schema.d.ts.map +1 -1
- package/dist/config/config-schema.js +6 -0
- package/dist/config/config-schema.js.map +1 -1
- package/dist/detect/skill-source-conflict.d.ts +47 -0
- package/dist/detect/skill-source-conflict.d.ts.map +1 -0
- package/dist/detect/skill-source-conflict.js +99 -0
- package/dist/detect/skill-source-conflict.js.map +1 -0
- package/dist/installers/cursor-installer.d.ts +6 -7
- package/dist/installers/cursor-installer.d.ts.map +1 -1
- package/dist/installers/cursor-installer.js +15 -20
- package/dist/installers/cursor-installer.js.map +1 -1
- package/dist/installers/paths.d.ts +28 -6
- package/dist/installers/paths.d.ts.map +1 -1
- package/dist/installers/paths.js +72 -20
- package/dist/installers/paths.js.map +1 -1
- package/dist/installers/registry.d.ts +8 -2
- package/dist/installers/registry.d.ts.map +1 -1
- package/dist/installers/registry.js +14 -7
- package/dist/installers/registry.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +3 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/configure.d.ts +8 -0
- package/dist/tools/configure.d.ts.map +1 -1
- package/dist/tools/configure.js +17 -1
- package/dist/tools/configure.js.map +1 -1
- package/dist/tools/list.d.ts +2 -0
- package/dist/tools/list.d.ts.map +1 -1
- package/dist/tools/list.js +12 -0
- package/dist/tools/list.js.map +1 -1
- package/manifest.json +5 -4
- package/package.json +3 -2
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-marketplace.json",
|
|
3
|
+
"name": "skillforge",
|
|
4
|
+
"description": "SkillForge — universal Markdown skills for Claude Code and any MCP client.",
|
|
5
|
+
"owner": {
|
|
6
|
+
"name": "Lyu Pro",
|
|
7
|
+
"email": "lyupro.dev@gmail.com"
|
|
8
|
+
},
|
|
9
|
+
"plugins": [
|
|
10
|
+
{
|
|
11
|
+
"name": "skillforge",
|
|
12
|
+
"source": {
|
|
13
|
+
"source": "npm",
|
|
14
|
+
"package": "@lyupro/skillforge-mcp"
|
|
15
|
+
},
|
|
16
|
+
"description": "Universal Skills MCP server — load Markdown skills from arbitrary folders, lazy-by-design, cross-tool.",
|
|
17
|
+
"version": "1.3.0",
|
|
18
|
+
"author": {
|
|
19
|
+
"name": "Lyu Pro",
|
|
20
|
+
"email": "lyupro.dev@gmail.com"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/lyupro/skillforge-mcp",
|
|
23
|
+
"repository": "https://github.com/lyupro/skillforge-mcp",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"keywords": [
|
|
26
|
+
"skills",
|
|
27
|
+
"mcp",
|
|
28
|
+
"productivity",
|
|
29
|
+
"developer-tools",
|
|
30
|
+
"cross-tool",
|
|
31
|
+
"lazy-loading"
|
|
32
|
+
],
|
|
33
|
+
"category": "productivity"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
|
+
"name": "skillforge",
|
|
4
|
+
"version": "1.3.0",
|
|
5
|
+
"description": "Universal Skills MCP server — load Markdown skills from arbitrary folders, lazy-by-design, cross-tool.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Lyu Pro",
|
|
8
|
+
"email": "lyupro.dev@gmail.com",
|
|
9
|
+
"url": "https://lyupro.com"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/lyupro/skillforge-mcp",
|
|
12
|
+
"repository": "https://github.com/lyupro/skillforge-mcp",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"skills",
|
|
16
|
+
"mcp",
|
|
17
|
+
"productivity",
|
|
18
|
+
"developer-tools",
|
|
19
|
+
"cross-tool",
|
|
20
|
+
"lazy-loading"
|
|
21
|
+
],
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"skillforge": {
|
|
24
|
+
"command": "node",
|
|
25
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/dist/cli/dispatcher.js", "serve"]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to **SkillForge MCP** are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
4
4
|
|
|
5
|
+
## [1.3.0] — 2026-05-16
|
|
6
|
+
|
|
7
|
+
Folder ergonomics — address folders by a short alias, toggle them on and off, and filter skills by folder tag.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Folder alias. A folder entry now carries an optional kebab-case `alias` — a single short handle for a folder. `skillforge folders add <path> --alias <name>` registers it; `skillforge folders remove <name>` and the new `skillforge folders alias <path|alias> <name>` accept the alias in place of the full absolute path. The full path keeps working everywhere. Aliases are validated kebab-case and unique across registered folders — a collision fails with exit ≠ 0 and leaves the config untouched. The `skills__configure` `add_folder` action accepts an `alias` field too, and `folders list` shows an `ALIAS` column.
|
|
12
|
+
- `skillforge folders enable <path|alias>` / `skillforge folders disable <path|alias>` — a two-way toggle for the `enabled` flag. `folders add --disabled` previously had no inverse; re-activating a folder meant hand-editing `config.json`. A disabled folder stays in the config but is skipped on scan.
|
|
13
|
+
- Folder-tag filtering. Folder `tags` were written to the config but never read. `skills__list` now accepts a `folderTag` argument that keeps only skills under folders carrying that tag, and `skillforge folders list --tag <name>` filters the listing the same way. `docs/CONFIGURATION.md` gains a "tags vs alias" section: `alias` is one unique handle per folder for addressing, `tags` are many shared labels for grouping.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- `src/cli/folders.ts` split into four flat sibling modules (`folders.ts`, `folders-handlers.ts`, `folders-format.ts`, `folders-shared.ts`) to stay under the 400-line file gate. The `dispatcher.ts` import path is unchanged.
|
|
18
|
+
|
|
19
|
+
### Verified
|
|
20
|
+
|
|
21
|
+
- 561 / 561 tests passing + 1 win32-skip.
|
|
22
|
+
- `pnpm lint` (`tsc --noEmit`) clean.
|
|
23
|
+
- `pnpm build` clean.
|
|
24
|
+
- `pnpm check:size` — all source files ≤ 400 lines.
|
|
25
|
+
|
|
26
|
+
## [1.2.0] — 2026-05-16
|
|
27
|
+
|
|
28
|
+
Terminal-side tooling — inspect MCP tools and manage skill folders without an LLM session, plus repo-local install scope and Claude Code plugin packaging.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- `skillforge tools` CLI subcommand — prints the 5 MCP tools the server exposes (`skills__list`, `skills__get`, `skills__invoke`, `skills__configure`, `skills__reload`) with each tool's description, parameters, and an example invocation. Pass `--json` for machine-readable output. Lets you confirm the tool surface without starting an LLM session.
|
|
33
|
+
- `skillforge folders` CLI subcommand — manage skill folders from the terminal: `folders list [--json]`, `folders add <path> [--priority N] [--tags a,b] [--disabled]`, `folders remove <path>`, `folders reset --yes`. Previously folder management was only reachable via the `skills__configure` MCP tool inside an LLM session; both surfaces now read and write the same persisted config.
|
|
34
|
+
- `--scope global|project` flag on `skillforge install` / `skillforge uninstall`. The default `global` scope edits each host's home-directory config (unchanged behavior). `--scope project` wires SkillForge into a repo-local config rooted at the current directory — `.mcp.json` (Claude Code), `.codex/config.toml` (Codex CLI), `.cursor/mcp.json` (Cursor).
|
|
35
|
+
- Skill-source conflict detection. Registering a folder that already lives inside another tool's native skill store (a Claude Code plugin cache or a Gemini CLI extension) now surfaces a hint to disable the duplicate source — otherwise the same skills load twice and skill names collide. The `folders add` CLI prints the hint; the `skills__configure` `add_folder` action returns it as a `conflictHint` field. The conflict is informational only — it never blocks the folder from being added, and SkillForge never edits another tool's config.
|
|
36
|
+
- Claude Code plugin packaging — `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json`. SkillForge can now be installed via `claude plugin install` (rich `/plugins` UI card) in addition to `claude mcp add` and `skillforge install`.
|
|
37
|
+
|
|
38
|
+
### Verified
|
|
39
|
+
|
|
40
|
+
- 535 / 535 tests passing + 1 win32-skip.
|
|
41
|
+
- `pnpm lint` (`tsc --noEmit`) clean.
|
|
42
|
+
- `pnpm build` clean.
|
|
43
|
+
- `pnpm check:size` — all source files ≤ 400 lines.
|
|
44
|
+
|
|
5
45
|
## [1.1.1] — 2026-05-15
|
|
6
46
|
|
|
7
47
|
Single-bin dispatcher — fixes `npx @lyupro/skillforge-mcp install --all` hanging on stdin.
|
|
@@ -138,5 +178,8 @@ All 10 verified through real parse pipeline + `StrategyFactory.create()` correct
|
|
|
138
178
|
- **`pnpm build`** clean.
|
|
139
179
|
- **`pnpm smoke`** end-to-end via subprocess `dist/server.js` — LoggingDecorator trace visible.
|
|
140
180
|
|
|
181
|
+
[1.3.0]: https://github.com/lyupro/skillforge-mcp/releases/tag/v1.3.0
|
|
182
|
+
[1.2.0]: https://github.com/lyupro/skillforge-mcp/releases/tag/v1.2.0
|
|
183
|
+
[1.1.1]: https://github.com/lyupro/skillforge-mcp/releases/tag/v1.1.1
|
|
141
184
|
[1.1.0]: https://github.com/lyupro/skillforge-mcp/releases/tag/v1.1.0
|
|
142
185
|
[1.0.0]: https://github.com/lyupro/skillforge-mcp/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://nodejs.org)
|
|
7
7
|
[](https://modelcontextprotocol.io)
|
|
8
8
|
|
|
9
|
-
**v1.
|
|
9
|
+
**v1.3.0** — 5 MCP tools, one-command install across Claude Code / Codex CLI / Cursor, terminal `tools` + `folders` subcommands (folder aliases, enable/disable toggle, tag filter), global/project install scopes, Claude Code plugin packaging, 561 tests, 10 sample skills, modular architecture (all source files ≤ 400 lines).
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -25,21 +25,37 @@ One skill folder. One config file. Any tool can ask for any skill on demand.
|
|
|
25
25
|
| No cross-tool format | Each tool ships its own skill layout | Universal frontmatter parser auto-detects Claude / Codex / persona / custom dialects |
|
|
26
26
|
| Skill execution = "just inline body in prompt" | No scripts, no caching, no timeouts, no composition | Strategy pattern (prompt / script / hybrid) + decorator chain (logging → timeout → cache) + composite skills with cycle detection |
|
|
27
27
|
|
|
28
|
-
## One-command install
|
|
28
|
+
## One-command install
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
npx @lyupro/skillforge-mcp install --all
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
Auto-detects Claude Code, Codex CLI, and Cursor on your machine and wires SkillForge into each. Supports `--dry-run`, `--uninstall`, and `--force`.
|
|
34
|
+
Auto-detects Claude Code, Codex CLI, and Cursor on your machine and wires SkillForge into each. Supports `--dry-run`, `--uninstall`, and `--force`.
|
|
35
|
+
|
|
36
|
+
By default the installer edits each host's global config. Pass `--scope project` to wire SkillForge into a repo-local config rooted at the current directory instead — `.mcp.json` (Claude Code), `.codex/config.toml` (Codex CLI), `.cursor/mcp.json` (Cursor):
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx @lyupro/skillforge-mcp install --all --scope project
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Full reference: [docs/INSTALL_CLI.md](./docs/INSTALL_CLI.md).
|
|
35
43
|
|
|
36
44
|
## Quick Start
|
|
37
45
|
|
|
38
|
-
### Option 1 — Claude Code
|
|
46
|
+
### Option 1 — Claude Code plugin (recommended)
|
|
47
|
+
|
|
48
|
+
SkillForge ships a Claude Code plugin manifest, so it installs through the native `/plugins` UI with a rich plugin card:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
/plugin marketplace add lyupro/skillforge-mcp
|
|
52
|
+
/plugin install skillforge
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or install it directly:
|
|
39
56
|
|
|
40
57
|
```bash
|
|
41
|
-
|
|
42
|
-
/plugin install skillforge-mcp@lyupro-llm-plugins
|
|
58
|
+
claude plugin install skillforge@lyupro/skillforge-mcp
|
|
43
59
|
```
|
|
44
60
|
|
|
45
61
|
Restart your Claude Code session. The five tools (`skills__list`, `skills__get`, `skills__invoke`, `skills__configure`, `skills__reload`) appear in the tool list.
|
|
@@ -50,7 +66,11 @@ Restart your Claude Code session. The five tools (`skills__list`, `skills__get`,
|
|
|
50
66
|
claude mcp add skillforge -- npx -y @lyupro/skillforge-mcp
|
|
51
67
|
```
|
|
52
68
|
|
|
53
|
-
Works for any MCP host that can spawn a stdio command (Claude Code, Codex CLI, Cursor).
|
|
69
|
+
Works for any MCP host that can spawn a stdio command (Claude Code, Codex CLI, Cursor). Or let the install CLI wire every detected host at once:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx -y @lyupro/skillforge-mcp install --all
|
|
73
|
+
```
|
|
54
74
|
|
|
55
75
|
### Option 3 — local build
|
|
56
76
|
|
|
@@ -75,11 +95,67 @@ After the install step, run these three checks from inside any wired LLM tool se
|
|
|
75
95
|
|
|
76
96
|
If any call fails with `[skillforge] fatal:` on stderr, the most likely cause is a corrupt config file or a missing folder path — the error message points at the offending file. Delete or fix `~/.lyupro/.skillforge/config.json` and retry.
|
|
77
97
|
|
|
98
|
+
## CLI commands
|
|
99
|
+
|
|
100
|
+
The `skillforge` / `skillforge-mcp` binary is a dispatcher — the first positional argument selects a subcommand. Run `skillforge --help` for the full list.
|
|
101
|
+
|
|
102
|
+
| Command | Purpose |
|
|
103
|
+
|---------|---------|
|
|
104
|
+
| `serve` | Run the stdio MCP server. Default when no command is given. |
|
|
105
|
+
| `install` | Wire SkillForge into Claude Code / Codex CLI / Cursor. Flags: `--claude` / `--codex` / `--cursor` / `--all`, `--dry-run`, `--uninstall`, `--force`, `--entry npx\|local`, `--binary-path <path>`, `--scope global\|project`. |
|
|
106
|
+
| `uninstall` | Reverse a previous install. Accepts the same `--scope global\|project` flag. |
|
|
107
|
+
| `tools` | Print the 5 MCP tools the server exposes (name, description, parameters, example). Pass `--json` for machine-readable output. |
|
|
108
|
+
| `folders` | Manage skill folders from the terminal — `list` / `add` / `remove` / `alias` / `enable` / `disable` / `reset`. |
|
|
109
|
+
| `--version`, `-v` | Print the package version. |
|
|
110
|
+
| `--help`, `-h` | Print combined usage. |
|
|
111
|
+
|
|
112
|
+
### Inspect the MCP tools — `skillforge tools`
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
skillforge tools # human-readable reference
|
|
116
|
+
skillforge tools --json # machine-readable: { "tools": [ ... ] }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Prints every MCP tool the server exposes (`skills__list`, `skills__get`, `skills__invoke`, `skills__configure`, `skills__reload`) with its description, parameters, and an example invocation — handy for confirming the surface without starting a session.
|
|
120
|
+
|
|
121
|
+
### Manage skill folders from the terminal — `skillforge folders`
|
|
122
|
+
|
|
123
|
+
Folder management is also available from the shell, not just via the `skills__configure` MCP tool inside an LLM session:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
skillforge folders list [--json] [--tag <name>] # print registered folders
|
|
127
|
+
skillforge folders add <path> [flags] # register a folder
|
|
128
|
+
skillforge folders remove <path|alias> # remove a folder entry
|
|
129
|
+
skillforge folders alias <path|alias> <name> # set or change a folder alias
|
|
130
|
+
skillforge folders enable <path|alias> # re-activate a disabled folder
|
|
131
|
+
skillforge folders disable <path|alias> # deactivate a folder (kept in config)
|
|
132
|
+
skillforge folders reset --yes # reset folders to the default (empty) list
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`add` flags:
|
|
136
|
+
|
|
137
|
+
- `--priority <n>` — folder priority (default `100`; higher wins on name collisions).
|
|
138
|
+
- `--alias <name>` — a short kebab-case handle, unique across folders. Lets `remove` / `enable` / `disable` target the folder without typing the full path.
|
|
139
|
+
- `--tags <a,b,c>` — comma-separated tags. Filter on them via `folders list --tag <name>` or the `skills__list` `folderTag` argument.
|
|
140
|
+
- `--disabled` — register the folder disabled.
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
skillforge folders add ~/.lyupro/skills --priority 50 --alias core --tags work,review
|
|
144
|
+
skillforge folders disable core # address it by alias, not by path
|
|
145
|
+
skillforge folders list --tag work # only folders tagged "work"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`alias` is one unique handle per folder (addressing); `tags` are many shared labels (grouping and filtering) — see [docs/CONFIGURATION.md](./docs/CONFIGURATION.md) for the full contrast.
|
|
149
|
+
|
|
150
|
+
`reset` requires `--yes` to apply — without it, the command prints what would change and makes no edits. All `folders` actions read and write the same persisted config (`~/.lyupro/.skillforge/config.json`) as the `skills__configure` MCP tool.
|
|
151
|
+
|
|
152
|
+
If you register a folder that already lives inside another tool's native skill store (a Claude Code plugin cache or a Gemini CLI extension), `folders add` prints a hint to disable the duplicate source so the same skills don't load twice. SkillForge only prints the hint — it never edits another tool's config.
|
|
153
|
+
|
|
78
154
|
## MCP tool surface
|
|
79
155
|
|
|
80
156
|
| Tool | Purpose |
|
|
81
157
|
|------|---------|
|
|
82
|
-
| `skills__list` | Enumerate available skills (metadata only). Filters: `folder`, `search`, `source`. |
|
|
158
|
+
| `skills__list` | Enumerate available skills (metadata only). Filters: `folder`, `search`, `source`, `folderTag`. |
|
|
83
159
|
| `skills__get` | Fetch full SKILL.md body + metadata for one skill. |
|
|
84
160
|
| `skills__invoke` | Execute a skill via its assigned strategy, wrapped in the decorator chain (Logging → Timeout → Cache). Composite skills (`metadata.skills: [a, b]`) walk nested skills sequentially with DFS cycle detection. |
|
|
85
161
|
| `skills__configure` | Manage configured folders + manual blacklist. Actions: `add_folder`, `remove_folder`, `list_folders`, `set_blacklist`, `get_blacklist`, `reset`. Persists to the config file and reconciles in-process state without restart. |
|
|
@@ -165,15 +241,21 @@ For production use with untrusted skill authors, run SkillForge inside Docker or
|
|
|
165
241
|
|
|
166
242
|
## Updating
|
|
167
243
|
|
|
244
|
+
Pick the block that matches how you installed.
|
|
245
|
+
|
|
168
246
|
```bash
|
|
169
|
-
#
|
|
170
|
-
/plugin update skillforge
|
|
247
|
+
# Installed as a Claude Code plugin
|
|
248
|
+
/plugin update skillforge
|
|
249
|
+
|
|
250
|
+
# Installed via the install CLI (global npm package)
|
|
251
|
+
npm install -g @lyupro/skillforge-mcp@latest
|
|
252
|
+
# host wiring already points at the global bin — restart the host session
|
|
171
253
|
|
|
172
|
-
#
|
|
254
|
+
# Installed as a bare MCP server (npx)
|
|
173
255
|
claude mcp remove skillforge
|
|
174
256
|
claude mcp add skillforge -- npx -y @lyupro/skillforge-mcp@latest
|
|
175
257
|
|
|
176
|
-
# Local-build install
|
|
258
|
+
# Local-build install (git clone)
|
|
177
259
|
cd skillforge-mcp
|
|
178
260
|
git pull
|
|
179
261
|
pnpm install
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/cli/dispatcher.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/cli/dispatcher.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAwCH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAa1D;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAsBD;;;;;;GAMG;AACH,wBAAsB,IAAI,CACxB,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAO,GACnD,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkCxB"}
|
package/dist/cli/dispatcher.js
CHANGED
|
@@ -24,20 +24,36 @@
|
|
|
24
24
|
* without an `--package=` override.
|
|
25
25
|
*/
|
|
26
26
|
import { main as installMain } from './install.js';
|
|
27
|
+
import { main as toolsMain } from './tools.js';
|
|
28
|
+
import { main as foldersMain } from './folders.js';
|
|
27
29
|
const USAGE = `skillforge-mcp — universal Skills MCP server + install CLI.
|
|
28
30
|
|
|
29
31
|
Usage:
|
|
30
32
|
skillforge-mcp <command> [flags]
|
|
31
33
|
|
|
32
34
|
Commands:
|
|
35
|
+
serve Run the stdio MCP server. Default when no command is given.
|
|
36
|
+
Example: skillforge-mcp serve
|
|
33
37
|
install Wire SkillForge into Claude Code / Codex CLI / Cursor.
|
|
38
|
+
Defaults to the host's global config; pass --scope project to
|
|
39
|
+
edit a repo-local config rooted at the current directory.
|
|
34
40
|
Run "skillforge-mcp install --help" for installer flags.
|
|
41
|
+
Example: skillforge-mcp install --all
|
|
42
|
+
Example: skillforge-mcp install --all --scope project
|
|
35
43
|
uninstall Reverse a previous install. Forwards to "install --uninstall".
|
|
36
|
-
|
|
44
|
+
Accepts the same --scope global|project flag.
|
|
45
|
+
Example: skillforge-mcp uninstall --all
|
|
46
|
+
tools List the 5 MCP tools the server exposes (params + examples).
|
|
47
|
+
Pass --json for machine-readable output.
|
|
48
|
+
Example: skillforge-mcp tools --json
|
|
49
|
+
folders Manage skill folders from the terminal (list/add/remove/reset).
|
|
50
|
+
Run "skillforge-mcp folders" for sub-action usage.
|
|
51
|
+
Example: skillforge-mcp folders add ~/.lyupro/skills
|
|
37
52
|
|
|
38
53
|
Options:
|
|
39
54
|
--help, -h Show this message.
|
|
40
|
-
--version
|
|
55
|
+
--version, -v Print the package version.
|
|
56
|
+
Example: skillforge-mcp --version
|
|
41
57
|
|
|
42
58
|
Quick start:
|
|
43
59
|
npx -y @lyupro/skillforge-mcp install --all
|
|
@@ -98,6 +114,12 @@ export async function main(rawArgv, overrides = {}) {
|
|
|
98
114
|
if (first === 'uninstall') {
|
|
99
115
|
return installMain(['--uninstall', ...rawArgv.slice(1)]);
|
|
100
116
|
}
|
|
117
|
+
if (first === 'tools') {
|
|
118
|
+
return toolsMain(rawArgv.slice(1));
|
|
119
|
+
}
|
|
120
|
+
if (first === 'folders') {
|
|
121
|
+
return foldersMain(rawArgv.slice(1));
|
|
122
|
+
}
|
|
101
123
|
if (first === 'serve' || first === undefined) {
|
|
102
124
|
const start = overrides.startServe ?? defaultStartServe;
|
|
103
125
|
await start();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../src/cli/dispatcher.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,KAAK,GAAG
|
|
1
|
+
{"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../src/cli/dispatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table rendering for the `folders list` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Split out of `folders.ts` so the entry module stays under the 400-line
|
|
5
|
+
* file-size gate. Pure formatting — no I/O.
|
|
6
|
+
*/
|
|
7
|
+
import type { FolderEntry } from '../config/config-schema.js';
|
|
8
|
+
/** Render the registered folders as a fixed-width text table. */
|
|
9
|
+
export declare function formatFoldersTable(folders: FolderEntry[]): string;
|
|
10
|
+
//# sourceMappingURL=folders-format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders-format.d.ts","sourceRoot":"","sources":["../../src/cli/folders-format.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAkCjE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table rendering for the `folders list` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Split out of `folders.ts` so the entry module stays under the 400-line
|
|
5
|
+
* file-size gate. Pure formatting — no I/O.
|
|
6
|
+
*/
|
|
7
|
+
/** Render the registered folders as a fixed-width text table. */
|
|
8
|
+
export function formatFoldersTable(folders) {
|
|
9
|
+
if (folders.length === 0) {
|
|
10
|
+
return 'No folders registered.\n';
|
|
11
|
+
}
|
|
12
|
+
const rows = folders.map((f) => ({
|
|
13
|
+
priority: String(f.priority),
|
|
14
|
+
enabled: f.enabled ? 'yes' : 'no',
|
|
15
|
+
alias: f.alias !== undefined && f.alias.length > 0 ? f.alias : '-',
|
|
16
|
+
tags: f.tags.length > 0 ? f.tags.join(',') : '-',
|
|
17
|
+
path: f.path,
|
|
18
|
+
}));
|
|
19
|
+
const headers = {
|
|
20
|
+
priority: 'PRIORITY',
|
|
21
|
+
enabled: 'ENABLED',
|
|
22
|
+
alias: 'ALIAS',
|
|
23
|
+
tags: 'TAGS',
|
|
24
|
+
path: 'PATH',
|
|
25
|
+
};
|
|
26
|
+
const width = {
|
|
27
|
+
priority: Math.max(headers.priority.length, ...rows.map((r) => r.priority.length)),
|
|
28
|
+
enabled: Math.max(headers.enabled.length, ...rows.map((r) => r.enabled.length)),
|
|
29
|
+
alias: Math.max(headers.alias.length, ...rows.map((r) => r.alias.length)),
|
|
30
|
+
tags: Math.max(headers.tags.length, ...rows.map((r) => r.tags.length)),
|
|
31
|
+
};
|
|
32
|
+
const pad = (text, len) => text.padEnd(len);
|
|
33
|
+
const lines = [
|
|
34
|
+
`${pad(headers.priority, width.priority)} ${pad(headers.enabled, width.enabled)} ${pad(headers.alias, width.alias)} ${pad(headers.tags, width.tags)} ${headers.path}`,
|
|
35
|
+
];
|
|
36
|
+
for (const r of rows) {
|
|
37
|
+
lines.push(`${pad(r.priority, width.priority)} ${pad(r.enabled, width.enabled)} ${pad(r.alias, width.alias)} ${pad(r.tags, width.tags)} ${r.path}`);
|
|
38
|
+
}
|
|
39
|
+
return `${lines.join('\n')}\n`;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=folders-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders-format.js","sourceRoot":"","sources":["../../src/cli/folders-format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action handlers for the `folders` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Each handler takes a `ConfigStore`, the post-action args, and stdout/stderr
|
|
5
|
+
* sinks; it returns a process exit code. Split out of `folders.ts` so the
|
|
6
|
+
* entry module stays under the 400-line file-size gate.
|
|
7
|
+
*/
|
|
8
|
+
import type { ConfigStore } from '../config/config-store.js';
|
|
9
|
+
export declare function handleList(store: ConfigStore, rest: string[], stdout: (t: string) => void, stderr: (t: string) => void): Promise<number>;
|
|
10
|
+
export declare function handleAdd(store: ConfigStore, rest: string[], stdout: (t: string) => void, stderr: (t: string) => void, isDirectory: (p: string) => Promise<boolean>): Promise<number>;
|
|
11
|
+
export declare function handleRemove(store: ConfigStore, rest: string[], stdout: (t: string) => void, stderr: (t: string) => void): Promise<number>;
|
|
12
|
+
export declare function handleAlias(store: ConfigStore, rest: string[], stdout: (t: string) => void, stderr: (t: string) => void): Promise<number>;
|
|
13
|
+
export declare function handleEnable(store: ConfigStore, rest: string[], stdout: (t: string) => void, stderr: (t: string) => void): Promise<number>;
|
|
14
|
+
export declare function handleDisable(store: ConfigStore, rest: string[], stdout: (t: string) => void, stderr: (t: string) => void): Promise<number>;
|
|
15
|
+
export declare function handleReset(store: ConfigStore, rest: string[], stdout: (t: string) => void, stderr: (t: string) => void): Promise<number>;
|
|
16
|
+
//# sourceMappingURL=folders-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders-handlers.d.ts","sourceRoot":"","sources":["../../src/cli/folders-handlers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAU7D,wBAAsB,UAAU,CAC9B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,EAC3B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,MAAM,CAAC,CAkCjB;AAED,wBAAsB,SAAS,CAC7B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,EAC3B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,EAC3B,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAC3C,OAAO,CAAC,MAAM,CAAC,CAqDjB;AAED,wBAAsB,YAAY,CAChC,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,EAC3B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,EAC3B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED,wBAAsB,YAAY,CAChC,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,EAC3B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,EAC3B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,EAC3B,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GAC1B,OAAO,CAAC,MAAM,CAAC,CAqBjB"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action handlers for the `folders` subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Each handler takes a `ConfigStore`, the post-action args, and stdout/stderr
|
|
5
|
+
* sinks; it returns a process exit code. Split out of `folders.ts` so the
|
|
6
|
+
* entry module stays under the 400-line file-size gate.
|
|
7
|
+
*/
|
|
8
|
+
import { resolve } from 'node:path';
|
|
9
|
+
import { defaultConfig } from '../config/config-schema.js';
|
|
10
|
+
import { detectSkillSourceConflict, formatConflictHint, } from '../detect/skill-source-conflict.js';
|
|
11
|
+
import { formatFoldersTable } from './folders-format.js';
|
|
12
|
+
import { findFolderEntry, isValidAlias, parseAddFlags } from './folders-shared.js';
|
|
13
|
+
export async function handleList(store, rest, stdout, stderr) {
|
|
14
|
+
let asJson = false;
|
|
15
|
+
let tagFilter;
|
|
16
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
17
|
+
const arg = rest[i];
|
|
18
|
+
if (arg === '--json') {
|
|
19
|
+
asJson = true;
|
|
20
|
+
}
|
|
21
|
+
else if (arg === '--tag') {
|
|
22
|
+
const value = rest[i + 1];
|
|
23
|
+
if (value === undefined) {
|
|
24
|
+
stderr(`skillforge folders list: --tag requires a value\n`);
|
|
25
|
+
return 2;
|
|
26
|
+
}
|
|
27
|
+
tagFilter = value;
|
|
28
|
+
i += 1;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
stderr(`skillforge folders list: unknown flag: ${arg}\n`);
|
|
32
|
+
return 2;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const config = await store.load();
|
|
36
|
+
const folders = tagFilter !== undefined
|
|
37
|
+
? config.folders.filter((f) => f.tags.includes(tagFilter))
|
|
38
|
+
: config.folders;
|
|
39
|
+
if (asJson) {
|
|
40
|
+
stdout(`${JSON.stringify({ folders }, null, 2)}\n`);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
stdout(formatFoldersTable(folders));
|
|
44
|
+
}
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
export async function handleAdd(store, rest, stdout, stderr, isDirectory) {
|
|
48
|
+
const rawPath = rest[0];
|
|
49
|
+
if (rawPath === undefined || rawPath.startsWith('--')) {
|
|
50
|
+
stderr(`skillforge folders add: missing <path>\n`);
|
|
51
|
+
return 2;
|
|
52
|
+
}
|
|
53
|
+
const flags = parseAddFlags(rest.slice(1));
|
|
54
|
+
if (flags === null) {
|
|
55
|
+
stderr(`skillforge folders add: invalid or malformed flag\n`);
|
|
56
|
+
return 2;
|
|
57
|
+
}
|
|
58
|
+
if (flags.alias !== undefined && !isValidAlias(flags.alias)) {
|
|
59
|
+
stderr(`skillforge folders add: invalid --alias "${flags.alias}" — use kebab-case (e.g. my-folder)\n`);
|
|
60
|
+
return 2;
|
|
61
|
+
}
|
|
62
|
+
const absPath = resolve(rawPath);
|
|
63
|
+
if (!(await isDirectory(absPath))) {
|
|
64
|
+
stderr(`skillforge folders add: path does not exist or is not a directory: ${absPath}\n`);
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
const config = await store.load();
|
|
68
|
+
const alreadyPresent = config.folders.some((f) => resolve(f.path) === absPath);
|
|
69
|
+
if (alreadyPresent) {
|
|
70
|
+
stdout(`Folder already registered: ${absPath}\n`);
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
if (flags.alias !== undefined) {
|
|
74
|
+
const aliasTaken = config.folders.some((f) => f.alias === flags.alias);
|
|
75
|
+
if (aliasTaken) {
|
|
76
|
+
stderr(`skillforge folders add: alias already in use: ${flags.alias}\n`);
|
|
77
|
+
return 2;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const entry = {
|
|
81
|
+
path: absPath,
|
|
82
|
+
priority: flags.priority ?? 100,
|
|
83
|
+
enabled: !flags.disabled,
|
|
84
|
+
tags: flags.tags ?? [],
|
|
85
|
+
...(flags.alias !== undefined ? { alias: flags.alias } : {}),
|
|
86
|
+
};
|
|
87
|
+
config.folders.push(entry);
|
|
88
|
+
await store.save(config);
|
|
89
|
+
stdout(`Registered folder: ${absPath}\n`);
|
|
90
|
+
// Informational only: a conflict does not block the add or change the exit code.
|
|
91
|
+
const conflict = detectSkillSourceConflict(absPath);
|
|
92
|
+
if (conflict !== null) {
|
|
93
|
+
stdout(`${formatConflictHint(conflict)}\n`);
|
|
94
|
+
}
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
export async function handleRemove(store, rest, stdout, stderr) {
|
|
98
|
+
const token = rest[0];
|
|
99
|
+
if (token === undefined || token.startsWith('--')) {
|
|
100
|
+
stderr(`skillforge folders remove: missing <path>\n`);
|
|
101
|
+
return 2;
|
|
102
|
+
}
|
|
103
|
+
const config = await store.load();
|
|
104
|
+
const entry = findFolderEntry(config.folders, token);
|
|
105
|
+
if (entry === null) {
|
|
106
|
+
stderr(`skillforge folders remove: no registered folder matches: ${token}\n`);
|
|
107
|
+
return 1;
|
|
108
|
+
}
|
|
109
|
+
config.folders = config.folders.filter((f) => f !== entry);
|
|
110
|
+
await store.save(config);
|
|
111
|
+
stdout(`Removed folder: ${entry.path}\n`);
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
export async function handleAlias(store, rest, stdout, stderr) {
|
|
115
|
+
const token = rest[0];
|
|
116
|
+
const name = rest[1];
|
|
117
|
+
if (token === undefined || token.startsWith('--') || name === undefined) {
|
|
118
|
+
stderr(`skillforge folders alias: usage: folders alias <path> <name>\n`);
|
|
119
|
+
return 2;
|
|
120
|
+
}
|
|
121
|
+
if (!isValidAlias(name)) {
|
|
122
|
+
stderr(`skillforge folders alias: invalid alias "${name}" — use kebab-case (e.g. my-folder)\n`);
|
|
123
|
+
return 2;
|
|
124
|
+
}
|
|
125
|
+
const config = await store.load();
|
|
126
|
+
const entry = findFolderEntry(config.folders, token);
|
|
127
|
+
if (entry === null) {
|
|
128
|
+
stderr(`skillforge folders alias: no registered folder matches: ${token}\n`);
|
|
129
|
+
return 1;
|
|
130
|
+
}
|
|
131
|
+
const aliasTaken = config.folders.some((f) => f !== entry && f.alias === name);
|
|
132
|
+
if (aliasTaken) {
|
|
133
|
+
stderr(`skillforge folders alias: alias already in use: ${name}\n`);
|
|
134
|
+
return 2;
|
|
135
|
+
}
|
|
136
|
+
entry.alias = name;
|
|
137
|
+
await store.save(config);
|
|
138
|
+
stdout(`Set alias "${name}" for folder: ${entry.path}\n`);
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
export async function handleEnable(store, rest, stdout, stderr) {
|
|
142
|
+
const token = rest[0];
|
|
143
|
+
if (token === undefined || token.startsWith('--')) {
|
|
144
|
+
stderr(`skillforge folders enable: missing <path|alias>\n`);
|
|
145
|
+
return 2;
|
|
146
|
+
}
|
|
147
|
+
const config = await store.load();
|
|
148
|
+
const entry = findFolderEntry(config.folders, token);
|
|
149
|
+
if (entry === null) {
|
|
150
|
+
stderr(`skillforge folders enable: no registered folder matches: ${token}\n`);
|
|
151
|
+
return 1;
|
|
152
|
+
}
|
|
153
|
+
entry.enabled = true;
|
|
154
|
+
await store.save(config);
|
|
155
|
+
stdout(`Enabled folder: ${entry.path}\n`);
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
export async function handleDisable(store, rest, stdout, stderr) {
|
|
159
|
+
const token = rest[0];
|
|
160
|
+
if (token === undefined || token.startsWith('--')) {
|
|
161
|
+
stderr(`skillforge folders disable: missing <path|alias>\n`);
|
|
162
|
+
return 2;
|
|
163
|
+
}
|
|
164
|
+
const config = await store.load();
|
|
165
|
+
const entry = findFolderEntry(config.folders, token);
|
|
166
|
+
if (entry === null) {
|
|
167
|
+
stderr(`skillforge folders disable: no registered folder matches: ${token}\n`);
|
|
168
|
+
return 1;
|
|
169
|
+
}
|
|
170
|
+
entry.enabled = false;
|
|
171
|
+
await store.save(config);
|
|
172
|
+
stdout(`Disabled folder: ${entry.path}\n`);
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
175
|
+
export async function handleReset(store, rest, stdout, stderr) {
|
|
176
|
+
const confirmed = rest.includes('--yes');
|
|
177
|
+
for (const arg of rest) {
|
|
178
|
+
if (arg !== '--yes') {
|
|
179
|
+
stderr(`skillforge folders reset: unknown flag: ${arg}\n`);
|
|
180
|
+
return 2;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!confirmed) {
|
|
184
|
+
const config = await store.load();
|
|
185
|
+
stdout(`Would reset ${config.folders.length} folder(s) to the default (empty) list.\n` +
|
|
186
|
+
`Re-run with --yes to apply. No changes were made.\n`);
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
const config = await store.load();
|
|
190
|
+
config.folders = defaultConfig().folders;
|
|
191
|
+
await store.save(config);
|
|
192
|
+
stdout(`Reset folders to the default (empty) list.\n`);
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=folders-handlers.js.map
|