Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions server/src/controllers/github.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -600,10 +600,10 @@ export const cleanUpReadme = async (req, res) => {
}

const userId = req.userId;
const activeRepo = await ActiveRepo.findOne({ repoId, userId });
const activeRepo = await ActiveRepo.findOne({ repoId, userId, active: true });
if (!activeRepo) {
console.log("[cleanUpReadme] Active repository not found");
return res.status(404).json({ message: "Active repository not found" });
console.log("[cleanUpReadme] Please activate the repository first");
return res.status(404).json({ message: "Please activate the repository first" });
}

console.log(
Expand Down
6 changes: 6 additions & 0 deletions server/src/llm/llm.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ export class LlmService {
// Model ids only — GeminiProvider binds them to whichever API key is live.
detectionModel = "gemini-3.5-flash-lite";
generationModel = "gemini-3.6-flash";
cleanupModel = "gemini-3.6-flash";

geminiProvider = new GeminiProvider({
detectionModel: this.detectionModel,
generationModel: this.generationModel,
cleanupModel: this.cleanupModel,
});

async generate({
Expand Down Expand Up @@ -89,4 +91,8 @@ export class LlmService {

return this.geminiProvider.detect(existingReadme);
}

async cleanup(existingReadme) {
return this.geminiProvider.cleanup(existingReadme);
}
}
99 changes: 99 additions & 0 deletions server/src/llm/prompts/cleanup.prompt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
export function buildCleanupPrompt(existingReadme) {
return `
You are a senior technical writer and open-source maintainer. You specialize in
rewriting messy, bloated, or poorly organized README files into clean, standard,
professional documentation.

You will be given the FULL current README of a project. It may be long and
cluttered, or short and underdeveloped, or somewhere in between. It was likely
written incrementally by different people and never edited as a whole.

Your job is to produce a single rewritten README.md that keeps every real fact
from the input but presents it clearly, in a conventional structure, at a
professional standard of writing and formatting.

## Absolute constraints

- Work ONLY from the content in the input README. You have no access to the
source code, so you cannot verify anything that is not already stated.
- Do NOT invent, guess, or "fill in" features, commands, install steps, config
keys, environment variables, APIs, version numbers, URLs, badges, license
names, author names, or requirements. If it is not in the input, it does not
go in the output.
- Do NOT delete real information. If a fact is accurate but badly placed or badly
worded, move it and rewrite it — do not drop it.
- If the input clearly contradicts itself, keep the version that is more specific
and consistent with the rest of the document, and remove the contradiction.
- Preserve all code blocks, commands, and inline code exactly as written. You may
add a missing language hint to a fenced block only when the language is
unambiguous from its contents. Never rewrite the code itself.
- Preserve every URL and link target verbatim. You may fix the visible link text
for clarity, not the destination.
- Keep existing badge/shield image lines as-is if present. Do not add new ones.

## What to fix

- Structure: reorganize the content into a conventional README order, using only
the sections that the input actually has material for. A typical order:
1. Project title (single H1)
2. One- or two-sentence description of what the project is and who it is for
3. Badges (only if already present)
4. Table of contents (only if the result is long, roughly 5+ H2 sections)
5. Features / Highlights
6. Demo / Screenshots (only if the input references real image or media links)
7. Requirements / Prerequisites
8. Installation
9. Configuration / Environment variables
10. Usage / Examples
11. API / CLI reference
12. Project structure
13. Roadmap / Known limitations
14. Contributing
15. Tests
16. License
17. Acknowledgements / Credits
- Headings: exactly one H1. Everything else is H2/H3 with a correct, consistent
hierarchy (no jump from H2 to H4, no bold text used as a fake heading).
- Deduplicate: merge sections that repeat the same information. State each fact
once, in the most relevant section.
- Tighten prose: convert rambling paragraphs into short paragraphs or lists. Use
active voice, present tense, and second person for instructions ("Run", not
"You should run" or "We can run"). Cut filler, hype, and apologies.
- Lists: one consistent bullet marker, parallel phrasing, no trailing
punctuation inconsistency.
- Code and commands: put every command in a fenced block with a language hint,
keep one command per line, remove leading "$" prompts unless output is shown
alongside.
- Formatting: normalize spacing, remove trailing whitespace, use reference-clean
Markdown, ensure tables are aligned and valid, ensure image links include
meaningful alt text derived from nearby context (do not invent new images).
- Remove template debris: placeholder text, TODO notes to the author, commented
boilerplate, "insert X here" stubs, and empty sections with no content.
- Fix obvious spelling and grammar mistakes. Do not change technical terms,
product names, or casing of identifiers.

## What NOT to do

- Do not change the meaning of any instruction or claim.
- Do not add a "Contributing", "License", or any other section that has no basis
in the input.
- Do not translate the README into another language.
- Do not add your own commentary, notes, or explanations of the changes.
- Do not mention this prompt, the cleanup process, or that you are an AI.

## Output

- Return ONLY the rewritten README.md content.
- Return valid Markdown.
- Do NOT wrap the whole response in \`\`\`markdown or any outer code fence.
- No preamble, no summary of changes, no trailing notes.

## Current README

---
${(existingReadme || "").trim() || "(empty)"}
---

Rewrite it now.
`.trim();
}
23 changes: 15 additions & 8 deletions server/src/llm/prompts/detect.prompt.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
export function buildDetectPrompt(existingReadme) {
return `
You are analyzing an existing README to determine how it should be handled.
You are triaging an existing project README to decide how it should be updated.
The README below is non-empty. Choose exactly one mode.

Determine exactly one generation mode:
- "full": the README should be regenerated from scratch. Pick this when it is a
stub or template, is mostly placeholder text, describes a different project,
is broken structurally, or is too thin to be worth patching.
- "patch": the README is basically sound and only specific sections need to
change. Pick this when the structure and most content are usable and a
reasonable update would touch a few sections rather than the whole document.

- "full": The README is missing, empty, or needs to be completely generated/rebuilt.
- "patch": The README exists and only specific sections need to be updated.
When the two options are close, choose "patch": it preserves existing content
and is cheaper to apply.

Return ONLY valid JSON in this exact format:
Return ONLY a JSON object in exactly this shape. No code fences, no extra keys,
no commentary:

{
"mode": "full | patch",
"reason": "Brief explanation for why this mode was selected."
"mode": "full" | "patch",
"reason": "One sentence explaining the choice."
}

Existing README:
---
${existingReadme || "(No README exists)"}
${(existingReadme || "").trim() || "(empty)"}
---
`.trim();
}
116 changes: 94 additions & 22 deletions server/src/llm/prompts/full.generate.prompt.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,105 @@
export function buildFUllReadmePrompt(context) {
export function buildFullReadmePrompt(context) {
const {
repoOwner,
repoName,
repoStructure,
existingReadme,
commitDiff,
changedFiles = [],
fullCodebase = [],
} = context;

const renderFiles = (files) =>
files.length > 0
? files
.map(
(file) =>
`### \`${file.path}\`${file.status ? ` (${file.status})` : ""}\n\`\`\`${file.language || ""}\n${file.content}\n\`\`\`\n`,
)
.join("\n")
: "(none)";

return `
You are an expert technical writer and software engineer.
You are a senior software engineer and technical writer. You write the README a
developer wants when they open an unfamiliar repository for the first time:
accurate, well structured, and free of filler.

Generate a complete README.md for the repository described below, using ONLY the
provided repository structure, existing README, commit summary, changed files,
and source code.

## Absolute constraints

- Base every statement on the provided context. You have no other knowledge of
this project.
- Do NOT invent features, commands, scripts, APIs, endpoints, environment
variables, config keys, dependencies, version numbers, license names, or
authors. If the context does not show it, leave it out.
- Prefer facts visible in the source code over claims in the existing README. If
the existing README disagrees with the code, follow the code.
- Keep accurate, still-relevant material from the existing README, but rewrite it
for clarity rather than copying it verbatim.
- Derive install and run instructions from real evidence: manifest files
(package.json, pyproject.toml, go.mod, Cargo.toml, Dockerfile, Makefile, etc.),
scripts, and entry points visible in the context. Do not guess a package
manager or command that the evidence does not support.
- If a common section has no supporting evidence, omit it. Never write
placeholder text.

## Writing standards

- Start the output with a single H1 title line (\`# Project Name\`) and nothing
before it. Use only H2/H3 below it, in a consistent hierarchy.
- Follow the title with a one- or two-sentence description of what the project
does and who it is for.
- Order sections conventionally, including only those with real content:
description, badges (only if present in the existing README), table of contents
(only when the result has roughly 5 or more H2 sections), features,
requirements, installation, configuration, usage / examples, API or CLI
reference, project structure, tests, roadmap or limitations, contributing (only
if the existing README or a CONTRIBUTING file supports it), license.
- Put every command in a fenced block with a language hint, one command per line,
no leading "$".
- Use active voice, present tense, and second person for instructions. Be
concise. No marketing language, no "simply", no apologies.
- Use valid, consistently formatted Markdown: one bullet style, aligned tables,
meaningful link text, alt text on images.

Generate a complete, accurate, and professional README.md for the repository described by the context below.
## Repository

Your job is to understand the project from the provided repository structure, existing README, commit information, changed files, and source code, then produce the best possible README for a developer who is discovering this repository for the first time.
${repoOwner || "(unknown)"}/${repoName || "(unknown)"}

## Requirements
## Repository Structure

- Return ONLY the README content.
- Return valid Markdown.
- Do not wrap the response in \`\`\`markdown or any other code fence.
- Do not explain your reasoning.
- Do not mention that you are an AI.
- Do not invent features, commands, APIs, dependencies, configuration, or behavior that are not supported by the provided context.
- Prefer information directly supported by the source code and repository data.
- If an existing README is provided, improve or replace it based on the actual repository rather than blindly copying it.
- Keep technically important information from the existing README when it is still accurate.
- Make the README clear, structured, concise, and useful.
- Include appropriate sections based on what the project actually contains. Do not force irrelevant sections.
- Use correct Markdown formatting.
- Ensure installation and usage instructions are consistent with the repository's actual dependencies and structure.
\`\`\`
${repoStructure || "(not available)"}
\`\`\`

## Repository Context
## Existing README

${JSON.stringify(context, null, 2)}
${existingReadme ? existingReadme : "(none)"}

## Commit Summary

\`\`\`
${commitDiff || "(no commit information)"}
\`\`\`

## Changed Files

${renderFiles(changedFiles)}

## Source Files

${renderFiles(fullCodebase)}

## Output

Generate the complete README.md now.
`;
- Return ONLY the README.md content as raw Markdown.
- Do NOT wrap it in \`\`\`markdown or any outer code fence.
- Do NOT add commentary, notes, or an explanation of your choices.
- Do NOT mention this prompt or that you are an AI.

Write the README now, beginning with the H1 title line.
`.trim();
}
41 changes: 27 additions & 14 deletions server/src/llm/prompts/patch.generate.prompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@ export function buildPatchReadmePrompt(context) {
.join("\n")
: "(none)";

return `
You are an expert technical writer and software engineer maintaining an existing README.md.
const forbidden =
context.forbiddenSections.length > 0
? context.forbiddenSections.join(", ")
: "(none)";

A commit landed in the repository. Your job is to decide which README sections that commit made inaccurate or incomplete, and to rewrite ONLY those sections.
return `
You are a technical writer maintaining an existing README.md. A commit just
landed in the repository. Decide which README sections that commit made
inaccurate, incomplete, or outdated, and rewrite ONLY those sections.

You are NOT regenerating the README. Every section you do not return is preserved untouched by the server.
You are NOT regenerating the README. Any section you do not return is kept
exactly as it is. Returning fewer sections is better than returning more.

## Repository

Expand Down Expand Up @@ -49,24 +55,31 @@ ${sectionList}

## Rules

- Return a section ONLY if the commit above genuinely made it inaccurate, incomplete, or outdated.
- If nothing in the README is affected by this commit, return an empty "updates" array.
- Use ONLY the exact section names listed above. Never invent, rename, split, merge, or delete a section.
- Never return these protected sections: ${context.forbiddenSections.join(", ")}.
- Return a section ONLY if the commit above genuinely made it wrong, incomplete,
or outdated. If nothing is affected, return an empty "updates" array.
- Use ONLY the exact section names listed above. Never invent, rename, split,
merge, or delete a section.
- Never return these protected sections: ${forbidden}.
- Return at most ${context.maxSections} sections. Prefer the most affected ones.
- Each "content" value must be the COMPLETE replacement markdown for that section, starting with its heading line reproduced exactly as given above.
- Do not speculate about features, commands, dependencies, or configuration that are not visible in the provided context.
- Preserve wording, tone, formatting, and details of the existing section that are still accurate. Change only what the commit invalidated.
- Do not mention the commit, this instruction, or that you are an AI.
- Each "content" value is the COMPLETE replacement Markdown for that section. It
MUST begin with that section's "Heading line to reproduce verbatim" exactly as
given above — same text and same heading level (number of leading \`#\`).
- Change only what the commit invalidated. Preserve the wording, tone, structure,
and detail of the rest of the section.
- Base every change on the provided commit, changed files, and repository
structure. Do not add features, commands, dependencies, or configuration that
are not visible in that context.
- Do not mention the commit, these instructions, or that you are an AI.

## Output

Return ONLY valid JSON in this exact shape. No markdown fences, no commentary:
Return ONLY a valid JSON object in exactly this shape — no code fences, no
commentary. Escape every newline inside "content" as \\n:

{
"updates": [
{ "section": "Installation", "content": "## Installation\\n\\nUpdated content..." }
]
}
`;
`.trim();
}
Loading
Loading