Some checks are pending
CI / build-check-test (push) Waiting to run
New extensions shipping with Agent JAE: - jae-rules: Per-project .jae/rules.md injection - cost-tracker: Token/cost tracking with budget limits - project-dna: Auto-analyze project fingerprint - teach-mode: Pedagogical explanation mode - bookmarks: File bookmark management - replay: Session recording and asciicast export - checkpoints: Git-based undo/time-travel - pr-review: Autonomous PR diff review - auto-docs: Auto-generate README and docs - screenshot-context: Vision analysis for screenshots - deploy: One-command deploy (Docker/Vercel/rsync/static) - pair-programming: Real-time file watching with feedback - dashboard: Terminal HUD with live stats - swarm: Multi-agent parallel task execution - skill-marketplace: Search/install/publish skills - widget-api-demo: TUI widget API demonstration
139 lines
5.5 KiB
TypeScript
139 lines
5.5 KiB
TypeScript
import type { ExtensionAPI } from "@jaeswift/jae-coding-agent";
|
|
import { existsSync, readFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
|
|
export default function (pi: ExtensionAPI) {
|
|
pi.on("before_agent_start", async (_event, ctx) => {
|
|
const dnaPath = join(ctx.cwd, ".jae", "project-dna.md");
|
|
if (existsSync(dnaPath)) {
|
|
const dna = readFileSync(dnaPath, "utf-8").trim();
|
|
if (dna.length > 0) {
|
|
return { systemPrompt: `\n\n# Project DNA (auto-generated fingerprint)\n\n${dna}` };
|
|
}
|
|
}
|
|
});
|
|
|
|
pi.on("session_start", async (_event, ctx) => {
|
|
const dnaPath = join(ctx.cwd, ".jae", "project-dna.md");
|
|
if (!existsSync(dnaPath)) {
|
|
ctx.ui.notify("\u{1F9EC} No project DNA found. Run /dna to auto-analyze your project.", "info");
|
|
}
|
|
});
|
|
|
|
pi.registerCommand("dna", {
|
|
description: "Auto-analyze project and generate .jae/project-dna.md fingerprint",
|
|
handler: async (args, ctx) => {
|
|
const cwd = ctx.cwd;
|
|
const jaeDir = join(cwd, ".jae");
|
|
const dnaPath = join(jaeDir, "project-dna.md");
|
|
|
|
if (args.trim() === "show" && existsSync(dnaPath)) {
|
|
const dna = readFileSync(dnaPath, "utf-8");
|
|
ctx.ui.notify(`\u{1F9EC} Project DNA:\n${dna.substring(0, 1500)}`, "info");
|
|
return;
|
|
}
|
|
|
|
ctx.ui.notify("\u{1F50D} Analyzing project...", "info");
|
|
|
|
const sections: string[] = ["# Project DNA\n"];
|
|
|
|
// Tech stack detection
|
|
const checks = [
|
|
{ file: "package.json", label: "Node.js" },
|
|
{ file: "Cargo.toml", label: "Rust" },
|
|
{ file: "go.mod", label: "Go" },
|
|
{ file: "pyproject.toml", label: "Python (pyproject)" },
|
|
{ file: "requirements.txt", label: "Python (pip)" },
|
|
{ file: "Gemfile", label: "Ruby" },
|
|
{ file: "pom.xml", label: "Java (Maven)" },
|
|
{ file: "build.gradle", label: "Java/Kotlin (Gradle)" },
|
|
{ file: "docker-compose.yml", label: "Docker Compose" },
|
|
{ file: "Dockerfile", label: "Docker" },
|
|
{ file: ".github/workflows", label: "GitHub Actions" },
|
|
{ file: "tsconfig.json", label: "TypeScript" },
|
|
{ file: ".eslintrc.json", label: "ESLint" },
|
|
{ file: "vitest.config.ts", label: "Vitest" },
|
|
{ file: "jest.config.js", label: "Jest" },
|
|
{ file: "tailwind.config.js", label: "Tailwind CSS" },
|
|
{ file: "next.config.js", label: "Next.js" },
|
|
{ file: "vite.config.ts", label: "Vite" },
|
|
];
|
|
|
|
const detected: string[] = [];
|
|
for (const c of checks) {
|
|
if (existsSync(join(cwd, c.file))) detected.push(c.label);
|
|
}
|
|
sections.push(`## Tech Stack\n${detected.length > 0 ? detected.map((d) => `- ${d}`).join("\n") : "- Unknown"}\n`);
|
|
|
|
// Package.json details
|
|
const pkgPath = join(cwd, "package.json");
|
|
if (existsSync(pkgPath)) {
|
|
try {
|
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
sections.push(
|
|
`## Package Info\n- Name: ${pkg.name || "unknown"}\n- Version: ${pkg.version || "unknown"}\n- Description: ${pkg.description || "none"}\n`,
|
|
);
|
|
if (pkg.dependencies) {
|
|
const deps = Object.keys(pkg.dependencies).slice(0, 20);
|
|
sections.push(`## Dependencies (top 20)\n${deps.map((d) => `- ${d}`).join("\n")}\n`);
|
|
}
|
|
if (pkg.scripts) {
|
|
const scripts = Object.entries(pkg.scripts).slice(0, 15) as [string, string][];
|
|
sections.push(`## Scripts\n${scripts.map(([k, v]) => `- \`${k}\`: ${v}`).join("\n")}\n`);
|
|
}
|
|
} catch {
|
|
/* ignore parse errors */
|
|
}
|
|
}
|
|
|
|
// Git info
|
|
try {
|
|
const gitResult = await pi.exec("git", ["log", "--oneline", "-10"], { cwd });
|
|
if (gitResult.code === 0 && gitResult.stdout.trim()) {
|
|
sections.push(`## Recent Git History\n\`\`\`\n${gitResult.stdout.trim()}\n\`\`\`\n`);
|
|
}
|
|
const branchResult = await pi.exec("git", ["branch", "--show-current"], { cwd });
|
|
if (branchResult.code === 0 && branchResult.stdout.trim()) {
|
|
sections.push(`## Current Branch: ${branchResult.stdout.trim()}\n`);
|
|
}
|
|
} catch {
|
|
/* not a git repo */
|
|
}
|
|
|
|
// File stats
|
|
try {
|
|
const findResult = await pi.exec(
|
|
"find",
|
|
[".", "-maxdepth", "3", "-type", "f", "-not", "-path", "*/node_modules/*", "-not", "-path", "*/.git/*", "-not", "-path", "*/dist/*", "-not", "-path", "*/.jae/*"],
|
|
{ cwd },
|
|
);
|
|
if (findResult.code === 0) {
|
|
const files = findResult.stdout.trim().split("\n").filter(Boolean);
|
|
sections.push(`## File Count: ${files.length}\n`);
|
|
const extCounts: Record<string, number> = {};
|
|
for (const f of files) {
|
|
const ext = f.includes(".") ? (f.split(".").pop() || "none") : "(no ext)";
|
|
extCounts[ext] = (extCounts[ext] || 0) + 1;
|
|
}
|
|
const sorted = Object.entries(extCounts)
|
|
.sort((a, b) => b[1] - a[1])
|
|
.slice(0, 15);
|
|
sections.push(`## File Types\n${sorted.map(([ext, count]) => `- .${ext}: ${count}`).join("\n")}\n`);
|
|
}
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
|
|
sections.push(`\n---\nGenerated: ${new Date().toISOString()}\n`);
|
|
|
|
const dnaContent = sections.join("\n");
|
|
if (!existsSync(jaeDir)) mkdirSync(jaeDir, { recursive: true });
|
|
writeFileSync(dnaPath, dnaContent, "utf-8");
|
|
|
|
ctx.ui.notify(
|
|
`\u{1F9EC} Project DNA generated (${dnaContent.length} chars). Saved to .jae/project-dna.md`,
|
|
"info",
|
|
);
|
|
},
|
|
});
|
|
}
|