@luanpdd/kit-mcp 1.21.0 → 1.22.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/LICENSE +21 -21
- package/README.md +648 -648
- package/kit/COMANDOS.md +138 -138
- package/kit/README.md +76 -52
- package/kit/agents/advisor-researcher.md +106 -106
- package/kit/agents/assumptions-analyzer.md +107 -107
- package/kit/agents/auditor-consistencia-isolamento.md +380 -0
- package/kit/agents/codebase-mapper.md +768 -768
- package/kit/agents/crm-pipeline-implementer.md +17 -0
- package/kit/agents/debugger.md +772 -772
- package/kit/agents/detector-tenant-quente.md +337 -0
- package/kit/agents/example-reviewer.md +21 -21
- package/kit/agents/executor.md +523 -523
- package/kit/agents/integration-checker.md +200 -200
- package/kit/agents/multi-tenant-isolation-auditor.md +10 -0
- package/kit/agents/nyquist-auditor.md +178 -178
- package/kit/agents/phase-researcher.md +696 -696
- package/kit/agents/plan-checker.md +272 -272
- package/kit/agents/planner.md +891 -891
- package/kit/agents/project-researcher.md +652 -652
- package/kit/agents/research-synthesizer.md +245 -245
- package/kit/agents/roadmapper.md +677 -677
- package/kit/agents/supabase-architect.md +10 -0
- package/kit/agents/supabase-migration-writer.md +12 -0
- package/kit/agents/ui-auditor.md +437 -437
- package/kit/agents/ui-checker.md +302 -302
- package/kit/agents/ui-researcher.md +355 -355
- package/kit/agents/user-profiler.md +175 -175
- package/kit/agents/validador-evolucao-schema.md +335 -0
- package/kit/agents/verifier.md +728 -728
- package/kit/commands/adicionar-backlog.md +75 -75
- package/kit/commands/adicionar-fase.md +42 -42
- package/kit/commands/adicionar-tarefa.md +45 -45
- package/kit/commands/adicionar-testes.md +41 -41
- package/kit/commands/ajuda.md +21 -21
- package/kit/commands/atualizar.md +37 -37
- package/kit/commands/auditar-marco.md +179 -179
- package/kit/commands/auditar-uat.md +23 -23
- package/kit/commands/autonomo.md +40 -40
- package/kit/commands/branch-pr.md +24 -24
- package/kit/commands/concluir-marco.md +247 -247
- package/kit/commands/configuracoes.md +36 -36
- package/kit/commands/dados-distribuidos.md +188 -0
- package/kit/commands/definir-perfil.md +10 -10
- package/kit/commands/depurar.md +190 -190
- package/kit/commands/discutir-fase.md +131 -131
- package/kit/commands/entrar-discord.md +17 -17
- package/kit/commands/estatisticas.md +18 -18
- package/kit/commands/example-greeting.md +33 -33
- package/kit/commands/executar-fase.md +58 -58
- package/kit/commands/expresso.md +56 -56
- package/kit/commands/fase-ui.md +34 -34
- package/kit/commands/fazer.md +57 -57
- package/kit/commands/fio.md +125 -125
- package/kit/commands/fluxos-trabalho.md +64 -64
- package/kit/commands/forense.md +176 -176
- package/kit/commands/gerenciador.md +38 -38
- package/kit/commands/inserir-fase.md +31 -31
- package/kit/commands/limpeza.md +17 -17
- package/kit/commands/listar-hipoteses-fase.md +45 -45
- package/kit/commands/listar-workspaces.md +18 -18
- package/kit/commands/mapear-codebase.md +70 -70
- package/kit/commands/nota.md +33 -33
- package/kit/commands/novo-marco.md +43 -43
- package/kit/commands/novo-projeto.md +41 -41
- package/kit/commands/novo-workspace.md +43 -43
- package/kit/commands/pausar-trabalho.md +37 -37
- package/kit/commands/perfil-usuario.md +45 -45
- package/kit/commands/pesquisar-fase.md +195 -195
- package/kit/commands/planejar-fase.md +67 -67
- package/kit/commands/planejar-lacunas.md +33 -33
- package/kit/commands/plantar-ideia.md +25 -25
- package/kit/commands/progresso.md +24 -24
- package/kit/commands/proximo.md +30 -30
- package/kit/commands/publicar.md +490 -490
- package/kit/commands/rapido.md +35 -35
- package/kit/commands/reaplicar-patches.md +124 -124
- package/kit/commands/relatorio-sessao.md +19 -19
- package/kit/commands/remover-fase.md +31 -31
- package/kit/commands/remover-workspace.md +26 -26
- package/kit/commands/resumo-marco.md +50 -50
- package/kit/commands/retomar-trabalho.md +40 -40
- package/kit/commands/revisar-backlog.md +60 -60
- package/kit/commands/revisar-ui.md +32 -32
- package/kit/commands/revisar.md +37 -37
- package/kit/commands/saude.md +21 -21
- package/kit/commands/setup-notion.md +93 -93
- package/kit/commands/sync-main.md +68 -68
- package/kit/commands/validar-fase.md +35 -35
- package/kit/commands/verificar-tarefas.md +44 -44
- package/kit/commands/verificar-trabalho.md +64 -64
- package/kit/file-manifest.json +27 -15
- package/kit/framework/bin/lib/commands.cjs +959 -959
- package/kit/framework/bin/lib/config.cjs +442 -442
- package/kit/framework/bin/lib/core.cjs +1230 -1230
- package/kit/framework/bin/lib/frontmatter.cjs +336 -336
- package/kit/framework/bin/lib/init.cjs +1442 -1442
- package/kit/framework/bin/lib/milestone.cjs +252 -252
- package/kit/framework/bin/lib/model-profiles.cjs +68 -68
- package/kit/framework/bin/lib/phase.cjs +888 -888
- package/kit/framework/bin/lib/profile-output.cjs +952 -952
- package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
- package/kit/framework/bin/lib/roadmap.cjs +329 -329
- package/kit/framework/bin/lib/security.cjs +382 -382
- package/kit/framework/bin/lib/state.cjs +1031 -1031
- package/kit/framework/bin/lib/template.cjs +222 -222
- package/kit/framework/bin/lib/uat.cjs +282 -282
- package/kit/framework/bin/lib/verify.cjs +888 -888
- package/kit/framework/bin/lib/workstream.cjs +491 -491
- package/kit/framework/bin/tools.cjs +918 -918
- package/kit/framework/commands/workstreams.md +63 -63
- package/kit/framework/references/checkpoints.md +778 -778
- package/kit/framework/references/continuation-format.md +249 -249
- package/kit/framework/references/decimal-phase-calculation.md +64 -64
- package/kit/framework/references/git-integration.md +295 -295
- package/kit/framework/references/git-planning-commit.md +38 -38
- package/kit/framework/references/model-profile-resolution.md +36 -36
- package/kit/framework/references/model-profiles.md +139 -139
- package/kit/framework/references/phase-argument-parsing.md +61 -61
- package/kit/framework/references/planning-config.md +202 -202
- package/kit/framework/references/questioning.md +162 -162
- package/kit/framework/references/tdd.md +263 -263
- package/kit/framework/references/ui-brand.md +160 -160
- package/kit/framework/references/user-profiling.md +657 -657
- package/kit/framework/references/verification-patterns.md +612 -612
- package/kit/framework/references/workstream-flag.md +58 -58
- package/kit/framework/templates/DEBUG.md +164 -164
- package/kit/framework/templates/UAT.md +265 -265
- package/kit/framework/templates/UI-SPEC.md +100 -100
- package/kit/framework/templates/VALIDATION.md +76 -76
- package/kit/framework/templates/claude-md.md +122 -122
- package/kit/framework/templates/codebase/architecture.md +185 -185
- package/kit/framework/templates/codebase/concerns.md +205 -205
- package/kit/framework/templates/codebase/conventions.md +204 -204
- package/kit/framework/templates/codebase/integrations.md +192 -192
- package/kit/framework/templates/codebase/stack.md +158 -158
- package/kit/framework/templates/codebase/structure.md +199 -199
- package/kit/framework/templates/codebase/testing.md +301 -301
- package/kit/framework/templates/config.json +44 -44
- package/kit/framework/templates/context.md +352 -352
- package/kit/framework/templates/continue-here.md +78 -78
- package/kit/framework/templates/copilot-instructions.md +7 -7
- package/kit/framework/templates/debug-subagent-prompt.md +91 -91
- package/kit/framework/templates/dev-preferences.md +20 -20
- package/kit/framework/templates/discovery.md +146 -146
- package/kit/framework/templates/discussion-log.md +63 -63
- package/kit/framework/templates/milestone-archive.md +123 -123
- package/kit/framework/templates/milestone.md +115 -115
- package/kit/framework/templates/phase-prompt.md +610 -610
- package/kit/framework/templates/planner-subagent-prompt.md +117 -117
- package/kit/framework/templates/project.md +186 -186
- package/kit/framework/templates/requirements.md +231 -231
- package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
- package/kit/framework/templates/research-project/FEATURES.md +147 -147
- package/kit/framework/templates/research-project/PITFALLS.md +200 -200
- package/kit/framework/templates/research-project/STACK.md +120 -120
- package/kit/framework/templates/research-project/SUMMARY.md +170 -170
- package/kit/framework/templates/research.md +419 -419
- package/kit/framework/templates/retrospective.md +54 -54
- package/kit/framework/templates/roadmap.md +202 -202
- package/kit/framework/templates/state.md +176 -176
- package/kit/framework/templates/summary-complex.md +59 -59
- package/kit/framework/templates/summary-minimal.md +41 -41
- package/kit/framework/templates/summary-standard.md +48 -48
- package/kit/framework/templates/summary.md +209 -209
- package/kit/framework/templates/user-profile.md +146 -146
- package/kit/framework/templates/user-setup.md +256 -256
- package/kit/framework/templates/verification-report.md +258 -258
- package/kit/framework/workflows/add-phase.md +112 -112
- package/kit/framework/workflows/add-tests.md +351 -351
- package/kit/framework/workflows/add-todo.md +158 -158
- package/kit/framework/workflows/audit-milestone.md +340 -340
- package/kit/framework/workflows/audit-uat.md +109 -109
- package/kit/framework/workflows/autonomous.md +891 -891
- package/kit/framework/workflows/check-todos.md +177 -177
- package/kit/framework/workflows/cleanup.md +152 -152
- package/kit/framework/workflows/complete-milestone.md +696 -696
- package/kit/framework/workflows/diagnose-issues.md +231 -231
- package/kit/framework/workflows/discovery-phase.md +289 -289
- package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
- package/kit/framework/workflows/discuss-phase.md +784 -784
- package/kit/framework/workflows/do.md +104 -104
- package/kit/framework/workflows/execute-phase.md +838 -838
- package/kit/framework/workflows/execute-plan.md +510 -510
- package/kit/framework/workflows/fast.md +102 -102
- package/kit/framework/workflows/forensics.md +265 -265
- package/kit/framework/workflows/health.md +181 -181
- package/kit/framework/workflows/help.md +619 -619
- package/kit/framework/workflows/insert-phase.md +130 -130
- package/kit/framework/workflows/list-phase-assumptions.md +178 -178
- package/kit/framework/workflows/list-workspaces.md +56 -56
- package/kit/framework/workflows/manager.md +362 -362
- package/kit/framework/workflows/map-codebase.md +377 -377
- package/kit/framework/workflows/milestone-summary.md +223 -223
- package/kit/framework/workflows/new-milestone.md +486 -486
- package/kit/framework/workflows/new-project.md +1159 -1159
- package/kit/framework/workflows/new-workspace.md +237 -237
- package/kit/framework/workflows/next.md +97 -97
- package/kit/framework/workflows/node-repair.md +92 -92
- package/kit/framework/workflows/note.md +156 -156
- package/kit/framework/workflows/pause-work.md +176 -176
- package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
- package/kit/framework/workflows/plan-phase.md +765 -765
- package/kit/framework/workflows/plant-seed.md +169 -169
- package/kit/framework/workflows/pr-branch.md +129 -129
- package/kit/framework/workflows/profile-user.md +450 -450
- package/kit/framework/workflows/progress.md +507 -507
- package/kit/framework/workflows/quick.md +757 -757
- package/kit/framework/workflows/remove-phase.md +155 -155
- package/kit/framework/workflows/remove-workspace.md +90 -90
- package/kit/framework/workflows/research-phase.md +82 -82
- package/kit/framework/workflows/resume-project.md +326 -326
- package/kit/framework/workflows/review.md +228 -228
- package/kit/framework/workflows/session-report.md +146 -146
- package/kit/framework/workflows/settings.md +283 -283
- package/kit/framework/workflows/ship.md +228 -228
- package/kit/framework/workflows/stats.md +60 -60
- package/kit/framework/workflows/transition.md +671 -671
- package/kit/framework/workflows/ui-phase.md +302 -302
- package/kit/framework/workflows/ui-review.md +165 -165
- package/kit/framework/workflows/update.md +323 -323
- package/kit/framework/workflows/validate-phase.md +174 -174
- package/kit/framework/workflows/verify-phase.md +252 -252
- package/kit/framework/workflows/verify-work.md +637 -637
- package/kit/hooks/check-update.js +118 -118
- package/kit/hooks/context-monitor.js +163 -163
- package/kit/hooks/prompt-guard.js +103 -103
- package/kit/hooks/statusline.js +125 -125
- package/kit/hooks/workflow-guard.js +101 -101
- package/kit/settings.json +45 -45
- package/kit/skills/_shared-dados-distribuidos/glossary.md +224 -0
- package/kit/skills/armadilhas-sistemas-distribuidos/SKILL.md +447 -0
- package/kit/skills/audit-log-multi-tenant/SKILL.md +6 -0
- package/kit/skills/cascading-failures/SKILL.md +4 -0
- package/kit/skills/consistencia-leitura-replica/SKILL.md +385 -0
- package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +17 -0
- package/kit/skills/escolha-modelo-consistencia/SKILL.md +495 -0
- package/kit/skills/evolucao-schema-compativel/SKILL.md +448 -0
- package/kit/skills/example-skill/SKILL.md +42 -42
- package/kit/skills/multi-tenant-performance-scaling/SKILL.md +4 -0
- package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +4 -0
- package/kit/skills/postgres-isolamento-concorrencia/SKILL.md +552 -0
- package/kit/skills/streams-eventos-cdc/SKILL.md +712 -0
- package/kit/skills/supabase-cron-queues/SKILL.md +9 -0
- package/kit/skills/supabase-migrations/SKILL.md +10 -0
- package/kit/skills/super-admin-platform-pattern/SKILL.md +4 -0
- package/kit/skills/tenant-quente-mitigacao/SKILL.md +605 -0
- package/package.json +63 -63
- package/src/core/kit.js +216 -216
- package/src/core/reflect.js +247 -247
- package/src/core/reverse-sync.js +372 -372
- package/src/core/sync.js +418 -418
- package/src/core/watch.js +121 -121
package/package.json
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@luanpdd/kit-mcp",
|
|
3
|
-
"version": "1.
|
|
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
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"kit-mcp": "bin/mcp.js",
|
|
8
|
-
"kit": "bin/cli.js"
|
|
9
|
-
},
|
|
10
|
-
"publishConfig": {
|
|
11
|
-
"access": "public"
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"bin/",
|
|
15
|
-
"src/",
|
|
16
|
-
"kit/",
|
|
17
|
-
"gates/",
|
|
18
|
-
"README.md",
|
|
19
|
-
"LICENSE"
|
|
20
|
-
],
|
|
21
|
-
"keywords": [
|
|
22
|
-
"mcp",
|
|
23
|
-
"model-context-protocol",
|
|
24
|
-
"claude",
|
|
25
|
-
"cursor",
|
|
26
|
-
"codex",
|
|
27
|
-
"gemini",
|
|
28
|
-
"agents",
|
|
29
|
-
"skills"
|
|
30
|
-
],
|
|
31
|
-
"author": "luanpdd",
|
|
32
|
-
"license": "MIT",
|
|
33
|
-
"repository": {
|
|
34
|
-
"type": "git",
|
|
35
|
-
"url": "git+https://github.com/luanpdd/kit-mcp.git"
|
|
36
|
-
},
|
|
37
|
-
"engines": {
|
|
38
|
-
"node": ">=20"
|
|
39
|
-
},
|
|
40
|
-
"scripts": {
|
|
41
|
-
"start": "node bin/mcp.js",
|
|
42
|
-
"cli": "node bin/cli.js",
|
|
43
|
-
"smoke": "node bin/cli.js kit list-agents | head -5",
|
|
44
|
-
"test": "node test/run.mjs test/unit",
|
|
45
|
-
"test:all": "node test/run.mjs test",
|
|
46
|
-
"test:integration": "node test/run.mjs test/integration",
|
|
47
|
-
"test:mutation": "stryker run",
|
|
48
|
-
"prepublishOnly": "node scripts/regen-manifest.js && node scripts/update-readme-counts.js && node test/run.mjs test/unit && node test/run.mjs test/integration"
|
|
49
|
-
},
|
|
50
|
-
"dependencies": {
|
|
51
|
-
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
52
|
-
"commander": "^14.0.3",
|
|
53
|
-
"picocolors": "^1.1.1"
|
|
54
|
-
},
|
|
55
|
-
"optionalDependencies": {
|
|
56
|
-
"@inquirer/prompts": "^8.4.2",
|
|
57
|
-
"chokidar": "^5.0.0",
|
|
58
|
-
"open": "^11.0.0"
|
|
59
|
-
},
|
|
60
|
-
"devDependencies": {
|
|
61
|
-
"@stryker-mutator/core": "9.6.1"
|
|
62
|
-
}
|
|
63
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@luanpdd/kit-mcp",
|
|
3
|
+
"version": "1.22.0",
|
|
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
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"kit-mcp": "bin/mcp.js",
|
|
8
|
+
"kit": "bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"bin/",
|
|
15
|
+
"src/",
|
|
16
|
+
"kit/",
|
|
17
|
+
"gates/",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"model-context-protocol",
|
|
24
|
+
"claude",
|
|
25
|
+
"cursor",
|
|
26
|
+
"codex",
|
|
27
|
+
"gemini",
|
|
28
|
+
"agents",
|
|
29
|
+
"skills"
|
|
30
|
+
],
|
|
31
|
+
"author": "luanpdd",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/luanpdd/kit-mcp.git"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=20"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"start": "node bin/mcp.js",
|
|
42
|
+
"cli": "node bin/cli.js",
|
|
43
|
+
"smoke": "node bin/cli.js kit list-agents | head -5",
|
|
44
|
+
"test": "node test/run.mjs test/unit",
|
|
45
|
+
"test:all": "node test/run.mjs test",
|
|
46
|
+
"test:integration": "node test/run.mjs test/integration",
|
|
47
|
+
"test:mutation": "stryker run",
|
|
48
|
+
"prepublishOnly": "node scripts/regen-manifest.js && node scripts/update-readme-counts.js && node test/run.mjs test/unit && node test/run.mjs test/integration"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
52
|
+
"commander": "^14.0.3",
|
|
53
|
+
"picocolors": "^1.1.1"
|
|
54
|
+
},
|
|
55
|
+
"optionalDependencies": {
|
|
56
|
+
"@inquirer/prompts": "^8.4.2",
|
|
57
|
+
"chokidar": "^5.0.0",
|
|
58
|
+
"open": "^11.0.0"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@stryker-mutator/core": "9.6.1"
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/core/kit.js
CHANGED
|
@@ -1,216 +1,216 @@
|
|
|
1
|
-
// Read the canonical kit/ directory and return a structured index.
|
|
2
|
-
// Source of truth: kit/agents/*.md, kit/commands/*.md, kit/skills/*/SKILL.md
|
|
3
|
-
//
|
|
4
|
-
// Frontmatter is parsed loosely (no external dep) — we only need name & description.
|
|
5
|
-
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
import fs from 'node:fs/promises';
|
|
8
|
-
import { fileURLToPath } from 'node:url';
|
|
9
|
-
|
|
10
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
-
const __dirname = path.dirname(__filename);
|
|
12
|
-
|
|
13
|
-
// PERF-02: Frontmatter regexes compiled once at module load (was being recompiled
|
|
14
|
-
// on every readMdDir / readSkillsDir entry — 60+ times per listKit call).
|
|
15
|
-
const FRONTMATTER_SPLIT_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
16
|
-
const FRONTMATTER_RAW_RE = /^(---\r?\n[\s\S]*?\r?\n---\r?\n?)/;
|
|
17
|
-
const YAML_KEY_RE = /^([A-Za-z0-9_-]+):\s*(.*)$/;
|
|
18
|
-
|
|
19
|
-
// Resolution order for the kit root (re-evaluated on each call so env-var
|
|
20
|
-
// overrides set after module load — e.g. by the CLI preAction hook — work):
|
|
21
|
-
// 1. explicit `kitRoot` opt passed by caller
|
|
22
|
-
// 2. KIT_MCP_KIT_ROOT env var (per-session override)
|
|
23
|
-
// 3. ./kit relative to this package (the bundled example kit)
|
|
24
|
-
export const BUNDLED_KIT_ROOT = path.resolve(__dirname, '../../kit');
|
|
25
|
-
export function resolveKitRoot(kitRoot) {
|
|
26
|
-
if (kitRoot) return path.resolve(kitRoot);
|
|
27
|
-
if (process.env.KIT_MCP_KIT_ROOT) return path.resolve(process.env.KIT_MCP_KIT_ROOT);
|
|
28
|
-
return BUNDLED_KIT_ROOT;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// PERF-01: TTL cache for listKit output. Repeated calls within KIT_CACHE_TTL_MS
|
|
32
|
-
// return the cached value — sync/reverse-sync/MCP list-* tools used to walk the
|
|
33
|
-
// disk on every invocation. Trade-off: callers that edit kit/ inside the same
|
|
34
|
-
// process may see stale data for up to 30s. Acceptable for MCP/CLI ergonomics.
|
|
35
|
-
const KIT_CACHE_TTL_MS = 30_000;
|
|
36
|
-
const kitCache = new Map(); // `${kitRoot}:${mode}` -> { value, ts }
|
|
37
|
-
|
|
38
|
-
// PERF-S1: when sync runs in mode=reference (default), the body/content of each
|
|
39
|
-
// kit file is never used — only frontmatter (name + description). Reading just
|
|
40
|
-
// the first STUB_READ_BYTES is enough for any frontmatter we'd ever produce and
|
|
41
|
-
// avoids loading 50 KB+ files (planner.md etc) from disk.
|
|
42
|
-
const STUB_READ_BYTES = 4096;
|
|
43
|
-
|
|
44
|
-
export function clearKitCache() { kitCache.clear(); }
|
|
45
|
-
|
|
46
|
-
export async function listKit(kitRoot, opts = {}) {
|
|
47
|
-
kitRoot = resolveKitRoot(kitRoot);
|
|
48
|
-
const stubsOnly = opts.stubsOnly === true;
|
|
49
|
-
const cacheKey = `${kitRoot}:${stubsOnly ? 'stubs' : 'full'}`;
|
|
50
|
-
const cached = kitCache.get(cacheKey);
|
|
51
|
-
if (cached && Date.now() - cached.ts < KIT_CACHE_TTL_MS) {
|
|
52
|
-
return cached.value;
|
|
53
|
-
}
|
|
54
|
-
const [agents, commands, skills, skillsExtras] = await Promise.all([
|
|
55
|
-
readMdDir(path.join(kitRoot, 'agents'), 'agent', { stubsOnly }),
|
|
56
|
-
readMdDir(path.join(kitRoot, 'commands'), 'command', { stubsOnly }),
|
|
57
|
-
readSkillsDir(path.join(kitRoot, 'skills'), { stubsOnly }),
|
|
58
|
-
readSkillsDir(path.join(kitRoot, 'skills-extras'), { stubsOnly }).catch(() => []),
|
|
59
|
-
]);
|
|
60
|
-
const value = { agents, commands, skills, skillsExtras, kitRoot, stubsOnly };
|
|
61
|
-
kitCache.set(cacheKey, { value, ts: Date.now() });
|
|
62
|
-
return value;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Read just enough bytes from the head of the file to capture the frontmatter.
|
|
66
|
-
// Returns the partial string. fs.open + fd.read avoids the OS pre-fetching the
|
|
67
|
-
// rest of the file (which fs.readFile would force).
|
|
68
|
-
async function readHead(absPath, n) {
|
|
69
|
-
const fd = await fs.open(absPath, 'r');
|
|
70
|
-
try {
|
|
71
|
-
const buf = Buffer.alloc(n);
|
|
72
|
-
const { bytesRead } = await fd.read(buf, 0, n, 0);
|
|
73
|
-
return buf.subarray(0, bytesRead).toString('utf8');
|
|
74
|
-
} finally {
|
|
75
|
-
await fd.close();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async function readMdDir(dir, kind, { stubsOnly = false } = {}) {
|
|
80
|
-
let entries;
|
|
81
|
-
try {
|
|
82
|
-
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
83
|
-
} catch {
|
|
84
|
-
return [];
|
|
85
|
-
}
|
|
86
|
-
const out = [];
|
|
87
|
-
for (const e of entries) {
|
|
88
|
-
if (!e.isFile() || !e.name.endsWith('.md')) continue;
|
|
89
|
-
const absPath = path.join(dir, e.name);
|
|
90
|
-
const raw = stubsOnly
|
|
91
|
-
? await readHead(absPath, STUB_READ_BYTES)
|
|
92
|
-
: await fs.readFile(absPath, 'utf8');
|
|
93
|
-
const { frontmatter, body } = splitFrontmatter(raw);
|
|
94
|
-
const item = {
|
|
95
|
-
kind,
|
|
96
|
-
name: e.name.replace(/\.md$/, ''),
|
|
97
|
-
absPath,
|
|
98
|
-
frontmatter,
|
|
99
|
-
frontmatterRaw: matchFrontmatterRaw(raw),
|
|
100
|
-
description: frontmatter?.description ?? firstNonEmptyLine(body),
|
|
101
|
-
};
|
|
102
|
-
if (!stubsOnly) {
|
|
103
|
-
item.body = body;
|
|
104
|
-
item.content = raw;
|
|
105
|
-
}
|
|
106
|
-
out.push(item);
|
|
107
|
-
}
|
|
108
|
-
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function readSkillsDir(dir, { stubsOnly = false } = {}) {
|
|
112
|
-
let entries;
|
|
113
|
-
try {
|
|
114
|
-
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
115
|
-
} catch {
|
|
116
|
-
return [];
|
|
117
|
-
}
|
|
118
|
-
const out = [];
|
|
119
|
-
for (const e of entries) {
|
|
120
|
-
if (!e.isDirectory()) continue;
|
|
121
|
-
const skillPath = path.join(dir, e.name, 'SKILL.md');
|
|
122
|
-
let raw;
|
|
123
|
-
try {
|
|
124
|
-
raw = stubsOnly
|
|
125
|
-
? await readHead(skillPath, STUB_READ_BYTES)
|
|
126
|
-
: await fs.readFile(skillPath, 'utf8');
|
|
127
|
-
} catch { continue; }
|
|
128
|
-
const { frontmatter, body } = splitFrontmatter(raw);
|
|
129
|
-
const item = {
|
|
130
|
-
kind: 'skill',
|
|
131
|
-
name: e.name,
|
|
132
|
-
absPath: skillPath,
|
|
133
|
-
dirPath: path.join(dir, e.name),
|
|
134
|
-
frontmatter,
|
|
135
|
-
frontmatterRaw: matchFrontmatterRaw(raw),
|
|
136
|
-
description: frontmatter?.description ?? firstNonEmptyLine(body),
|
|
137
|
-
};
|
|
138
|
-
if (!stubsOnly) {
|
|
139
|
-
item.body = body;
|
|
140
|
-
item.skillContent = raw;
|
|
141
|
-
}
|
|
142
|
-
out.push(item);
|
|
143
|
-
}
|
|
144
|
-
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// --- minimal YAML-ish frontmatter parser (no deps) ---
|
|
148
|
-
// Handles `key: value`, `key: >` multiline, but NOT nested objects/arrays.
|
|
149
|
-
// Good enough for our SKILL.md / agent.md headers.
|
|
150
|
-
|
|
151
|
-
function splitFrontmatter(raw) {
|
|
152
|
-
const m = raw.match(FRONTMATTER_SPLIT_RE);
|
|
153
|
-
if (!m) return { frontmatter: null, body: raw };
|
|
154
|
-
return { frontmatter: parseLooseYaml(m[1]), body: m[2] };
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function matchFrontmatterRaw(raw) {
|
|
158
|
-
const m = raw.match(FRONTMATTER_RAW_RE);
|
|
159
|
-
return m ? m[1] : '';
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function parseLooseYaml(text) {
|
|
163
|
-
const out = {};
|
|
164
|
-
const lines = text.split(/\r?\n/);
|
|
165
|
-
let i = 0;
|
|
166
|
-
while (i < lines.length) {
|
|
167
|
-
const line = lines[i];
|
|
168
|
-
const m = line.match(YAML_KEY_RE);
|
|
169
|
-
if (!m) { i++; continue; }
|
|
170
|
-
const key = m[1];
|
|
171
|
-
let val = m[2];
|
|
172
|
-
if (val === '>' || val === '|') {
|
|
173
|
-
// Multiline: collect indented lines
|
|
174
|
-
const collected = [];
|
|
175
|
-
i++;
|
|
176
|
-
while (i < lines.length && /^\s+/.test(lines[i])) {
|
|
177
|
-
collected.push(lines[i].replace(/^\s+/, ''));
|
|
178
|
-
i++;
|
|
179
|
-
}
|
|
180
|
-
out[key] = collected.join(' ').trim();
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
out[key] = val.trim().replace(/^["']|["']$/g, '');
|
|
184
|
-
i++;
|
|
185
|
-
}
|
|
186
|
-
return out;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function firstNonEmptyLine(body) {
|
|
190
|
-
for (const line of body.split(/\r?\n/)) {
|
|
191
|
-
const t = line.trim();
|
|
192
|
-
if (!t) continue; // blank
|
|
193
|
-
if (t.startsWith('#')) continue; // markdown heading
|
|
194
|
-
if (t.startsWith('<!--')) continue; // HTML comment (e.g. STUB_MARKER)
|
|
195
|
-
return t.slice(0, 200);
|
|
196
|
-
}
|
|
197
|
-
return '';
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// --- search helpers ---
|
|
201
|
-
|
|
202
|
-
export function searchKit(kit, query) {
|
|
203
|
-
const q = query.toLowerCase();
|
|
204
|
-
const all = [...kit.agents, ...kit.commands, ...kit.skills, ...kit.skillsExtras];
|
|
205
|
-
return all.filter(item =>
|
|
206
|
-
item.name.toLowerCase().includes(q) ||
|
|
207
|
-
(item.description ?? '').toLowerCase().includes(q)
|
|
208
|
-
).map(({ kind, name, description, absPath }) => ({ kind, name, description, absPath }));
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export function findItem(kit, kind, name) {
|
|
212
|
-
const buckets = { agent: kit.agents, command: kit.commands, skill: [...kit.skills, ...kit.skillsExtras] };
|
|
213
|
-
const b = buckets[kind];
|
|
214
|
-
if (!b) throw new Error(`Unknown kind: ${kind}`);
|
|
215
|
-
return b.find(x => x.name === name) ?? null;
|
|
216
|
-
}
|
|
1
|
+
// Read the canonical kit/ directory and return a structured index.
|
|
2
|
+
// Source of truth: kit/agents/*.md, kit/commands/*.md, kit/skills/*/SKILL.md
|
|
3
|
+
//
|
|
4
|
+
// Frontmatter is parsed loosely (no external dep) — we only need name & description.
|
|
5
|
+
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import fs from 'node:fs/promises';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
|
|
13
|
+
// PERF-02: Frontmatter regexes compiled once at module load (was being recompiled
|
|
14
|
+
// on every readMdDir / readSkillsDir entry — 60+ times per listKit call).
|
|
15
|
+
const FRONTMATTER_SPLIT_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
16
|
+
const FRONTMATTER_RAW_RE = /^(---\r?\n[\s\S]*?\r?\n---\r?\n?)/;
|
|
17
|
+
const YAML_KEY_RE = /^([A-Za-z0-9_-]+):\s*(.*)$/;
|
|
18
|
+
|
|
19
|
+
// Resolution order for the kit root (re-evaluated on each call so env-var
|
|
20
|
+
// overrides set after module load — e.g. by the CLI preAction hook — work):
|
|
21
|
+
// 1. explicit `kitRoot` opt passed by caller
|
|
22
|
+
// 2. KIT_MCP_KIT_ROOT env var (per-session override)
|
|
23
|
+
// 3. ./kit relative to this package (the bundled example kit)
|
|
24
|
+
export const BUNDLED_KIT_ROOT = path.resolve(__dirname, '../../kit');
|
|
25
|
+
export function resolveKitRoot(kitRoot) {
|
|
26
|
+
if (kitRoot) return path.resolve(kitRoot);
|
|
27
|
+
if (process.env.KIT_MCP_KIT_ROOT) return path.resolve(process.env.KIT_MCP_KIT_ROOT);
|
|
28
|
+
return BUNDLED_KIT_ROOT;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// PERF-01: TTL cache for listKit output. Repeated calls within KIT_CACHE_TTL_MS
|
|
32
|
+
// return the cached value — sync/reverse-sync/MCP list-* tools used to walk the
|
|
33
|
+
// disk on every invocation. Trade-off: callers that edit kit/ inside the same
|
|
34
|
+
// process may see stale data for up to 30s. Acceptable for MCP/CLI ergonomics.
|
|
35
|
+
const KIT_CACHE_TTL_MS = 30_000;
|
|
36
|
+
const kitCache = new Map(); // `${kitRoot}:${mode}` -> { value, ts }
|
|
37
|
+
|
|
38
|
+
// PERF-S1: when sync runs in mode=reference (default), the body/content of each
|
|
39
|
+
// kit file is never used — only frontmatter (name + description). Reading just
|
|
40
|
+
// the first STUB_READ_BYTES is enough for any frontmatter we'd ever produce and
|
|
41
|
+
// avoids loading 50 KB+ files (planner.md etc) from disk.
|
|
42
|
+
const STUB_READ_BYTES = 4096;
|
|
43
|
+
|
|
44
|
+
export function clearKitCache() { kitCache.clear(); }
|
|
45
|
+
|
|
46
|
+
export async function listKit(kitRoot, opts = {}) {
|
|
47
|
+
kitRoot = resolveKitRoot(kitRoot);
|
|
48
|
+
const stubsOnly = opts.stubsOnly === true;
|
|
49
|
+
const cacheKey = `${kitRoot}:${stubsOnly ? 'stubs' : 'full'}`;
|
|
50
|
+
const cached = kitCache.get(cacheKey);
|
|
51
|
+
if (cached && Date.now() - cached.ts < KIT_CACHE_TTL_MS) {
|
|
52
|
+
return cached.value;
|
|
53
|
+
}
|
|
54
|
+
const [agents, commands, skills, skillsExtras] = await Promise.all([
|
|
55
|
+
readMdDir(path.join(kitRoot, 'agents'), 'agent', { stubsOnly }),
|
|
56
|
+
readMdDir(path.join(kitRoot, 'commands'), 'command', { stubsOnly }),
|
|
57
|
+
readSkillsDir(path.join(kitRoot, 'skills'), { stubsOnly }),
|
|
58
|
+
readSkillsDir(path.join(kitRoot, 'skills-extras'), { stubsOnly }).catch(() => []),
|
|
59
|
+
]);
|
|
60
|
+
const value = { agents, commands, skills, skillsExtras, kitRoot, stubsOnly };
|
|
61
|
+
kitCache.set(cacheKey, { value, ts: Date.now() });
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Read just enough bytes from the head of the file to capture the frontmatter.
|
|
66
|
+
// Returns the partial string. fs.open + fd.read avoids the OS pre-fetching the
|
|
67
|
+
// rest of the file (which fs.readFile would force).
|
|
68
|
+
async function readHead(absPath, n) {
|
|
69
|
+
const fd = await fs.open(absPath, 'r');
|
|
70
|
+
try {
|
|
71
|
+
const buf = Buffer.alloc(n);
|
|
72
|
+
const { bytesRead } = await fd.read(buf, 0, n, 0);
|
|
73
|
+
return buf.subarray(0, bytesRead).toString('utf8');
|
|
74
|
+
} finally {
|
|
75
|
+
await fd.close();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function readMdDir(dir, kind, { stubsOnly = false } = {}) {
|
|
80
|
+
let entries;
|
|
81
|
+
try {
|
|
82
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
83
|
+
} catch {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
const out = [];
|
|
87
|
+
for (const e of entries) {
|
|
88
|
+
if (!e.isFile() || !e.name.endsWith('.md')) continue;
|
|
89
|
+
const absPath = path.join(dir, e.name);
|
|
90
|
+
const raw = stubsOnly
|
|
91
|
+
? await readHead(absPath, STUB_READ_BYTES)
|
|
92
|
+
: await fs.readFile(absPath, 'utf8');
|
|
93
|
+
const { frontmatter, body } = splitFrontmatter(raw);
|
|
94
|
+
const item = {
|
|
95
|
+
kind,
|
|
96
|
+
name: e.name.replace(/\.md$/, ''),
|
|
97
|
+
absPath,
|
|
98
|
+
frontmatter,
|
|
99
|
+
frontmatterRaw: matchFrontmatterRaw(raw),
|
|
100
|
+
description: frontmatter?.description ?? firstNonEmptyLine(body),
|
|
101
|
+
};
|
|
102
|
+
if (!stubsOnly) {
|
|
103
|
+
item.body = body;
|
|
104
|
+
item.content = raw;
|
|
105
|
+
}
|
|
106
|
+
out.push(item);
|
|
107
|
+
}
|
|
108
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function readSkillsDir(dir, { stubsOnly = false } = {}) {
|
|
112
|
+
let entries;
|
|
113
|
+
try {
|
|
114
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
115
|
+
} catch {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
const out = [];
|
|
119
|
+
for (const e of entries) {
|
|
120
|
+
if (!e.isDirectory()) continue;
|
|
121
|
+
const skillPath = path.join(dir, e.name, 'SKILL.md');
|
|
122
|
+
let raw;
|
|
123
|
+
try {
|
|
124
|
+
raw = stubsOnly
|
|
125
|
+
? await readHead(skillPath, STUB_READ_BYTES)
|
|
126
|
+
: await fs.readFile(skillPath, 'utf8');
|
|
127
|
+
} catch { continue; }
|
|
128
|
+
const { frontmatter, body } = splitFrontmatter(raw);
|
|
129
|
+
const item = {
|
|
130
|
+
kind: 'skill',
|
|
131
|
+
name: e.name,
|
|
132
|
+
absPath: skillPath,
|
|
133
|
+
dirPath: path.join(dir, e.name),
|
|
134
|
+
frontmatter,
|
|
135
|
+
frontmatterRaw: matchFrontmatterRaw(raw),
|
|
136
|
+
description: frontmatter?.description ?? firstNonEmptyLine(body),
|
|
137
|
+
};
|
|
138
|
+
if (!stubsOnly) {
|
|
139
|
+
item.body = body;
|
|
140
|
+
item.skillContent = raw;
|
|
141
|
+
}
|
|
142
|
+
out.push(item);
|
|
143
|
+
}
|
|
144
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// --- minimal YAML-ish frontmatter parser (no deps) ---
|
|
148
|
+
// Handles `key: value`, `key: >` multiline, but NOT nested objects/arrays.
|
|
149
|
+
// Good enough for our SKILL.md / agent.md headers.
|
|
150
|
+
|
|
151
|
+
function splitFrontmatter(raw) {
|
|
152
|
+
const m = raw.match(FRONTMATTER_SPLIT_RE);
|
|
153
|
+
if (!m) return { frontmatter: null, body: raw };
|
|
154
|
+
return { frontmatter: parseLooseYaml(m[1]), body: m[2] };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function matchFrontmatterRaw(raw) {
|
|
158
|
+
const m = raw.match(FRONTMATTER_RAW_RE);
|
|
159
|
+
return m ? m[1] : '';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function parseLooseYaml(text) {
|
|
163
|
+
const out = {};
|
|
164
|
+
const lines = text.split(/\r?\n/);
|
|
165
|
+
let i = 0;
|
|
166
|
+
while (i < lines.length) {
|
|
167
|
+
const line = lines[i];
|
|
168
|
+
const m = line.match(YAML_KEY_RE);
|
|
169
|
+
if (!m) { i++; continue; }
|
|
170
|
+
const key = m[1];
|
|
171
|
+
let val = m[2];
|
|
172
|
+
if (val === '>' || val === '|') {
|
|
173
|
+
// Multiline: collect indented lines
|
|
174
|
+
const collected = [];
|
|
175
|
+
i++;
|
|
176
|
+
while (i < lines.length && /^\s+/.test(lines[i])) {
|
|
177
|
+
collected.push(lines[i].replace(/^\s+/, ''));
|
|
178
|
+
i++;
|
|
179
|
+
}
|
|
180
|
+
out[key] = collected.join(' ').trim();
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
out[key] = val.trim().replace(/^["']|["']$/g, '');
|
|
184
|
+
i++;
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function firstNonEmptyLine(body) {
|
|
190
|
+
for (const line of body.split(/\r?\n/)) {
|
|
191
|
+
const t = line.trim();
|
|
192
|
+
if (!t) continue; // blank
|
|
193
|
+
if (t.startsWith('#')) continue; // markdown heading
|
|
194
|
+
if (t.startsWith('<!--')) continue; // HTML comment (e.g. STUB_MARKER)
|
|
195
|
+
return t.slice(0, 200);
|
|
196
|
+
}
|
|
197
|
+
return '';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// --- search helpers ---
|
|
201
|
+
|
|
202
|
+
export function searchKit(kit, query) {
|
|
203
|
+
const q = query.toLowerCase();
|
|
204
|
+
const all = [...kit.agents, ...kit.commands, ...kit.skills, ...kit.skillsExtras];
|
|
205
|
+
return all.filter(item =>
|
|
206
|
+
item.name.toLowerCase().includes(q) ||
|
|
207
|
+
(item.description ?? '').toLowerCase().includes(q)
|
|
208
|
+
).map(({ kind, name, description, absPath }) => ({ kind, name, description, absPath }));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function findItem(kit, kind, name) {
|
|
212
|
+
const buckets = { agent: kit.agents, command: kit.commands, skill: [...kit.skills, ...kit.skillsExtras] };
|
|
213
|
+
const b = buckets[kind];
|
|
214
|
+
if (!b) throw new Error(`Unknown kind: ${kind}`);
|
|
215
|
+
return b.find(x => x.name === name) ?? null;
|
|
216
|
+
}
|