pumuki 6.3.170 → 6.3.172

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.
@@ -21,31 +21,6 @@ type JsonRpcResponse = {
21
21
  };
22
22
 
23
23
  const MCP_PROTOCOL_VERSION = '2024-11-05';
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';
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
- };
49
24
 
50
25
  const toJsonRpcId = (value: unknown): JsonRpcId => {
51
26
  if (typeof value === 'string' || typeof value === 'number' || value === null) {
@@ -60,7 +35,7 @@ const sendMessage = (message: JsonRpcResponse): void => {
60
35
 
61
36
  const sendResult = (id: JsonRpcId, result: unknown): void => {
62
37
  sendMessage({
63
- jsonrpc: JSON_RPC_VERSION,
38
+ jsonrpc: '2.0',
64
39
  id,
65
40
  result,
66
41
  });
@@ -68,7 +43,7 @@ const sendResult = (id: JsonRpcId, result: unknown): void => {
68
43
 
69
44
  const sendError = (id: JsonRpcId, code: number, message: string): void => {
70
45
  sendMessage({
71
- jsonrpc: JSON_RPC_VERSION,
46
+ jsonrpc: '2.0',
72
47
  id,
73
48
  error: {
74
49
  code,
@@ -77,10 +52,10 @@ const sendError = (id: JsonRpcId, code: number, message: string): void => {
77
52
  });
78
53
  };
79
54
 
80
- const canConnectToAddress = async (host: string, port: number): Promise<boolean> =>
55
+ const isPortInUse = async (host: string, port: number): Promise<boolean> =>
81
56
  await new Promise((resolve) => {
82
57
  const socket = new Socket();
83
- socket.setTimeout(PORT_PROBE_TIMEOUT_MS);
58
+ socket.setTimeout(600);
84
59
  socket.once('connect', () => {
85
60
  socket.destroy();
86
61
  resolve(true);
@@ -96,14 +71,13 @@ const canConnectToAddress = async (host: string, port: number): Promise<boolean>
96
71
  socket.connect(port, host);
97
72
  });
98
73
 
99
- const findAvailableListenerNumber = async (host: string): Promise<number> =>
74
+ const findEphemeralPort = async (host: string): Promise<number> =>
100
75
  await new Promise((resolve, reject) => {
101
76
  const probe = createServer();
102
77
  probe.once('error', reject);
103
- probe.listen(EPHEMERAL_LISTENER_PORT, host, () => {
78
+ probe.listen(0, host, () => {
104
79
  const address = probe.address();
105
- const port =
106
- address && typeof address === 'object' ? address.port : EPHEMERAL_LISTENER_PORT;
80
+ const port = address && typeof address === 'object' ? address.port : 0;
107
81
  probe.close(() => resolve(port));
108
82
  });
109
83
  });
@@ -111,62 +85,45 @@ const findAvailableListenerNumber = async (host: string): Promise<number> =>
111
85
  const fetchJson = async (url: string): Promise<unknown> => {
112
86
  const response = await fetch(url);
113
87
  const text = await response.text();
114
- if (text.trim().length === EMPTY_TEXT_LENGTH) {
88
+ if (text.trim().length === 0) {
115
89
  return {};
116
90
  }
117
91
  return JSON.parse(text) as unknown;
118
92
  };
119
93
 
120
- const writeDebugHealthProbeFailure = (error: unknown): void => {
121
- if (process.env.PUMUKI_DEBUG !== '1') {
122
- return;
123
- }
124
-
125
- const message = error instanceof Error ? error.message : String(error);
126
- process.stderr.write(`[pumuki-mcp-evidence-stdio] health probe fallback: ${message}\n`);
127
- };
128
-
129
94
  const startOrReuseEvidenceHttp = async (): Promise<{
130
95
  host: string;
131
96
  port: number;
132
97
  route: string;
133
98
  }> => {
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`;
99
+ const host = process.env.PUMUKI_EVIDENCE_HOST ?? '127.0.0.1';
100
+ const route = process.env.PUMUKI_EVIDENCE_ROUTE ?? '/ai-evidence';
101
+ const parsedPort = Number.parseInt(process.env.PUMUKI_EVIDENCE_PORT ?? '', 10);
102
+ const preferredPort = Number.isFinite(parsedPort) ? parsedPort : 7341;
103
+ const requestedPort = preferredPort > 0 ? preferredPort : await findEphemeralPort(host);
104
+ const healthUrl = `http://${host}:${requestedPort}/health`;
146
105
 
147
106
  try {
148
107
  const health = (await fetchJson(healthUrl)) as { status?: string };
149
108
  if (health.status === 'ok') {
150
- return { host, port: requestedListener, route };
109
+ return { host, port: requestedPort, route };
151
110
  }
152
- } catch (error) {
153
- writeDebugHealthProbeFailure(error);
111
+ } catch {
112
+ // ignored
154
113
  }
155
114
 
156
- const listenerInUse = await canConnectToAddress(host, requestedListener);
157
- const resolvedListener = listenerInUse
158
- ? await findAvailableListenerNumber(host)
159
- : requestedListener;
115
+ const portInUse = await isPortInUse(host, requestedPort);
116
+ const resolvedPort = portInUse ? await findEphemeralPort(host) : requestedPort;
160
117
  startEvidenceContextServer({
161
118
  host,
162
- port: resolvedListener,
119
+ port: resolvedPort,
163
120
  route,
164
121
  repoRoot: process.cwd(),
165
122
  });
166
123
 
167
124
  return {
168
125
  host,
169
- port: resolvedListener,
126
+ port: resolvedPort,
170
127
  route,
171
128
  };
172
129
  };
@@ -234,8 +191,8 @@ const run = async (): Promise<void> => {
234
191
  ] as const;
235
192
 
236
193
  const handleRequest = async (request: JsonRpcRequest): Promise<void> => {
237
- if (request.jsonrpc !== JSON_RPC_VERSION) {
238
- sendError(toJsonRpcId(request.id), JSON_RPC_INVALID_REQUEST, 'Invalid JSON-RPC version.');
194
+ if (request.jsonrpc !== '2.0') {
195
+ sendError(toJsonRpcId(request.id), -32600, 'Invalid JSON-RPC version.');
239
196
  return;
240
197
  }
241
198
 
@@ -251,7 +208,7 @@ const run = async (): Promise<void> => {
251
208
  protocolVersion: MCP_PROTOCOL_VERSION,
252
209
  serverInfo: {
253
210
  name: 'pumuki-evidence-stdio',
254
- version: TOOL_IMPLEMENTATION_VERSION,
211
+ version: '1.0.0',
255
212
  },
256
213
  capabilities: {
257
214
  tools: {
@@ -292,7 +249,7 @@ const run = async (): Promise<void> => {
292
249
  const name = typeof params.name === 'string' ? params.name : '';
293
250
  const tool = toolsCatalog.find((entry) => entry.name === name);
294
251
  if (!tool) {
295
- sendError(id, JSON_RPC_INVALID_PARAMS, `Unknown tool: ${name}`);
252
+ sendError(id, -32602, `Unknown tool: ${name}`);
296
253
  return;
297
254
  }
298
255
  const payload = await fetchJson(`${baseUrl}${tool.path}`);
@@ -327,7 +284,7 @@ const run = async (): Promise<void> => {
327
284
  const uri = typeof params.uri === 'string' ? params.uri : '';
328
285
  const resource = resourcesCatalog.find((entry) => entry.uri === uri);
329
286
  if (!resource) {
330
- sendError(id, JSON_RPC_INVALID_PARAMS, `Unknown resource URI: ${uri}`);
287
+ sendError(id, -32602, `Unknown resource URI: ${uri}`);
331
288
  return;
332
289
  }
333
290
  const payload = await fetchJson(`${baseUrl}${resource.path}`);
@@ -343,31 +300,31 @@ const run = async (): Promise<void> => {
343
300
  return;
344
301
  }
345
302
 
346
- sendError(id, JSON_RPC_METHOD_NOT_FOUND, `Method not found: ${method}`);
303
+ sendError(id, -32601, `Method not found: ${method}`);
347
304
  };
348
305
 
349
306
  const processBuffer = (): void => {
350
307
  while (true) {
351
308
  const lineEnd = textBuffer.indexOf('\n');
352
- if (lineEnd === LINE_BREAK_NOT_FOUND) {
309
+ if (lineEnd === -1) {
353
310
  return;
354
311
  }
355
312
  const rawLine = textBuffer.slice(0, lineEnd).trim();
356
- textBuffer = textBuffer.slice(lineEnd + LINE_BREAK_WIDTH);
357
- if (rawLine.length === EMPTY_TEXT_LENGTH) {
313
+ textBuffer = textBuffer.slice(lineEnd + 1);
314
+ if (rawLine.length === 0) {
358
315
  continue;
359
316
  }
360
317
  let payload: JsonRpcRequest;
361
318
  try {
362
319
  payload = JSON.parse(rawLine) as JsonRpcRequest;
363
320
  } catch {
364
- sendError(null, JSON_RPC_PARSE_ERROR, 'Parse error');
321
+ sendError(null, -32700, 'Parse error');
365
322
  continue;
366
323
  }
367
324
  void handleRequest(payload).catch((error) => {
368
325
  const id = toJsonRpcId(payload.id);
369
326
  const message = error instanceof Error ? error.message : 'Internal error';
370
- sendError(id, JSON_RPC_INTERNAL_ERROR, message);
327
+ sendError(id, -32603, message);
371
328
  });
372
329
  }
373
330
  };
@@ -381,5 +338,5 @@ const run = async (): Promise<void> => {
381
338
  void run().catch((error) => {
382
339
  const message = error instanceof Error ? error.message : 'Unknown MCP stdio bridge error';
383
340
  process.stderr.write(`[pumuki-mcp-evidence-stdio] ${message}\n`);
384
- process.exit(PROCESS_FAILURE_EXIT_CODE);
341
+ process.exit(1);
385
342
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.170",
4
- "description": "Enterprise AST Intelligence and delivery gate for AI-assisted teams: Git hooks, SDD/OpenSpec, skills enforcement, MCP context, and audit evidence for iOS, Android, backend, and frontend repositories.",
3
+ "version": "6.3.172",
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": {
7
7
  "pumuki": "bin/pumuki.js",