@klitchevo/code-council 0.0.11 → 0.0.13

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/README.md CHANGED
@@ -19,6 +19,7 @@ An MCP (Model Context Protocol) server that provides AI-powered code review usin
19
19
  - 📋 **Plan Review** - Review implementation plans before writing code
20
20
  - 📝 **Git Changes Review** - Review staged, unstaged, branch diffs, or specific commits
21
21
  - 💬 **Council Discussions** - Multi-turn conversations with the AI council for deeper exploration
22
+ - 🏭 **TPS Audit** - Toyota Production System analysis for flow, waste, bottlenecks, and quality
22
23
  - ⚡ **Parallel Execution** - All models run concurrently for fast results
23
24
 
24
25
  ## Quick Start
@@ -284,6 +285,46 @@ Use discuss_with_council with session_id=<id-from-previous-response> to ask: Can
284
285
  - Rate limited to 10 requests per minute per session
285
286
  - Context windowing keeps conversations efficient
286
287
 
288
+ ### `tps_audit`
289
+
290
+ Analyze any codebase using Toyota Production System (TPS) principles. Generates beautiful HTML reports with scores for flow, waste, bottlenecks, and quality.
291
+
292
+ **Parameters:**
293
+ - `path` (optional): Path to repository root (auto-detects git root if not provided)
294
+ - `focus_areas` (optional): Specific areas to focus on (e.g., `["flow", "security", "performance"]`)
295
+ - `max_files` (optional): Maximum files to analyze (default: 50, max: 100)
296
+ - `file_types` (optional): File extensions to include (default: common source files)
297
+ - `include_sensitive` (optional): Include potentially sensitive files (default: false)
298
+ - `output_format` (optional): `html`, `markdown`, or `json` (default: `html`)
299
+
300
+ **Example usage in Claude:**
301
+ ```
302
+ Use tps_audit to analyze this repository
303
+ ```
304
+
305
+ ```
306
+ Use tps_audit with output_format=markdown and focus_areas=["security", "performance"]
307
+ ```
308
+
309
+ **What it analyzes:**
310
+ - **Flow**: How data and control flow through the system, entry points, pathways
311
+ - **Muda (Waste)**: The 7 wastes - defects, overproduction, waiting, transportation, inventory, motion, extra-processing
312
+ - **Bottlenecks**: Where flow is constrained, severity and impact
313
+ - **Jidoka**: Built-in quality, fail-fast patterns, error handling
314
+ - **Recommendations**: Prioritized improvements with effort/impact ratings
315
+
316
+ **Security features:**
317
+ - Automatically skips sensitive files (`.env`, credentials, keys, tokens)
318
+ - Scans file contents for embedded secrets (AWS keys, GitHub PATs, etc.)
319
+ - Validates paths to prevent directory traversal attacks
320
+ - Enforces size limits to prevent resource exhaustion
321
+
322
+ **Output:**
323
+ Reports are saved to `.code-council/` directory:
324
+ - `tps-audit.html` - Interactive styled report with glass-morphism dark theme
325
+ - `tps-audit.md` - Markdown version
326
+ - `tps-audit.json` - Raw JSON data
327
+
287
328
  ### `list_review_config`
288
329
 
289
330
  Show which AI models are currently configured for each review type.
@@ -300,6 +341,7 @@ You can customize which AI models are used for reviews by setting environment va
300
341
  - `BACKEND_REVIEW_MODELS` - Models for backend reviews
301
342
  - `PLAN_REVIEW_MODELS` - Models for plan reviews
302
343
  - `DISCUSSION_MODELS` - Models for council discussions
344
+ - `TPS_AUDIT_MODELS` - Models for TPS codebase audits
303
345
  - `TEMPERATURE` - Control response randomness (0.0-2.0, default: 0.3)
304
346
  - `MAX_TOKENS` - Maximum response tokens (default: 16384)
305
347
 
