refactor: remove redundant context from prompts, delegate to nerve-dev skill

- Remove nerveYaml injection from planner (skill has it)
- Remove sensesDir/nerveRoot from coder and tester (skill has conventions)
- Prompts now just say 'read the skill' instead of inlining knowledge
- BuildSenseGeneratorDeps reduced to { provider, cwd }
- index.ts drops getNerveYaml(), SENSES_DIR, readFileSync
This commit is contained in:
小橘 2026-04-28 04:50:21 +00:00
parent e460d64786
commit 645f0bacf2
8 changed files with 28 additions and 92 deletions

View File

@ -8,23 +8,19 @@ import type { SenseMeta } from "./moderator.js";
export type BuildSenseGeneratorDeps = {
provider: LlmProvider;
nerveRoot: string;
sensesDir: string;
nerveYaml: string;
cwd: string;
};
export function buildSenseGenerator({
provider,
nerveRoot,
sensesDir,
nerveYaml,
cwd,
}: BuildSenseGeneratorDeps): WorkflowDefinition<SenseMeta> {
return {
name: "sense-generator",
roles: {
planner: buildPlannerRole({ provider, cwd: nerveRoot, nerveYaml }),
coder: buildCoderRole({ provider, cwd: nerveRoot, sensesDir, nerveRoot }),
tester: buildTesterRole({ provider, sensesDir, nerveRoot }),
planner: buildPlannerRole({ provider, cwd }),
coder: buildCoderRole({ provider, cwd }),
tester: buildTesterRole({ provider }),
},
moderator,
};

View File

@ -1,4 +1,3 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { spawnSafe } from "@uncaged/nerve-workflow-utils";
import { buildSenseGenerator } from "./build.js";
@ -7,7 +6,6 @@ import { buildSenseGenerator } from "./build.js";
const HOME = process.env.HOME ?? "/home/azureuser";
const NERVE_ROOT = join(HOME, ".uncaged-nerve");
const SENSES_DIR = join(NERVE_ROOT, "senses");
// --- Resolve provider ---
@ -28,23 +26,11 @@ if (!apiKey || !baseUrl) {
throw new Error("Set DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL");
}
// --- Build context ---
function getNerveYaml(): string {
try {
return readFileSync(join(NERVE_ROOT, "nerve.yaml"), "utf-8");
} catch {
return "# nerve.yaml unavailable";
}
}
// --- Wire up ---
const workflow = buildSenseGenerator({
provider: { apiKey, baseUrl, model },
nerveRoot: NERVE_ROOT,
sensesDir: SENSES_DIR,
nerveYaml: getNerveYaml(),
cwd: NERVE_ROOT,
});
export default workflow;

View File

@ -11,17 +11,13 @@ export const coderMetaSchema = z.object({
export type BuildCoderDeps = {
provider: LlmProvider;
cwd: string;
sensesDir: string;
nerveRoot: string;
};
export function buildCoderRole({ provider, cwd, sensesDir, nerveRoot }: BuildCoderDeps) {
export function buildCoderRole({ provider, cwd }: BuildCoderDeps) {
return createCursorRole<CoderMeta>({
cwd,
mode: "default",
prompt: async (threadId) => coderPrompt({ threadId, sensesDir, nerveRoot }),
prompt: async (threadId) => coderPrompt({ threadId }),
extract: { provider, schema: coderMetaSchema },
});
}

View File

@ -1,16 +1,7 @@
export function coderPrompt(vars: {
threadId: string;
sensesDir: string;
nerveRoot: string;
}): string {
return `Read the workflow thread for the planner's sense design: \`nerve thread ${vars.threadId}\`
export function coderPrompt({ threadId }: { threadId: string }): string {
return `Read the workflow thread for the planner's sense design: \`nerve thread ${threadId}\`
Read the nerve-dev skill for sense file structure and conventions: \`cat node_modules/@uncaged/nerve-skills/nerve-dev/SKILL.md\`
Implement the sense. Create exactly:
1. The sense directory under ${vars.sensesDir}/<sense-name>/
2. index.js export async function compute(db, _peers), import schema from "./schema.ts"
3. schema.ts drizzle-orm/sqlite-core
4. migrations/0001_init.sql must match schema.ts
5. Update ${vars.nerveRoot}/nerve.yaml add sense config + reflex entry
Follow the patterns from existing senses. Create all files now.`;
Implement the sense following the patterns from existing senses and the skill guide.
Create all required files and update nerve.yaml.`;
}

View File

@ -11,16 +11,13 @@ export const plannerMetaSchema = z.object({
export type BuildPlannerDeps = {
provider: LlmProvider;
cwd: string;
nerveYaml: string;
};
export function buildPlannerRole({ provider, cwd, nerveYaml }: BuildPlannerDeps) {
export function buildPlannerRole({ provider, cwd }: BuildPlannerDeps) {
return createCursorRole<PlannerMeta>({
cwd,
mode: "ask",
prompt: async (threadId) => plannerPrompt({ threadId, nerveYaml }),
prompt: async (threadId) => plannerPrompt({ threadId }),
extract: { provider, schema: plannerMetaSchema },
});
}

View File

@ -1,10 +1,9 @@
export function plannerPrompt({ threadId, nerveYaml }: {
threadId: string;
nerveYaml: string;
}): string {
export function plannerPrompt({ threadId }: { threadId: string }): string {
return `You are planning a new Nerve sense.
Read the workflow thread for the user's request: \`nerve thread ${threadId}\`
Read the nerve-dev skill for sense conventions: \`cat node_modules/@uncaged/nerve-skills/nerve-dev/SKILL.md\`
Also look at existing senses in the \`senses/\` directory for patterns.
Pick a good kebab-case name for this sense. Produce a PLAN (not code) in markdown:
@ -14,14 +13,5 @@ Pick a good kebab-case name for this sense. Produce a PLAN (not code) in markdow
### Compute Logic step-by-step, specific Node.js APIs or shell commands
### Trigger Config group, interval, throttle, timeout
For reference examples of existing senses (schema, compute logic, migrations), read the nerve-dev skill:
\`cat node_modules/@uncaged/nerve-skills/nerve-dev/SKILL.md\`
Also look at existing senses in the \`senses/\` directory for patterns.
Current nerve.yaml:
\`\`\`yaml
${nerveYaml}
\`\`\`
Output ONLY the plan. Be precise and implementation-ready.`;
}

View File

@ -10,15 +10,11 @@ export const testerMetaSchema = z.object({
export type BuildTesterDeps = {
provider: LlmProvider;
sensesDir: string;
nerveRoot: string;
};
export function buildTesterRole({ provider, sensesDir, nerveRoot }: BuildTesterDeps) {
export function buildTesterRole({ provider }: BuildTesterDeps) {
return createHermesRole<TesterMeta>({
prompt: async (threadId) => testerPrompt({ threadId, sensesDir, nerveRoot }),
prompt: async (threadId) => testerPrompt({ threadId }),
extract: { provider, schema: testerMetaSchema },
});
}

View File

@ -1,32 +1,16 @@
export function testerPrompt(vars: {
threadId: string;
sensesDir: string;
nerveRoot: string;
}): string {
export function testerPrompt({ threadId }: { threadId: string }): string {
return `You are testing a newly created Nerve sense end-to-end.
Read the workflow thread for context: \`nerve thread ${vars.threadId}\`
The planner named the sense and the coder created the files.
Read the workflow thread for context: \`nerve thread ${threadId}\`
Read the nerve-dev skill for expected file structure: \`cat node_modules/@uncaged/nerve-skills/nerve-dev/SKILL.md\`
Verify the full lifecycle:
1. Check files exist under ${vars.sensesDir}/<sense-name>/
- index.js, schema.ts, migrations/0001_init.sql
All three must exist.
2. Check ${vars.nerveRoot}/nerve.yaml has the sense config and reflex entry
The sense name should appear under \`senses:\` with group, throttle, etc.
3. Run \`nerve sense list\` — confirm the sense appears in the output
1. Check all required sense files exist
2. Check nerve.yaml has the sense config
3. Run \`nerve sense list\` — confirm the sense appears
4. Run \`nerve sense trigger <sense-name>\` — should complete without error
5. Wait a few seconds, then run \`nerve sense query <sense-name>\`
Keep retrying (up to ~20 seconds) until it returns at least one row.
If it still says "0 rows", that's a failure.
6. If any step fails, run \`nerve logs\` to check for errors and include
relevant log lines in your report.
5. Run \`nerve sense query <sense-name>\` — retry up to 20s until rows appear
6. If any step fails, run \`nerve logs\` and include relevant errors
Output a clear summary: what you checked, what passed, what failed, and why.`;
}