@klitchevo/code-council 0.2.0 → 0.2.3

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
@@ -119,7 +119,7 @@ npx @klitchevo/code-council review --help
119
119
 
120
120
  ## GitHub Actions
121
121
 
122
- Automatically review PRs with multiple AI models. Findings appear as **inline comments** on the exact lines of code.
122
+ Automatically review PRs with multiple AI models. Findings appear as **inline comments** on the exact lines of code. Code fixes use GitHub's **suggestion syntax** for one-click apply. Re-runs automatically clean up old comments.
123
123
 
124
124
  ### Quick Setup
125
125
 
@@ -237,7 +237,7 @@ function toNumber(val) {
237
237
  if (typeof val === "number") return val;
238
238
  if (typeof val === "string") {
239
239
  const n = parseFloat(val);
240
- return isNaN(n) ? null : n;
240
+ return Number.isNaN(n) ? null : n;
241
241
  }
242
242
  return null;
243
243
  }
@@ -312,4 +312,4 @@ export {
312
312
  buildSynthesisUserMessage,
313
313
  parseTpsAnalysis
314
314
  };
315
- //# sourceMappingURL=chunk-IVKLQD6M.js.map
315
+ //# sourceMappingURL=chunk-AEDZOTVA.js.map
@@ -15,7 +15,6 @@ function formatReport(report, format = "markdown") {
15
15
  return formatJson(report);
16
16
  case "html":
17
17
  return formatHtml(report);
18
- case "markdown":
19
18
  default:
20
19
  return formatMarkdown(report);
21
20
  }
@@ -396,4 +395,4 @@ export {
396
395
  formatter_exports,
397
396
  init_formatter
398
397
  };
399
- //# sourceMappingURL=chunk-SYMFCPGM.js.map
398
+ //# sourceMappingURL=chunk-HVF7WG6A.js.map
@@ -5,11 +5,11 @@ import {
5
5
  buildBatchUserMessage,
6
6
  buildSynthesisUserMessage,
7
7
  buildUserMessage
8
- } from "./chunk-IVKLQD6M.js";
8
+ } from "./chunk-AEDZOTVA.js";
9
9
  import {
10
10
  formatter_exports,
11
11
  init_formatter
12
- } from "./chunk-SYMFCPGM.js";
12
+ } from "./chunk-HVF7WG6A.js";
13
13
  import {
14
14
  __toCommonJS,
15
15
  init_esm_shims
@@ -929,6 +929,7 @@ You MUST respond with valid JSON matching this schema:
929
929
  "endLine": 45
930
930
  },
931
931
  "suggestion": "How to fix or improve",
932
+ "suggestedCode": "The actual corrected code that should replace the problematic code (if applicable)",
932
933
  "rawExcerpt": "The original text from the review that describes this finding",
933
934
  "confidence": 0.95
934
935
  }
@@ -956,6 +957,7 @@ ${Object.entries(SEVERITY_DESCRIPTIONS).map(([sev, desc]) => `- **${sev}**: ${de
956
957
  5. **Set confidence**: Higher (0.8-1.0) for clear, explicit issues; lower (0.5-0.7) for inferred or ambiguous ones
957
958
  6. **Don't duplicate**: Each distinct issue should appear once
958
959
  7. **Handle empty reviews**: If no issues found, return {"findings": []}
960
+ 8. **Include code fixes**: When a fix involves specific code changes, provide the actual corrected code in the suggestedCode field (not explanation, just the code)
959
961
 
960
962
  ## Important
961
963
 
@@ -1060,12 +1062,13 @@ var ExtractionResponseSchema = z2.object({
1060
1062
  description: z2.string(),
1061
1063
  location: z2.object({
1062
1064
  file: z2.string(),
1063
- line: z2.number().optional(),
1064
- endLine: z2.number().optional()
1065
- }).optional(),
1066
- suggestion: z2.string().optional(),
1065
+ line: z2.number().nullish(),
1066
+ endLine: z2.number().nullish()
1067
+ }).nullish(),
1068
+ suggestion: z2.string().nullish(),
1069
+ suggestedCode: z2.string().nullish(),
1067
1070
  rawExcerpt: z2.string(),
1068
- confidence: z2.number().min(0).max(1).optional()
1071
+ confidence: z2.number().min(0).max(1).nullish()
1069
1072
  })
1070
1073
  )
1071
1074
  });
@@ -1148,6 +1151,14 @@ function generateFindingId() {
1148
1151
  const random = Math.random().toString(36).substring(2, 8);
1149
1152
  return toFindingId(`finding-${timestamp}-${random}`);
1150
1153
  }
1154
+ function repairJson(jsonStr) {
1155
+ let repaired = jsonStr;
1156
+ repaired = repaired.replace(/,(\s*[}\]])/g, "$1");
1157
+ repaired = repaired.replace(/"([^"]*(?:\\"[^"]*)*)"/g, (match) => {
1158
+ return match.replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
1159
+ });
1160
+ return repaired;
1161
+ }
1151
1162
  function parseExtractionResponse(responseText, sourceModel) {
1152
1163
  let jsonStr = responseText.trim();
1153
1164
  if (jsonStr.startsWith("```json")) {
@@ -1159,6 +1170,7 @@ function parseExtractionResponse(responseText, sourceModel) {
1159
1170
  jsonStr = jsonStr.slice(0, -3);
1160
1171
  }
1161
1172
  jsonStr = jsonStr.trim();
1173
+ jsonStr = repairJson(jsonStr);
1162
1174
  const raw = JSON.parse(jsonStr);
1163
1175
  const parsed = ExtractionResponseSchema.parse(raw);
1164
1176
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1172,13 +1184,14 @@ function parseExtractionResponse(responseText, sourceModel) {
1172
1184
  description: f.description,
1173
1185
  location: f.location ? {
1174
1186
  file: f.location.file,
1175
- line: f.location.line,
1176
- endLine: f.location.endLine
1187
+ line: f.location.line ?? void 0,
1188
+ endLine: f.location.endLine ?? void 0
1177
1189
  } : void 0,
1178
- suggestion: f.suggestion,
1190
+ suggestion: f.suggestion ?? void 0,
1191
+ suggestedCode: f.suggestedCode ?? void 0,
1179
1192
  rawExcerpt: f.rawExcerpt,
1180
1193
  extractedAt: now,
1181
- confidence: f.confidence
1194
+ confidence: f.confidence ?? void 0
1182
1195
  })
1183
1196
  );
