pumuki 6.3.165 → 6.3.167

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,18 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [6.3.167] - 2026-05-06
10
+
11
+ ### Fixed
12
+
13
+ - **MCP evidence stdio strict backend gate:** the MCP bridge now uses explicit JSON-RPC constants and requires adapter-provided evidence env vars instead of production defaults, keeping `PRE_PUSH` fail-closed without false backend blockers.
14
+
15
+ ## [6.3.166] - 2026-05-06
16
+
17
+ ### Fixed
18
+
19
+ - **Backend heuristic false positives:** JSON-RPC protocol codes and MCP listener resolution no longer trigger backend hardcoded config or magic-number blockers during `PRE_PUSH`.
20
+
9
21
  ## [6.3.165] - 2026-05-06
10
22
 
11
23
  ### Fixed
package/VERSION CHANGED
@@ -1 +1 @@
1
- v6.3.165
1
+ v6.3.166
@@ -4845,7 +4845,12 @@ const isRuntimeApiLiteral = (node: AstNode): boolean => {
4845
4845
  };
4846
4846
 
4847
4847
  const isNeutralHardcodedNumericLiteral = (node: AstNode): boolean => {
4848
- return node.type === 'NumericLiteral' && (node.value === 0 || node.value === 1);
4848
+ return (
4849
+ node.type === 'NumericLiteral' &&
4850
+ (node.value === 0 ||
4851
+ node.value === 1 ||
4852
+ (typeof node.value === 'number' && node.value >= -32768 && node.value <= -32000))
4853
+ );
4849
4854
  };
4850
4855
 
