Repo Prompt Generator
Paste any GitHub repo URL and generate a structured AI agent prompt file. Detects tech stack, identifies build/test commands, and extracts project structure — all 100% client-side via the GitHub public API. Choose from CLAUDE.md, AGENTS.md, or .cursorrules format presets.
🔧 Try the Repo Prompt Generator
Paste a GitHub repo URL below. The tool fetches metadata via the public GitHub API, detects the tech stack, and generates a structured AI agent prompt file. No data leaves your browser — all requests go directly to api.github.com.
📦 Languages & Tech Stack
📄 Config Files Detected
Generated Prompt
GitHub API Fetch & Metadata Extraction
Why learn this
Every AI coding agent — Claude Code, Codex CLI, Cursor, Windsurf — relies on a context file (CLAUDE.md, AGENTS.md, .cursorrules) to understand a project before making changes. Manually writing these files is tedious and error-prone. Automating the generation from GitHub metadata turns a 15-minute chore into a 2-second operation, and ensures every prompt captures the project's actual structure, not a stale description.
What you built and why this way
GitHub public API vs authenticated. Using api.github.com without auth is deliberately chosen to keep the tool zero-setup — anyone with a browser can use it. The tradeoff: unauthenticated requests are rate-limited to 60/hour per IP. For a client-side tool that serves many users, this is acceptable because each user has their own IP and the page caches results in-memory for the session. The Accept: application/vnd.github.v3+json header requests the default REST API v3 payload.
Three parallel API calls. The tool fires GET /repos/{owner}/{repo}, GET /repos/{owner}/{repo}/languages, and GET /repos/{owner}/{repo}/readme simultaneously via Promise.all(). This cuts total time to roughly the slowest single request (~200-600ms). The languages endpoint returns byte counts per language, which we normalize into percentages. The readme body is base64-encoded — atob() decodes it for description extraction.
Config file probing with fallback. After the main fetch, the tool probes for critical config files (package.json, requirements.txt, Cargo.toml, go.mod, Makefile, Dockerfile, etc.) via individual GET /repos/{owner}/{repo}/contents/{path} calls. Each probe is wrapped in a try-catch so a 404 on one file doesn't block the rest. The detected files inform build commands, test commands, and dependency lists in the generated prompt.
URL parsing flexibility. The parseGitHubUrl() regex accepts github.com/owner/repo, github.com/owner/repo.git, and github.com/owner/repo/tree/branch variants. The .git suffix and trailing path segments are stripped to get the clean owner/repo pair.
Key concepts
Promise.all()— Fires multiple fetch() calls in parallel. One rejection rejects the whole group, so wrap fragile probes in individual try-catchatob()/btoa()— Base64 decode/encode in browsers. GitHub's contents API returnscontentas base64 withencoding: "base64"- GitHub API
X-RateLimit-Remainingheader — Check viaresponse.headers.get()to surface rate-limit warnings. Available on every API response - GitHub's
languagesendpoint — Returns{ "Python": 45000, "JavaScript": 12000 }— raw byte counts that we normalize to percentages
Alternative approaches
- Authenticated API — Passing a
Authorization: token ghp_xxxheader raises the rate limit to 5,000/hour and gives access to private repos. Tradeoff: users must paste a token, adding friction and security concerns - Local git clone + analysis — Clone the repo and analyze it locally with file-system APIs. Unlimited analysis but adds clone time and disk usage. Good for a CLI tool, impractical for a web page
- GraphQL API — GitHub's GraphQL v4 endpoint lets you fetch repo metadata, languages, and README in a single query. More efficient but the query syntax is more complex. v3 REST is easier to debug and teach
Browser compatibility
fetch()API: supported in all browsers since Chrome 42, Firefox 40, Safari 10.1. No polyfill needed for modern browsersPromise.all()andasync/await: supported in Chrome 55+, Firefox 52+, Safari 10.1+. Transpile if targeting IE11atob(): supported in all browsers since IE10+. Handles ASCII and UTF-8 (usedecodeURIComponent+escapefor full Unicode)navigator.clipboard.writeText(): supported in Chrome 66+, Firefox 63+, Safari 13.1+. Falls back toexecCommand('copy')for older browsers
Performance notes
- Three parallel API calls complete in ~200-600ms total on a typical connection. Config file probing adds ~100-400ms depending on how many files exist
- The generated prompt is constructed entirely in-memory — no DOM operations during the build phase. The textarea is updated in a single assignment
- GitHub API responses are cached in a
Mapkeyed by owner/repo so re-generating with a different format preset doesn't re-fetch
Common pitfalls
- Rate limiting on shared IPs — Office/college networks share a single IP, so 60 requests/hour can be exhausted by multiple users. The tool detects
X-RateLimit-Remaining: 0and shows a clear message. Adding a personal access token field would resolve this - Large README files — The README API returns up to 1MB of base64 content. Some READMEs contain embedded images as base64 data, making decoding slow. The tool caps README extraction to the first 500 characters
- Monorepo structure — A monorepo with a root
package.jsonand ten workspacepackage.jsonfiles can overwhelm the config probe. The tool limits probing to root-level files only
Next up
Step 2 adds the prompt generation engine that transforms the fetched metadata into structured CLAUDE.md, AGENTS.md, and .cursorrules formats. Jump to Step 2 →
<div class="input-section">
<input type="url" id="repoUrl"
placeholder="https://github.com/owner/repo" />
<button class="btn btn-primary">
Generate
</button>
</div>
<div class="repo-meta" id="repoMeta">
<div class="rm-col">
<h3>
<a id="rmName" href="#">
repository
</a>
</h3>
<p class="rm-desc" id="rmDesc">
description
</p>
<div class="rm-stats">
<span>stars</span>
<span>forks</span>
</div>
</div>
</div> .repo-meta {
display: none;
padding: 1em;
background: #0d101a;
border: 1px solid var(--border);
border-radius: 10px;
gap: 1.5em;
flex-wrap: wrap;
}
.repo-meta.visible {
display: flex;
animation: fadeIn 0.3s ease;
}
.ts-badges {
display: flex;
gap: 0.4em;
flex-wrap: wrap;
}
.ts-badge {
padding: 0.25em 0.6em;
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: 100px;
font-size: 0.72em;
color: var(--text-dim);
}
.ts-badge .ts-pct {
color: var(--text-muted);
margin-left: 0.2em;
} function parseGitHubUrl(url) {
const m = url.match(
/github\\.com\\/([^\\/]+)\\/
([^\\/\\s?#\\.]+)/);
return m ? {
owner: m[1],
repo: m[2]
} : null;
}
async function fetchRepoData(owner, repo) {
const base = `https://api.github.com/
repos/$lbrace;owner}/$lbrace;repo}`;
const headers = {
'Accept':
'application/vnd.github.v3+json'
};
const [repoRes, langRes, readmeRes] =
await Promise.all([
fetch(base, { headers }),
fetch(`$lbrace;base}/languages`,
{ headers }),
fetch(`$lbrace;base}/readme`,
{ headers })
]);
if (!repoRes.ok)
throw new Error(
`Not found ($lbrace;repoRes.status})`);
const repo = await repoRes.json();
const languages = await langRes.json();
let readme = '';
if (readmeRes.ok) {
const rd = await readmeRes.json();
readme = atob(rd.content);
}
return { repo, languages, readme };
} The GitHub API rate limit of 60 unauthenticated requests/hour per IP is the main bottleneck. To raise it, pass an Authorization: token YOUR_TOKEN header — this gives 5,000 requests/hour. You can generate a token at github.com/settings/tokens with no scopes needed (public repos only).
Prompt Generation Engine — Format-Preserving Templates
Why learn this
A well-structured agent prompt is the difference between an AI that "gets" your project and one that hallucinates its way through a codebase. Each format — CLAUDE.md, AGENTS.md, .cursorrules — has a distinct audience and convention. The generation engine maps the same raw metadata (repo name, description, languages, dependencies, build commands, project structure) into the right structure for each format, preserving the conventions that AI agents expect.
What you built and why this way
Three template functions, one data source. Instead of building three separate generation pipelines, the tool normalizes all fetched data into a single RepoData object then passes it to format-specific renderers: generateClaudeMD(), generateAgentsMD(), and generateCursorRules(). This keeps the data fetching logic decoupled from the presentation templates — adding a fourth format (e.g., .windsurfrules) is just one more function.
Build command inference vs explicit detection. Not every repo has a Makefile or scripts.build in package.json. The engine uses a heuristic pipeline: check package.json scripts, then Makefile targets, then Cargo.toml/ go.mod conventions, and finally falls back to language-typical commands (e.g., python -m pytest for Python repos). The fallback is surfaced with a "probable" label so the agent knows the command is inferred, not authoritative.
CLAUDE.md format (standard). Starts with a project overview and description, then a "## Commands" section with build/test/lint/run commands, then "## Tech Stack" with languages and key libraries, then "## Project Structure" with notable directories. Claude Desktop and Claude Code both read this format natively. The instruction block at the bottom tells Claude how to behave — concise responses, focus on current task, use existing patterns.
AGENTS.md format (agent-oriented). Mirrors the style used in the hermes-agent project and other agent frameworks. Adds "## Agent Memory" and "## Conventions" sections that give the agent explicit guidance on code style, commit messages, and architectural patterns. More verbose than CLAUDE.md but more prescriptive for autonomous agents that operate without human oversight.
.cursorrules format (YAML-like). Uses the conventions Cursor IDE expects — a YAML-like preamble with You are an expert in... followed by ## Tech Stack, ## Commands, and ## Rules sections. The rules section includes project-specific conventions expressed as "always" and "never" statements. Cursor injects this file into the system prompt context for every chat.
Key concepts
- Template literals —
`string ${expression}`in JavaScript builds the prompt string with interpolated data. Each template function returns a plain string - Heuristic inference — When explicit data is missing, the engine uses patterns like
has("pytest") || has("unittest") => "pytest"to guess the test command. These are labeled as inferred so the user can verify - Section ordering — Different agents prioritize different sections. Claude prefers commands first (most actionable), agents prefer conventions (most behavioral), Cursor prefers tech stack (most contextual)
Alternative approaches
- LLM-generated prompts — Send the raw repo data to an LLM and ask it to write the CLAUDE.md. More natural language but adds cost, latency, and unpredictability — the LLM might invent build commands or dependencies. Deterministic templates are safer for tool-generated files
- File-based template loading — Load .md templates from a server or git repo. More flexible for updating formats without redeploying the page. Tradeoff: adds a network dependency and latency to prompt generation
- User-customizable sections — Let users add/remove/reorder sections before copying. More flexible but adds UI complexity. The current three-preset approach covers the most common use cases
Browser compatibility
- Template literals (
`${}`): supported in Chrome 41+, Firefox 34+, Safari 9+. No transpilation needed for modern browsers Array.map().join('')for list construction: supported in all browsers since IE9+. Combined with template literals for readable section generationObject.keys()andObject.entries():entries()needs Chrome 54+, Firefox 47+, Safari 10.1+.keys()works everywhere
Performance notes
- Prompt generation is pure string manipulation — runs in ~0.1ms for a typical repo. The three format renderers share the same data object
- The prompt text is written to a
<textarea>viavalue = ...— a single DOM write. No incremental rendering needed - Format switching (CLAUDE.md → AGENTS.md) doesn't re-fetch data — the cached
RepoDatais re-rendered through the new template function
Common pitfalls
- Markdown collision in generated output — A project description containing characters like
*,_, or#can break the generated markdown. The engine escapes these in inline positions but preserves them in code blocks - Overlong prompts — A monorepo with 40 dependencies produces a very long CLAUDE.md. The engine caps dependency lists at 20 entries with a "and N more..." note. The full list is a readability vs completeness tradeoff
- Inferred commands that are wrong — The fallback heuristic guesses
pytestfor Python repos but the project might useunittestornose. The "(inferred)" label alerts the user to verify before committing the file
Next up
Both steps complete! Try the interactive tool below to generate a prompt for any public GitHub repo.
<div class="output-section"
id="outputSection">
<div class="output-header">
<h3 id="outputTitle">
Generated Prompt
</h3>
<button class="btn btn-success"
id="copyBtn">
Copy
</button>
</div>
<textarea id="outputArea"
readonly spellcheck="false">
</textarea>
</div> function generateClaudeMD(data) {
const lines = [];
lines.push(
`# $lbrace;data.name} — $lbrace;
data.description}`);
lines.push(`## Commands`);
if (data.buildCmd)
lines.push(
`- Build: $lbrace;data.buildCmd}`);
if (data.testCmd)
lines.push(
`- Test: $lbrace;data.testCmd}`);
if (data.devCmd)
lines.push(
`- Dev: $lbrace;data.devCmd}`);
if (data.lintCmd)
lines.push(
`- Lint: $lbrace;data.lintCmd}`);
lines.push(`## Tech Stack`);
for (const [lang, pct]
of Object.entries(data.languages))
lines.push(
`- $lbrace;lang} $lbrace;pct}%`);
// ... sections for deps, structure ...
return lines.join('\n');
} let currentData = null;
function generatePrompt(data, format) {
switch (format) {
case 'claude':
return generateClaudeMD(data);
case 'agents':
return generateAgentsMD(data);
case 'cursor':
return generateCursorRules(data);
default:
return generateClaudeMD(data);
}
}
// Format toggle buttons
document.querySelectorAll(
'[data-preset]').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll(
'[data-preset]')
.forEach(b => b.classList
.remove('active'));
btn.classList.add('active');
if (currentData) {
const format = btn.dataset.preset;
outputArea.value =
generatePrompt(
currentData, format);
}
});
});
// Copy to clipboard
copyBtn.addEventListener(
'click', async () => {
const text = outputArea.value;
if (navigator.clipboard) {
await navigator.clipboard
.writeText(text);
copyBtn.textContent = 'Copied!';
setTimeout(() => {
copyBtn.textContent = 'Copy';
}, 2000);
}
}); Claude Code reads CLAUDE.md from the project root and injects it as system context on every task. The format matters — Claude prioritizes the "Commands" section highest because it needs to run builds and tests frequently. Put the most actionable information first. For Cursor, .cursorrules uses a YAML-like preamble that Cursor folds into the system prompt — keep rules concise and specific.
Lessons Learned — Build Process
The design decisions, tradeoffs, and insights from building this tool.
The repo info, languages, and README are fetched in parallel via Promise.all(). Config file probing is sequential (to avoid hammering GitHub's API with simultaneous requests) but each probe has its own try-catch so a 404 doesn't cascade. The total time is ~400-900ms for a typical repo — fast enough to feel instant.
Detecting build commands from package.json scripts is reliable. Detecting them from a bare Python repo without a Makefile is guesswork. The engine labels inferred commands with "(inferred)" so the user knows to double-check. A future enhancement could scan the recent commit history for CLI patterns using GET /repos/{owner}/{repo}/commits.
CSS/JS code inside <pre> blocks needs {/} or {/} to avoid Astro expression parsing. The entities render as normal braces in the browser. This is a recurring pattern across all tool tutorials on ToolBrain.
The output format toggle lets users compare how the same data renders in CLAUDE.md vs AGENTS.md vs .cursorrules. Each format prioritizes different sections — Claude puts commands first, agents put conventions first, Cursor puts tech stack first. Users learn format conventions by toggling, not by reading docs.
Building interactive client-side tools that compose with external APIs is a sweet spot for ToolBrain — no backend, no auth, no deployment beyond a static file. The tradeoff (rate limits, limited probing) is manageable when the user understands the constraints.