@@ -0,0 +1,142 @@
1
+ // src/prompts/tps-audit.ts
2
+ var SYSTEM_PROMPT = `You are an expert Toyota Production System (TPS) consultant analyzing software codebases. Your role is to "walk the production line" - examining how code flows from input to output, identifying waste (muda), spotting bottlenecks, and suggesting continuous improvement (kaizen).
3
+
4
+ ## TPS Principles for Software
5
+
6
+ ### 1. FLOW (Nagare)
7
+ Analyze how data and control flow through the system:
8
+ - Identify entry points and exit points
9
+ - Map the critical paths
10
+ - Look for smooth, uninterrupted flow
11
+ - Identify where flow is blocked or redirected
12
+ - Check for single-piece flow vs batch processing
13
+
14
+ ### 2. WASTE (Muda) - The 7 Wastes in Software
15
+ Identify instances of each waste type:
16
+
17
+ **Defects**: Bugs, error-prone code, missing validation
18
+ **Overproduction**: Features nobody uses, over-engineered solutions
19
+ **Waiting**: Blocking I/O, synchronous when async would work, slow tests
20
+ **Non-utilized Talent**: Manual tasks that could be automated, repetitive code
21
+ **Transportation**: Unnecessary data transformation, excessive API calls
22
+ **Inventory**: Dead code, unused imports/exports, stale dependencies
23
+ **Motion**: Complex navigation, scattered related code, poor organization
24
+ **Extra-processing**: Premature optimization, unnecessary abstraction layers
25
+
26
+ ### 3. BOTTLENECKS
27
+ Identify constraints that limit throughput:
28
+ - Synchronous operations that block
29
+ - Single points of failure
30
+ - Resource contention
31
+ - N+1 queries or API calls
32
+ - Sequential operations that could be parallel
33
+
34
+ ### 4. PULL vs PUSH
35
+ Evaluate if work is demand-driven:
36
+ - Lazy evaluation vs eager computation
37
+ - On-demand loading vs preloading everything
38
+ - Event-driven vs polling
39
+ - Streaming vs buffering all data
40
+
41
+ ### 5. JIDOKA (Built-in Quality)
42
+ Assess quality mechanisms:
43
+ - Error handling and recovery
44
+ - Validation at boundaries
45
+ - Fail-fast patterns
46
+ - Type safety usage
47
+ - Test coverage signals
48
+
49
+ ### 6. STANDARDIZATION
50
+ Look for consistency:
51
+ - Code style consistency
52
+ - Pattern usage consistency
53
+ - Error handling patterns
54
+ - Naming conventions
55
+ - File organization
56
+
57
+ ## Scoring Guidelines
58
+
59
+ **Overall Score (0-100)**:
60
+ - 90-100: Exceptional flow, minimal waste, excellent quality
61
+ - 70-89: Good practices, some waste, room for improvement
62
+ - 50-69: Average, significant waste or flow issues
63
+ - 30-49: Poor flow, excessive waste, quality concerns
64
+ - 0-29: Critical issues, major redesign needed
65
+
66
+ **Flow Score**: How smoothly does data/control move through the system?
67
+ **Waste Score**: Higher = less waste (100 = no waste identified)
68
+ **Quality Score**: Built-in quality mechanisms, error handling, type safety
69
+
70
+ ## Output Requirements
71
+
72
+ You MUST respond with valid JSON matching the TpsAnalysis interface. Do not include any text before or after the JSON.
73
+
74
+ Focus on:
75
+ 1. Actionable findings with specific file/line references
76
+ 2. Prioritized recommendations (quick wins first)
77
+ 3. Concrete suggestions, not vague advice
78
+ 4. Balanced assessment - acknowledge strengths too
79
+ 5. Effort estimates for recommendations`;
80
+ function buildUserMessage(aggregatedContent, options) {
81
+ const parts = [];
82
+ if (options?.repoName) {
83
+ parts.push(`## Repository: ${options.repoName}`);
84
+ }
85
+ if (options?.focusAreas && options.focusAreas.length > 0) {
86
+ parts.push(
87
+ `## Focus Areas
88
+ Pay special attention to: ${options.focusAreas.join(", ")}`
89
+ );
90
+ }
91
+ if (options?.additionalContext) {
92
+ parts.push(`## Additional Context
93
+ ${options.additionalContext}`);
94
+ }
95
+ parts.push(`## Codebase to Audit
96
+
97
+ Analyze this codebase using Toyota Production System principles. Walk the production line from entry points through to outputs. Identify waste, bottlenecks, and improvement opportunities.
98
+
99
+ ${aggregatedContent}
100
+
101
+ ## Response Format
102
+
103
+ Respond with ONLY valid JSON matching the TpsAnalysis interface. Include:
104
+ - Scores for overall, flow, waste, and quality (0-100)
105
+ - Flow analysis with entry points and pathways
106
+ - Specific bottlenecks with locations and suggestions
107
+ - Waste items categorized by the 7 types
108
+ - Jidoka (built-in quality) assessment
109
+ - Prioritized recommendations
110
+ - Summary with strengths, concerns, and quick wins
111
+
112
+ Your JSON response:`);
113
+ return parts.join("\n\n");
114
+ }
115
+ function parseTpsAnalysis(response) {
116
+ try {
117
+ let jsonStr = response.trim();
118
+ if (jsonStr.startsWith("```json")) {
119
+ jsonStr = jsonStr.slice(7);
120
+ } else if (jsonStr.startsWith("```")) {
121
+ jsonStr = jsonStr.slice(3);
122
+ }
123
+ if (jsonStr.endsWith("```")) {
124
+ jsonStr = jsonStr.slice(0, -3);
125
+ }
126
+ jsonStr = jsonStr.trim();
127
+ const parsed = JSON.parse(jsonStr);
128
+ if (typeof parsed.scores?.overall !== "number" || !Array.isArray(parsed.bottlenecks) || !Array.isArray(parsed.recommendations)) {
129
+ return null;
130
+ }
131
+ return parsed;
132
+ } catch {
133
+ return null;
134
+ }
135
+ }
136
+
137
+ export {
138
+ SYSTEM_PROMPT,
139
+ buildUserMessage,
140
+ parseTpsAnalysis
141
+ };
142
+ //# sourceMappingURL=chunk-Y77R7523.js.map
package/dist/index.js CHANGED
@@ -1,4 +1,8 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ SYSTEM_PROMPT,
4
+ buildUserMessage
5
+ } from "./chunk-Y77R7523.js";
2
6
 
3
7
  // src/index.ts
4
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -77,64 +81,10 @@ var DISCUSSION_MODELS = parseModels(
77
81
  process.env.DISCUSSION_MODELS,
78
82
  DEFAULT_MODELS
79
83
  );
80
-
81
- // src/logger.ts
82
- var Logger = class {
83
- isDevelopment = process.env.NODE_ENV === "development";
84
- debugEnabled = process.env.DEBUG === "true";
85
- log(level, message, context) {
86
- const logEntry = {
87
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
88
- level,
89
- message,
90
- ...context
91
- };
92
- if (this.isDevelopment) {
93
- const emoji = {
94
- debug: "\u{1F50D}",
95
- info: "\u2139\uFE0F",
96
- warn: "\u26A0\uFE0F",
97
- error: "\u274C"
98
- }[level];
99
- console.error(
100
- `${emoji} [${level.toUpperCase()}] ${message}`,
101
- context ? JSON.stringify(context, null, 2) : ""
102
- );
103
- } else {
104
- console.error(JSON.stringify(logEntry));
105
- }
106
- }
107
- debug(message, context) {
108
- if (this.debugEnabled) {
109
- this.log("debug", message, context);
110
- }
111
- }
112
- info(message, context) {
113
- this.log("info", message, context);
114
- }
115
- warn(message, context) {
116
- this.log("warn", message, context);
117
- }
118
- error(message, error, context) {
119
- const errorContext = {
120
- ...context
121
- };
122
- if (error instanceof Error) {
123
- errorContext.error = {
124
- name: error.name,
125
- message: error.message,
126
- stack: error.stack
127
- };
128
- } else if (error) {
129
- errorContext.error = error;
130
- }
131
- this.log("error", message, errorContext);
132
- }
133
- };
134
- var logger = new Logger();
135
-
136
- // src/review-client.ts
137
- import { OpenRouter } from "@openrouter/sdk";
84
+ var TPS_AUDIT_MODELS = parseModels(
85
+ process.env.TPS_AUDIT_MODELS,
86
+ DEFAULT_MODELS
87
+ );
138
88
 
139
89
  // src/errors.ts
140
90
  var AppError = class extends Error {
@@ -198,9 +148,67 @@ function formatError(error) {
198
148
  };
199
149
  }
200
150
 
