@luanpdd/kit-mcp 0.4.0 → 0.5.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/CHANGELOG.md +27 -1
- package/README.md +12 -10
- package/package.json +1 -1
- package/src/core/registry.js +9 -5
- package/src/core/sync.js +57 -2
- package/src/mcp-server/index.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,30 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) · Versioning:
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.5.0] - 2026-05-03
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Mirror-tree sync for `framework` and `hooks`.** `kit/framework/` (124 files: workflows, templates, references, libs) and `kit/hooks/` (5 files) are now projected into `.claude/framework/` and `.claude/hooks/` on every `sync install claude-code`. Without this, the bundled slash-commands like `/novo-marco` were broken-by-design — they referenced `@./.claude/framework/workflows/new-milestone.md` and similar paths that never existed in the destination project. Now they resolve correctly end-to-end.
|
|
13
|
+
- New `mode: 'mirror-tree'` capability spec in `src/core/registry.js`. Each mirror-tree entry has a `source` (relative path inside `kit/`) and a `path` (destination path in the target project).
|
|
14
|
+
- A `.kit-mcp-managed` marker file is written at the root of each managed tree so `kit sync remove` can recursively clean up the directory **only** when the marker is present. Trees you authored yourself (without the marker) are never touched.
|
|
15
|
+
- CI smoke test asserts `.claude/framework/workflows/new-milestone.md`, `.claude/framework/templates/project.md`, and `.claude/hooks/workflow-guard.js` are projected, and that `sync remove` cleans them up.
|
|
16
|
+
- New CI safety test: `sync remove` against a `.claude/framework/` directory with no marker preserves user content.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- `statusOf` now reports `framework` and `hooks` capability paths.
|
|
20
|
+
- README capability matrix gained two columns (`framework`, `hooks`) and a paragraph explaining the mirror-tree semantics.
|
|
21
|
+
|
|
22
|
+
### Migration
|
|
23
|
+
No action needed — `npx -y @luanpdd/kit-mcp@latest sync install claude-code --project-root .` projects the new directories automatically. If you had a manually-created `.claude/framework/` or `.claude/hooks/`, kit-mcp will overwrite individual files but won't delete user files; `sync remove` continues to leave them alone.
|
|
24
|
+
|
|
25
|
+
## [0.4.1] - 2026-05-03
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- `src/mcp-server/index.js` was importing `DEFAULT_KIT_ROOT` from `core/kit.js`, but that export was renamed to `BUNDLED_KIT_ROOT` / `resolveKitRoot` during the v0.2.0 refactor. The unused import wasn't caught by CI (which only smoke-tests CLI commands, not MCP server boot) and made the server crash on `npx -y @luanpdd/kit-mcp` for any sync/install command. Removed the dead import — server now boots cleanly.
|
|
29
|
+
|
|
30
|
+
### Tests (suggestion)
|
|
31
|
+
- CI should boot `node bin/mcp.js` and validate exit. Tracked in roadmap.
|
|
32
|
+
|
|
9
33
|
## [0.4.0] - 2026-05-03
|
|
10
34
|
|
|
11
35
|
### Changed
|
|
@@ -150,7 +174,9 @@ npx -y @luanpdd/kit-mcp sync install claude-code --project-root .
|
|
|
150
174
|
- CLI mirror of all MCP tools.
|
|
151
175
|
- `install` command that registers kit-mcp into an IDE's MCP config (JSON for Claude/Cursor/Gemini/Windsurf, TOML for Codex).
|
|
152
176
|
|
|
153
|
-
[Unreleased]: https://github.com/luanpdd/kit-mcp/compare/v0.
|
|
177
|
+
[Unreleased]: https://github.com/luanpdd/kit-mcp/compare/v0.5.0...HEAD
|
|
178
|
+
[0.5.0]: https://github.com/luanpdd/kit-mcp/compare/v0.4.1...v0.5.0
|
|
179
|
+
[0.4.1]: https://github.com/luanpdd/kit-mcp/compare/v0.4.0...v0.4.1
|
|
154
180
|
[0.4.0]: https://github.com/luanpdd/kit-mcp/compare/v0.3.0...v0.4.0
|
|
155
181
|
[0.3.0]: https://github.com/luanpdd/kit-mcp/compare/v0.2.1...v0.3.0
|
|
156
182
|
[0.2.0]: https://github.com/luanpdd/kit-mcp/compare/v0.1.6...v0.2.0
|
package/README.md
CHANGED
|
@@ -159,19 +159,21 @@ kit sync watch --all # watch + auto-detect every IDE al
|
|
|
159
159
|
|
|
160
160
|
**Per-IDE projection** — what each target receives:
|
|
161
161
|
|
|
162
|
-
| IDE | rules → | agents → | commands → | skills → |
|
|
163
|
-
|
|
164
|
-
| Claude Code | `CLAUDE.md` | `.claude/agents/*.md` | `.claude/commands/*.md` | `.claude/skills/*/` |
|
|
165
|
-
| Cursor | `.cursor/rules/*.mdc` | `.cursor/agents/*.md` | — | — |
|
|
166
|
-
| Codex | `AGENTS.md` | — | — | `.codex/skills/*/` |
|
|
167
|
-
| Gemini CLI | `GEMINI.md` | — | — | `.gemini/skills/*/` |
|
|
168
|
-
| Copilot | `.github/copilot-instructions.md` | `.github/agents/*.agent` | — | `.github/skills/*/` |
|
|
169
|
-
| Windsurf | `.windsurf/rules/*.md` | `.windsurf/agents/*.md` | — | `.windsurf/skills/*/` |
|
|
170
|
-
| Antigravity | `.agents/rules/*.md` | `.agents/agents/*.md` | — | `.agents/workflows/*/` |
|
|
171
|
-
| Trae | `.trae/rules/*.md` | `.trae/agents/*.md` | — | — |
|
|
162
|
+
| IDE | rules → | agents → | commands → | skills → | framework → | hooks → |
|
|
163
|
+
|---|---|---|---|---|---|---|
|
|
164
|
+
| Claude Code | `CLAUDE.md` | `.claude/agents/*.md` | `.claude/commands/*.md` | `.claude/skills/*/` | `.claude/framework/**` | `.claude/hooks/**` |
|
|
165
|
+
| Cursor | `.cursor/rules/*.mdc` | `.cursor/agents/*.md` | — | — | — | — |
|
|
166
|
+
| Codex | `AGENTS.md` | — | — | `.codex/skills/*/` | — | — |
|
|
167
|
+
| Gemini CLI | `GEMINI.md` | — | — | `.gemini/skills/*/` | — | — |
|
|
168
|
+
| Copilot | `.github/copilot-instructions.md` | `.github/agents/*.agent` | — | `.github/skills/*/` | — | — |
|
|
169
|
+
| Windsurf | `.windsurf/rules/*.md` | `.windsurf/agents/*.md` | — | `.windsurf/skills/*/` | — | — |
|
|
170
|
+
| Antigravity | `.agents/rules/*.md` | `.agents/agents/*.md` | — | `.agents/workflows/*/` | — | — |
|
|
171
|
+
| Trae | `.trae/rules/*.md` | `.trae/agents/*.md` | — | — | — | — |
|
|
172
172
|
|
|
173
173
|
A capability marked `—` is not supported by that IDE. Adding a new IDE = one entry in [`src/core/registry.js`](src/core/registry.js).
|
|
174
174
|
|
|
175
|
+
**About `framework` and `hooks`:** these are *mirror-tree* capabilities — the entire `kit/framework/` and `kit/hooks/` subtrees are copied verbatim into `.claude/framework/` and `.claude/hooks/`. They're needed by the bundled workflow because slash-commands like `/novo-marco` reference framework files via paths like `@./.claude/framework/workflows/new-milestone.md`. A `.kit-mcp-managed` marker is written at the root of each managed tree so `kit sync remove` can clean up safely without touching directories you authored yourself.
|
|
176
|
+
|
|
175
177
|
### `kit install ...` — register kit-mcp into an IDE's MCP config
|
|
176
178
|
|
|
177
179
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luanpdd/kit-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Generic infrastructure to ship YOUR personal kit of agents/commands/skills as an MCP server, with cross-IDE sync (Claude Code, Cursor, Codex, Gemini, Windsurf, Antigravity, Copilot, Trae).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/core/registry.js
CHANGED
|
@@ -20,6 +20,8 @@ export const TARGETS = {
|
|
|
20
20
|
agents: { path: '.claude/agents/', mode: 'multi', extension: '.md' },
|
|
21
21
|
commands: { path: '.claude/commands/', mode: 'multi', extension: '.md' },
|
|
22
22
|
skills: { path: '.claude/skills/', mode: 'multi-dir' },
|
|
23
|
+
framework:{ path: '.claude/framework/', mode: 'mirror-tree', source: 'framework' },
|
|
24
|
+
hooks: { path: '.claude/hooks/', mode: 'mirror-tree', source: 'hooks' },
|
|
23
25
|
mcpConfig:{ path: '.mcp.json', strategy: 'merge-mcpServers-json',
|
|
24
26
|
userPath: '~/.claude.json', userKey: 'mcpServers' },
|
|
25
27
|
},
|
|
@@ -92,11 +94,13 @@ export function listTargets() {
|
|
|
92
94
|
id,
|
|
93
95
|
label: t.label,
|
|
94
96
|
capabilities: {
|
|
95
|
-
rules:
|
|
96
|
-
agents:
|
|
97
|
-
commands:
|
|
98
|
-
skills:
|
|
99
|
-
|
|
97
|
+
rules: !!t.rules,
|
|
98
|
+
agents: !!t.agents,
|
|
99
|
+
commands: !!t.commands,
|
|
100
|
+
skills: !!t.skills,
|
|
101
|
+
framework: !!t.framework,
|
|
102
|
+
hooks: !!t.hooks,
|
|
103
|
+
mcpConfig: !!t.mcpConfig,
|
|
100
104
|
},
|
|
101
105
|
}));
|
|
102
106
|
}
|
package/src/core/sync.js
CHANGED
|
@@ -15,6 +15,8 @@ import { getTarget } from './registry.js';
|
|
|
15
15
|
import { listKit, resolveKitRoot } from './kit.js';
|
|
16
16
|
|
|
17
17
|
const STUB_MARKER = '<!-- kit-mcp:reference -->';
|
|
18
|
+
const MANAGED_MARKER_FILE = '.kit-mcp-managed';
|
|
19
|
+
const MANAGED_MARKER_BODY = '# Managed by @luanpdd/kit-mcp — this directory is overwritten on every `kit sync install`.\n# Do not edit files here directly; edit the canonical source under kit/ and re-run sync.\n# Removing this file disables `kit sync remove` cleanup of this tree.\n';
|
|
18
20
|
|
|
19
21
|
export async function syncTo(targetId, opts = {}) {
|
|
20
22
|
const target = getTarget(targetId);
|
|
@@ -62,21 +64,62 @@ export async function syncTo(targetId, opts = {}) {
|
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
// Mirror-tree capabilities (framework, hooks) — copy a whole subtree of kit/<source>
|
|
68
|
+
// into target.<cap>.path, preserving relative structure. Dropped a marker file at the
|
|
69
|
+
// root so `kit sync remove` can clean up the tree safely.
|
|
70
|
+
for (const cap of ['framework', 'hooks']) {
|
|
71
|
+
const spec = target[cap];
|
|
72
|
+
if (!spec || spec.mode !== 'mirror-tree') continue;
|
|
73
|
+
const srcRoot = path.join(kitRoot, spec.source);
|
|
74
|
+
const dstRoot = path.join(projectRoot, spec.path);
|
|
75
|
+
const files = await walkTree(srcRoot);
|
|
76
|
+
if (files.length === 0) continue;
|
|
77
|
+
ops.push({ path: path.join(dstRoot, MANAGED_MARKER_FILE), content: MANAGED_MARKER_BODY, kind: cap });
|
|
78
|
+
for (const f of files) {
|
|
79
|
+
const dst = path.join(dstRoot, f.rel);
|
|
80
|
+
ops.push({ path: dst, srcAbs: f.abs, kind: cap, treeCopy: true });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
65
84
|
if (!dryRun) {
|
|
66
85
|
for (const op of ops) {
|
|
67
86
|
await fs.mkdir(path.dirname(op.path), { recursive: true });
|
|
68
|
-
|
|
87
|
+
if (op.treeCopy) {
|
|
88
|
+
await fs.copyFile(op.srcAbs, op.path);
|
|
89
|
+
} else {
|
|
90
|
+
await fs.writeFile(op.path, op.content, 'utf8');
|
|
91
|
+
}
|
|
69
92
|
}
|
|
70
93
|
}
|
|
71
94
|
|
|
72
95
|
return { target: targetId, mode, projectRoot, kitRoot, written: ops.map(o => o.path), dryRun };
|
|
73
96
|
}
|
|
74
97
|
|
|
98
|
+
async function walkTree(dir) {
|
|
99
|
+
const out = [];
|
|
100
|
+
async function visit(current, relPrefix) {
|
|
101
|
+
let entries;
|
|
102
|
+
try { entries = await fs.readdir(current, { withFileTypes: true }); }
|
|
103
|
+
catch { return; }
|
|
104
|
+
for (const e of entries) {
|
|
105
|
+
const abs = path.join(current, e.name);
|
|
106
|
+
const rel = relPrefix ? `${relPrefix}/${e.name}` : e.name;
|
|
107
|
+
if (e.isDirectory()) {
|
|
108
|
+
await visit(abs, rel);
|
|
109
|
+
} else if (e.isFile()) {
|
|
110
|
+
out.push({ abs, rel });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
await visit(dir, '');
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
|
|
75
118
|
export async function statusOf(targetId, opts = {}) {
|
|
76
119
|
const target = getTarget(targetId);
|
|
77
120
|
const projectRoot = path.resolve(opts.projectRoot ?? process.cwd());
|
|
78
121
|
const checks = [];
|
|
79
|
-
for (const cap of ['rules', 'agents', 'commands', 'skills']) {
|
|
122
|
+
for (const cap of ['rules', 'agents', 'commands', 'skills', 'framework', 'hooks']) {
|
|
80
123
|
if (!target[cap]) continue;
|
|
81
124
|
const probe = path.join(projectRoot, target[cap].path);
|
|
82
125
|
let exists = false;
|
|
@@ -105,6 +148,18 @@ export async function removeFrom(targetId, opts = {}) {
|
|
|
105
148
|
}
|
|
106
149
|
} catch {}
|
|
107
150
|
}
|
|
151
|
+
// Mirror-tree capabilities: only remove if our marker is present (we manage the whole subtree).
|
|
152
|
+
for (const cap of ['framework', 'hooks']) {
|
|
153
|
+
const spec = target[cap];
|
|
154
|
+
if (!spec || spec.mode !== 'mirror-tree') continue;
|
|
155
|
+
const dir = path.join(projectRoot, spec.path);
|
|
156
|
+
const marker = path.join(dir, MANAGED_MARKER_FILE);
|
|
157
|
+
try {
|
|
158
|
+
await fs.access(marker);
|
|
159
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
160
|
+
removed.push(dir);
|
|
161
|
+
} catch {}
|
|
162
|
+
}
|
|
108
163
|
return { target: targetId, projectRoot, removed };
|
|
109
164
|
}
|
|
110
165
|
|
package/src/mcp-server/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
12
12
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
13
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
14
14
|
|
|
15
|
-
import { listKit, searchKit, findItem
|
|
15
|
+
import { listKit, searchKit, findItem } from '../core/kit.js';
|
|
16
16
|
import { listTargets } from '../core/registry.js';
|
|
17
17
|
import { syncTo, statusOf, removeFrom } from '../core/sync.js';
|
|
18
18
|
import { detectReverse, applyReverse } from '../core/reverse-sync.js';
|