@luanpdd/kit-mcp 1.0.0 → 1.2.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 CHANGED
@@ -6,6 +6,154 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) · Versioning:
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.2.0] - 2026-05-04
10
+
11
+ **GUI sidecar de acompanhamento.** Janela web localhost paralela mostra ao vivo (via Server-Sent Events) o que kit-mcp está fazendo enquanto sua IDE chama tools — `sync install`, `reverse-sync apply`, `gates run`. Sidecar é totalmente opt-in: quem não invoca `kit ui` continua com a experiência v1.1 idêntica.
12
+
13
+ ### Adicionado — Phase 11: Lock arquitetural
14
+ - ADR consolidado em `.planning/decisions.md` (porta 7100-7199, lockfile em `os.tmpdir()` keyed por sha1(projectRoot), idle 30min default, sem auth no v1.2 com mitigação compensatória)
15
+ - Threat model em `docs/sidecar-security.md`
16
+ - 2 audit gates novos no CI: stdout discipline em `src/ui/` (proíbe `console.log`/`process.stdout.write`) e dep budget (≤ baseline+1)
17
+
18
+ ### Adicionado — Phase 12: Fundações
19
+ - `src/ui/events.js` — schema de evento, validador puro, `makeEvent`, `newRunId`
20
+ - `src/ui/port.js` — `findFreePort` na faixa 7100-7199 com retry-loop
21
+ - `src/ui/lockfile.js` — `acquireLock` atômico via `O_EXCL`, `probeStale` via `process.kill(pid, 0)` + healthz HTTP
22
+
23
+ ### Adicionado — Phase 13: Servidor HTTP + SSE
24
+ - `src/ui/server.js` — http.Server nativo, bind 127.0.0.1 literal, 5 rotas (`/`, `/events` SSE, `/healthz`, `/state`, `/publish`, `/shutdown`)
25
+ - Heartbeat `: ping\n\n` cada 15s; reconnect auto via EventSource native + `retry: 3000`
26
+ - Ring buffer in-memory de 200 eventos (FIFO; sem persistência em disco)
27
+ - Cap de 32 conexões SSE; cleanup quádruplo (req+res × close+error)
28
+ - Idle shutdown 30min default (`--idle-ms 0` desabilita)
29
+ - Encerramento gracioso em SIGINT/SIGTERM com active sockets destruídos
30
+ - Validação de `Host` header (mitiga DNS rebinding) e `Origin` em endpoints non-GET
31
+ - `bin/ui.js` entry detached
32
+
33
+ ### Adicionado — Phase 14: UI estática single-file
34
+ - `src/ui/static/index.html` (~470 LOC) — vanilla DOM + EventSource, sem build step
35
+ - Lista cronológica + auto-scroll + `<details>` expand
36
+ - Badges coloridos por tipo (`run.start`, `run.end`, `tool_invocation`, `progress`, `milestone`, `error`, `shutdown`)
37
+ - Status conexão (CONNECTING/OPEN/CLOSED) + reconexão automática
38
+ - Filter por tipo (chips) + substring search
39
+ - Pause/resume com buffer + autoscroll toggle
40
+ - Dark mode automático via `prefers-color-scheme`
41
+ - Banner de shutdown PT-BR em CLOSED >5s ou evento `shutdown`
42
+ - CSP estrito (`default-src 'self'; ...; frame-ancestors 'none'`)
43
+
44
+ ### Adicionado — Phase 15: Publisher + wrapper + browser-open
45
+ - `src/ui/client.js` — `publish(event, {projectRoot})` fire-and-forget, cache TTL 5s, falha silenciosa em ECONNREFUSED
46
+ - `src/ui/wrapper.js` — `wrapProgressForUi(onProgress, ctx)` multiplexa terminal + sidecar; helpers `.done/.error/.emit`; `redactPath` central scrubando `$HOME → ~` e `projectRoot → <project>` em TODO payload
47
+ - `src/ui/browser.js` — wrapper sobre `open@11` com detection de headless (CI, DISPLAY, SSH, WSL, sandbox); fallback "imprime URL no stderr"
48
+ - Nova dep: `open@^11.0.0` (única adição; budget atingido em 6/6)
49
+
50
+ ### Adicionado — Phase 16: CLI integration
51
+ - `kit ui start` — sobe sidecar foreground (Ctrl+C mata); flags `--port`, `--idle-ms`, `--no-open`
52
+ - `kit ui stop` — POST /shutdown
53
+ - `kit ui status` — exibe pid, port, uptime, eventos, subscribers
54
+ - `kit ui open` — reabre browser na sidecar atual
55
+ - Auto-detect: `kit sync install` e `kit reverse-sync apply` checam lockfile e wrappam `onProgress` automaticamente quando sidecar está rodando
56
+ - Opt-out global via `--no-ui` flag ou `KIT_MCP_NO_UI=1` env var
57
+
58
+ ### Adicionado — Phase 17: MCP --auto-spawn
59
+ - `src/ui/auto-spawn.js` — `ensureSidecar({projectRoot})` checa lockfile + healthz; se ausente, spawna `bin/ui.js` em **detached** com `windowsHide: true` e `stdio: ['ignore', 'ignore', 'inherit']` (fecha stdout completamente — não pode poluir canal MCP do parent)
60
+ - 3 tools MCP ganham campo opcional `autoSpawn: boolean` no inputSchema:
61
+ - `sync` (action=install)
62
+ - `reverse-sync` (action=apply)
63
+ - `gates` (nova action `run`, com autoSpawn)
64
+ - Tools triviais (`kit`, `forensics`, `install`) **não** ganham autoSpawn — explicit-out por design
65
+
66
+ ### Adicionado — Phase 18: Hardening + release
67
+ - 3 hardening tests novos: kill -9 recovery, multi-publisher race, MCP stdio uncorrupted (validação rigorosa do REQ SEC-04 em produção)
68
+ - README seção "Live UI" com primeiros passos
69
+ - `npm pack --dry-run` valida que `src/ui/static/index.html` é incluído no tarball
70
+
71
+ ### Corrigido
72
+ - **REL-01 (bug pré-existente):** `kit --version` agora lê de `package.json` em vez de retornar string hardcoded `1.0.0`. Em v1.0/v1.1 o comando exibia versão errada — corrigido nesta release.
73
+
74
+ ### Stable API additions (1.x compatible)
75
+
76
+ A v1.0 commitment continua válida. Estas adições são parte do contrato:
77
+
78
+ - **MCP tool `sync` inputSchema:** campo opcional `autoSpawn: boolean` em action=install. Tools que não passam mantêm comportamento idêntico.
79
+ - **MCP tool `reverse-sync` inputSchema:** campo opcional `autoSpawn: boolean` em action=apply.
80
+ - **MCP tool `gates` inputSchema:** campo opcional `autoSpawn: boolean` E nova action `run` com `id`/`projectRoot`/`autoSpawn` campos.
81
+ - **CLI subgroup `kit ui`:** novo grupo com `start | stop | status | open` subcommands.
82
+ - **CLI flag `--no-ui` global** + env var `KIT_MCP_NO_UI=1` — opt-out do auto-detect de sidecar.
83
+ - **Stable runtime guarantee:** core (`syncTo`, `applyReverse`, `runGate`) é literalmente intocado. Wrapper de `onProgress` é montado APENAS no callsite (CLI handler ou MCP tool handler).
84
+
85
+ ### Migration
86
+
87
+ **Usuários v1.1 não precisam fazer nada.** Sidecar é estritamente opt-in.
88
+
89
+ Para experimentar a UI:
90
+ ```bash
91
+ # 1. Em um terminal:
92
+ kit ui start
93
+
94
+ # 2. Em outro (ou via Claude Code/Cursor):
95
+ kit sync install claude-code
96
+
97
+ # A janela mostra o progresso em tempo real.
98
+ ```
99
+
100
+ Para tools MCP, passe `autoSpawn: true` quando quiser auto-abrir:
101
+ ```jsonc
102
+ { "tool": "sync", "arguments": { "action": "install", "target": "claude-code", "autoSpawn": true } }
103
+ ```
104
+
105
+ ### Threat model resumido
106
+
107
+ Sidecar é **localhost only**, single-user, dev workstation. Sem auth (mitigado por bind 127.0.0.1 + Host/Origin check + CSP estrito + path scrubbing). Sem persistência. Sem TLS (loopback). Detalhes em [`docs/sidecar-security.md`](docs/sidecar-security.md).
108
+
109
+ ## [1.1.0] - 2026-05-03
110
+
111
+ **Visual feedback in the terminal.** Running `kit ...` now prints colored tables, progress bars, summary panels and interactive selectors instead of the raw JSON-to-stdout default of v1.0. Programmatic consumers add `--json` to restore the previous behavior.
112
+
113
+ ### Added — Phase 6: UI primitives
114
+ - `src/core/ui.js` — single module exposing `c` (color helpers), `icons`, `spinner`, `progress`, `select`, `confirm`, `summary`. Respects `NO_COLOR`, `FORCE_COLOR`, `process.stdout.isTTY`. Animations write to stderr so stdout stays clean for `--json` piping.
115
+ - Deps: `picocolors` (~3KB, zero subdeps) and `@inquirer/prompts` (modular — only `select`+`confirm` imported).
116
+
117
+ ### Added — Phase 7: `--json` flag, default human
118
+ - `--json` global flag preserves v1.0's JSON-to-stdout behavior for programmatic consumers.
119
+ - Without `--json`: every subcommand renders a human-readable table or summary panel via `src/cli/render.js`.
120
+ - `kit get` is unchanged (still raw, cat-like).
121
+
122
+ ### Added — Phase 8: Progress + spinner
123
+ - `syncTo` and `applyReverse` accept an `opts.onProgress({ phase, current, total, label })` callback. Default no-op preserves backward compat.
124
+ - CLI wraps long ops in `withProgress(label, total, fn)` and short ops in `withSpinner(text, fn)`. TTY animates; pipes/CI emit linear status text (`10%, 20%, ...`).
125
+
126
+ ### Added — Phase 9: Interactive selectors + diff confirm
127
+ - `install write [target]` and `sync install [target]` — when target argument is omitted in TTY mode, opens a select prompt listing all 8 IDEs with labels.
128
+ - `install write` always previews the JSON/TOML to be written and asks `Apply these changes? (y/N)` before applying. `--yes` or `--json` bypasses the prompt for CI/programmatic use.
129
+ - In non-TTY mode without target: exits with a helpful message ("pass the value as a flag instead").
130
+
131
+ ### Stable API additions (1.x compatible)
132
+
133
+ The 1.0 commitment is unchanged. These additions become part of the contract:
134
+
135
+ - **`--json` global flag.** Behavior locked: JSON-to-stdout, no ANSI codes, no progress on stderr, prompts replaced by descriptive errors.
136
+ - **`onProgress` callback signature** on `syncTo` and `applyReverse`: `({ phase, current, total, label }) => void`. Adding optional fields is non-breaking.
137
+ - **Interactive selectors fall back to errors in non-TTY**, not to defaults — programs MUST pass the target as argument or use `--json`.
138
+
139
+ ### Migration
140
+
141
+ Programs and scripts that piped `kit ... | jq` need to add `--json` explicitly:
142
+ ```bash
143
+ # Before (v1.0):
144
+ kit list-agents | jq '.[].name'
145
+
146
+ # After (v1.1):
147
+ kit list-agents --json | jq '.[].name'
148
+ ```
149
+
150
+ Interactive shell users get the new visual output automatically — no flags needed.
151
+
152
+ ### Tests
153
+ - `test/unit/ui.test.js` — 6 new tests covering `summary` rendering, `NO_COLOR` honored, icons set.
154
+ - `test/integration/cli-roundtrip.test.js` — 4 new tests covering `--json` opt-in, default human output, selector fallback in non-TTY for `install write` / `sync install`.
155
+ - Total: 49 unit + 9 integration = **58 tests** in ~4s. CI verde 6/6 (Ubuntu/macOS/Windows × Node 20/22).
156
+
9
157
  ## [1.0.0] - 2026-05-03