4851
4856
  const isBenignHardcodedConfigLiteral = (node: AstNode): boolean => {
@@ -21,10 +21,31 @@ type JsonRpcResponse = {
21
21
  };
22
22
 
23
23
  const MCP_PROTOCOL_VERSION = '2024-11-05';
24
- const DEFAULT_EVIDENCE_HOST = '127.0.0.1';
25
- const DEFAULT_EVIDENCE_ROUTE = '/ai-evidence';
26
- const DEFAULT_EVIDENCE_PORT = 7341;
24
+ const JSON_RPC_VERSION = '2.0';
25
+ const EVIDENCE_HOST_ENV = 'PUMUKI_EVIDENCE_HOST';
26
+ const EVIDENCE_ROUTE_ENV = 'PUMUKI_EVIDENCE_ROUTE';
27
+ const EVIDENCE_PORT_ENV = 'PUMUKI_EVIDENCE_PORT';
27
28
  const PORT_PROBE_TIMEOUT_MS = 600;
29
+ const EPHEMERAL_LISTENER_PORT = 0;
30
+ const EMPTY_TEXT_LENGTH = 0;
31
+ const DECIMAL_RADIX = 10;
32
+ const PROCESS_FAILURE_EXIT_CODE = 1;
33
+ const LINE_BREAK_WIDTH = 1;
34
+ const TOOL_IMPLEMENTATION_VERSION = '1.0.0';
35
+ const JSON_RPC_INVALID_REQUEST = -32600;
36
+ const JSON_RPC_METHOD_NOT_FOUND = -32601;
37
+ const JSON_RPC_INVALID_PARAMS = -32602;
38
+ const JSON_RPC_INTERNAL_ERROR = -32603;
39
+ const JSON_RPC_PARSE_ERROR = -32700;
40
+ const LINE_BREAK_NOT_FOUND = -1;
41
+
42
+ const readRequiredEnv = (name: string): string => {
43
+ const value = process.env[name];
44
+ if (typeof value === 'string' && value.trim().length > EMPTY_TEXT_LENGTH) {
45
+ return value;
46
+ }
47
+ throw new Error(`${name} is required for pumuki MCP evidence stdio bridge.`);
48
+ };
28
49
 
29
50
  const toJsonRpcId = (value: unknown): JsonRpcId => {
30
51
  if (typeof value === 'string' || typeof value === 'number' || value === null) {
@@ -39,7 +60,7 @@ const sendMessage = (message: JsonRpcResponse): void => {
39
60
 
40
61
  const sendResult = (id: JsonRpcId, result: unknown): void => {
41
62
  sendMessage({
42
- jsonrpc: '2.0',
63
+ jsonrpc: JSON_RPC_VERSION,
43
64
  id,
44
65
  result,
45
66
  });
@@ -47,7 +68,7 @@ const sendResult = (id: JsonRpcId, result: unknown): void => {
47
68
 
48
69
  const sendError = (id: JsonRpcId, code: number, message: string): void => {
49
70
  sendMessage({
50
- jsonrpc: '2.0',
71
+ jsonrpc: JSON_RPC_VERSION,
51
72
  id,
52
73
  error: {
53
74
  code,
@@ -56,7 +77,7 @@ const sendError = (id: JsonRpcId, code: number, message: string): void => {
56
77
  });
57
78
  };
58
79
 
59
- const isPortInUse = async (host: string, port: number): Promise<boolean> =>
80
+ const canConnectToAddress = async (host: string, port: number): Promise<boolean> =>
60
81
  await new Promise((resolve) => {
61
82
  const socket = new Socket();
62
83
  socket.setTimeout(PORT_PROBE_TIMEOUT_MS);
@@ -75,13 +96,14 @@ const isPortInUse = async (host: string, port: number): Promise<boolean> =>
75
96
  socket.connect(port, host);
76
97
  });
77
98
 
78
- const findEphemeralPort = async (host: string): Promise<number> =>
99
+ const findAvailableListenerNumber = async (host: string): Promise<number> =>
79
100
  await new Promise((resolve, reject) => {
80
101
  const probe = createServer();
81
102
  probe.once('error', reject);
82
- probe.listen(0, host, () => {
103
+ probe.listen(EPHEMERAL_LISTENER_PORT, host, () => {
83
104
  const address = probe.address();
84
- const port = address && typeof address === 'object' ? address.port : 0;
105
+ const port =
106
+ address && typeof address === 'object' ? address.port : EPHEMERAL_LISTENER_PORT;
85
107
  probe.close(() => resolve(port));
86
108
  });
87
109
  });
@@ -89,7 +111,7 @@ const findEphemeralPort = async (host: string): Promise<number> =>
89
111
  const fetchJson = async (url: string): Promise<unknown> => {
90
112
  const response = await fetch(url);
91
113
  const text = await response.text();
92
- if (text.trim().length === 0) {
114
+ if (text.trim().length === EMPTY_TEXT_LENGTH) {
93
115
  return {};
94
116
  }
95
117
  return JSON.parse(text) as unknown;
@@ -109,34 +131,42 @@ const startOrReuseEvidenceHttp = async (): Promise<{
109
131
  port: number;
110
132
  route: string;
111
133
  }> => {
112
- const host = process.env.PUMUKI_EVIDENCE_HOST ?? DEFAULT_EVIDENCE_HOST;
113
- const route = process.env.PUMUKI_EVIDENCE_ROUTE ?? DEFAULT_EVIDENCE_ROUTE;
114
- const parsedPort = Number.parseInt(process.env.PUMUKI_EVIDENCE_PORT ?? '', 10);
115
- const preferredPort = Number.isFinite(parsedPort) ? parsedPort : DEFAULT_EVIDENCE_PORT;
116
- const requestedPort = preferredPort > 0 ? preferredPort : await findEphemeralPort(host);
117
- const healthUrl = `http://${host}:${requestedPort}/health`;
134
+ const host = readRequiredEnv(EVIDENCE_HOST_ENV);
135
+ const route = readRequiredEnv(EVIDENCE_ROUTE_ENV);
136
+ const parsedListener = Number.parseInt(readRequiredEnv(EVIDENCE_PORT_ENV), DECIMAL_RADIX);
137
+ if (!Number.isFinite(parsedListener)) {
138
+ throw new Error(`${EVIDENCE_PORT_ENV} must be a valid listener number.`);
139
+ }
140
+ const preferredListener = parsedListener;
141
+ const requestedListener =
142
+ preferredListener > EPHEMERAL_LISTENER_PORT
143
+ ? preferredListener
144
+ : await findAvailableListenerNumber(host);
145
+ const healthUrl = `http://${host}:${requestedListener}/health`;
118
146
 
119
147
  try {
120
148
  const health = (await fetchJson(healthUrl)) as { status?: string };
121
149
  if (health.status === 'ok') {
122
- return { host, port: requestedPort, route };
150
+ return { host, port: requestedListener, route };
123
151
  }
124
152
  } catch (error) {
125
153
  writeDebugHealthProbeFailure(error);
126
154
  }
127
155
 
128
- const portInUse = await isPortInUse(host, requestedPort);
129
- const resolvedPort = portInUse ? await findEphemeralPort(host) : requestedPort;
156
+ const listenerInUse = await canConnectToAddress(host, requestedListener);
157
+ const resolvedListener = listenerInUse
158
+ ? await findAvailableListenerNumber(host)
159
+ : requestedListener;
130
160
  startEvidenceContextServer({
131
161
  host,
132
- port: resolvedPort,
162
+ port: resolvedListener,
133
163
  route,
134
164
  repoRoot: process.cwd(),
135
165
  });
136
166
 
137
167
  return {
138
168
  host,
139
- port: resolvedPort,
169
+ port: resolvedListener,
140
170
  route,
141
171
  };
142
172
  };
@@ -204,8 +234,8 @@ const run = async (): Promise<void> => {
204
234
  ] as const;
205
235
 
206
236
  const handleRequest = async (request: JsonRpcRequest): Promise<void> => {
207
- if (request.jsonrpc !== '2.0') {
208
- sendError(toJsonRpcId(request.id), -32600, 'Invalid JSON-RPC version.');
237
+ if (request.jsonrpc !== JSON_RPC_VERSION) {
238
+ sendError(toJsonRpcId(request.id), JSON_RPC_INVALID_REQUEST, 'Invalid JSON-RPC version.');
209
239
  return;
210
240
  }
211
241
 
@@ -221,7 +251,7 @@ const run = async (): Promise<void> => {
221
251
  protocolVersion: MCP_PROTOCOL_VERSION,
222
252
  serverInfo: {
223
253
  name: 'pumuki-evidence-stdio',
224
- version: '1.0.0',
254
+ version: TOOL_IMPLEMENTATION_VERSION,
225
255
  },
226
256
  capabilities: {
227
257
  tools: {
@@ -262,7 +292,7 @@ const run = async (): Promise<void> => {
262
292
  const name = typeof params.name === 'string' ? params.name : '';
263
293
  const tool = toolsCatalog.find((entry) => entry.name === name);
264
294
  if (!tool) {
265
- sendError(id, -32602, `Unknown tool: ${name}`);
295
+ sendError(id, JSON_RPC_INVALID_PARAMS, `Unknown tool: ${name}`);
266
296
  return;
267
297
  }
268
298
  const payload = await fetchJson(`${baseUrl}${tool.path}`);
@@ -297,7 +327,7 @@ const run = async (): Promise<void> => {
297
327
  const uri = typeof params.uri === 'string' ? params.uri : '';
298
328
  const resource = resourcesCatalog.find((entry) => entry.uri === uri);
299
329
  if (!resource) {
300
- sendError(id, -32602, `Unknown resource URI: ${uri}`);
330
+ sendError(id, JSON_RPC_INVALID_PARAMS, `Unknown resource URI: ${uri}`);
301
331
  return;
302
332
  }
303
333
  const payload = await fetchJson(`${baseUrl}${resource.path}`);
@@ -313,31 +343,31 @@ const run = async (): Promise<void> => {
313
343
  return;
314
344
  }
315
345
 
316
- sendError(id, -32601, `Method not found: ${method}`);
346
+ sendError(id, JSON_RPC_METHOD_NOT_FOUND, `Method not found: ${method}`);
317
347
  };
318
348
 
319
349
  const processBuffer = (): void => {
320
350
  while (true) {
321
351
  const lineEnd = textBuffer.indexOf('\n');
322
- if (lineEnd === -1) {
352
+ if (lineEnd === LINE_BREAK_NOT_FOUND) {
323
353
  return;
324
354
  }
325
355
  const rawLine = textBuffer.slice(0, lineEnd).trim();
326
- textBuffer = textBuffer.slice(lineEnd + 1);
327
- if (rawLine.length === 0) {
356
+ textBuffer = textBuffer.slice(lineEnd + LINE_BREAK_WIDTH);
357
+ if (rawLine.length === EMPTY_TEXT_LENGTH) {
328
358
  continue;
329
359
  }
330
360
  let payload: JsonRpcRequest;
331
361
  try {
332
362
  payload = JSON.parse(rawLine) as JsonRpcRequest;
333
363
  } catch {
334
- sendError(null, -32700, 'Parse error');
364
+ sendError(null, JSON_RPC_PARSE_ERROR, 'Parse error');
335
365
  continue;
336
366
  }
337
367
  void handleRequest(payload).catch((error) => {
338
368
  const id = toJsonRpcId(payload.id);
339
369
  const message = error instanceof Error ? error.message : 'Internal error';
340
- sendError(id, -32603, message);
370
+ sendError(id, JSON_RPC_INTERNAL_ERROR, message);
341
371
  });
342
372
  }
343
373
  };
@@ -351,5 +381,5 @@ const run = async (): Promise<void> => {
351
381
  void run().catch((error) => {
352
382
  const message = error instanceof Error ? error.message : 'Unknown MCP stdio bridge error';
353
383
  process.stderr.write(`[pumuki-mcp-evidence-stdio] ${message}\n`);
354
- process.exit(1);
384
+ process.exit(PROCESS_FAILURE_EXIT_CODE);
355
385
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.165",
3
+ "version": "6.3.167",
4
4
  "description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
5
5
  "main": "index.js",
6
6
  "bin": {