@luanpdd/kit-mcp 0.5.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -1
- package/README.md +18 -4
- package/kit/commands/adicionar-backlog.md +1 -1
- package/kit/commands/adicionar-fase.md +1 -1
- package/kit/commands/adicionar-tarefa.md +1 -1
- package/kit/commands/concluir-marco.md +1 -1
- package/kit/commands/definir-perfil.md +1 -1
- package/kit/commands/depurar.md +1 -1
- package/kit/commands/fio.md +1 -1
- package/kit/commands/inserir-fase.md +1 -1
- package/package.json +5 -2
- package/src/core/kit.js +4 -1
- package/src/core/reverse-sync.js +93 -4
- package/src/core/sync.js +10 -6
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,51 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) · Versioning:
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.0.0] - 2026-05-03
|
|
10
|
+
|
|
11
|
+
**First stable release.** kit-mcp now commits to backwards compatibility on the surfaces listed under "Stable API" below; breaking changes there require a 2.0.0 bump.
|
|
12
|
+
|
|
13
|
+
### Added — Phase 1: Tooling debt
|
|
14
|
+
- `.github/dependabot.yml` — weekly grouped npm + github-actions updates.
|
|
15
|
+
- GitHub Release object created for v0.5.0 (was stuck on v0.2.0 "cleanup" as Latest).
|
|
16
|
+
- `.github/workflows/publish.yml` now creates a GitHub Release object automatically on every `v*` tag push, with notes extracted from this CHANGELOG. Closes the gap permanently.
|
|
17
|
+
|
|
18
|
+
### Fixed — Phase 2: Slash-command parser
|
|
19
|
+
- `src/core/sync.js` — `renderReference` reorders the stub body so the first non-blank line is the H1 + description blockquote, not the `<!-- kit-mcp:reference -->` marker. Strict downstream parsers (notably Claude Desktop's skill listing) now surface the real description.
|
|
20
|
+
- `src/core/kit.js` — `firstNonEmptyLine` skips lines starting with `<!--` as a defensive fallback when the canonical has no frontmatter description.
|
|
21
|
+
- `kit/commands/*` — 8 commands (`adicionar-backlog`, `adicionar-fase`, `adicionar-tarefa`, `concluir-marco`, `definir-perfil`, `depurar`, `fio`, `inserir-fase`) had unquoted angle-bracket `argument-hint` values that strict YAML parsers misinterpreted as flow-style flags. Now consistently quoted.
|
|
22
|
+
|
|
23
|
+
### Added — Phase 3: Reverse-sync for mirror-tree caps
|
|
24
|
+
- `detectReverse` now walks `.claude/framework/` and `.claude/hooks/` and reports any byte-for-byte difference vs `kit/<source>/<rel>`. The `.kit-mcp-managed` marker is automatically excluded from candidates.
|
|
25
|
+
- `applyReverse` adds `applyMirrorTreeOne` for `framework`/`hooks` candidates: `skip`, `overwrite`, `merge` (degenerates to overwrite — no frontmatter to preserve), `rename` (writes to `kit/<source>/<rel>.from-<tag>.<ext>` preserving the original).
|
|
26
|
+
- `--only framework/<rel>` / `--only hooks/<file>` filters narrow apply to one file.
|
|
27
|
+
- README "kit reverse-sync" section updated with the new examples.
|
|
28
|
+
|
|
29
|
+
### Added — Phase 4: Test infrastructure
|
|
30
|
+
- `node:test`-based runner — zero dependencies. `test/run.mjs` walks for `*.test.js` files (works on Node 20+ where `--test` glob support is partial).
|
|
31
|
+
- 37 unit tests across `kit`, `sync`, `reverse-sync`, `gates`, `gate-runner`, `registry`.
|
|
32
|
+
- 5 integration tests spawning `bin/cli.js` end-to-end (incl. MCP server boot smoke).
|
|
33
|
+
- `test/fixtures/sample-kit/` minimal fixture (1 of each kind + framework template + hook + frontmatter-less command for fallback test).
|
|
34
|
+
- CI runs `npm test` + `npm run test:integration` before existing smoke + MCP boot, on Ubuntu / macOS / Windows × Node 20 / 22 (6/6 combinations).
|
|
35
|
+
- `package.json` scripts: `test`, `test:integration`, `test:all`.
|
|
36
|
+
|
|
37
|
+
### Stable API (commitments locked at 1.0.0)
|
|
38
|
+
|
|
39
|
+
The following surfaces are covered by SemVer — breaking changes require a 2.0.0 release:
|
|
40
|
+
|
|
41
|
+
- **`src/core/registry.js` TARGETS table format.** Adding capabilities, IDEs, or new modes is non-breaking. Renaming or removing existing capability keys (`rules`, `agents`, `commands`, `skills`, `framework`, `hooks`, `mcpConfig`) is breaking.
|
|
42
|
+
- **MCP tool action signatures.** Tool names (`kit`, `sync`, `reverse-sync`, `gates`, `forensics`, `install`) and their action-dispatch contracts are stable. New actions are non-breaking; renaming or removing existing actions is breaking.
|
|
43
|
+
- **CLI subcommand surface.** Top-level commands (`kit`, `sync`, `reverse-sync`, `gates`, `forensics`, `install`) and their action sub-commands are stable. New flags are non-breaking; renaming or removing existing ones is breaking.
|
|
44
|
+
- **`src/core/*.js` named exports.** Functions consumed programmatically (`listKit`, `searchKit`, `findItem`, `resolveKitRoot`, `BUNDLED_KIT_ROOT`, `syncTo`, `statusOf`, `removeFrom`, `detectReverse`, `applyReverse`, `listGates`, `getGate`, `gatesForStage`, `runGate`, `listTargets`, `getTarget`, `TARGETS`) keep their signatures. Adding new exports is non-breaking; signature changes are breaking.
|
|
45
|
+
- **Stub format.** Files written by sync `--mode reference` keep the `<!-- kit-mcp:reference -->` marker somewhere in the body so `sync remove` and `reverse-sync detect` continue to identify them. Position within the body may change; presence is the contract.
|
|
46
|
+
- **`.kit-mcp-managed` marker semantics.** Mirror-tree directories (`framework/`, `hooks/`) are managed only when the marker is present at the root. Without it, `sync remove` never deletes the tree.
|
|
47
|
+
|
|
48
|
+
### Migration
|
|
49
|
+
|
|
50
|
+
No code changes required for users on 0.5.0 — `npm install @luanpdd/kit-mcp@latest` brings in 1.0.0 with the same behavior plus the parser fixes, reverse-sync expansions, and test coverage.
|
|
51
|
+
|
|
52
|
+
If you were on 0.4.0 (deprecated) or earlier, upgrade to skip the import-time crash and missing-framework regression entirely.
|
|
53
|
+
|
|
9
54
|
## [0.5.0] - 2026-05-03
|
|
10
55
|
|
|
11
56
|
### Added
|
|
@@ -174,7 +219,8 @@ npx -y @luanpdd/kit-mcp sync install claude-code --project-root .
|
|
|
174
219
|
- CLI mirror of all MCP tools.
|
|
175
220
|
- `install` command that registers kit-mcp into an IDE's MCP config (JSON for Claude/Cursor/Gemini/Windsurf, TOML for Codex).
|
|
176
221
|
|
|
177
|
-
[Unreleased]: https://github.com/luanpdd/kit-mcp/compare/
|
|
222
|
+
[Unreleased]: https://github.com/luanpdd/kit-mcp/compare/v1.0.0...HEAD
|
|
223
|
+
[1.0.0]: https://github.com/luanpdd/kit-mcp/compare/v0.5.0...v1.0.0
|
|
178
224
|
[0.5.0]: https://github.com/luanpdd/kit-mcp/compare/v0.4.1...v0.5.0
|
|
179
225
|
[0.4.1]: https://github.com/luanpdd/kit-mcp/compare/v0.4.0...v0.4.1
|
|
180
226
|
[0.4.0]: https://github.com/luanpdd/kit-mcp/compare/v0.3.0...v0.4.0
|
package/README.md
CHANGED
|
@@ -194,16 +194,19 @@ kit install write claude-code --scope user --via global # assumes `npm ins
|
|
|
194
194
|
|
|
195
195
|
### `kit reverse-sync ...` — bring IDE edits back to the canonical kit
|
|
196
196
|
|
|
197
|
-
If you edited an agent/command/skill **directly inside the IDE's folder** (`.claude/agents/foo.md`, `.
|
|
197
|
+
If you edited an agent/command/skill/framework/hook **directly inside the IDE's folder** (`.claude/agents/foo.md`, `.claude/framework/workflows/bar.md`, `.claude/hooks/baz.js`, …) instead of in your kit, this brings those edits back so the canonical absorbs them.
|
|
198
198
|
|
|
199
199
|
```bash
|
|
200
200
|
kit reverse-sync detect claude-code --project-root .
|
|
201
201
|
kit reverse-sync apply claude-code --project-root . --strategy merge --dry-run
|
|
202
202
|
kit reverse-sync apply claude-code --project-root . --strategy merge
|
|
203
203
|
kit reverse-sync apply claude-code --project-root . --strategy overwrite --only agent/foo
|
|
204
|
+
kit reverse-sync apply claude-code --project-root . --strategy overwrite --only framework/workflows/new-milestone.md
|
|
204
205
|
```
|
|
205
206
|
|
|
206
|
-
**Strategies:** `skip` (list-only), `merge` (canonical frontmatter + edited body), `overwrite`, `rename` (preserve both as `-from-{ide}.md`).
|
|
207
|
+
**Strategies:** `skip` (list-only), `merge` (canonical frontmatter + edited body — for agents/commands/skills), `overwrite`, `rename` (preserve both as `-from-{ide}.md`).
|
|
208
|
+
|
|
209
|
+
**Mirror-tree caps (`framework`, `hooks`):** files have no frontmatter, so `merge` degenerates to `overwrite` (with a note). The `.kit-mcp-managed` marker is automatically excluded from candidates. Filter individual files with `--only framework/<rel>` or `--only hooks/<file>`.
|
|
207
210
|
|
|
208
211
|
### `kit gates ...` — reusable workflow gates
|
|
209
212
|
|
|
@@ -469,16 +472,27 @@ PRs welcome.
|
|
|
469
472
|
|
|
470
473
|
---
|
|
471
474
|
|
|
472
|
-
##
|
|
475
|
+
## Tests
|
|
476
|
+
|
|
477
|
+
Built on `node:test` (zero dependencies). Two suites:
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
npm test # unit — kit parser, sync (all modes), reverse-sync, gates, registry
|
|
481
|
+
npm run test:integration # integration — spawns bin/cli.js end-to-end on a temp project
|
|
482
|
+
npm run test:all # both
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
Plus the original quick smokes:
|
|
473
486
|
|
|
474
487
|
```bash
|
|
475
488
|
node bin/cli.js kit list-agents | head -5 # 19 bundled agents
|
|
476
489
|
node bin/cli.js sync targets # 8 IDEs
|
|
477
490
|
node bin/cli.js gates list # 5 gates
|
|
478
491
|
node bin/cli.js install dry-run claude-code --via npx
|
|
479
|
-
node bin/mcp.js < /dev/null & sleep 1; kill %1 # MCP server boots and waits on stdio
|
|
480
492
|
```
|
|
481
493
|
|
|
494
|
+
CI runs unit + integration + smoke + MCP boot on Ubuntu / macOS / Windows × Node 20 / 22 on every push and PR.
|
|
495
|
+
|
|
482
496
|
---
|
|
483
497
|
|
|
484
498
|
## License
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: definir-perfil
|
|
3
3
|
description: Altera o perfil de modelo para os agentes framework (quality/balanced/budget/inherit)
|
|
4
|
-
argument-hint: <perfil (quality|balanced|budget|inherit)>
|
|
4
|
+
argument-hint: "<perfil (quality|balanced|budget|inherit)>"
|
|
5
5
|
model: haiku
|
|
6
6
|
allowed-tools:
|
|
7
7
|
- Bash
|
package/kit/commands/depurar.md
CHANGED
package/kit/commands/fio.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luanpdd/kit-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.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": {
|
|
@@ -41,7 +41,10 @@
|
|
|
41
41
|
"scripts": {
|
|
42
42
|
"start": "node bin/mcp.js",
|
|
43
43
|
"cli": "node bin/cli.js",
|
|
44
|
-
"smoke": "node bin/cli.js kit list-agents | head -5"
|
|
44
|
+
"smoke": "node bin/cli.js kit list-agents | head -5",
|
|
45
|
+
"test": "node test/run.mjs test/unit",
|
|
46
|
+
"test:integration": "node test/run.mjs test/integration",
|
|
47
|
+
"test:all": "node test/run.mjs test"
|
|
45
48
|
},
|
|
46
49
|
"dependencies": {
|
|
47
50
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
package/src/core/kit.js
CHANGED
|
@@ -135,7 +135,10 @@ function parseLooseYaml(text) {
|
|
|
135
135
|
function firstNonEmptyLine(body) {
|
|
136
136
|
for (const line of body.split(/\r?\n/)) {
|
|
137
137
|
const t = line.trim();
|
|
138
|
-
if (
|
|
138
|
+
if (!t) continue; // blank
|
|
139
|
+
if (t.startsWith('#')) continue; // markdown heading
|
|
140
|
+
if (t.startsWith('<!--')) continue; // HTML comment (e.g. STUB_MARKER)
|
|
141
|
+
return t.slice(0, 200);
|
|
139
142
|
}
|
|
140
143
|
return '';
|
|
141
144
|
}
|
package/src/core/reverse-sync.js
CHANGED
|
@@ -35,6 +35,11 @@ export async function detectReverse(targetId, opts = {}) {
|
|
|
35
35
|
if (target.agents) await scanCapability(candidates, 'agent', target.agents, projectRoot, kit.agents, kitRoot);
|
|
36
36
|
if (target.commands) await scanCapability(candidates, 'command', target.commands, projectRoot, kit.commands, kitRoot);
|
|
37
37
|
if (target.skills) await scanSkills (candidates, target.skills, projectRoot, [...kit.skills, ...kit.skillsExtras], kitRoot);
|
|
38
|
+
for (const cap of ['framework', 'hooks']) {
|
|
39
|
+
const spec = target[cap];
|
|
40
|
+
if (!spec || spec.mode !== 'mirror-tree') continue;
|
|
41
|
+
await scanMirrorTree(candidates, cap, spec, projectRoot, kitRoot);
|
|
42
|
+
}
|
|
38
43
|
|
|
39
44
|
return { target: targetId, projectRoot, kitRoot, candidates };
|
|
40
45
|
}
|
|
@@ -117,6 +122,51 @@ async function scanSkills(candidates, capCfg, projectRoot, kitSkills, kitRoot) {
|
|
|
117
122
|
}
|
|
118
123
|
}
|
|
119
124
|
|
|
125
|
+
async function scanMirrorTree(candidates, cap, spec, projectRoot, kitRoot) {
|
|
126
|
+
const dstRoot = path.join(projectRoot, spec.path);
|
|
127
|
+
const srcRoot = path.join(kitRoot, spec.source);
|
|
128
|
+
const files = await walkRel(dstRoot);
|
|
129
|
+
for (const rel of files) {
|
|
130
|
+
if (rel === '.kit-mcp-managed' || path.basename(rel) === '.kit-mcp-managed') continue;
|
|
131
|
+
const dstPath = path.join(dstRoot, rel);
|
|
132
|
+
const srcPath = path.join(srcRoot, rel);
|
|
133
|
+
let dstBuf, srcBuf;
|
|
134
|
+
try { dstBuf = await fs.readFile(dstPath); } catch { continue; }
|
|
135
|
+
try { srcBuf = await fs.readFile(srcPath); } catch { srcBuf = null; }
|
|
136
|
+
if (!srcBuf) {
|
|
137
|
+
candidates.push({
|
|
138
|
+
kind: cap, name: rel, target: spec.path, destPath: dstPath, kitPath: srcPath,
|
|
139
|
+
reason: 'new-in-ide',
|
|
140
|
+
diffSummary: `+${dstBuf.length} bytes (no kit source)`,
|
|
141
|
+
});
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (dstBuf.equals(srcBuf)) continue;
|
|
145
|
+
candidates.push({
|
|
146
|
+
kind: cap, name: rel, target: spec.path, destPath: dstPath, kitPath: srcPath,
|
|
147
|
+
reason: 'modified-in-ide',
|
|
148
|
+
diffSummary: `${dstBuf.length} bytes vs ${srcBuf.length} canonical (${dstBuf.length - srcBuf.length >= 0 ? '+' : ''}${dstBuf.length - srcBuf.length})`,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function walkRel(root) {
|
|
154
|
+
const out = [];
|
|
155
|
+
async function visit(current, prefix) {
|
|
156
|
+
let entries;
|
|
157
|
+
try { entries = await fs.readdir(current, { withFileTypes: true }); }
|
|
158
|
+
catch { return; }
|
|
159
|
+
for (const e of entries) {
|
|
160
|
+
const abs = path.join(current, e.name);
|
|
161
|
+
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
|
162
|
+
if (e.isDirectory()) await visit(abs, rel);
|
|
163
|
+
else if (e.isFile()) out.push(rel);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
await visit(root, '');
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
|
|
120
170
|
// --- apply ---
|
|
121
171
|
|
|
122
172
|
export async function applyReverse(targetId, opts = {}) {
|
|
@@ -139,6 +189,13 @@ export async function applyReverse(targetId, opts = {}) {
|
|
|
139
189
|
|
|
140
190
|
async function applyOne(c, strategy, opts) {
|
|
141
191
|
const dryRun = !!opts.dryRun;
|
|
192
|
+
const isMirrorTree = c.kind === 'framework' || c.kind === 'hooks';
|
|
193
|
+
|
|
194
|
+
// Mirror-tree files don't have stub boilerplate — copy bytes verbatim.
|
|
195
|
+
if (isMirrorTree) {
|
|
196
|
+
return applyMirrorTreeOne(c, strategy, dryRun);
|
|
197
|
+
}
|
|
198
|
+
|
|
142
199
|
const destContent = await fs.readFile(c.destPath, 'utf8');
|
|
143
200
|
const stripped = stripStubBoilerplate(destContent);
|
|
144
201
|
|
|
@@ -147,7 +204,6 @@ async function applyOne(c, strategy, opts) {
|
|
|
147
204
|
return 'skipped';
|
|
148
205
|
|
|
149
206
|
case 'overwrite': {
|
|
150
|
-
// Replace canonical with the destination content (stripped of stub boilerplate)
|
|
151
207
|
if (!dryRun) {
|
|
152
208
|
await fs.mkdir(path.dirname(c.kitPath), { recursive: true });
|
|
153
209
|
await fs.writeFile(c.kitPath, stripped, 'utf8');
|
|
@@ -156,8 +212,6 @@ async function applyOne(c, strategy, opts) {
|
|
|
156
212
|
}
|
|
157
213
|
|
|
158
214
|
case 'merge': {
|
|
159
|
-
// Frontmatter from canonical (if present), body from destination.
|
|
160
|
-
// If canonical doesn't exist (new-in-ide), this degenerates to overwrite.
|
|
161
215
|
let merged = stripped;
|
|
162
216
|
if (c.reason === 'modified-in-ide') {
|
|
163
217
|
try {
|
|
@@ -173,7 +227,6 @@ async function applyOne(c, strategy, opts) {
|
|
|
173
227
|
}
|
|
174
228
|
|
|
175
229
|
case 'rename': {
|
|
176
|
-
// Write next to canonical with a -from-<targetfolder>.md suffix
|
|
177
230
|
const base = c.kitPath.replace(/\.md$/, '');
|
|
178
231
|
const tag = path.basename(path.dirname(path.dirname(c.destPath))).replace(/^\./, '');
|
|
179
232
|
const out = `${base}-from-${tag || 'ide'}.md`;
|
|
@@ -189,6 +242,42 @@ async function applyOne(c, strategy, opts) {
|
|
|
189
242
|
}
|
|
190
243
|
}
|
|
191
244
|
|
|
245
|
+
async function applyMirrorTreeOne(c, strategy, dryRun) {
|
|
246
|
+
switch (strategy) {
|
|
247
|
+
case 'skip':
|
|
248
|
+
return 'skipped';
|
|
249
|
+
|
|
250
|
+
case 'overwrite':
|
|
251
|
+
case 'merge': {
|
|
252
|
+
// For framework/hooks files there's no frontmatter to preserve,
|
|
253
|
+
// so 'merge' degenerates to overwrite. Returning a verb that
|
|
254
|
+
// signals the degradation.
|
|
255
|
+
const verb = strategy === 'merge' ? 'merged (overwrite, no frontmatter)' : 'overwritten';
|
|
256
|
+
if (!dryRun) {
|
|
257
|
+
await fs.mkdir(path.dirname(c.kitPath), { recursive: true });
|
|
258
|
+
await fs.copyFile(c.destPath, c.kitPath);
|
|
259
|
+
}
|
|
260
|
+
return dryRun ? `${strategy} (dry-run)` : verb;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
case 'rename': {
|
|
264
|
+
// Write to kit/<source>/<rel>.from-<tag> preserving extension after the tag.
|
|
265
|
+
const ext = path.extname(c.kitPath);
|
|
266
|
+
const stem = c.kitPath.slice(0, c.kitPath.length - ext.length);
|
|
267
|
+
const tag = path.basename(path.dirname(path.dirname(c.destPath))).replace(/^\./, '') || 'ide';
|
|
268
|
+
const out = `${stem}.from-${tag}${ext}`;
|
|
269
|
+
if (!dryRun) {
|
|
270
|
+
await fs.mkdir(path.dirname(out), { recursive: true });
|
|
271
|
+
await fs.copyFile(c.destPath, out);
|
|
272
|
+
}
|
|
273
|
+
return dryRun ? `rename → ${out} (dry-run)` : `renamed → ${out}`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
default:
|
|
277
|
+
return `unknown strategy: ${strategy}`;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
192
281
|
// --- helpers ---
|
|
193
282
|
|
|
194
283
|
function isCleanStub(content) {
|
package/src/core/sync.js
CHANGED
|
@@ -196,15 +196,19 @@ function renderReference(item, kitRoot, outPath, isSkill) {
|
|
|
196
196
|
? item.frontmatterRaw
|
|
197
197
|
: synthFrontmatter(item);
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
//
|
|
201
|
-
|
|
199
|
+
// Body must NOT start with the STUB_MARKER comment — IDE listings (e.g. Claude Desktop)
|
|
200
|
+
// that take the first non-blank body line as the visible description would surface
|
|
201
|
+
// "<!-- kit-mcp:reference -->" instead of the real description. So we open with the
|
|
202
|
+
// H1 + description blockquote, and tuck the marker at the end as a trailing comment.
|
|
203
|
+
const descLine = item.description ? `\n> ${item.description}\n` : '';
|
|
204
|
+
return `${fm}
|
|
202
205
|
# ${item.name}
|
|
203
|
-
|
|
206
|
+
${descLine}
|
|
204
207
|
> Canonical source: [\`${rel}\`](${rel})
|
|
205
|
-
${desc}
|
|
206
|
-
> Generated by kit-mcp at ${new Date().toISOString()}.
|
|
207
208
|
> Edit the source file in the kit, not this stub.
|
|
209
|
+
> Generated by kit-mcp at ${new Date().toISOString()}.
|
|
210
|
+
|
|
211
|
+
${STUB_MARKER}
|
|
208
212
|
`;
|
|
209
213
|
}
|
|
210
214
|
|