151
+ // src/logger.ts
152
+ var Logger = class {
153
+ isDevelopment = process.env.NODE_ENV === "development";
154
+ debugEnabled = process.env.DEBUG === "true";
155
+ log(level, message, context) {
156
+ const logEntry = {
157
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
158
+ level,
159
+ message,
160
+ ...context
161
+ };
162
+ if (this.isDevelopment) {
163
+ const emoji = {
164
+ debug: "\u{1F50D}",
165
+ info: "\u2139\uFE0F",
166
+ warn: "\u26A0\uFE0F",
167
+ error: "\u274C"
168
+ }[level];
169
+ console.error(
170
+ `${emoji} [${level.toUpperCase()}] ${message}`,
171
+ context ? JSON.stringify(context, null, 2) : ""
172
+ );
173
+ } else {
174
+ console.error(JSON.stringify(logEntry));
175
+ }
176
+ }
177
+ debug(message, context) {
178
+ if (this.debugEnabled) {
179
+ this.log("debug", message, context);
180
+ }
181
+ }
182
+ info(message, context) {
183
+ this.log("info", message, context);
184
+ }
185
+ warn(message, context) {
186
+ this.log("warn", message, context);
187
+ }
188
+ error(message, error, context) {
189
+ const errorContext = {
190
+ ...context
191
+ };
192
+ if (error instanceof Error) {
193
+ errorContext.error = {
194
+ name: error.name,
195
+ message: error.message,
196
+ stack: error.stack
197
+ };
198
+ } else if (error) {
199
+ errorContext.error = error;
200
+ }
201
+ this.log("error", message, errorContext);
202
+ }
203
+ };
204
+ var logger = new Logger();
205
+
206
+ // src/review-client.ts
207
+ import { OpenRouter } from "@openrouter/sdk";
208
+
201
209
  // src/prompts/backend-review.ts