1184
1197
  }
@@ -2073,7 +2086,7 @@ async function buildConsensusReport(reviews, consensusClient, options = {}) {
2073
2086
  disagreements: disagreements.length,
2074
2087
  executionMs: report.executionTimeMs
2075
2088
  });
2076
- const { formatReport } = await import("./formatter-D42TRSLL.js");
2089
+ const { formatReport } = await import("./formatter-FIH7J57R.js");
2077
2090
  const formatted = formatReport(report, outputFormat);
2078
2091
  return { report, formatted };
2079
2092
  }
@@ -2217,4 +2230,4 @@ export {
2217
2230
  gitReviewSchema,
2218
2231
  handleGitReview
2219
2232
  };
2220
- //# sourceMappingURL=chunk-KO6UIMSL.js.map
2233
+ //# sourceMappingURL=chunk-ZDBY7AGR.js.map
@@ -5,9 +5,9 @@ import {
5
5
  formatForHostExtraction,
6
6
  handleGitReview,
7
7
  initializeConfig
8
- } from "./chunk-KO6UIMSL.js";
9
- import "./chunk-IVKLQD6M.js";
10
- import "./chunk-SYMFCPGM.js";
8
+ } from "./chunk-ZDBY7AGR.js";
9
+ import "./chunk-AEDZOTVA.js";
10
+ import "./chunk-HVF7WG6A.js";
11
11
  import {
12
12
  init_esm_shims
13
13
  } from "./chunk-UFR2SVK2.js";
@@ -43,7 +43,7 @@ function parseUnifiedDiff(diffText) {
43
43
  });
44
44
  }
45
45
  const match = line.match(/^diff --git a\/(.+) b\/(.+)$/);