10
158
 
11
159
  **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.
@@ -219,7 +367,8 @@ npx -y @luanpdd/kit-mcp sync install claude-code --project-root .
219
367
  - CLI mirror of all MCP tools.
220
368
  - `install` command that registers kit-mcp into an IDE's MCP config (JSON for Claude/Cursor/Gemini/Windsurf, TOML for Codex).
221
369
 
222
- [Unreleased]: https://github.com/luanpdd/kit-mcp/compare/v1.0.0...HEAD
370
+ [Unreleased]: https://github.com/luanpdd/kit-mcp/compare/v1.1.0...HEAD
371
+ [1.1.0]: https://github.com/luanpdd/kit-mcp/compare/v1.0.0...v1.1.0
223
372
  [1.0.0]: https://github.com/luanpdd/kit-mcp/compare/v0.5.0...v1.0.0
224
373
  [0.5.0]: https://github.com/luanpdd/kit-mcp/compare/v0.4.1...v0.5.0
225
374
  [0.4.1]: https://github.com/luanpdd/kit-mcp/compare/v0.4.0...v0.4.1
package/README.md CHANGED
@@ -55,7 +55,7 @@ kit-mcp/
55
55
  └── README.md ← you are here
56
56
  ```
57
57
 
58
- **Lines of source code:** ~1100. **Runtime dependencies:** 3 (`@modelcontextprotocol/sdk`, `commander`, `chokidar`). **Build step:** none — plain ESM Node.js 20+.
58
+ **Lines of source code:** ~1300. **Runtime dependencies:** 5 (`@modelcontextprotocol/sdk`, `commander`, `chokidar`, `picocolors`, `@inquirer/prompts`). **Build step:** none — plain ESM Node.js 20+.
59
59
 
60
60
  ### About the bundled workflow
61
61
 
@@ -128,7 +128,17 @@ For other IDEs, swap `claude-code` for `cursor`, `codex`, `gemini-cli`, `windsur
128
128
 
