@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 +10 -0
- package/package.json +1 -1
- package/src/ai/generate-sections.js +23 -2
- package/src/ai/prompts.js +28 -7
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
|
@@ -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
|
|
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${
|
|
528
|
-
md += `## Architectural Weaknesses\n\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
|
|