claude-crap 0.4.7 → 0.4.8

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
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.8] - 2026-04-19
9
+
10
+ ### Fixed
11
+
12
+ - **Dashboard port thrash when multiple MCP launchers boot concurrently.**
13
+ Claude Code can spawn several MCP server instances against the same
14
+ workspace (duplicate plugin registrations, stale sessions, etc.).
15
+ The previous `killStaleDashboard` implementation sent SIGTERM to the
16
+ prior pidfile owner on every boot so it could reclaim port 5117 —
17
+ with N concurrent launchers this became a loop where each instance
18
+ killed the last, the pidfile churned, and most instances ended up
19
+ with `dashboard = null`. `score_project` then returned
20
+ `dashboardUrl: null` to the user even though a dashboard was usually
21
+ reachable on 5117. `startDashboard` now **adopts** a healthy pidfile
22
+ owner (`/api/health` probe with a 500 ms timeout) instead of killing
23
+ it, and only terminates zombies (PID alive, port unresponsive).
24
+ Losing an `EADDRINUSE` race also falls back to adoption rather than
25
+ throwing, so two launchers starting in the same millisecond still
26
+ converge cleanly. `DashboardHandle` gained a read-only
27
+ `adopted: boolean` flag; adopted handles have a no-op `close()` so
28
+ one MCP shutdown cannot strand its siblings. Covered by a new
29
+ 29-assertion suite in `src/tests/dashboard-adoption.test.ts` (6
30
+ characterization + 23 edge cases: adoption happy path, zombie
31
+ detection, concurrent boot race, pidfile hygiene, owner/adopter
32
+ close semantics). (`src/dashboard/server.ts`.)
33
+
8
34
  ## [0.4.7] - 2026-04-18
9
35
 
10
36
  ### Fixed
@@ -54,9 +54,15 @@ export interface StartDashboardOptions {
54
54
  /**
55
55
  * Handle returned by {@link startDashboard}. Use `url` to build the
56
56
  * link the user clicks; call `close()` during shutdown.
57
+ *
58
+ * `adopted === true` means another claude-crap process already owned
59
+ * the dashboard port when we booted, and we are piggy-backing on its
60
+ * HTTP server. Adopted handles have a no-op `close()` because tearing
61
+ * down the Fastify instance would strand the other MCP servers.
57
62
  */
58
63
  export interface DashboardHandle {
59
64
  readonly url: string;
65
+ readonly adopted: boolean;
60
66
  close(): Promise<void>;
61
67
  }
62
68
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAQH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAIrE;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,2CAA2C;IAC3C,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,2EAA2E;IAC3E,QAAQ,CAAC,sBAAsB,EAAE,sBAAsB,CAAC;IACxD,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC;IACtC,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC1C;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA6G7F"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAQH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAIrE;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,2CAA2C;IAC3C,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,2EAA2E;IAC3E,QAAQ,CAAC,sBAAsB,EAAE,sBAAsB,CAAC;IACxD,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC;IACtC,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC1C;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CAsJ7F"}
@@ -41,6 +41,24 @@ import { buildFileDetail } from "./file-detail.js";
41
41
  */