129
129
  ## CLI reference
130
130
 
131
- The CLI mirrors the MCP tools 1:1. Output is always JSON to stdout. The global `--kit-root` flag overrides the kit source for any subcommand.
131
+ The CLI mirrors the MCP tools 1:1. **By default the CLI prints colored, human-readable tables and summary panels.** Add `--json` to restore raw JSON-to-stdout (machine-readable, the default in v1.0). The global `--kit-root` flag overrides the kit source for any subcommand.
132
+
133
+ ```bash
134
+ kit list-agents # human: colored table, name + description
135
+ kit list-agents --json # machine: JSON array
136
+
137
+ kit sync install claude-code # human: progress bar + summary panel
138
+ kit sync install claude-code --json # machine: full result object
139
+ ```
140
+
141
+ In non-TTY mode (pipes, CI), animations degrade to linear status lines automatically. `NO_COLOR=1` disables colors entirely; `FORCE_COLOR=1` forces them on even in pipes.
132
142
 
133
143
  ### `kit kit ...` — browse the kit
134
144
 
@@ -182,8 +192,12 @@ kit install dry-run claude-code --scope user --via npx # preview the JSON
182
192
  kit install write claude-code --scope user --via npx # portable: uses `npx @luanpdd/kit-mcp`
183
193
  kit install write claude-code --scope project --via local # local clone: uses ./bin/mcp.js absolute path
