feat: add ImportAnalyzer agent and GitHub mirror endpoint
- POST /api/mirror: clones public GitHub repo and pushes to Gitea as-is - ImportAnalyzer agent: reads unknown codebase, writes CODEBASE_MAP.md and MIGRATION_PLAN.md in plain language for non-technical founders - Register ImportAnalyzer agent and prompt in agents/index.ts Made-with: Cursor
This commit is contained in:
12
src/agents/import-analyzer.ts
Normal file
12
src/agents/import-analyzer.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { registerAgent, pick } from './registry';
|
||||||
|
|
||||||
|
registerAgent({
|
||||||
|
name: 'ImportAnalyzer',
|
||||||
|
description: 'Reads an imported codebase end-to-end and produces CODEBASE_MAP.md and MIGRATION_PLAN.md',
|
||||||
|
model: 'B',
|
||||||
|
promptId: 'import-analyzer',
|
||||||
|
tools: pick([
|
||||||
|
'read_file', 'write_file', 'list_directory', 'find_files', 'search_code',
|
||||||
|
'git_commit_and_push',
|
||||||
|
])
|
||||||
|
});
|
||||||
@@ -4,6 +4,7 @@ import '../prompts/coder';
|
|||||||
import '../prompts/pm';
|
import '../prompts/pm';
|
||||||
import '../prompts/marketing';
|
import '../prompts/marketing';
|
||||||
import '../prompts/atlas';
|
import '../prompts/atlas';
|
||||||
|
import '../prompts/import-analyzer';
|
||||||
|
|
||||||
// Import agent files — side effects register each agent into the registry
|
// Import agent files — side effects register each agent into the registry
|
||||||
import './orchestrator';
|
import './orchestrator';
|
||||||
@@ -11,6 +12,7 @@ import './coder';
|
|||||||
import './pm';
|
import './pm';
|
||||||
import './marketing';
|
import './marketing';
|
||||||
import './atlas';
|
import './atlas';
|
||||||
|
import './import-analyzer';
|
||||||
|
|
||||||
// Re-export public API
|
// Re-export public API
|
||||||
export { AgentConfig, AGENTS, getAgent, allAgents, pick } from './registry';
|
export { AgentConfig, AGENTS, getAgent, allAgents, pick } from './registry';
|
||||||
|
|||||||
97
src/prompts/import-analyzer.ts
Normal file
97
src/prompts/import-analyzer.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import { registerPrompt } from './loader';
|
||||||
|
|
||||||
|
registerPrompt('import-analyzer', `
|
||||||
|
You are a senior software architect performing a codebase audit on a newly imported project.
|
||||||
|
Your job is to thoroughly read the entire codebase, understand what it does and how it's built,
|
||||||
|
then produce two documents: CODEBASE_MAP.md and MIGRATION_PLAN.md.
|
||||||
|
|
||||||
|
## Your goal
|
||||||
|
|
||||||
|
The founder who owns this project is non-technical. They need to understand what they have
|
||||||
|
before deciding what to do with it. Write everything in plain language — no jargon, no
|
||||||
|
assumptions that they know what "Docker" or "BigQuery" means without a brief explanation.
|
||||||
|
|
||||||
|
## Step 1 — Explore the full codebase
|
||||||
|
|
||||||
|
Use list_directory and find_files to map every folder and file.
|
||||||
|
Use read_file to read key files:
|
||||||
|
- README files (any depth)
|
||||||
|
- package.json, requirements.txt, pyproject.toml (understand dependencies)
|
||||||
|
- next.config.*, vite.config.*, Dockerfile, docker-compose.yml (understand deployment)
|
||||||
|
- Any existing .md documentation
|
||||||
|
- Main entry point files (index.ts, main.py, app.py, server.ts, etc.)
|
||||||
|
- Environment variable files (.env.example — NEVER read actual .env files)
|
||||||
|
|
||||||
|
Do NOT read every file. Read enough to understand the purpose and structure of each component.
|
||||||
|
|
||||||
|
## Step 2 — Write CODEBASE_MAP.md
|
||||||
|
|
||||||
|
Create this file at the root of the repo. Structure it like this:
|
||||||
|
|
||||||
|
# Codebase Map
|
||||||
|
|
||||||
|
## What this project does
|
||||||
|
[1–2 sentences in plain language explaining what the product is]
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### [Component name] — [folder path]
|
||||||
|
**Type:** [Web app / API server / Background job / AI agent / Scripts / etc.]
|
||||||
|
**Language/Framework:** [e.g. Next.js 14 + TypeScript]
|
||||||
|
**What it does:** [1–2 sentences plain language]
|
||||||
|
**Status:** [Active / Incomplete / Legacy / Unknown]
|
||||||
|
**Can deploy to Coolify:** [Yes / No / Maybe — with brief reason]
|
||||||
|
|
||||||
|
[repeat for each component]
|
||||||
|
|
||||||
|
## External Services Required
|
||||||
|
[List every external service the project depends on, with a plain-language explanation of what it does]
|
||||||
|
- **[Service name]**: [What it is, e.g. "Google BigQuery — stores all the analytics data"]
|
||||||
|
|
||||||
|
## Tech Stack Summary
|
||||||
|
[Bullet list of languages and key frameworks]
|
||||||
|
|
||||||
|
## What's missing
|
||||||
|
[Any obvious gaps: no tests, no CI, missing config files, etc.]
|
||||||
|
|
||||||
|
## Step 3 — Write MIGRATION_PLAN.md
|
||||||
|
|
||||||
|
Create this file at the root of the repo. Structure it like this:
|
||||||
|
|
||||||
|
# Migration Plan
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
[2–3 sentences: what's in good shape, what needs work, overall recommendation]
|
||||||
|
|
||||||
|
## Recommended Actions
|
||||||
|
|
||||||
|
### Deploy immediately (ready as-is)
|
||||||
|
[List components that can be deployed to Coolify right now, with the folder path and any config notes]
|
||||||
|
|
||||||
|
### Keep on existing infrastructure
|
||||||
|
[List components that should stay where they are and why — e.g. GCP Cloud Functions that depend on BigQuery]
|
||||||
|
|
||||||
|
### Migrate with work required
|
||||||
|
[List components that could move to Coolify but need changes first]
|
||||||
|
|
||||||
|
### Archive or remove
|
||||||
|
[Anything that looks abandoned, duplicate, or no longer needed]
|
||||||
|
|
||||||
|
## First steps
|
||||||
|
[Numbered list of the 3–5 most important things to do, in order, written for a non-technical founder]
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
[Things I couldn't determine from the code alone that the founder should clarify]
|
||||||
|
|
||||||
|
## Step 4 — Commit both files
|
||||||
|
|
||||||
|
Once both documents are written, commit them with:
|
||||||
|
message: "docs: add CODEBASE_MAP and MIGRATION_PLAN from import analysis"
|
||||||
|
|
||||||
|
## Important rules
|
||||||
|
- Never modify any existing files — only create the two new .md files
|
||||||
|
- Never read .env files or files with credentials
|
||||||
|
- Write for a non-technical founder — explain everything plainly
|
||||||
|
- If you can't understand something, say so honestly in the document
|
||||||
|
- Be specific: name actual files, folders, line counts, frameworks
|
||||||
|
`.trim());
|
||||||
@@ -92,6 +92,73 @@ app.get('/health', (_req: Request, res: Response) => {
|
|||||||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// GitHub mirror — clone a public GitHub repo and push to Gitea as-is
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
app.post('/api/mirror', async (req: Request, res: Response) => {
|
||||||
|
const { github_url, gitea_repo, project_name } = req.body as {
|
||||||
|
github_url?: string;
|
||||||
|
gitea_repo?: string; // e.g. "mark/opsos"
|
||||||
|
project_name?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!github_url || !gitea_repo) {
|
||||||
|
res.status(400).json({ error: '"github_url" and "gitea_repo" are required' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { execSync } = await import('child_process');
|
||||||
|
const fs = await import('fs');
|
||||||
|
const path = await import('path');
|
||||||
|
const os = await import('os');
|
||||||
|
|
||||||
|
const mirrorId = `mirror_${Date.now()}`;
|
||||||
|
const tmpDir = path.join(os.tmpdir(), mirrorId);
|
||||||
|
|
||||||
|
const gitea = {
|
||||||
|
apiUrl: process.env.GITEA_API_URL || '',
|
||||||
|
apiToken: process.env.GITEA_API_TOKEN || '',
|
||||||
|
username: process.env.GITEA_USERNAME || ''
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Build authenticated Gitea push URL
|
||||||
|
// GITEA_API_URL is like https://git.vibnai.com — strip /api/v1 if present
|
||||||
|
const giteaBase = gitea.apiUrl.replace(/\/api\/v1\/?$/, '');
|
||||||
|
const authedPushUrl = `${giteaBase}/${gitea_repo}.git`
|
||||||
|
.replace('https://', `https://${gitea.username}:${gitea.apiToken}@`);
|
||||||
|
|
||||||
|
console.log(`[mirror] Cloning ${github_url} → ${tmpDir}`);
|
||||||
|
fs.mkdirSync(tmpDir, { recursive: true });
|
||||||
|
|
||||||
|
// Mirror-clone the GitHub repo (preserves all branches + tags)
|
||||||
|
execSync(`git clone --mirror "${github_url}" "${tmpDir}/.git"`, {
|
||||||
|
stdio: 'pipe',
|
||||||
|
timeout: 120_000
|
||||||
|
});
|
||||||
|
execSync(`git config --bool core.bare false`, { cwd: tmpDir, stdio: 'pipe' });
|
||||||
|
execSync(`git checkout`, { cwd: tmpDir, stdio: 'pipe' });
|
||||||
|
|
||||||
|
// Point origin at Gitea and push all refs
|
||||||
|
execSync(`git remote set-url origin "${authedPushUrl}"`, { cwd: tmpDir, stdio: 'pipe' });
|
||||||
|
execSync(`git push --mirror origin`, { cwd: tmpDir, stdio: 'pipe', timeout: 120_000 });
|
||||||
|
|
||||||
|
console.log(`[mirror] Pushed ${gitea_repo} successfully`);
|
||||||
|
res.json({ success: true, gitea_repo, github_url });
|
||||||
|
} catch (err) {
|
||||||
|
const msg = err instanceof Error ? err.message : String(err);
|
||||||
|
console.error(`[mirror] Failed:`, msg);
|
||||||
|
res.status(500).json({ error: 'Mirror failed', details: msg });
|
||||||
|
} finally {
|
||||||
|
// Clean up temp dir
|
||||||
|
try {
|
||||||
|
const { execSync: rm } = await import('child_process');
|
||||||
|
rm(`rm -rf "${tmpDir}"`, { stdio: 'pipe' });
|
||||||
|
} catch { /* best effort */ }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// List available agents
|
// List available agents
|
||||||
app.get('/api/agents', (_req: Request, res: Response) => {
|
app.get('/api/agents', (_req: Request, res: Response) => {
|
||||||
const agents = Object.values(AGENTS).map(a => ({
|
const agents = Object.values(AGENTS).map(a => ({
|
||||||
|
|||||||
Reference in New Issue
Block a user