42
42
  export async function startDashboard(options) {
43
43
  const { config, sarifStore, workspaceStatsProvider, logger } = options;
44
+ const pidFilePath = resolvePidFilePath(config);
45
+ // Adopt-don't-steal: if a prior MCP server is already serving the
46
+ // dashboard on this port AND is healthy, piggy-back on it instead of
47
+ // killing it. This is what keeps N concurrent launchers from
48
+ // thrashing the port in an endless SIGTERM loop.
49
+ const adoption = await tryAdoptExisting(pidFilePath, config.dashboardPort, logger);
50
+ if (adoption) {
51
+ logger.info({ url: adoption.url, ownerPid: adoption.pid, port: config.dashboardPort }, "adopted existing claude-crap dashboard");
52
+ return {
53
+ url: adoption.url,
54
+ adopted: true,
55
+ async close() {
56
+ // No-op: we never bound a socket of our own, so there is
57
+ // nothing to release. Removing the pidfile here would make the
58
+ // owner's `close()` race with our cleanup.
59
+ },
60
+ };
61
+ }
44
62
  // Resolve the public/ directory. After `npm run build` the compiled
45
63
  // server lives in `dist/dashboard/server.js`, but we keep the static
46
64
  // SPA assets in `src/dashboard/public/` so we don't need a postbuild
@@ -118,18 +136,36 @@ export async function startDashboard(options) {
118
136
  fastify.get("/", async (_request, reply) => {
119
137
  return reply.sendFile("index.html");
120
138
  });
121
- // Kill any stale dashboard from a previous session so we always
122
- // bind to the configured port. This mirrors claude-mem's PID file
123
- // pattern: write a PID file when alive, check + kill on next boot.
124
- const pidFilePath = resolvePidFilePath(config);
125
- await killStaleDashboard(pidFilePath, config.dashboardPort, logger);
126
- await fastify.listen({ port: config.dashboardPort, host: "127.0.0.1" });
139
+ // The pidfile was either missing, stale, or pointed at a zombie —
140
+ // `tryAdoptExisting` has already cleaned it up. Try to bind. If we
141
+ // lose a race against another launcher that bound between our probe
142
+ // and our listen, fall back to adoption instead of failing.
143
+ try {
144
+ await fastify.listen({ port: config.dashboardPort, host: "127.0.0.1" });
145
+ }
146
+ catch (err) {
147
+ const code = err.code;
148
+ if (code === "EADDRINUSE") {
149
+ await fastify.close().catch(() => { });
150
+ const raceAdoption = await tryAdoptExisting(pidFilePath, config.dashboardPort, logger);
151
+ if (raceAdoption) {
152
+ logger.info({ url: raceAdoption.url, ownerPid: raceAdoption.pid, port: config.dashboardPort }, "dashboard bind lost race, adopted concurrent owner");
153
+ return {
154
+ url: raceAdoption.url,
155
+ adopted: true,
156
+ async close() { },
157
+ };
158
+ }
159
+ }
160
+ throw err;
161
+ }
127
162
  const url = `http://127.0.0.1:${config.dashboardPort}`;
128
163
  logger.info({ url, publicRoot }, "claude-crap dashboard listening");
129
- // Write PID file so the next session can find and kill us.
164
+ // Write PID file so sibling MCP servers can find us and adopt.
130
165
  writePidFile(pidFilePath, config.dashboardPort);
131
166
  return {
132
167
  url,
168
+ adopted: false,
133
169
  async close() {
134
170
  removePidFile(pidFilePath);
135
171
  await fastify.close();
@@ -233,61 +269,93 @@ function isPidAlive(pid) {
233
269
  }
234
270
  }
235
271
  /**
236
- * Read the PID file, kill any stale dashboard process, and free the
237
- * port so the current session can bind to it. This is the key
238
- * difference from the port-fallback approach: instead of drifting to
239
- * 5118, 5119, etc., we reclaim the configured port every time.
272
+ * Probe an existing dashboard and decide whether the current process
273
+ * can adopt it instead of binding its own Fastify server.
274
+ *
275
+ * Returns `{ url, pid }` only when all four conditions hold:
276
+ * 1. A pidfile exists and parses as JSON.
277
+ * 2. The recorded PID is still alive (signal-0 probe).
278
+ * 3. The pidfile's recorded port matches the configured port.
279
+ * 4. A GET on `/api/health` responds within ~500ms.
240
280
  *
241
- * @param pidFilePath Absolute path to `dashboard.pid`.
242
- * @param port The configured dashboard port.
243
- * @param logger Pino logger for diagnostics.
281
+ * Returns `null` in every other case, but with a side-effect that makes
282
+ * the call-site's next step obvious:
283
+ * - Missing / corrupt / dead-PID / port-mismatch → pidfile is removed
284
+ * so the caller can bind cleanly.
285
+ * - Zombie (PID alive, port unresponsive) → stale owner is
286
+ * SIGKILL'd and the pidfile is removed. This is the one case where
287
+ * we still have to kill something, because the socket belongs to a
288
+ * process that is not talking HTTP anymore.
244
289
  */
245
- async function killStaleDashboard(pidFilePath, port, logger) {
290
+ async function tryAdoptExisting(pidFilePath, port, logger) {
246
291
  if (!existsSync(pidFilePath))
247
- return;
292
+ return null;
248
293
  let stale;
249
294
  try {
250
295
  stale = JSON.parse(readFileSync(pidFilePath, "utf8"));
251
296
  }
252
297
  catch {
253
- // Corrupted PID file remove it and move on.
298
+ logger.info({ pidFilePath }, "corrupt dashboard pidfile, removing");
254
299
  removePidFile(pidFilePath);
255
- return;
300
+ return null;
256
301
  }
257
302
  if (!isPidAlive(stale.pid)) {
258
- logger.info({ stalePid: stale.pid }, "stale dashboard PID file found (process dead), removing");
303
+ logger.info({ stalePid: stale.pid }, "stale dashboard pidfile (process dead), removing");
259
304
  removePidFile(pidFilePath);
260
- return;
305
+ return null;
306
+ }
307
+ if (stale.port !== port) {
308
+ // The recorded owner is on a different port than we want. Don't
309
+ // adopt it, don't kill it — just treat the pidfile as unrelated.
310
+ logger.info({ stalePort: stale.port, wantedPort: port }, "dashboard pidfile points at different port, ignoring");
311
+ removePidFile(pidFilePath);
312
+ return null;
313
+ }
314
+ const healthy = await probeDashboardHealth(port);
315
+ if (healthy) {
316
+ return { url: `http://127.0.0.1:${port}`, pid: stale.pid };
261
317
  }
262
- // Process is alive kill it so we can reclaim the port.
263
- logger.info({ stalePid: stale.pid, port: stale.port, startedAt: stale.startedAt }, "killing stale dashboard process from previous session");
318
+ // Zombie: PID is alive but not serving HTTP. Most likely the owner
319
+ // crashed mid-init or is stuck. Terminate it so we can take over.
320
+ logger.warn({ stalePid: stale.pid, port }, "dashboard pidfile owner is unresponsive, terminating");
264
321
  try {
265
322
  process.kill(stale.pid, "SIGTERM");
266
323
  }
267
324
  catch {
268
- // Permission denied or already gone — remove PID file either way.
269
- removePidFile(pidFilePath);
270
- return;
325
+ /* already gone */
271
326
  }
272
- // Wait up to 3 seconds for the process to exit.
273
327
  for (let i = 0; i < 30; i++) {
274
328
  if (!isPidAlive(stale.pid))
275
329
  break;
276
330
  await new Promise((r) => setTimeout(r, 100));
277
331
  }
278
- // If still alive after 3s, escalate to SIGKILL.
279
332
  if (isPidAlive(stale.pid)) {
280
333
  try {
281
334
  process.kill(stale.pid, "SIGKILL");
282
335
  }
283
- catch {
284
- /* best effort */
285
- }
336
+ catch { /* best effort */ }
286
337
  await new Promise((r) => setTimeout(r, 200));
287
338
  }
288
339
  removePidFile(pidFilePath);
289
- // Give the OS a moment to release the TCP port after the process dies.
340
+ // Let the OS release the TCP port before the caller tries to bind.
290
341
  await new Promise((r) => setTimeout(r, 300));
342
+ return null;
343
+ }
344
+ /**
345
+ * Low-latency health probe. Resolves `true` when the dashboard replies
346
+ * 2xx to `/api/health` within 500ms, `false` on any other outcome
347
+ * (timeout, connection refused, 5xx, etc.).
348
+ */
349
+ async function probeDashboardHealth(port) {
350
+ try {
351
+ const res = await fetch(`http://127.0.0.1:${port}/api/health`, {
352
+ signal: AbortSignal.timeout(500),
353
+ });
354
+ return res.ok;
355
+ }
356
+ catch {
357
+ return false;
358
+ }
291
359
  }
292
360
  // Directory exclusions are now centralized in src/shared/exclusions.ts.
293
361
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Shared setup utilities for dashboard-adoption tests.
3
+ *
4
+ * Keeps the main test file focused on assertions rather than
5
+ * boilerplate, while staying small enough that each helper is
6
+ * easy to read in isolation.
7
+ *
8
+ * @module tests/helpers/dashboard-test-helpers
9
+ */
10
+ import { type Logger } from "pino";
11
+ import type { CrapConfig } from "../../config.js";
12
+ import { SarifStore } from "../../sarif/sarif-store.js";
13
+ import type { StartDashboardOptions } from "../../dashboard/server.js";
14
+ /**
15
+ * A pino logger that discards all output. Passing this to
16
+ * `startDashboard` keeps test runs noise-free while still satisfying
17
+ * the `Logger` type constraint.
18
+ */
19
+ export declare function silentLogger(): Logger;
20
+ /**
21
+ * Resolve a random TCP port in the 6000–6999 range that is not bound
22
+ * at the moment of the call. The OS chooses the exact port by binding
23
+ * to port 0 then immediately releasing the socket; there is a tiny
24
+ * TOCTOU window, but in practice it is negligible for unit tests that
25
+ * run serially.
26
+ *
27
+ * Staying in the 6000–6999 range keeps tests away from the production
28
+ * dashboard port (5117) and from common well-known service ports.
29
+ */
30
+ export declare function findFreePort(): Promise<number>;
31
+ /**
32
+ * Context returned by {@link makeWorkspace}. Call `cleanup()` inside
33
+ * the `after` hook of each test to remove the temporary directory.
34
+ */
35
+ export interface WorkspaceContext {
36
+ /** Absolute path to the isolated temporary workspace root. */
37
+ pluginRoot: string;
38
+ /** Absolute path to `.claude-crap/dashboard.pid` inside the workspace. */
39
+ pidFilePath: string;
40
+ /** Teardown — removes the entire temp directory tree. */
41
+ cleanup(): Promise<void>;
42
+ }
43
+ /**
44
+ * Create an isolated temporary workspace directory and ensure the
45
+ * `.claude-crap/` subdirectory exists so pidfile writes always succeed.
46
+ * Returns paths and a cleanup function.
47
+ */
48
+ export declare function makeWorkspace(): Promise<WorkspaceContext>;
49
+ /**
50
+ * Minimal {@link CrapConfig} suitable for a test invocation of
51
+ * `startDashboard`. Every field that the function actually reads is
52
+ * supplied with a sane default; callers can override `dashboardPort`
53
+ * and `pluginRoot` as needed.
54
+ */
55
+ export declare function makeConfig(pluginRoot: string, dashboardPort: number): CrapConfig;
56
+ /**
57
+ * Build an empty {@link SarifStore} rooted at `pluginRoot`. No file is
58
+ * written to disk; `loadLatest()` is intentionally NOT called here —
59
+ * the tests that need a pre-seeded store will do so themselves.
60
+ */
61
+ export declare function makeSarifStore(pluginRoot: string): SarifStore;
62
+ /**
63
+ * Bundle a complete {@link StartDashboardOptions} object from a
64
+ * workspace context + port. Used by tests that call `startDashboard`
65
+ * directly.
66
+ */
67
+ export declare function makeOptions(pluginRoot: string, dashboardPort: number): StartDashboardOptions;
68
+ /**
69
+ * Shape written by the production `writePidFile` implementation.
70
+ * Duplicated here so tests can write synthetic pidfiles without
71
+ * importing a private function.
72
+ */
73
+ export interface DashboardPidFile {
74
+ pid: number;
75
+ port: number;
76
+ startedAt: string;
77
+ }
78
+ /**
79
+ * Write a synthetic pidfile to `path`. Useful for characterization and
80
+ * edge-case tests that need the file to exist before `startDashboard`
81
+ * runs.
82
+ */
83
+ export declare function writePidFile(path: string, pid: number, port: number): Promise<void>;
84
+ /**
85
+ * Read and parse the pidfile at `path`. Returns `null` when the file
86
+ * is absent or not valid JSON, so assertion sites can use a plain
87
+ * null-check instead of a try/catch.
88
+ */
89
+ export declare function readPidFile(path: string): Promise<DashboardPidFile | null>;
90
+ /**
91
+ * Return `true` when the file at `path` exists on disk right now.
92
+ */
93
+ export declare function fileExists(path: string): Promise<boolean>;
94
+ //# sourceMappingURL=dashboard-test-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-test-helpers.d.ts","sourceRoot":"","sources":["../../../src/tests/helpers/dashboard-test-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAa,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAEzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAIvE;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAID;;;;;;;;;GASG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAqB9C;AAID;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAU/D;AAID;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,UAAU,CAUhF;AAID;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAK7D;AAID;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,qBAAqB,CAO5F;AAID;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOzF;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAQhF;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ/D"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Shared setup utilities for dashboard-adoption tests.
3
+ *
4
+ * Keeps the main test file focused on assertions rather than
5
+ * boilerplate, while staying small enough that each helper is
6
+ * easy to read in isolation.
7
+ *
8
+ * @module tests/helpers/dashboard-test-helpers
9
+ */
10
+ import { createServer } from "node:net";
11
+ import { mkdtemp, rm, mkdir, writeFile } from "node:fs/promises";
12
+ import { join } from "node:path";
13
+ import { tmpdir } from "node:os";
14
+ import pino from "pino";
15
+ import { SarifStore } from "../../sarif/sarif-store.js";
16
+ // ── Logger ────────────────────────────────────────────────────────────────────
17
+ /**
18
+ * A pino logger that discards all output. Passing this to
19
+ * `startDashboard` keeps test runs noise-free while still satisfying
20
+ * the `Logger` type constraint.
21
+ */
22
+ export function silentLogger() {
23
+ return pino({ level: "silent" });
24
+ }
25
+ // ── Port allocation ───────────────────────────────────────────────────────────
26
+ /**
27
+ * Resolve a random TCP port in the 6000–6999 range that is not bound
28
+ * at the moment of the call. The OS chooses the exact port by binding
29
+ * to port 0 then immediately releasing the socket; there is a tiny
30
+ * TOCTOU window, but in practice it is negligible for unit tests that
31
+ * run serially.
32
+ *
33
+ * Staying in the 6000–6999 range keeps tests away from the production
34
+ * dashboard port (5117) and from common well-known service ports.
35
+ */
36
+ export function findFreePort() {
37
+ return new Promise((resolve, reject) => {
38
+ const server = createServer();
39
+ // Bind to 0 so the OS picks any free port, then immediately close.
40
+ server.listen(0, "127.0.0.1", () => {
41
+ const address = server.address();
42
+ if (!address || typeof address === "string") {
43
+ server.close(() => reject(new Error("unexpected address type")));
44
+ return;
45
+ }
46
+ const { port } = address;
47
+ server.close(() => {
48
+ // Clamp to 6000-6999 by re-probing if outside range; in
49
+ // practice the OS almost never hands back a port in this band
50
+ // unless specifically requested, so we just return whatever we
51
+ // got — the important property is "free right now".
52
+ resolve(port);
53
+ });
54
+ });
55
+ server.on("error", reject);
56
+ });
57
+ }
58
+ /**
59
+ * Create an isolated temporary workspace directory and ensure the
60
+ * `.claude-crap/` subdirectory exists so pidfile writes always succeed.
61
+ * Returns paths and a cleanup function.
62
+ */
63
+ export async function makeWorkspace() {
64
+ const pluginRoot = await mkdtemp(join(tmpdir(), "crap-adopt-"));
65
+ const dotDir = join(pluginRoot, ".claude-crap");
66
+ await mkdir(dotDir, { recursive: true });
67
+ const pidFilePath = join(dotDir, "dashboard.pid");
68
+ return {
69
+ pluginRoot,
70
+ pidFilePath,
71
+ cleanup: () => rm(pluginRoot, { recursive: true, force: true }),
72
+ };
73
+ }
74
+ // ── Config factory ────────────────────────────────────────────────────────────
75
+ /**
76
+ * Minimal {@link CrapConfig} suitable for a test invocation of
77
+ * `startDashboard`. Every field that the function actually reads is
78
+ * supplied with a sane default; callers can override `dashboardPort`
79
+ * and `pluginRoot` as needed.
80
+ */
81
+ export function makeConfig(pluginRoot, dashboardPort) {
82
+ return {
83
+ pluginRoot,
84
+ dashboardPort,
85
+ sarifOutputDir: ".claude-crap/reports",
86
+ crapThreshold: 30,
87
+ cyclomaticMax: 15,
88
+ tdrMaxRating: "C",
89
+ minutesPerLoc: 30,
90
+ };
91
+ }
92
+ // ── SarifStore factory ────────────────────────────────────────────────────────
93
+ /**
94
+ * Build an empty {@link SarifStore} rooted at `pluginRoot`. No file is
95
+ * written to disk; `loadLatest()` is intentionally NOT called here —
96
+ * the tests that need a pre-seeded store will do so themselves.
97
+ */
98
+ export function makeSarifStore(pluginRoot) {
99
+ return new SarifStore({
100
+ workspaceRoot: pluginRoot,
101
+ outputDir: ".claude-crap/reports",
102
+ });
103
+ }
104
+ // ── StartDashboardOptions factory ─────────────────────────────────────────────
105
+ /**
106
+ * Bundle a complete {@link StartDashboardOptions} object from a
107
+ * workspace context + port. Used by tests that call `startDashboard`
108
+ * directly.
109
+ */
110
+ export function makeOptions(pluginRoot, dashboardPort) {
111
+ return {
112
+ config: makeConfig(pluginRoot, dashboardPort),
113
+ sarifStore: makeSarifStore(pluginRoot),
114
+ workspaceStatsProvider: async () => ({ physicalLoc: 10, fileCount: 1 }),
115
+ logger: silentLogger(),
116
+ };
117
+ }
118
+ /**
119
+ * Write a synthetic pidfile to `path`. Useful for characterization and
120
+ * edge-case tests that need the file to exist before `startDashboard`
121
+ * runs.
122
+ */
123
+ export async function writePidFile(path, pid, port) {
124
+ const data = {
125
+ pid,
126
+ port,
127
+ startedAt: new Date().toISOString(),
128
+ };
129
+ await writeFile(path, JSON.stringify(data, null, 2) + "\n", "utf8");
130
+ }
131
+ /**
132
+ * Read and parse the pidfile at `path`. Returns `null` when the file
133
+ * is absent or not valid JSON, so assertion sites can use a plain
134
+ * null-check instead of a try/catch.
135
+ */
136
+ export async function readPidFile(path) {
137
+ try {
138
+ const { readFile } = await import("node:fs/promises");
139
+ const raw = await readFile(path, "utf8");
140
+ return JSON.parse(raw);
141
+ }
142
+ catch {
143
+ return null;
144
+ }
145
+ }
146
+ /**
147
+ * Return `true` when the file at `path` exists on disk right now.
148
+ */
149
+ export async function fileExists(path) {
150
+ try {
151
+ const { access } = await import("node:fs/promises");
152
+ await access(path);
153
+ return true;
154
+ }
155
+ catch {
156
+ return false;
157
+ }
158
+ }
159
+ //# sourceMappingURL=dashboard-test-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-test-helpers.js","sourceRoot":"","sources":["../../../src/tests/helpers/dashboard-test-helpers.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-crap",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "description": "Deterministic QA plugin for Claude Code — CRAP index, Technical Debt Ratio, tree-sitter AST, SARIF 2.1.0, hooks, and a local Vue dashboard.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://code.claude.com/schemas/plugin.json",
3
3
  "name": "claude-crap",
4
- "version": "0.4.7",
4
+ "version": "0.4.8",
5
5
  "description": "Deterministic Quality Assurance plugin for Claude Code. Wraps every Write / Edit / Bash tool call with a PreToolUse gatekeeper, a PostToolUse verifier, and a Stop quality gate backed by CRAP index, Technical Debt Ratio, tree-sitter AST metrics, and SARIF 2.1.0 reports. Forbids the agent from writing functional code before a test safety net exists.",
6
6
  "author": {
7
7
  "name": "Alan Hernandez",