184
194
  kit install write claude-code --scope user --via global # assumes `npm install -g @luanpdd/kit-mcp`
195
+ kit install write # no target: opens an interactive selector (TTY)
196
+ kit install write claude-code --yes # CI: skip the confirm prompt
185
197
  ```
186
198
 
199
+ Since v1.1, `install write` always **previews** the JSON/TOML it's about to write and asks you to confirm. Pass `--yes` (CI mode) or `--json` to bypass the prompt. Without a target argument in TTY mode, you get an arrow-key selector listing all 8 IDEs.
200
+
187
201
  `--via` decides how the IDE will invoke the server:
188
202
 
189
203
  | Mode | Command in IDE config | When to use |
@@ -258,6 +272,38 @@ kit forensics load-replay <id> --project-root .
258
272
 
259
273
  The proposal is always saved to `.planning/learnings/{agent}.proposal.md` first; the canonical is only modified after explicit confirmation (or `--apply`). MCP `forensics.reflect` never auto-applies.
260
274
 
275
+ ### `kit ui ...` — live process viewer (sidecar) — _new in 1.2_
276
+
277
+ A tiny localhost web app that streams what kit-mcp is doing while your IDE drives it. Strictly opt-in: ignore it and v1.1 behavior is unchanged.
278
+
279
+ ```bash
280
+ # In one terminal — keeps running until Ctrl+C
281
+ kit ui start
282
+
283
+ # In another terminal (or via Claude Code / Cursor) — runs as before, but events
284
+ # are now also broadcast to the sidecar window
285
+ kit sync install claude-code
286
+
287
+ # Tools too: pass autoSpawn:true on the MCP side, or just `kit ui start` first
288
+ kit ui status
289
+ kit ui stop
290
+ ```
291
+
292
+ What you get:
293
+
294
+ - A single browser tab at `http://127.0.0.1:7100` (or the next free port up to 7199)
295
+ - Live event stream over Server-Sent Events — `tool_invocation`, `progress`, `error`, `milestone`, `run.start`, `run.end`
296
+ - Filters by event type and substring; pause/resume; auto-scroll; dark mode tracks the OS
297
+ - The sidecar shuts itself down after 30 minutes of idle; `--idle-ms 0` disables that
298
+
299
+ **Auto-spawn from MCP tools.** Pass `autoSpawn: true` in the inputSchema of `sync` (action=install), `reverse-sync` (action=apply), or `gates` (action=run). The MCP server will spawn `bin/ui.js` detached, wait for it to come online, open your default browser, and stream that tool's progress. Trivial tools (`kit list-*`, `forensics`, `install`) deliberately don't accept `autoSpawn` — the overhead isn't worth it.
300
+
301
+ **Opt-out always available.** From the CLI: pass `--no-ui` or set `KIT_MCP_NO_UI=1`. The sidecar is never started behind your back; it's only used when a lockfile is already present (someone ran `kit ui start` or `autoSpawn: true`).
302
+
303
+ **Security model.** Sidecar binds to `127.0.0.1` literally — never `0.0.0.0`, never `localhost` (which resolves to `::1` on Windows). Every route validates the `Host` header to mitigate DNS rebinding. CSP is strict (`default-src 'self'; …; frame-ancestors 'none'`). Paths in payloads are scrubbed (`$HOME → ~`, `<projectRoot> → <project>`) so screenshots don't leak directory structure. No persistence, no TLS, no auth — single-user dev workstation only. Full threat model in [`docs/sidecar-security.md`](docs/sidecar-security.md).
304
+
305
+ **First-run quirks.** Windows Defender / macOS firewall may prompt the first time `kit ui start` binds. Approving "Private networks" is enough — the server doesn't accept anything from outside loopback regardless. On WSL, `kit ui start` opens the URL in the Windows host browser via `wslview`. In CI / SSH / `TERM=dumb`, browser launch is suppressed and the URL is printed to stderr instead.
306
+
261
307
  ---
