From a811660a3360d6d69166b0f688fe09cd8efe997b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=A9=98?= Date: Tue, 28 Apr 2026 03:32:51 +0000 Subject: [PATCH] refactor(sense-generator): extract prompts to prompt.md templates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each role's prompt is now a separate markdown file with {{mustache}} placeholders, loaded at module init and interpolated at runtime. 小橘 🍊(NEKO TeamοΌ‰ --- .../sense-generator/roles/coder/index.ts | 25 ++++++------- .../sense-generator/roles/coder/prompt.md | 10 ++++++ .../sense-generator/roles/planner/index.ts | 35 ++++++------------- .../sense-generator/roles/planner/prompt.md | 21 +++++++++++ 4 files changed, 52 insertions(+), 39 deletions(-) create mode 100644 workflows/sense-generator/roles/coder/prompt.md create mode 100644 workflows/sense-generator/roles/planner/prompt.md diff --git a/workflows/sense-generator/roles/coder/index.ts b/workflows/sense-generator/roles/coder/index.ts index 0f1d328..5c9966d 100644 --- a/workflows/sense-generator/roles/coder/index.ts +++ b/workflows/sense-generator/roles/coder/index.ts @@ -1,8 +1,14 @@ import { createCursorRole } from "@uncaged/nerve-workflow-utils"; +import { readFileSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; import { resolveDashScopeProvider, NERVE_ROOT, SENSES_DIR } from "../shared.js"; import { coderMetaSchema } from "../types.js"; import type { SenseMeta } from "../types.js"; +const __dirname = dirname(fileURLToPath(import.meta.url)); +const PROMPT = readFileSync(join(__dirname, "prompt.md"), "utf-8"); + export async function buildCoderRole() { const provider = await resolveDashScopeProvider(); if (provider === null) { @@ -12,19 +18,10 @@ export async function buildCoderRole() { cwd: NERVE_ROOT, mode: "default", prompt: async (threadId) => - `Read the workflow thread for the planner's sense design: \`nerve thread ${threadId}\` - -Implement the sense. Create exactly: -1. The sense directory under ${SENSES_DIR}// -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 ${NERVE_ROOT}/nerve.yaml β€” add sense config + reflex entry - -Follow the patterns from existing senses. Create all files now.`, - extract: { - provider, - schema: coderMetaSchema, - }, + PROMPT + .replace("{{threadId}}", threadId) + .replace("{{sensesDir}}", SENSES_DIR) + .replace("{{nerveRoot}}", NERVE_ROOT), + extract: { provider, schema: coderMetaSchema }, }); } diff --git a/workflows/sense-generator/roles/coder/prompt.md b/workflows/sense-generator/roles/coder/prompt.md new file mode 100644 index 0000000..b8ac1dd --- /dev/null +++ b/workflows/sense-generator/roles/coder/prompt.md @@ -0,0 +1,10 @@ +Read the workflow thread for the planner's sense design: `nerve thread {{threadId}}` + +Implement the sense. Create exactly: +1. The sense directory under {{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 {{nerveRoot}}/nerve.yaml β€” add sense config + reflex entry + +Follow the patterns from existing senses. Create all files now. diff --git a/workflows/sense-generator/roles/planner/index.ts b/workflows/sense-generator/roles/planner/index.ts index 9e49c4c..b52cb84 100644 --- a/workflows/sense-generator/roles/planner/index.ts +++ b/workflows/sense-generator/roles/planner/index.ts @@ -1,8 +1,13 @@ import { createCursorRole } from "@uncaged/nerve-workflow-utils"; +import { readFileSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; import { resolveDashScopeProvider, buildSenseExamples, getNerveYaml, NERVE_ROOT } from "../shared.js"; import { plannerMetaSchema } from "../types.js"; import type { SenseMeta } from "../types.js"; +const __dirname = dirname(fileURLToPath(import.meta.url)); +const PROMPT = readFileSync(join(__dirname, "prompt.md"), "utf-8"); const senseExamples = buildSenseExamples(); const nerveYaml = getNerveYaml(); @@ -15,30 +20,10 @@ export async function buildPlannerRole() { cwd: NERVE_ROOT, mode: "ask", prompt: async (threadId) => - `You are planning a new Nerve sense. - -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: - -## Sense Design -### Name β€” kebab-case -### Fields β€” name, type (integer/real/text), description -### Compute Logic β€” step-by-step, specific Node.js APIs or shell commands -### Trigger Config β€” group, interval, throttle, timeout - -Reference senses: -${senseExamples} - -Current nerve.yaml: -\`\`\`yaml -${nerveYaml} -\`\`\` - -Output ONLY the plan. Be precise and implementation-ready.`, - extract: { - provider, - schema: plannerMetaSchema, - }, + PROMPT + .replace("{{threadId}}", threadId) + .replace("{{senseExamples}}", senseExamples) + .replace("{{nerveYaml}}", nerveYaml), + extract: { provider, schema: plannerMetaSchema }, }); } diff --git a/workflows/sense-generator/roles/planner/prompt.md b/workflows/sense-generator/roles/planner/prompt.md new file mode 100644 index 0000000..434e6ef --- /dev/null +++ b/workflows/sense-generator/roles/planner/prompt.md @@ -0,0 +1,21 @@ +You are planning a new Nerve sense. + +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: + +## Sense Design +### Name β€” kebab-case +### Fields β€” name, type (integer/real/text), description +### Compute Logic β€” step-by-step, specific Node.js APIs or shell commands +### Trigger Config β€” group, interval, throttle, timeout + +Reference senses: +{{senseExamples}} + +Current nerve.yaml: +```yaml +{{nerveYaml}} +``` + +Output ONLY the plan. Be precise and implementation-ready.