@chappibunny/repolens 1.7.0 → 1.7.1

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
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to RepoLens will be documented in this file.
4
4
 
5
+ ## 1.7.1
6
+
7
+ ### 🛡️ AI Output Guardrails
8
+
9
+ - **System prompt hardening**: Added anti-conversational rules — AI now instructed to never offer additional work, never ask questions, never use second-person address (except onboarding), and to back every claim with context evidence.
10
+ - **Evidence-only constraints**: Architecture weaknesses, exec summary risks, and onboarding complexity hotspots now require concrete evidence from context data (cycle counts, coupling metrics, orphan files). No speculation.
11
+ - **Output sanitizer**: New `sanitizeAIOutput()` strips conversational patterns (`"If you want"`, `"I can produce"`, `"Shall I"`, `"Let me know"`) from both structured JSON and plain-text AI responses before they reach documents.
12
+ - **Structured renderer sanitization**: `renderArchitectureOverviewJSON` now sanitizes weakness bullet items, removing conversational lines even if they survive prompt-level constraints.
13
+ - **Dual-path coverage**: Sanitization applies to both structured JSON mode (Path A) and plain-text fallback (Path B), closing all AI output paths.
14
+
5
15
  ## 1.7.0
6
16
 
7
17
  ### ✨ Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chappibunny/repolens",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "description": "AI-assisted documentation intelligence system for technical and non-technical audiences",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -18,6 +18,27 @@ import {
18
18
  import { identifyFlowDependencies } from "../analyzers/flow-inference.js";
19
19
  import { info, warn } from "../utils/logger.js";
20
20
 