262
308
 
263
309
  ## MCP usage
package/bin/ui.js ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ // bin/ui.js — entry point for the sidecar HTTP server.
3
+ //
4
+ // Used both directly (when the user runs `kit ui start`) and as the spawn target
5
+ // when `--auto-spawn` is enabled on an MCP tool.
6
+ //
7
+ // Logging discipline: all output goes to stderr. stdout is reserved so that
8
+ // callers can pipe `node bin/ui.js | jq` without UI server chatter contaminating
9
+ // data streams (and so that this file can never poison an MCP stdio channel).
10
+
11
+ import process from 'node:process';
12
+ import { createServer } from '../src/ui/server.js';
13
+
14
+ function parseArgs(argv) {
15
+ const args = { projectRoot: process.cwd(), port: undefined, idleMs: undefined };
16
+ for (let i = 0; i < argv.length; i += 1) {
17
+ const a = argv[i];
18
+ if (a === '--project-root' && argv[i + 1]) { args.projectRoot = argv[i + 1]; i += 1; }
19
+ else if (a === '--port' && argv[i + 1]) { args.port = Number(argv[i + 1]); i += 1; }
20
+ else if (a === '--idle-ms' && argv[i + 1]) { args.idleMs = Number(argv[i + 1]); i += 1; }
21
+ else if (a === '--version') { args.printVersion = true; }
22
+ else if (a === '--help' || a === '-h') { args.help = true; }
23
+ }
24
+ return args;
25
+ }
26
+
27
+ const args = parseArgs(process.argv.slice(2));
28
+
29
+ if (args.help) {
30
+ process.stderr.write([
31
+ 'kit-mcp sidecar entry — usually invoked via `kit ui start`',
32
+ '',
33
+ 'Usage: node bin/ui.js [options]',
34
+ ' --project-root <path> project root for lockfile keying (default: cwd)',
35
+ ' --port <n> bind to a specific port (default: auto-pick 7100-7199)',
36
+ ' --idle-ms <ms> idle shutdown timeout (default: 1800000 = 30min; 0 = never)',
37
+ ' --version print version and exit',
38
+ ' --help this text',
39
+ '',
40
+ ].join('\n'));
41
+ process.exit(0);
42
+ }
43
+
44
+ let pkgVersion = null;
45
+ try {
46
+ const { default: pkg } = await import('../package.json', { with: { type: 'json' } });
47
+ pkgVersion = pkg.version;
48
+ } catch {
49
+ // ok — version may not be available in some packaged contexts
50
+ }
51
+
52
+ if (args.printVersion) {
53
+ process.stderr.write(`${pkgVersion ?? 'unknown'}\n`);
54
+ process.exit(0);
55
+ }
56
+
57
+ const server = createServer({
58
+ projectRoot: args.projectRoot,
59
+ version: pkgVersion,
60
+ idleMs: args.idleMs,
61
+ });
62
+
63
+ try {
64
+ const { port } = await server.start({ port: args.port });
65
+ process.stderr.write(`[kit-mcp ui] listening on http://127.0.0.1:${port}/\n`);
66
+ process.stderr.write(`[kit-mcp ui] project: ${args.projectRoot}\n`);
67
+ } catch (err) {
68
+ if (err.code === 'ELIVE') {
69
+ process.stderr.write(`[kit-mcp ui] sidecar already running for this project (pid=${err.lock?.pid}, port=${err.lock?.port})\n`);
70
+ process.exit(2);
71
+ }
72
+ process.stderr.write(`[kit-mcp ui] failed to start: ${err.message}\n`);
73
+ process.exit(1);
74
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luanpdd/kit-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.2.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": {
@@ -47,8 +47,11 @@
47
47
  "test:all": "node test/run.mjs test"
48
48
  },
49
49
  "dependencies": {
50
+ "@inquirer/prompts": "^8.4.2",
50
51
  "@modelcontextprotocol/sdk": "^1.0.0",
51
52
  "chokidar": "^5.0.0",
52
- "commander": "^12.1.0"
53
+ "commander": "^12.1.0",
54
+ "open": "^11.0.0",
55
+ "picocolors": "^1.1.1"
53
56
  }
54
57
  }