Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
import { readFile, readdir } from 'node:fs/promises';
import { existsSync } from 'node:fs';
import path from 'node:path';
import type { CopperheadConfig } from '../config.js';
import type { ConstraintRegistry } from '../memory/constraints.js';
/** SPEC §4.3 — verbatim requirements. */
const SYSTEM_RULES = `You are a hardware design agent working on real KiCad source files. Edit s-expressions surgically; never regenerate a whole file.
You cannot edit any file until your change proposal validates. Write the proposal first; the edit tools appear only after it passes.
The design docs are the memory. Read them before proposing anything; update them with everything you change.
Hold ALL constraints simultaneously: electrical budgets (e.g. sleep current), voltage ranges, package availability, strapping pins, RTC-capability, antenna keepouts. A part that satisfies the obvious constraint but violates a budget is a bug.
Check the MCU strapping table before assigning any pin. Check quiescent/leakage current of every part against the power budget in SPEC.md.
Nothing is done until ERC (and DRC when applicable) passes. Read the violation report; do not guess.
Write a one-line rationale next to every decision. If you remove a part, record why the absence is intentional (a missing pullup can look like a mistake).
If a request would violate a documented budget or constraint, stop and say so — do not silently comply.`;
const WORKFLOW = `Workflow for every run:
1. The design docs are already loaded below. Plan: state in one short block what will change, which files are affected, which constraints are at risk.
2. Call propose_change with a change id (kebab-case), why, what changes, and tasks. Then call validate_change. Edit tools (edit_file, write_file) unlock only after validation passes.
3. Make the edits. Use the exact same net names and refdes everywhere. For .kicad_sch/.kicad_pcb use edit_file with unique anchors from the actual file text (read the file first). For renaming a net or refdes across a file, one edit_file call with replace_all: true beats many small edits.
4. Run run_erc after schematic edits (and run_drc after board edits). If violations: read them, fix, re-run.
5. After schematic edits also run check_legibility and reconcile every error-severity finding (advisories inform, they do not block). An electrically correct sheet that reads badly is not done: finish refuses while error findings are outstanding, the same way it refuses on a failing ERC.
6. Run check_drift; update any doc that references a changed value/part/pin in the same run.
7. Record every non-trivial decision with record_decision, and every stated/assumed/discovered constraint with record_constraint.
8. Call finish with outcome "done" when everything is verified, or outcome "refuse" (citing the violated budget/constraint) if the request should not be done. finish will list any unmet obligations; resolve them and call it again.
Turns are the scarce resource, not tool calls: the run has a hard turn budget, and every tool call in one reply executes in the same turn. When calls are independent — multiple record_constraint or resolve_affected calls (use resolutions: [...] to clear a backlog in one call), several read_file calls — issue them together in a single reply instead of one per turn.
Always send a populated \`args\` object that matches the tool's JSON Schema (e.g. read_file needs {"path": "..."}). Never open a stage with an empty-args call to probe a tool — it only returns an error and burns a whole turn.`;
export async function buildSystemPrompt(
repoRoot: string,
config: CopperheadConfig,
constraints: ConstraintRegistry,
): Promise<string> {
const parts = [SYSTEM_RULES, '', WORKFLOW];
if (Object.keys(config.budgets).length) {
parts.push('', '## Hard budgets (from .copperhead/config.json — treat as constraints)', '');
for (const [k, v] of Object.entries(config.budgets)) parts.push(`- ${k}: ${v}`);
}
if (Object.keys(constraints).length) {
parts.push('', '## Constraint registry (.copperhead/constraints.json)', '', '```json');
parts.push(JSON.stringify(constraints, null, 2), '```');
}
const docsDir = path.join(repoRoot, config.docs);
if (existsSync(docsDir)) {
const files = (await readdir(docsDir)).filter((f) => f.endsWith('.md')).sort();
for (const f of files) {
const text = await readFile(path.join(docsDir, f), 'utf8');
parts.push('', `## docs/${f}`, '', text);
}
}
if (config.schematic) {
parts.push('', `## KiCad files`, '', `- schematic: ${config.schematic}`);
if (config.board) parts.push(`- board: ${config.board}`);
}
return parts.join('\n');
}
ff4fe56b2This file is pinned to the selected release. Download the original to open it in your design tool.