From 645f0bacf238a50fbdb32d0356b188f51b65366f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=A9=98?= Date: Tue, 28 Apr 2026 04:50:21 +0000 Subject: [PATCH] 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 --- workflows/sense-generator/build.ts | 14 +++----- workflows/sense-generator/index.ts | 16 +--------- .../sense-generator/roles/coder/index.ts | 8 ++--- .../sense-generator/roles/coder/prompt.ts | 19 +++-------- .../sense-generator/roles/planner/index.ts | 7 ++-- .../sense-generator/roles/planner/prompt.ts | 16 ++-------- .../sense-generator/roles/tester/index.ts | 8 ++--- .../sense-generator/roles/tester/prompt.ts | 32 +++++-------------- 8 files changed, 28 insertions(+), 92 deletions(-) diff --git a/workflows/sense-generator/build.ts b/workflows/sense-generator/build.ts index 3ceb7c9..2ca74f7 100644 --- a/workflows/sense-generator/build.ts +++ b/workflows/sense-generator/build.ts @@ -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 { 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, }; diff --git a/workflows/sense-generator/index.ts b/workflows/sense-generator/index.ts index 73ddd30..41d7e41 100644 --- a/workflows/sense-generator/index.ts +++ b/workflows/sense-generator/index.ts @@ -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; diff --git a/workflows/sense-generator/roles/coder/index.ts b/workflows/sense-generator/roles/coder/index.ts index 6f71f54..a3e2291 100644 --- a/workflows/sense-generator/roles/coder/index.ts +++ b/workflows/sense-generator/roles/coder/index.ts @@ -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({ cwd, mode: "default", - prompt: async (threadId) => coderPrompt({ threadId, sensesDir, nerveRoot }), + prompt: async (threadId) => coderPrompt({ threadId }), extract: { provider, schema: coderMetaSchema }, }); } - - diff --git a/workflows/sense-generator/roles/coder/prompt.ts b/workflows/sense-generator/roles/coder/prompt.ts index 676a10e..151822b 100644 --- a/workflows/sense-generator/roles/coder/prompt.ts +++ b/workflows/sense-generator/roles/coder/prompt.ts @@ -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}// -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.`; } diff --git a/workflows/sense-generator/roles/planner/index.ts b/workflows/sense-generator/roles/planner/index.ts index 1e07783..a59fbeb 100644 --- a/workflows/sense-generator/roles/planner/index.ts +++ b/workflows/sense-generator/roles/planner/index.ts @@ -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({ cwd, mode: "ask", - prompt: async (threadId) => plannerPrompt({ threadId, nerveYaml }), + prompt: async (threadId) => plannerPrompt({ threadId }), extract: { provider, schema: plannerMetaSchema }, }); } - - diff --git a/workflows/sense-generator/roles/planner/prompt.ts b/workflows/sense-generator/roles/planner/prompt.ts index dcfe0ed..bb6cc73 100644 --- a/workflows/sense-generator/roles/planner/prompt.ts +++ b/workflows/sense-generator/roles/planner/prompt.ts @@ -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.`; } diff --git a/workflows/sense-generator/roles/tester/index.ts b/workflows/sense-generator/roles/tester/index.ts index dd81b7b..b8f66be 100644 --- a/workflows/sense-generator/roles/tester/index.ts +++ b/workflows/sense-generator/roles/tester/index.ts @@ -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({ - prompt: async (threadId) => testerPrompt({ threadId, sensesDir, nerveRoot }), + prompt: async (threadId) => testerPrompt({ threadId }), extract: { provider, schema: testerMetaSchema }, }); } - - diff --git a/workflows/sense-generator/roles/tester/prompt.ts b/workflows/sense-generator/roles/tester/prompt.ts index 6ac6471..bcac156 100644 --- a/workflows/sense-generator/roles/tester/prompt.ts +++ b/workflows/sense-generator/roles/tester/prompt.ts @@ -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}// - - 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 \` — should complete without error - -5. Wait a few seconds, then run \`nerve sense query \` - 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 \` — 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.`; }