refactor: remove buildSenseExamples, use @uncaged/nerve-skills for agent discovery

- Delete buildSenseExamples() (~25 lines of runtime file reading)
- Remove senseExamples from BuildSenseGeneratorDeps and BuildPlannerDeps
- Planner prompt now directs agent to read nerve-dev skill via npm package
- Clean up unused existsSync import

Closes xiaoju/nerve-workspace#2
This commit is contained in:
小橘 2026-04-28 04:38:33 +00:00
parent 69eb4ffe49
commit fc2ca13dc3
4 changed files with 10 additions and 40 deletions

View File

@ -10,7 +10,6 @@ export type BuildSenseGeneratorDeps = {
provider: LlmProvider; provider: LlmProvider;
nerveRoot: string; nerveRoot: string;
sensesDir: string; sensesDir: string;
senseExamples: string;
nerveYaml: string; nerveYaml: string;
}; };
@ -18,13 +17,12 @@ export function buildSenseGenerator({
provider, provider,
nerveRoot, nerveRoot,
sensesDir, sensesDir,
senseExamples,
nerveYaml, nerveYaml,
}: BuildSenseGeneratorDeps): WorkflowDefinition<SenseMeta> { }: BuildSenseGeneratorDeps): WorkflowDefinition<SenseMeta> {
return { return {
name: "sense-generator", name: "sense-generator",
roles: { roles: {
planner: buildPlannerRole({ provider, cwd: nerveRoot, senseExamples, nerveYaml }), planner: buildPlannerRole({ provider, cwd: nerveRoot, nerveYaml }),
coder: buildCoderRole({ provider, cwd: nerveRoot, sensesDir, nerveRoot }), coder: buildCoderRole({ provider, cwd: nerveRoot, sensesDir, nerveRoot }),
tester: buildTesterRole({ provider, sensesDir, nerveRoot }), tester: buildTesterRole({ provider, sensesDir, nerveRoot }),
}, },

View File

@ -1,4 +1,4 @@
import { existsSync, readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import { join } from "node:path"; import { join } from "node:path";
import { spawnSafe } from "@uncaged/nerve-workflow-utils"; import { spawnSafe } from "@uncaged/nerve-workflow-utils";
import { buildSenseGenerator } from "./build.js"; import { buildSenseGenerator } from "./build.js";
@ -38,39 +38,12 @@ function getNerveYaml(): string {
} }
} }
function buildSenseExamples(): string {
const examples: string[] = [];
for (const name of ["cpu-usage", "linux-system-health"]) {
const dir = join(SENSES_DIR, name);
if (!existsSync(dir)) continue;
const indexFile = existsSync(join(dir, "index.js"))
? readFileSync(join(dir, "index.js"), "utf-8")
: "";
const schema = existsSync(join(dir, "schema.ts"))
? readFileSync(join(dir, "schema.ts"), "utf-8")
: "";
const migrationDir = join(dir, "migrations");
let migration = "";
if (existsSync(join(migrationDir, "0001_init.sql"))) {
migration = readFileSync(join(migrationDir, "0001_init.sql"), "utf-8");
}
examples.push(
`### Example sense: ${name}\n\n` +
`**index.js:**\n\`\`\`js\n${indexFile}\n\`\`\`\n\n` +
`**schema.ts:**\n\`\`\`ts\n${schema}\n\`\`\`\n\n` +
`**migrations/0001_init.sql:**\n\`\`\`sql\n${migration}\n\`\`\``,
);
}
return examples.join("\n\n---\n\n");
}
// --- Wire up --- // --- Wire up ---
const workflow = buildSenseGenerator({ const workflow = buildSenseGenerator({
provider: { apiKey, baseUrl, model }, provider: { apiKey, baseUrl, model },
nerveRoot: NERVE_ROOT, nerveRoot: NERVE_ROOT,
sensesDir: SENSES_DIR, sensesDir: SENSES_DIR,
senseExamples: buildSenseExamples(),
nerveYaml: getNerveYaml(), nerveYaml: getNerveYaml(),
}); });

View File

@ -7,15 +7,14 @@ import { plannerPrompt } from "./prompt.js";
export type BuildPlannerDeps = { export type BuildPlannerDeps = {
provider: LlmProvider; provider: LlmProvider;
cwd: string; cwd: string;
senseExamples: string;
nerveYaml: string; nerveYaml: string;
}; };
export function buildPlannerRole({ provider, cwd, senseExamples, nerveYaml }: BuildPlannerDeps) { export function buildPlannerRole({ provider, cwd, nerveYaml }: BuildPlannerDeps) {
return createCursorRole<PlannerMeta>({ return createCursorRole<PlannerMeta>({
cwd, cwd,
mode: "ask", mode: "ask",
prompt: async (threadId) => plannerPrompt({ threadId, senseExamples, nerveYaml }), prompt: async (threadId) => plannerPrompt({ threadId, nerveYaml }),
extract: { provider, schema: plannerMetaSchema }, extract: { provider, schema: plannerMetaSchema },
}); });
} }

View File

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