@lyupro/skillforge-mcp 1.2.0 → 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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +22 -0
- package/README.md +26 -12
- 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 +7 -2
- package/dist/cli/folders.d.ts.map +1 -1
- package/dist/cli/folders.js +28 -177
- package/dist/cli/folders.js.map +1 -1
- package/dist/cli/tools.d.ts.map +1 -1
- package/dist/cli/tools.js +6 -0
- package/dist/cli/tools.js.map +1 -1
- 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/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 +2 -0
- package/dist/tools/configure.d.ts.map +1 -1
- package/dist/tools/configure.js +14 -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 +3 -3
- package/package.json +1 -1
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"package": "@lyupro/skillforge-mcp"
|
|
15
15
|
},
|
|
16
16
|
"description": "Universal Skills MCP server — load Markdown skills from arbitrary folders, lazy-by-design, cross-tool.",
|
|
17
|
-
"version": "1.
|
|
17
|
+
"version": "1.3.0",
|
|
18
18
|
"author": {
|
|
19
19
|
"name": "Lyu Pro",
|
|
20
20
|
"email": "lyupro.dev@gmail.com"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "skillforge",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.3.0",
|
|
5
5
|
"description": "Universal Skills MCP server — load Markdown skills from arbitrary folders, lazy-by-design, cross-tool.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Lyu Pro",
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
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
|
+
|
|
5
26
|
## [1.2.0] — 2026-05-16
|
|
6
27
|
|
|
7
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.
|
|
@@ -157,6 +178,7 @@ All 10 verified through real parse pipeline + `StrategyFactory.create()` correct
|
|
|
157
178
|
- **`pnpm build`** clean.
|
|
158
179
|
- **`pnpm smoke`** end-to-end via subprocess `dist/server.js` — LoggingDecorator trace visible.
|
|
159
180
|
|
|
181
|
+
[1.3.0]: https://github.com/lyupro/skillforge-mcp/releases/tag/v1.3.0
|
|
160
182
|
[1.2.0]: https://github.com/lyupro/skillforge-mcp/releases/tag/v1.2.0
|
|
161
183
|
[1.1.1]: https://github.com/lyupro/skillforge-mcp/releases/tag/v1.1.1
|
|
162
184
|
[1.1.0]: https://github.com/lyupro/skillforge-mcp/releases/tag/v1.1.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
|
|
|
@@ -105,7 +105,7 @@ The `skillforge` / `skillforge-mcp` binary is a dispatcher — the first positio
|
|
|
105
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
106
|
| `uninstall` | Reverse a previous install. Accepts the same `--scope global\|project` flag. |
|
|
107
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` / `reset`. |
|
|
108
|
+
| `folders` | Manage skill folders from the terminal — `list` / `add` / `remove` / `alias` / `enable` / `disable` / `reset`. |
|
|
109
109
|
| `--version`, `-v` | Print the package version. |
|
|
110
110
|
| `--help`, `-h` | Print combined usage. |
|
|
111
111
|
|
|
@@ -123,22 +123,30 @@ Prints every MCP tool the server exposes (`skills__list`, `skills__get`, `skills
|
|
|
123
123
|
Folder management is also available from the shell, not just via the `skills__configure` MCP tool inside an LLM session:
|
|
124
124
|
|
|
125
125
|
```bash
|
|
126
|
-
skillforge folders list [--json]
|
|
127
|
-
skillforge folders add <path> [flags]
|
|
128
|
-
skillforge folders remove <path>
|
|
129
|
-
skillforge folders
|
|
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
|
|
130
133
|
```
|
|
131
134
|
|
|
132
135
|
`add` flags:
|
|
133
136
|
|
|
134
137
|
- `--priority <n>` — folder priority (default `100`; higher wins on name collisions).
|
|
135
|
-
- `--
|
|
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.
|
|
136
140
|
- `--disabled` — register the folder disabled.
|
|
137
141
|
|
|
138
142
|
```bash
|
|
139
|
-
skillforge folders add ~/.lyupro/skills --priority 50 --tags work,review
|
|
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"
|
|
140
146
|
```
|
|
141
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
|
+
|
|
142
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.
|
|
143
151
|
|
|
144
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.
|
|
@@ -147,7 +155,7 @@ If you register a folder that already lives inside another tool's native skill s
|
|
|
147
155
|
|
|
148
156
|
| Tool | Purpose |
|
|
149
157
|
|------|---------|
|
|
150
|
-
| `skills__list` | Enumerate available skills (metadata only). Filters: `folder`, `search`, `source`. |
|
|
158
|
+
| `skills__list` | Enumerate available skills (metadata only). Filters: `folder`, `search`, `source`, `folderTag`. |
|
|
151
159
|
| `skills__get` | Fetch full SKILL.md body + metadata for one skill. |
|
|
152
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. |
|
|
153
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. |
|
|
@@ -233,15 +241,21 @@ For production use with untrusted skill authors, run SkillForge inside Docker or
|
|
|
233
241
|
|
|
234
242
|
## Updating
|
|
235
243
|
|
|
244
|
+
Pick the block that matches how you installed.
|
|
245
|
+
|
|
236
246
|
```bash
|
|
237
|
-
#
|
|
247
|
+
# Installed as a Claude Code plugin
|
|
238
248
|
/plugin update skillforge
|
|
239
249
|
|
|
240
|
-
# npm
|
|
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
|
|
253
|
+
|
|
254
|
+
# Installed as a bare MCP server (npx)
|
|
241
255
|
claude mcp remove skillforge
|
|
242
256
|
claude mcp add skillforge -- npx -y @lyupro/skillforge-mcp@latest
|
|
243
257
|
|
|
244
|
-
# Local-build install
|
|
258
|
+
# Local-build install (git clone)
|
|
245
259
|
cd skillforge-mcp
|
|
246
260
|
git pull
|
|
247
261
|
pnpm install
|
|
@@ -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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders-handlers.js","sourceRoot":"","sources":["../../src/cli/folders-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for the `folders` subcommand modules.
|
|
3
|
+
*
|
|
4
|
+
* Holds pure parsing/lookup logic split out of `folders.ts` so the entry
|
|
5
|
+
* module stays small and the handler modules can reuse it.
|
|
6
|
+
*/
|
|
7
|
+
import type { FolderEntry } from '../config/config-schema.js';
|
|
8
|
+
/** Kebab-case shape required for a folder alias (lowercase words joined by `-`). */
|
|
9
|
+
export declare const ALIAS_PATTERN: RegExp;
|
|
10
|
+
/** Whether `name` is a valid kebab-case alias token. */
|
|
11
|
+
export declare function isValidAlias(name: string): boolean;
|
|
12
|
+
/** Check whether a path exists and is a directory. Overridable for tests. */
|
|
13
|
+
export declare function defaultIsDirectory(p: string): Promise<boolean>;
|
|
14
|
+
/**
|
|
15
|
+
* Locate a registered folder by `token`, matching an alias first and falling
|
|
16
|
+
* back to a resolved-path comparison. Shared so `remove`/`alias` address the
|
|
17
|
+
* same entry the same way.
|
|
18
|
+
*/
|
|
19
|
+
export declare function findFolderEntry(folders: FolderEntry[], token: string): FolderEntry | null;
|
|
20
|
+
/** Parsed result of the flags accepted by `add`. */
|
|
21
|
+
export interface ParsedAddFlags {
|
|
22
|
+
priority?: number;
|
|
23
|
+
tags?: string[];
|
|
24
|
+
disabled: boolean;
|
|
25
|
+
alias?: string;
|
|
26
|
+
}
|
|
27
|
+
/** Parse the flags accepted by `add`. Returns null on a malformed flag. */
|
|
28
|
+
export declare function parseAddFlags(args: string[]): ParsedAddFlags | null;
|
|
29
|
+
//# sourceMappingURL=folders-shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders-shared.d.ts","sourceRoot":"","sources":["../../src/cli/folders-shared.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D,oFAAoF;AACpF,eAAO,MAAM,aAAa,QAA6B,CAAC;AAExD,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,6EAA6E;AAC7E,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOpE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAMzF;AAED,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,cAAc,GAAG,IAAI,CAmCnE"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for the `folders` subcommand modules.
|
|
3
|
+
*
|
|
4
|
+
* Holds pure parsing/lookup logic split out of `folders.ts` so the entry
|
|
5
|
+
* module stays small and the handler modules can reuse it.
|
|
6
|
+
*/
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
import { stat } from 'node:fs/promises';
|
|
9
|
+
/** Kebab-case shape required for a folder alias (lowercase words joined by `-`). */
|
|
10
|
+
export const ALIAS_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
11
|
+
/** Whether `name` is a valid kebab-case alias token. */
|
|
12
|
+
export function isValidAlias(name) {
|
|
13
|
+
return ALIAS_PATTERN.test(name);
|
|
14
|
+
}
|
|
15
|
+
/** Check whether a path exists and is a directory. Overridable for tests. */
|
|
16
|
+
export async function defaultIsDirectory(p) {
|
|
17
|
+
try {
|
|
18
|
+
const info = await stat(p);
|
|
19
|
+
return info.isDirectory();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Locate a registered folder by `token`, matching an alias first and falling
|
|
27
|
+
* back to a resolved-path comparison. Shared so `remove`/`alias` address the
|
|
28
|
+
* same entry the same way.
|
|
29
|
+
*/
|
|
30
|
+
export function findFolderEntry(folders, token) {
|
|
31
|
+
const byAlias = folders.find((f) => f.alias !== undefined && f.alias === token);
|
|
32
|
+
if (byAlias !== undefined)
|
|
33
|
+
return byAlias;
|
|
34
|
+
const absToken = resolve(token);
|
|
35
|
+
const byPath = folders.find((f) => resolve(f.path) === absToken);
|
|
36
|
+
return byPath ?? null;
|
|
37
|
+
}
|
|
38
|
+
/** Parse the flags accepted by `add`. Returns null on a malformed flag. */
|
|
39
|
+
export function parseAddFlags(args) {
|
|
40
|
+
let priority;
|
|
41
|
+
let tags;
|
|
42
|
+
let disabled = false;
|
|
43
|
+
let alias;
|
|
44
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
45
|
+
const arg = args[i];
|
|
46
|
+
if (arg === '--disabled') {
|
|
47
|
+
disabled = true;
|
|
48
|
+
}
|
|
49
|
+
else if (arg === '--priority') {
|
|
50
|
+
const value = args[i + 1];
|
|
51
|
+
if (value === undefined)
|
|
52
|
+
return null;
|
|
53
|
+
const n = Number(value);
|
|
54
|
+
if (!Number.isInteger(n))
|
|
55
|
+
return null;
|
|
56
|
+
priority = n;
|
|
57
|
+
i += 1;
|
|
58
|
+
}
|
|
59
|
+
else if (arg === '--tags') {
|
|
60
|
+
const value = args[i + 1];
|
|
61
|
+
if (value === undefined)
|
|
62
|
+
return null;
|
|
63
|
+
tags = value
|
|
64
|
+
.split(',')
|
|
65
|
+
.map((t) => t.trim())
|
|
66
|
+
.filter((t) => t.length > 0);
|
|
67
|
+
i += 1;
|
|
68
|
+
}
|
|
69
|
+
else if (arg === '--alias') {
|
|
70
|
+
const value = args[i + 1];
|
|
71
|
+
if (value === undefined)
|
|
72
|
+
return null;
|
|
73
|
+
alias = value;
|
|
74
|
+
i += 1;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { priority, tags, disabled, alias };
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=folders-shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folders-shared.js","sourceRoot":"","sources":["../../src/cli/folders-shared.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/folders.d.ts
CHANGED
|
@@ -10,13 +10,18 @@
|
|
|
10
10
|
* Usage:
|
|
11
11
|
* skillforge folders list [--json] List registered folders
|
|
12
12
|
* skillforge folders add <path> [flags] Register a folder
|
|
13
|
-
* skillforge folders remove <path>
|
|
13
|
+
* skillforge folders remove <path|alias> Remove a folder entry
|
|
14
|
+
* skillforge folders alias <path|alias> <name> Set/change a folder alias
|
|
14
15
|
* skillforge folders reset --yes Reset folders to default
|
|
15
16
|
*
|
|
16
17
|
* add flags:
|
|
17
|
-
* --priority <n> Folder priority (default 100
|
|
18
|
+
* --priority <n> Folder priority (default 100; higher wins on name collisions).
|
|
18
19
|
* --tags <a,b,c> Comma-separated tags.
|
|
19
20
|
* --disabled Register the folder disabled.
|
|
21
|
+
* --alias <name> Short kebab-case alias to address the folder later.
|
|
22
|
+
*
|
|
23
|
+
* This module keeps only the entry point + action dispatch; the handlers,
|
|
24
|
+
* table formatting, and shared parsing helpers live in sibling modules.
|
|
20
25
|
*/
|
|
21
26
|
export interface FoldersDeps {
|
|
22
27
|
stdout?: (text: string) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"folders.d.ts","sourceRoot":"","sources":["../../src/cli/folders.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"folders.d.ts","sourceRoot":"","sources":["../../src/cli/folders.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAcH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/C;AA8BD;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsCrF"}
|