46
- if (match && match[1] && match[2]) {
46
+ if (match?.[1] && match[2]) {
47
47
  currentFile = {
48
48
  oldPath: match[1],
49
49
  newPath: match[2],
@@ -74,7 +74,7 @@ function parseUnifiedDiff(diffText) {
74
74
  const hunkMatch = line.match(
75
75
  /@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/
76
76
  );
77
- if (hunkMatch && hunkMatch[1] && hunkMatch[3]) {
77
+ if (hunkMatch?.[1] && hunkMatch[3]) {
78
78
  const hunk = {
79
79
  oldStart: parseInt(hunkMatch[1], 10),
80
80
  oldCount: hunkMatch[2] ? parseInt(hunkMatch[2], 10) : 1,
@@ -214,7 +214,11 @@ function formatCommentBody(cluster, showModelAgreement) {
214
214
  lines.push(firstFinding.description);
215
215
  lines.push("");
216
216
  }
217
- if (firstFinding?.suggestion) {
217
+ if (firstFinding?.suggestedCode) {
218
+ lines.push("```suggestion");
219
+ lines.push(firstFinding.suggestedCode);
220
+ lines.push("```");
221
+ } else if (firstFinding?.suggestion) {
218
222
  lines.push(`**Suggestion:** ${firstFinding.suggestion}`);
219
223
  }
220
224
  return lines.join("\n").trim();
@@ -1044,4 +1048,4 @@ export {
1044
1048
  processResult,
1045
1049
  runCli
1046
1050
  };
1047
- //# sourceMappingURL=cli-M7YIR4XR.js.map
1051
+ //# sourceMappingURL=cli-PHU3RI5B.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  formatReport,
3
3
  init_formatter
4
- } from "./chunk-SYMFCPGM.js";
4
+ } from "./chunk-HVF7WG6A.js";
5
5
  import "./chunk-UFR2SVK2.js";
6
6
  init_formatter();
7
7
  export {
8
8
  formatReport
9
9
  };
10
- //# sourceMappingURL=formatter-D42TRSLL.js.map
10
+ //# sourceMappingURL=formatter-FIH7J57R.js.map
package/dist/index.js CHANGED
@@ -18,9 +18,9 @@ import {
18
18
  handleGitReview,
19
19
  initializeConfig,
20
20
  logger
21
- } from "./chunk-KO6UIMSL.js";
22
- import "./chunk-IVKLQD6M.js";
23
- import "./chunk-SYMFCPGM.js";
21
+ } from "./chunk-ZDBY7AGR.js";
22
+ import "./chunk-AEDZOTVA.js";
23
+ import "./chunk-HVF7WG6A.js";
24
24
  import {
25
25
  init_esm_shims
26
26
  } from "./chunk-UFR2SVK2.js";
@@ -1233,7 +1233,7 @@ function isSensitiveFile(filename) {
1233
1233
  for (const pattern of SENSITIVE_FILE_PATTERNS) {
1234
1234
  if (pattern.includes("*")) {
1235
1235
  const regex = new RegExp(
1236
- "^" + pattern.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$",
1236
+ `^${pattern.replace(/\./g, "\\.").replace(/\*/g, ".*")}$`,
1237
1237
  "i"
1238
1238
  );
1239
1239
  if (regex.test(lowerName)) {
@@ -1661,7 +1661,7 @@ async function handleTpsAudit(client, models, input) {
1661
1661
  });
1662
1662
  for (const result of results) {
1663
1663
  if (!result.error && result.review) {
1664
- const { parseTpsAnalysis } = await import("./tps-audit-DPIJH6JK.js");
1664
+ const { parseTpsAnalysis } = await import("./tps-audit-2DYJMPJ5.js");
1665
1665
  analysis = parseTpsAnalysis(result.review);
1666
1666
  if (analysis) break;
1667
1667
  }
@@ -1676,7 +1676,7 @@ async function handleTpsAudit(client, models, input) {
1676
1676
  };
1677
1677
  }
1678
1678
  async function handleBatchedTpsAudit(client, models, scanResult, focusAreas) {
1679
- const { parseTpsAnalysis } = await import("./tps-audit-DPIJH6JK.js");
1679
+ const { parseTpsAnalysis } = await import("./tps-audit-2DYJMPJ5.js");
1680
1680
  const repoName = scanResult.repoRoot.split("/").pop() ?? "unknown";
1681
1681
  const batches = createFileBatches(scanResult.files);
1682
1682
  const batchContents = batches.map((batch) => ({
@@ -1821,7 +1821,6 @@ function formatTpsAuditResults(auditResult) {
1821
1821
  );
1822
1822
  break;
1823
1823
  }
1824
- case "markdown":
1825
1824
  default: {
1826
1825
  const parts = [];
1827
1826
  parts.push("# TPS Audit Report\n");
@@ -1906,7 +1905,7 @@ ${r.review}
1906
1905
  var args = process.argv.slice(2);
1907
1906
  var command = args[0];
1908
1907
  if (command === "review" || command === "setup") {
1909
- import("./cli-M7YIR4XR.js").then(async ({ processResult, runCli }) => {
1908
+ import("./cli-PHU3RI5B.js").then(async ({ processResult, runCli }) => {
1910
1909
  try {
1911
1910
  const result = await runCli(process.argv);
1912
1911
  if (result) {
@@ -6,7 +6,7 @@ import {
6
6
  buildSynthesisUserMessage,
7
7
  buildUserMessage,
8
8
  parseTpsAnalysis
9
- } from "./chunk-IVKLQD6M.js";
9
+ } from "./chunk-AEDZOTVA.js";
10
10
  import "./chunk-UFR2SVK2.js";
11
11
  export {
12
12
  BATCH_SYSTEM_PROMPT,
@@ -17,4 +17,4 @@ export {
17
17
  buildUserMessage,
18
18
  parseTpsAnalysis
19
19
  };
20
- //# sourceMappingURL=tps-audit-DPIJH6JK.js.map
20
+ //# sourceMappingURL=tps-audit-2DYJMPJ5.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klitchevo/code-council",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
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",