21
+ // Strip conversational patterns that LLMs sometimes inject into documentation
22
+ const CONVERSATIONAL_PATTERNS = [
23
+ /^(?:[-*]\s*)?if you (?:want|need|would like|prefer)[^.\n]*[.\n]/gmi,
24
+ /^(?:[-*]\s*)?(?:shall|should) I [^.\n]*[.\n]/gmi,
25
+ /^(?:[-*]\s*)?(?:let me know|feel free)[^.\n]*[.\n]/gmi,
26
+ /^(?:[-*]\s*)?I can (?:also |additionally )?(?:produce|create|generate|help|provide|suggest|recommend)[^.\n]*[.\n]/gmi,
27
+ /^(?:[-*]\s*)?(?:would you like|do you want)[^.\n]*[.\n]/gmi,
28
+ /^(?:[-*]\s*)?(?:here is|here's) (?:a |the )?(?:summary|overview|breakdown)[^.\n]*:\s*$/gmi,
29
+ ];
30
+
31
+ function sanitizeAIOutput(text) {
32
+ if (!text || typeof text !== "string") return text;
33
+ let cleaned = text;
34
+ for (const pattern of CONVERSATIONAL_PATTERNS) {
35
+ cleaned = cleaned.replace(pattern, "");
36
+ }
37
+ // Collapse multiple blank lines left by removals
38
+ cleaned = cleaned.replace(/\n{3,}/g, "\n\n").trim();
39
+ return cleaned;
40
+ }
41
+
21
42
  /**
22
43
  * Try structured JSON mode first, fall back to plain-text AI, then deterministic.
23
44
  */
@@ -41,7 +62,7 @@ async function generateWithStructuredFallback(key, promptText, maxTokens, fallba
41
62
 
42
63
  if (result.success && result.parsed) {
43
64
  const md = renderStructuredToMarkdown(key, result.parsed);
44
- if (md) return md;
65
+ if (md) return sanitizeAIOutput(md);
45
66
  }
46
67
  // If structured mode failed, fall through to plain-text
47
68
  warn(`Structured AI failed for ${key}, trying plain-text mode...`);
@@ -60,7 +81,7 @@ async function generateWithStructuredFallback(key, promptText, maxTokens, fallba
60
81
  return fallbackFn();
61
82
  }
62
83
 
63
- return result.text;
84
+ return sanitizeAIOutput(result.text);
64
85
  }
65
86
 
66
87
  export async function generateExecutiveSummary(context, enrichment = {}) {
package/src/ai/prompts.js CHANGED
@@ -57,7 +57,12 @@ Rules:
57
57
  - Do not mention AI, LLMs, or that you are an assistant.
58
58
  - No markdown tables unless specifically requested.
59
59
  - Use simple formatting: headings, paragraphs, lists.
60
- - Maximum 2 heading levels deep within sections.`;
60
+ - Maximum 2 heading levels deep within sections.
61
+ - You are producing a static document, not participating in a conversation.
62
+ - Never offer to do additional work (no "If you want", "I can also", "Let me know", "Shall I").
63
+ - Never ask the reader questions or invite follow-up.
64
+ - Never address the reader in second person ("you") unless the document type requires it (e.g. onboarding).
65
+ - Every claim must be supported by concrete evidence from the supplied context data.`;
61
66
 
62
67
  export function createExecutiveSummaryPrompt(context) {
63
68
  return `Write an executive summary for a mixed audience of technical and non-technical readers.
@@ -70,7 +75,7 @@ Requirements:
70
75
  - Explain the main system areas using the domain information.
71
76
  - Explain the business capabilities implied by the codebase structure.
72
77
  - Mention key external dependencies only if they are present in the context.
73
- - Mention architectural or operational risks if they are strongly supported by the context.
78
+ - Mention architectural or operational risks only if they are directly supported by concrete data in the context (e.g. cycle counts, orphan files, coupling metrics).
74
79
  - Do not mention file counts more than once.
75
80
  - Maximum 500 words.
76
81
  - Use this structure:
@@ -89,7 +94,9 @@ Requirements:
89
94
 
90
95
  ## Operational and architectural risks
91
96
 
92
- ## Recommended focus areas`;
97
+ ## Recommended focus areas
98
+
99
+ IMPORTANT: Only list risks and focus areas that are directly evidenced by the context data. Do not speculate.`;
93
100
  }
94
101
 
95
102
  export function createSystemOverviewPrompt(context) {
@@ -183,7 +190,9 @@ Requirements:
183
190
 
184
191
  ## Architectural strengths
185
192
 
186
- ## Architectural weaknesses`;
193
+ ## Architectural weaknesses
194
+
195
+ IMPORTANT: Only list weaknesses that are directly evidenced by the context data (e.g. cycle counts, orphan files, high coupling metrics, missing layers). Do not speculate about what the system lacks.`;
187
196
  }
188
197
 
189
198
  export function createDataFlowsPrompt(flows, context) {
@@ -249,7 +258,9 @@ Requirements:
249
258
 
250
259
  ## What to understand first
251
260
 
252
- ## Known complexity hotspots`;
261
+ ## Known complexity hotspots
262
+
263
+ IMPORTANT: Only cite complexity hotspots that are supported by concrete evidence in the context (e.g. high import counts, circular dependencies, large file counts). Do not speculate about what might be complex.`;
253
264
  }
254
265
 
255
266
  export function createModuleSummaryPrompt(module, context) {
@@ -520,12 +531,22 @@ function renderBusinessDomainsJSON(d) {
520
531
  return md;
521
532
  }
522
533
 
534
+ function sanitizeBulletList(val) {
535
+ const raw = toBulletList(val);
536
+ if (!raw) return raw;
537
+ // Strip conversational lines from bullet lists
538
+ return raw.split("\n").filter(line => {
539
+ const lower = line.toLowerCase();
540
+ return !/(^|\s)(if you (?:want|need|would)|shall i |let me know|i can (?:also )?(?:produce|create|generate|help)|would you like|do you want|feel free)/i.test(lower);
541
+ }).join("\n");
542
+ }
543
+
523
544
  function renderArchitectureOverviewJSON(d) {
524
545
  let md = `# Architecture Overview\n\n`;
525
546
  md += `## Architecture Style\n\n${safeStr(d.style)}\n\n`;
526
547
  md += `## Layers\n\n${toHeadingSections(d.layers)}\n\n`;
527
- md += `## Architectural Strengths\n\n${toBulletList(d.strengths)}\n\n`;
528
- md += `## Architectural Weaknesses\n\n${toBulletList(d.weaknesses)}\n`;
548
+ md += `## Architectural Strengths\n\n${sanitizeBulletList(d.strengths)}\n\n`;
549
+ md += `## Architectural Weaknesses\n\n${sanitizeBulletList(d.weaknesses)}\n`;
529
550
  return md;
530
551
  }
531
552