202
- var SYSTEM_PROMPT = `You are an expert backend developer and security specialist. Review backend code for security, performance, and architecture.`;
203
- function buildUserMessage(code, reviewType = "full", language, context) {
210
+ var SYSTEM_PROMPT2 = `You are an expert backend developer and security specialist. Review backend code for security, performance, and architecture.`;
211
+ function buildUserMessage2(code, reviewType = "full", language, context) {
204
212
  const focusArea = getFocusArea(reviewType);
205
213
  const languageContext = language ? `Language/Framework: ${language}
206
214
  ` : "";
@@ -227,7 +235,7 @@ function getFocusArea(reviewType) {
227
235
  }
228
236
 
229
237
  // src/prompts/code-review.ts
230
- var SYSTEM_PROMPT2 = `You are an expert code reviewer. Analyze the code for:
238
+ var SYSTEM_PROMPT3 = `You are an expert code reviewer. Analyze the code for:
231
239
  - Code quality and best practices
232
240
  - Potential bugs and edge cases
233
241
  - Performance issues
@@ -235,7 +243,7 @@ var SYSTEM_PROMPT2 = `You are an expert code reviewer. Analyze the code for:
235
243
  - Maintainability concerns
236
244
 
237
245
  Provide specific, actionable feedback.`;
238
- function buildUserMessage2(code, context) {
246
+ function buildUserMessage3(code, context) {
239
247
  if (context) {
240
248
  return `${context}
241
249
 
@@ -251,8 +259,8 @@ ${code}
251
259
  }
252
260
 
253
261
  // src/prompts/frontend-review.ts
254
- var SYSTEM_PROMPT3 = `You are an expert frontend developer and UX specialist. Review frontend code for best practices.`;
255
- function buildUserMessage3(code, reviewType = "full", framework, context) {
262
+ var SYSTEM_PROMPT4 = `You are an expert frontend developer and UX specialist. Review frontend code for best practices.`;
263
+ function buildUserMessage4(code, reviewType = "full", framework, context) {
256
264
  const focusArea = getFocusArea2(reviewType);
257
265
  const frameworkContext = framework ? `Framework: ${framework}
258
266
  ` : "";
@@ -279,8 +287,8 @@ function getFocusArea2(reviewType) {
279
287
  }
280
288
 
281
289
  // src/prompts/plan-review.ts
282
- var SYSTEM_PROMPT4 = `You are an expert software architect and project planner. Review implementation plans before code is written to catch issues early.`;
283
- function buildUserMessage4(plan, reviewType = "full", context) {
290
+ var SYSTEM_PROMPT5 = `You are an expert software architect and project planner. Review implementation plans before code is written to catch issues early.`;
291
+ function buildUserMessage5(plan, reviewType = "full", context) {
284
292
  const focusArea = getFocusArea3(reviewType);
285
293
  const additionalContext = context ? `${context}
286
294
  ` : "";
@@ -380,17 +388,17 @@ var ReviewClient = class {
380
388
  * @returns Array of review results from each model
381
389
  */
382
390
  async reviewCode(code, models, context) {
383
- const userMessage = buildUserMessage2(code, context);
391
+ const userMessage = buildUserMessage3(code, context);
384
392
  return executeInParallel(
385
393
  models,
386
- (model) => this.chat(model, SYSTEM_PROMPT2, userMessage)
394
+ (model) => this.chat(model, SYSTEM_PROMPT3, userMessage)
387
395
  );
388
396
  }
389
397
  /**
390
398
  * Review frontend code for accessibility, performance, and UX
391
399
  */
392
400
  async reviewFrontend(code, models, options) {
393
- const userMessage = buildUserMessage3(
401
+ const userMessage = buildUserMessage4(
394
402
  code,
395
403
  options?.reviewType || "full",
396
404
  options?.framework,
@@ -398,14 +406,14 @@ var ReviewClient = class {
398
406
  );
399
407
  return executeInParallel(
400
408
  models,
401
- (model) => this.chat(model, SYSTEM_PROMPT3, userMessage)
409
+ (model) => this.chat(model, SYSTEM_PROMPT4, userMessage)
402
410
  );
403
411
  }
404
412
  /**
405
413
  * Review backend code for security, performance, and architecture
406
414
  */
407
415
  async reviewBackend(code, models, options) {
408
- const userMessage = buildUserMessage(
416
+ const userMessage = buildUserMessage2(
409
417
  code,
410
418
  options?.reviewType || "full",
411
419
  options?.language,
@@ -413,21 +421,21 @@ var ReviewClient = class {
413
421
  );
414
422
  return executeInParallel(
415
423
  models,
416
- (model) => this.chat(model, SYSTEM_PROMPT, userMessage)
424
+ (model) => this.chat(model, SYSTEM_PROMPT2, userMessage)
417
425
  );
418
426
  }
419
427
  /**
420
428
  * Review implementation plans before code is written
421
429
  */
422
430
  async reviewPlan(plan, models, options) {
423
- const userMessage = buildUserMessage4(
431
+ const userMessage = buildUserMessage5(
424
432
  plan,
425
433
  options?.reviewType || "full",
426
434
  options?.context
427
435
  );
428
436
  return executeInParallel(
429
437
  models,
430
- (model) => this.chat(model, SYSTEM_PROMPT4, userMessage)
438
+ (model) => this.chat(model, SYSTEM_PROMPT5, userMessage)
431
439
  );
432
440
  }
433
441
  /**
@@ -508,6 +516,30 @@ var ReviewClient = class {
508
516
  return this.chatMultiTurn(model, messages);
509
517
  });
510
518
  }
519
+ /**
520
+ * Perform TPS (Toyota Production System) audit on aggregated codebase content
521
+ * Analyzes code for flow, waste, bottlenecks, and quality using TPS principles
522
+ *
523
+ * @param aggregatedContent - Aggregated file contents from repo scanner
524
+ * @param models - Array of model identifiers to use
525
+ * @param options - Optional configuration
526
+ * @returns Array of TPS analysis results from each model
527
+ */
528
+ async tpsAudit(aggregatedContent, models, options) {
529
+ const userMessage = buildUserMessage(aggregatedContent, {
530
+ focusAreas: options?.focusAreas,
531
+ repoName: options?.repoName
532
+ });
533
+ logger.debug("Starting TPS audit", {
534
+ contentLength: aggregatedContent.length,
535
+ modelCount: models.length,
536
+ focusAreas: options?.focusAreas
537
+ });
538
+ return executeInParallel(
539
+ models,
540
+ (model) => this.chat(model, SYSTEM_PROMPT, userMessage)
541
+ );
542
+ }
511
543
  };
512
544
 
513
545
  // src/session/in-memory-store.ts
@@ -982,6 +1014,9 @@ async function handleDiscussCouncil(client2, input, sessionStore2) {
982
1014
  }
983
1015
 
984
1016
  // src/tools/factory.ts
1017
+ import { readFileSync } from "fs";
1018
+ import { dirname, join } from "path";
1019
+ import { fileURLToPath } from "url";
985
1020
  function formatResults(results) {
986
1021
  return results.map((r) => {
987
1022
  if (r.error) {
@@ -994,6 +1029,39 @@ function formatResults(results) {
994
1029
  ${r.review}`;
995
1030
  }).join("\n\n---\n\n");
996
1031
  }
1032
+ function formatResultsAsHtml(results, templatePath, data = {}) {
1033
+ try {
1034
+ let template = readFileSync(templatePath, "utf-8");
1035
+ const modelPerspectives = results.map((r) => ({
1036
+ model: r.model,
1037
+ content: r.error ? `Error: ${r.error}` : r.review,
1038
+ hasError: !!r.error
1039
+ }));
1040
+ const reportData = {
1041
+ analysis: data.analysis || null,
1042
+ repoName: data.repoName || "Unknown Repository",
1043
+ modelPerspectives,
1044
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
1045
+ };
1046
+ template = template.replace(
1047
+ "{{REPORT_DATA}}",
1048
+ JSON.stringify(reportData, null, 2)
1049
+ );
1050
+ return template;
1051
+ } catch (error) {
1052
+ logger.error("Failed to generate HTML report", error);
1053
+ return formatResults(results);
1054
+ }
1055
+ }
1056
+ function getTemplatesDir() {
1057
+ try {
1058
+ const __filename2 = fileURLToPath(import.meta.url);
1059
+ const __dirname2 = dirname(__filename2);
1060
+ return join(__dirname2, "..", "..", "templates");
1061
+ } catch {
1062
+ return join(process.cwd(), "templates");
1063
+ }
1064
+ }
997
1065
  function createReviewTool(server2, config) {
998
1066
  server2.registerTool(
999
1067
  config.name,
@@ -1239,6 +1307,598 @@ async function handlePlanReview(client2, input) {
1239
1307
  };
1240
1308
  }
1241
1309
 
1310
+ // src/tools/tps-audit.ts
1311
+ import { join as join4 } from "path";
1312
+ import { z as z7 } from "zod";
1313
+
1314
+ // src/utils/repo-scanner.ts
1315
+ import { readdirSync, readFileSync as readFileSync2, statSync } from "fs";
1316
+ import { extname, join as join3, relative as relative2 } from "path";
1317
+ import ignore from "ignore";
1318
+
1319
+ // src/utils/git-operations.ts
1320
+ import { execSync as execSync2 } from "child_process";
1321
+ import { existsSync, lstatSync, realpathSync } from "fs";
1322
+ import { dirname as dirname2, join as join2, normalize, relative, resolve } from "path";
1323
+ var MAX_TRAVERSAL_DEPTH = 20;
1324
+ function findGitRoot(startPath) {
1325
+ let currentPath = resolve(startPath);
1326
+ let depth = 0;
1327
+ while (depth < MAX_TRAVERSAL_DEPTH) {
1328
+ const gitPath = join2(currentPath, ".git");
1329
+ if (existsSync(gitPath)) {
1330
+ logger.debug("Found git root", { path: currentPath, depth });
1331
+ return currentPath;
1332
+ }
1333
+ const parentPath = dirname2(currentPath);
1334
+ if (parentPath === currentPath) {
1335
+ throw new Error(
1336
+ `Not in a git repository. Searched from ${startPath} to filesystem root.`
1337
+ );
1338
+ }
1339
+ currentPath = parentPath;
1340
+ depth++;
1341
+ }
1342
+ throw new Error(
1343
+ `Max directory traversal depth (${MAX_TRAVERSAL_DEPTH}) exceeded while searching for git root.`
1344
+ );
1345
+ }
1346
+ function isInsideRepo(filePath, repoRoot) {
1347
+ try {
1348
+ const normalizedRepo = normalize(resolve(repoRoot));
1349
+ const normalizedFile = normalize(resolve(filePath));
1350
+ const relativePath = relative(normalizedRepo, normalizedFile);
1351
+ if (relativePath.startsWith("..") || resolve(relativePath) === relativePath) {
1352
+ return false;
1353
+ }
1354
+ return true;
1355
+ } catch {
1356
+ return false;
1357
+ }
1358
+ }
1359
+ function resolveAndValidatePath(filePath, repoRoot) {
1360
+ try {
1361
+ const absolutePath = resolve(repoRoot, filePath);
1362
+ const stats = lstatSync(absolutePath);
1363
+ if (stats.isSymbolicLink()) {
1364
+ const realPath = realpathSync(absolutePath);
1365
+ if (!isInsideRepo(realPath, repoRoot)) {
1366
+ logger.warn("Symlink target outside repository", {
1367
+ symlink: filePath,
1368
+ target: realPath,
1369
+ repo: repoRoot
1370
+ });
1371
+ return null;
1372
+ }
1373
+ return realPath;
1374
+ }
1375
+ if (!isInsideRepo(absolutePath, repoRoot)) {
1376
+ return null;
1377
+ }
1378
+ return absolutePath;
1379
+ } catch (error) {
1380
+ logger.debug("Path resolution failed", { filePath, error });
1381
+ return null;
1382
+ }
1383
+ }
1384
+
1385
+ // src/utils/repo-scanner.ts
1386
+ var SENSITIVE_FILE_PATTERNS = [
1387
+ ".env",
1388
+ ".env.*",
1389
+ "*.pem",
1390
+ "*.key",
1391
+ "*.p12",
1392
+ "*.pfx",
1393
+ "*.crt",
1394
+ "*credentials*",
1395
+ "*secret*",
1396
+ "id_rsa*",
1397
+ "id_ed25519*",
1398
+ "id_dsa*",
1399
+ "id_ecdsa*",
1400
+ ".npmrc",
1401
+ ".pypirc",
1402
+ "kubeconfig",
1403
+ ".kube/config",
1404
+ ".docker/config.json",
1405
+ "*password*",
1406
+ "*token*",
1407
+ "auth.json",
1408
+ ".netrc",
1409
+ ".git-credentials",
1410
+ "*.keystore",
1411
+ "*.jks",
1412
+ "service-account*.json",
1413
+ "gcloud*.json"
1414
+ ];
1415
+ var SECRET_CONTENT_PATTERNS = [
1416
+ /AKIA[0-9A-Z]{16}/g,
1417
+ // AWS Access Key ID
1418
+ /-----BEGIN\s+(RSA |DSA |EC |OPENSSH )?PRIVATE KEY-----/g,
1419
+ // Private keys
1420
+ /-----BEGIN\s+PGP PRIVATE KEY BLOCK-----/g,
1421
+ // PGP private key
1422
+ /ghp_[a-zA-Z0-9]{36}/g,
1423
+ // GitHub Personal Access Token
1424
+ /gho_[a-zA-Z0-9]{36}/g,
1425
+ // GitHub OAuth Token
1426
+ /ghs_[a-zA-Z0-9]{36}/g,
1427
+ // GitHub Server Token
1428
+ /ghu_[a-zA-Z0-9]{36}/g,
1429
+ // GitHub User Token
1430
+ /sk-[a-zA-Z0-9]{48}/g,
1431
+ // OpenAI API Key
1432
+ /sk-proj-[a-zA-Z0-9]{48}/g,
1433
+ // OpenAI Project Key
1434
+ /xox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}/g,
1435
+ // Slack tokens
1436
+ /eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*/g,
1437
+ // JWT tokens
1438
+ /AIza[0-9A-Za-z_-]{35}/g,
1439
+ // Google API Key
1440
+ /[0-9a-f]{32}-us[0-9]+/g,
1441
+ // Mailchimp API Key
1442
+ /SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}/g,
1443
+ // SendGrid API Key
1444
+ /sq0[a-z]{3}-[0-9A-Za-z_-]{22}/g
1445
+ // Square tokens
1446
+ ];
1447
+ var DEFAULT_FILE_EXTENSIONS = [
1448
+ ".ts",
1449
+ ".tsx",
1450
+ ".js",
1451
+ ".jsx",
1452
+ ".mjs",
1453
+ ".cjs",
1454
+ ".py",
1455
+ ".go",
1456
+ ".rs",
1457
+ ".java",
1458
+ ".kt",
1459
+ ".scala",
1460
+ ".rb",
1461
+ ".php",
1462
+ ".cs",
1463
+ ".cpp",
1464
+ ".c",
1465
+ ".h",
1466
+ ".hpp",
1467
+ ".swift",
1468
+ ".vue",
1469
+ ".svelte",
1470
+ ".astro"
1471
+ ];
1472
+ var EXCLUDED_DIRS = [
1473
+ "node_modules",
1474
+ ".git",
1475
+ "dist",
1476
+ "build",
1477
+ "out",
1478
+ ".next",
1479
+ ".nuxt",
1480
+ "__pycache__",
1481
+ ".pytest_cache",
1482
+ ".mypy_cache",
1483
+ "venv",
1484
+ ".venv",
1485
+ "env",
1486
+ ".env",
1487
+ "vendor",
1488
+ "target",
1489
+ ".idea",
1490
+ ".vscode",
1491
+ "coverage",
1492
+ ".nyc_output"
1493
+ ];
1494
+ var HARD_LIMITS = {
1495
+ MAX_FILES: 100,
1496
+ MAX_FILE_SIZE: 100 * 1024,
1497
+ // 100KB per file
1498
+ MAX_TOTAL_SIZE: 1024 * 1024
1499
+ // 1MB total
1500
+ };
1501
+ var DEFAULT_OPTIONS = {
1502
+ maxFiles: 50,
1503
+ maxFileSize: 50 * 1024,
1504
+ // 50KB
1505
+ maxTotalSize: 500 * 1024,
1506
+ // 500KB
1507
+ fileTypes: DEFAULT_FILE_EXTENSIONS,
1508
+ skipSensitive: true,
1509
+ detectSecrets: true
1510
+ };
1511
+ function isSensitiveFile(filename) {
1512
+ const lowerName = filename.toLowerCase();
1513
+ for (const pattern of SENSITIVE_FILE_PATTERNS) {
1514
+ if (pattern.includes("*")) {
1515
+ const regex = new RegExp(
1516
+ "^" + pattern.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$",
1517
+ "i"
1518
+ );
1519
+ if (regex.test(lowerName)) {
1520
+ return true;
1521
+ }
1522
+ } else if (lowerName === pattern.toLowerCase() || lowerName.endsWith(`/${pattern.toLowerCase()}`)) {
1523
+ return true;
1524
+ }
1525
+ }
1526
+ return false;
1527
+ }
1528
+ function detectEmbeddedSecrets(content) {
1529
+ const detected = [];
1530
+ const secretTypes = [
1531
+ { pattern: SECRET_CONTENT_PATTERNS[0], name: "AWS Access Key" },
1532
+ { pattern: SECRET_CONTENT_PATTERNS[1], name: "Private Key" },
1533
+ { pattern: SECRET_CONTENT_PATTERNS[2], name: "PGP Private Key" },
1534
+ { pattern: SECRET_CONTENT_PATTERNS[3], name: "GitHub PAT" },
1535
+ { pattern: SECRET_CONTENT_PATTERNS[4], name: "GitHub OAuth Token" },
1536
+ { pattern: SECRET_CONTENT_PATTERNS[5], name: "GitHub Server Token" },
1537
+ { pattern: SECRET_CONTENT_PATTERNS[6], name: "GitHub User Token" },
1538
+ { pattern: SECRET_CONTENT_PATTERNS[7], name: "OpenAI API Key" },
1539
+ { pattern: SECRET_CONTENT_PATTERNS[8], name: "OpenAI Project Key" },
1540
+ { pattern: SECRET_CONTENT_PATTERNS[9], name: "Slack Token" },
1541
+ { pattern: SECRET_CONTENT_PATTERNS[10], name: "JWT Token" },
1542
+ { pattern: SECRET_CONTENT_PATTERNS[11], name: "Google API Key" },
1543
+ { pattern: SECRET_CONTENT_PATTERNS[12], name: "Mailchimp API Key" },
1544
+ { pattern: SECRET_CONTENT_PATTERNS[13], name: "SendGrid API Key" },
1545
+ { pattern: SECRET_CONTENT_PATTERNS[14], name: "Square Token" }
1546
+ ];
1547
+ for (const { pattern, name } of secretTypes) {
1548
+ if (!pattern) continue;
1549
+ pattern.lastIndex = 0;
1550
+ if (pattern.test(content)) {
1551
+ detected.push(name);
1552
+ }
1553
+ }
1554
+ return detected;
1555
+ }
1556
+ function isBinaryContent(content) {
1557
+ for (let i = 0; i < Math.min(content.length, 8e3); i++) {
1558
+ if (content[i] === 0) {
1559
+ return true;
1560
+ }
1561
+ }
1562
+ return false;
1563
+ }
1564
+ function estimateTokens(content) {
1565
+ return Math.ceil(content.length / 4);
1566
+ }
1567
+ async function scanRepository(startPath, options = {}) {
1568
+ const opts = {
1569
+ ...DEFAULT_OPTIONS,
1570
+ ...options,
1571
+ maxFiles: Math.min(
1572
+ options.maxFiles ?? DEFAULT_OPTIONS.maxFiles,
1573
+ HARD_LIMITS.MAX_FILES
1574
+ ),
1575
+ maxFileSize: Math.min(
1576
+ options.maxFileSize ?? DEFAULT_OPTIONS.maxFileSize,
1577
+ HARD_LIMITS.MAX_FILE_SIZE
1578
+ ),
1579
+ maxTotalSize: Math.min(
1580
+ options.maxTotalSize ?? DEFAULT_OPTIONS.maxTotalSize,
1581
+ HARD_LIMITS.MAX_TOTAL_SIZE
1582
+ )
1583
+ };
1584
+ const repoRoot = findGitRoot(startPath);
1585
+ logger.info("Scanning repository", { repoRoot, options: opts });
1586
+ const ig = ignore();
1587
+ try {
1588
+ const gitignorePath = join3(repoRoot, ".gitignore");
1589
+ const gitignoreContent = readFileSync2(gitignorePath, "utf-8");
1590
+ ig.add(gitignoreContent);
1591
+ } catch {
1592
+ }
1593
+ ig.add(EXCLUDED_DIRS);
1594
+ const files = [];
1595
+ const skipped = [];
1596
+ const warnings = [];
1597
+ let totalSize = 0;
1598
+ let totalFilesFound = 0;
1599
+ function scanDir(dirPath, depth = 0) {
1600
+ if (depth > 20) {
1601
+ return;
1602
+ }
1603
+ if (files.length >= opts.maxFiles) {
1604
+ return;
1605
+ }
1606
+ let entries;
1607
+ try {
1608
+ entries = readdirSync(dirPath);
1609
+ } catch {
1610
+ return;
1611
+ }
1612
+ for (const entry of entries) {
1613
+ if (files.length >= opts.maxFiles) {
1614
+ break;
1615
+ }
1616
+ const fullPath = join3(dirPath, entry);
1617
+ const relativePath = relative2(repoRoot, fullPath);
1618
+ if (ig.ignores(relativePath)) {
1619
+ continue;
1620
+ }
1621
+ const validPath = resolveAndValidatePath(fullPath, repoRoot);
1622
+ if (!validPath) {
1623
+ skipped.push({
1624
+ path: relativePath,
1625
+ reason: "Path outside repository or invalid symlink"
1626
+ });
1627
+ continue;
1628
+ }
1629
+ let stats2;
1630
+ try {
1631
+ stats2 = statSync(validPath);
1632
+ } catch {
1633
+ continue;
1634
+ }
1635
+ if (stats2.isDirectory()) {
1636
+ scanDir(fullPath, depth + 1);
1637
+ } else if (stats2.isFile()) {
1638
+ totalFilesFound++;
1639
+ const ext = extname(entry).toLowerCase();
1640
+ if (!opts.fileTypes.includes(ext)) {
1641
+ continue;
1642
+ }
1643
+ if (opts.skipSensitive && isSensitiveFile(entry)) {
1644
+ skipped.push({
1645
+ path: relativePath,
1646
+ reason: "Potentially sensitive file"
1647
+ });
1648
+ warnings.push(`Skipped sensitive file: ${relativePath}`);
1649
+ continue;
1650
+ }
1651
+ if (stats2.size > opts.maxFileSize) {
1652
+ skipped.push({
1653
+ path: relativePath,
1654
+ reason: `File too large (${stats2.size} bytes)`
1655
+ });
1656
+ continue;
1657
+ }
1658
+ if (totalSize + stats2.size > opts.maxTotalSize) {
1659
+ skipped.push({
1660
+ path: relativePath,
1661
+ reason: "Total size limit reached"
1662
+ });
1663
+ continue;
1664
+ }
1665
+ let content;
1666
+ try {
1667
+ const buffer = readFileSync2(validPath);
1668
+ if (isBinaryContent(buffer)) {
1669
+ skipped.push({ path: relativePath, reason: "Binary file" });
1670
+ continue;
1671
+ }
1672
+ content = buffer.toString("utf-8");
1673
+ } catch {
1674
+ skipped.push({ path: relativePath, reason: "Could not read file" });
1675
+ continue;
1676
+ }
1677
+ if (opts.detectSecrets) {
1678
+ const secrets = detectEmbeddedSecrets(content);
1679
+ if (secrets.length > 0) {
1680
+ skipped.push({
1681
+ path: relativePath,
1682
+ reason: `Contains potential secrets: ${secrets.join(", ")}`
1683
+ });
1684
+ warnings.push(
1685
+ `Skipped file with potential secrets: ${relativePath} (${secrets.join(", ")})`
1686
+ );
1687
+ continue;
1688
+ }
1689
+ }
1690
+ files.push({ path: relativePath, content });
1691
+ totalSize += stats2.size;
1692
+ }
1693
+ }
1694
+ }
1695
+ scanDir(repoRoot);
1696
+ const stats = {
1697
+ totalFilesFound,
1698
+ totalFilesIncluded: files.length,
1699
+ totalSize,
1700
+ tokenEstimate: files.reduce((sum, f) => sum + estimateTokens(f.content), 0)
1701
+ };
1702
+ logger.info("Repository scan complete", {
1703
+ filesIncluded: files.length,
1704
+ filesSkipped: skipped.length,
1705
+ totalSize,
1706
+ tokenEstimate: stats.tokenEstimate,
1707
+ warnings: warnings.length
1708
+ });
1709
+ return {
1710
+ files,
1711
+ skipped,
1712
+ warnings,
1713
+ stats,
1714
+ repoRoot
1715
+ };
1716
+ }
1717
+ function aggregateFiles(files) {
1718
+ return files.map(
1719
+ (f) => `=== FILE: ${f.path} ===
1720
+ ${f.content}
1721
+ === END FILE: ${f.path} ===`
1722
+ ).join("\n\n");
1723
+ }
1724
+
1725
+ // src/tools/tps-audit.ts
1726
+ var tpsAuditSchemaObj = z7.object({
1727
+ path: z7.string().optional().describe(
1728
+ "Path to repo root (auto-detects current directory if not provided)"
1729
+ ),
1730
+ focus_areas: z7.array(z7.string()).optional().describe("Specific areas to focus on (e.g., 'performance', 'security')"),
1731
+ max_files: z7.number().max(100).optional().describe("Maximum files to analyze (default: 50, max: 100)"),
1732
+ file_types: z7.array(z7.string()).optional().describe("File extensions to include (e.g., ['.ts', '.js'])"),
1733
+ include_sensitive: z7.boolean().optional().describe(
1734
+ "Include potentially sensitive files (default: false, use with caution)"
1735
+ ),
1736
+ output_format: z7.enum(["html", "markdown", "json"]).optional().describe("Output format (default: html)")
1737
+ });
1738
+ var tpsAuditSchema = tpsAuditSchemaObj.shape;
1739
+ async function handleTpsAudit(client2, models, input) {
1740
+ const startPath = input.path || process.cwd();
1741
+ const outputFormat = input.output_format || "html";
1742
+ logger.info("Starting TPS audit", {
1743
+ startPath,
1744
+ maxFiles: input.max_files,
1745
+ focusAreas: input.focus_areas,
1746
+ outputFormat,
1747
+ modelCount: models.length
1748
+ });
1749
+ const scanResult = await scanRepository(startPath, {
1750
+ maxFiles: input.max_files,
1751
+ fileTypes: input.file_types,
1752
+ skipSensitive: !input.include_sensitive,
1753
+ detectSecrets: !input.include_sensitive
1754
+ });
1755
+ if (scanResult.files.length === 0) {
1756
+ logger.warn("No files found to analyze", {
1757
+ totalFilesFound: scanResult.stats.totalFilesFound,
1758
+ skipped: scanResult.skipped.length
1759
+ });
1760
+ return {
1761
+ results: [
1762
+ {
1763
+ model: "system",
1764
+ review: "No files found to analyze. Check that the repository contains supported file types and that files are not excluded by .gitignore or security filters."
1765
+ }
1766
+ ],
1767
+ models: ["system"],
1768
+ scanResult,
1769
+ analysis: null,
1770
+ outputFormat
1771
+ };
1772
+ }
1773
+ if (scanResult.warnings.length > 0) {
1774
+ logger.warn("Security warnings during scan", {
1775
+ warnings: scanResult.warnings
1776
+ });
1777
+ }
1778
+ const aggregatedContent = aggregateFiles(scanResult.files);
1779
+ logger.info("Repository scanned", {
1780
+ filesIncluded: scanResult.files.length,
1781
+ totalSize: scanResult.stats.totalSize,
1782
+ tokenEstimate: scanResult.stats.tokenEstimate
1783
+ });
1784
+ if (scanResult.stats.tokenEstimate > 1e5) {
1785
+ logger.warn("Large token count", {
1786
+ estimate: scanResult.stats.tokenEstimate
1787
+ });
1788
+ }
1789
+ const results = await client2.tpsAudit(aggregatedContent, models, {
1790
+ focusAreas: input.focus_areas,
1791
+ repoName: scanResult.repoRoot.split("/").pop()
1792
+ });
1793
+ let analysis = null;
1794
+ for (const result of results) {
1795
+ if (!result.error && result.review) {
1796
+ const { parseTpsAnalysis } = await import("./tps-audit-GNK4VIKA.js");
1797
+ analysis = parseTpsAnalysis(result.review);
1798
+ if (analysis) break;
1799
+ }
1800
+ }
1801
+ return {
1802
+ results,
1803
+ models,
1804
+ scanResult,
1805
+ analysis,
1806
+ outputFormat
1807
+ };
1808
+ }
1809
+ function formatTpsAuditResults(auditResult) {
1810
+ const { results, scanResult, analysis, outputFormat } = auditResult;
1811
+ switch (outputFormat) {
1812
+ case "html": {
1813
+ const templatePath = join4(getTemplatesDir(), "tps-report.html");
1814
+ return formatResultsAsHtml(results, templatePath, {
1815
+ analysis,
1816
+ repoName: scanResult.repoRoot.split("/").pop()
1817
+ });
1818
+ }
1819
+ case "json": {
1820
+ return JSON.stringify(
1821
+ {
1822
+ analysis,
1823
+ scanStats: scanResult.stats,
1824
+ warnings: scanResult.warnings,
1825
+ skipped: scanResult.skipped,
1826
+ modelResponses: results.map((r) => ({
1827
+ model: r.model,
1828
+ hasError: !!r.error,
1829
+ content: r.error || r.review
1830
+ }))
1831
+ },
1832
+ null,
1833
+ 2
1834
+ );
1835
+ }
1836
+ case "markdown":
1837
+ default: {
1838
+ const parts = [];
1839
+ parts.push("# TPS Audit Report\n");
1840
+ parts.push(`**Repository:** ${scanResult.repoRoot}
1841
+ `);
1842
+ parts.push(`**Files Analyzed:** ${scanResult.files.length}
1843
+ `);
1844
+ parts.push(`**Token Estimate:** ~${scanResult.stats.tokenEstimate}
1845
+ `);
1846
+ if (scanResult.warnings.length > 0) {
1847
+ parts.push("\n## Warnings\n");
1848
+ for (const w of scanResult.warnings) {
1849
+ parts.push(`- ${w}
1850
+ `);
1851
+ }
1852
+ }
1853
+ if (analysis) {
1854
+ parts.push("\n## Scores\n");
1855
+ parts.push(`- **Overall:** ${analysis.scores.overall}/100
1856
+ `);
1857
+ parts.push(`- **Flow:** ${analysis.scores.flow}/100
1858
+ `);
1859
+ parts.push(`- **Waste Efficiency:** ${analysis.scores.waste}/100
1860
+ `);
1861
+ parts.push(`- **Quality:** ${analysis.scores.quality}/100
1862
+ `);
1863
+ parts.push("\n## Summary\n");
1864
+ parts.push("\n### Strengths\n");
1865
+ for (const s of analysis.summary.strengths) {
1866
+ parts.push(`- ${s}
1867
+ `);
1868
+ }
1869
+ parts.push("\n### Concerns\n");
1870
+ for (const c of analysis.summary.concerns) {
1871
+ parts.push(`- ${c}
1872
+ `);
1873
+ }
1874
+ parts.push("\n### Quick Wins\n");
1875
+ for (const q of analysis.summary.quickWins) {
1876
+ parts.push(`- ${q}
1877
+ `);
1878
+ }
1879
+ }
1880
+ parts.push("\n## Model Perspectives\n");
1881
+ results.forEach((r) => {
1882
+ if (r.error) {
1883
+ parts.push(`
1884
+ ### ${r.model}
1885
+
1886
+ **Error:** ${r.error}
1887
+ `);
1888
+ } else {
1889
+ parts.push(`
1890
+ ### ${r.model}
1891
+
1892
+ ${r.review}
1893
+ `);
1894
+ }
1895
+ parts.push("\n---\n");
1896
+ });
1897
+ return parts.join("");
1898
+ }
1899
+ }
1900
+ }
1901
+
1242
1902
  // src/index.ts
1243
1903
  var OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY;
1244
1904
  if (!OPENROUTER_API_KEY) {
@@ -1287,6 +1947,42 @@ createReviewTool(server, {
1287
1947
  inputSchema: gitReviewSchema,
1288
1948
  handler: (input) => handleGitReview(client, CODE_REVIEW_MODELS, input)
1289
1949
  });
1950
+ server.registerTool(
1951
+ "tps_audit",
1952
+ {
1953
+ description: "Toyota Production System audit - analyze a codebase for flow, waste, bottlenecks, and quality. Scans the repository, identifies entry points, maps data flow, and provides actionable recommendations. Outputs interactive HTML report by default, or markdown/JSON.",
1954
+ inputSchema: tpsAuditSchema
1955
+ },
1956
+ async (input) => {
1957
+ try {
1958
+ logger.debug("Starting tps_audit", {
1959
+ inputKeys: Object.keys(input)
1960
+ });
1961
+ const result = await handleTpsAudit(client, TPS_AUDIT_MODELS, input);
1962
+ const formattedOutput = formatTpsAuditResults(result);
1963
+ logger.info("Completed tps_audit", {
1964
+ modelCount: result.models.length,
1965
+ filesScanned: result.scanResult.files.length,
1966
+ outputFormat: result.outputFormat,
1967
+ hasAnalysis: !!result.analysis
1968
+ });
1969
+ return {
1970
+ content: [
1971
+ {
1972
+ type: "text",
1973
+ text: formattedOutput
1974
+ }
1975
+ ]
1976
+ };
1977
+ } catch (error) {
1978
+ logger.error(
1979
+ "Error in tps_audit",
1980
+ error instanceof Error ? error : new Error(String(error))
1981
+ );
1982
+ return formatError(error);
1983
+ }
1984
+ }
1985
+ );
1290
1986
  server.registerTool(
1291
1987
  "list_review_config",
1292
1988
  { description: "Show current model configuration" },
@@ -1322,7 +2018,8 @@ async function main() {
1322
2018
  frontendReviewModels: FRONTEND_REVIEW_MODELS,
1323
2019
  backendReviewModels: BACKEND_REVIEW_MODELS,
1324
2020
  planReviewModels: PLAN_REVIEW_MODELS,
1325
- discussionModels: DISCUSSION_MODELS
2021
+ discussionModels: DISCUSSION_MODELS,
2022
+ tpsAuditModels: TPS_AUDIT_MODELS
1326
2023
  });
1327
2024
  }
1328
2025
  main().catch((error) => {
@@ -0,0 +1,11 @@
1
+ import {
2
+ SYSTEM_PROMPT,
3
+ buildUserMessage,
4
+ parseTpsAnalysis
5
+ } from "./chunk-Y77R7523.js";
6
+ export {
7
+ SYSTEM_PROMPT,
8
+ buildUserMessage,
9
+ parseTpsAnalysis
10
+ };
11
+ //# sourceMappingURL=tps-audit-GNK4VIKA.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klitchevo/code-council",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "Multi-model AI code review server using OpenRouter - get diverse perspectives from multiple LLMs in parallel",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -64,6 +64,7 @@
64
64
  "dependencies": {
65
65
  "@modelcontextprotocol/sdk": "1.25.1",
66
66
  "@openrouter/sdk": "0.3.10",
67
+ "ignore": "^7.0.5",
67
68
  "zod": "4.2.1"
68
69
  },
69
70
  "devDependencies": {