feat: add reviewer role to all three workflows
- workflow-generator, sense-generator, gitea-issue-solver all now have: planner → coder → reviewer → tester → committer → END - Reviewer uses createHermesRole with git diff/status for static analysis - Checks: garbage files, secrets, debug code, unrelated changes - Planner prompt now requires Role Behavior sections for every role - Coder prompt now emphasizes reading initial user prompt for specifics
This commit is contained in:
parent
bd89dcaff6
commit
daf07b5746
@ -1,18 +1,21 @@
|
|||||||
import type { WorkflowDefinition } from "@uncaged/nerve-core";
|
import type { WorkflowDefinition } from "@uncaged/nerve-core";
|
||||||
|
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
||||||
import { moderator } from "./moderator.js";
|
import { moderator } from "./moderator.js";
|
||||||
import type { WorkflowMeta } from "./moderator.js";
|
import type { WorkflowMeta } from "./moderator.js";
|
||||||
import { buildIntakeRole } from "./roles/intake/index.js";
|
import { buildIntakeRole } from "./roles/intake/index.js";
|
||||||
import { buildIssueReaderRole } from "./roles/issue-reader/index.js";
|
import { buildIssueReaderRole } from "./roles/issue-reader/index.js";
|
||||||
import { buildPlannerRole } from "./roles/planner/index.js";
|
import { buildPlannerRole } from "./roles/planner/index.js";
|
||||||
import { buildImplementerRole } from "./roles/implementer/index.js";
|
import { buildImplementerRole } from "./roles/implementer/index.js";
|
||||||
|
import { buildReviewerRole } from "./roles/reviewer/index.js";
|
||||||
import { buildTesterRole } from "./roles/tester/index.js";
|
import { buildTesterRole } from "./roles/tester/index.js";
|
||||||
import { buildPrPublisherRole } from "./roles/pr-publisher/index.js";
|
import { buildPrPublisherRole } from "./roles/pr-publisher/index.js";
|
||||||
|
|
||||||
export type BuildGiteaIssueSolverDeps = {
|
export type BuildGiteaIssueSolverDeps = {
|
||||||
nerveRoot: string;
|
nerveRoot: string;
|
||||||
|
provider: LlmProvider | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function buildGiteaIssueSolver({ nerveRoot }: BuildGiteaIssueSolverDeps): WorkflowDefinition<WorkflowMeta> {
|
export function buildGiteaIssueSolver({ nerveRoot, provider }: BuildGiteaIssueSolverDeps): WorkflowDefinition<WorkflowMeta> {
|
||||||
return {
|
return {
|
||||||
name: "gitea-issue-solver",
|
name: "gitea-issue-solver",
|
||||||
roles: {
|
roles: {
|
||||||
@ -20,6 +23,7 @@ export function buildGiteaIssueSolver({ nerveRoot }: BuildGiteaIssueSolverDeps):
|
|||||||
"issue-reader": buildIssueReaderRole({ nerveRoot }),
|
"issue-reader": buildIssueReaderRole({ nerveRoot }),
|
||||||
planner: buildPlannerRole({ nerveRoot }),
|
planner: buildPlannerRole({ nerveRoot }),
|
||||||
implementer: buildImplementerRole({ nerveRoot }),
|
implementer: buildImplementerRole({ nerveRoot }),
|
||||||
|
reviewer: provider ? buildReviewerRole({ provider, nerveRoot }) : buildReviewerRole({ provider: { apiKey: "", baseUrl: "", model: "" }, nerveRoot }),
|
||||||
tester: buildTesterRole({ nerveRoot }),
|
tester: buildTesterRole({ nerveRoot }),
|
||||||
"pr-publisher": buildPrPublisherRole({ nerveRoot }),
|
"pr-publisher": buildPrPublisherRole({ nerveRoot }),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { buildGiteaIssueSolver } from "./build.js";
|
import { buildGiteaIssueSolver } from "./build.js";
|
||||||
|
import { resolveDashScopeProvider } from "./lib/provider.js";
|
||||||
|
|
||||||
const HOME = process.env.HOME ?? "/home/azureuser";
|
const HOME = process.env.HOME ?? "/home/azureuser";
|
||||||
const NERVE_ROOT = join(HOME, ".uncaged-nerve");
|
const NERVE_ROOT = join(HOME, ".uncaged-nerve");
|
||||||
|
|
||||||
const workflow = buildGiteaIssueSolver({ nerveRoot: NERVE_ROOT });
|
const provider = await resolveDashScopeProvider(NERVE_ROOT);
|
||||||
|
|
||||||
|
const workflow = buildGiteaIssueSolver({ nerveRoot: NERVE_ROOT, provider });
|
||||||
|
|
||||||
export default workflow;
|
export default workflow;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import type { IntakeMeta } from "./roles/intake/index.js";
|
|||||||
import type { IssueReaderMeta } from "./roles/issue-reader/index.js";
|
import type { IssueReaderMeta } from "./roles/issue-reader/index.js";
|
||||||
import type { PlannerMeta } from "./roles/planner/index.js";
|
import type { PlannerMeta } from "./roles/planner/index.js";
|
||||||
import type { ImplementerMeta } from "./roles/implementer/index.js";
|
import type { ImplementerMeta } from "./roles/implementer/index.js";
|
||||||
|
import type { ReviewerMeta } from "./roles/reviewer/index.js";
|
||||||
import type { TesterMeta } from "./roles/tester/index.js";
|
import type { TesterMeta } from "./roles/tester/index.js";
|
||||||
import type { PrPublisherMeta } from "./roles/pr-publisher/index.js";
|
import type { PrPublisherMeta } from "./roles/pr-publisher/index.js";
|
||||||
|
|
||||||
@ -13,6 +14,7 @@ export type WorkflowMeta = {
|
|||||||
"issue-reader": IssueReaderMeta;
|
"issue-reader": IssueReaderMeta;
|
||||||
planner: PlannerMeta;
|
planner: PlannerMeta;
|
||||||
implementer: ImplementerMeta;
|
implementer: ImplementerMeta;
|
||||||
|
reviewer: ReviewerMeta;
|
||||||
tester: TesterMeta;
|
tester: TesterMeta;
|
||||||
"pr-publisher": PrPublisherMeta;
|
"pr-publisher": PrPublisherMeta;
|
||||||
};
|
};
|
||||||
@ -42,7 +44,13 @@ export const moderator: Moderator<WorkflowMeta> = (context) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (last.role === "implementer") {
|
if (last.role === "implementer") {
|
||||||
return "tester";
|
return "reviewer";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last.role === "reviewer") {
|
||||||
|
const meta = last.meta as WorkflowMeta["reviewer"];
|
||||||
|
if (meta.approved) return "tester";
|
||||||
|
return "implementer";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last.role === "tester") {
|
if (last.role === "tester") {
|
||||||
|
|||||||
21
workflows/gitea-issue-solver/roles/reviewer/index.ts
Normal file
21
workflows/gitea-issue-solver/roles/reviewer/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
||||||
|
import { createHermesRole } from "@uncaged/nerve-workflow-utils";
|
||||||
|
import { reviewerPrompt } from "./prompt.js";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const reviewerMetaSchema = z.object({
|
||||||
|
approved: z.boolean().describe("true if the diff is clean and ready for tester validation"),
|
||||||
|
});
|
||||||
|
export type ReviewerMeta = z.infer<typeof reviewerMetaSchema>;
|
||||||
|
|
||||||
|
export type BuildReviewerDeps = {
|
||||||
|
provider: LlmProvider;
|
||||||
|
nerveRoot: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function buildReviewerRole({ provider, nerveRoot }: BuildReviewerDeps) {
|
||||||
|
return createHermesRole<ReviewerMeta>({
|
||||||
|
prompt: async (threadId) => reviewerPrompt({ threadId, nerveRoot }),
|
||||||
|
extract: { provider, schema: reviewerMetaSchema },
|
||||||
|
});
|
||||||
|
}
|
||||||
46
workflows/gitea-issue-solver/roles/reviewer/prompt.ts
Normal file
46
workflows/gitea-issue-solver/roles/reviewer/prompt.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
export function reviewerPrompt({ threadId, nerveRoot }: { threadId: string; nerveRoot: string }): string {
|
||||||
|
return `You are a **code reviewer** for Nerve workflow changes. You run after the coder and before the tester.
|
||||||
|
|
||||||
|
**IMPORTANT: The Nerve workspace is at \`${nerveRoot}\`. Always \`cd ${nerveRoot}\` first.**
|
||||||
|
|
||||||
|
Read the workflow thread for context: \`nerve thread ${threadId}\`
|
||||||
|
|
||||||
|
## Your job — static analysis of the git diff
|
||||||
|
|
||||||
|
Run these commands and analyze the output:
|
||||||
|
|
||||||
|
1. **\`cd ${nerveRoot} && git diff --stat\`** — see what files changed
|
||||||
|
2. **\`cd ${nerveRoot} && git diff\`** — read the actual diff
|
||||||
|
3. **\`cd ${nerveRoot} && git status --short\`** — check for untracked files
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
### 🔴 Must reject (approved: false)
|
||||||
|
- **Garbage files**: node_modules/, .DS_Store, pnpm cache artifacts (e.g. \`false/\` directory), build outputs (dist/) that shouldn't be committed
|
||||||
|
- **Secrets/credentials**: API keys, tokens, passwords hardcoded in the diff
|
||||||
|
- **Unrelated changes**: files modified outside the scope of the planner's design
|
||||||
|
|
||||||
|
### ⚠️ Flag but may still approve
|
||||||
|
- Debug code left behind (console.log, debugger, TODO/FIXME without context)
|
||||||
|
- Hardcoded paths that should be configurable
|
||||||
|
- Missing type exports that other files might need
|
||||||
|
|
||||||
|
### ✅ Verify
|
||||||
|
- All files from the planner's design are created/modified
|
||||||
|
- Code follows existing patterns (compare with sibling workflows)
|
||||||
|
- Meta types are simple routing signals (single boolean per role)
|
||||||
|
- No dynamic import()
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
Summarize what you found. If garbage files or secrets are present, list them explicitly so the coder knows what to clean up.
|
||||||
|
|
||||||
|
End with:
|
||||||
|
\`\`\`json
|
||||||
|
{ "approved": true }
|
||||||
|
\`\`\`
|
||||||
|
or
|
||||||
|
\`\`\`json
|
||||||
|
{ "approved": false }
|
||||||
|
\`\`\``;
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ import type { WorkflowDefinition } from "@uncaged/nerve-core";
|
|||||||
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
||||||
import { buildPlannerRole } from "./roles/planner/index.js";
|
import { buildPlannerRole } from "./roles/planner/index.js";
|
||||||
import { buildCoderRole } from "./roles/coder/index.js";
|
import { buildCoderRole } from "./roles/coder/index.js";
|
||||||
|
import { buildReviewerRole } from "./roles/reviewer/index.js";
|
||||||
import { buildTesterRole } from "./roles/tester/index.js";
|
import { buildTesterRole } from "./roles/tester/index.js";
|
||||||
import { buildCommitterRole } from "./roles/committer/index.js";
|
import { buildCommitterRole } from "./roles/committer/index.js";
|
||||||
import { moderator } from "./moderator.js";
|
import { moderator } from "./moderator.js";
|
||||||
@ -21,6 +22,7 @@ export function buildSenseGenerator({
|
|||||||
roles: {
|
roles: {
|
||||||
planner: buildPlannerRole({ provider, cwd }),
|
planner: buildPlannerRole({ provider, cwd }),
|
||||||
coder: buildCoderRole({ provider, cwd }),
|
coder: buildCoderRole({ provider, cwd }),
|
||||||
|
reviewer: buildReviewerRole({ provider, nerveRoot: cwd }),
|
||||||
tester: buildTesterRole({ provider, nerveRoot: cwd }),
|
tester: buildTesterRole({ provider, nerveRoot: cwd }),
|
||||||
committer: buildCommitterRole({ nerveRoot: cwd }),
|
committer: buildCommitterRole({ nerveRoot: cwd }),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,12 +2,14 @@ import { END } from "@uncaged/nerve-core";
|
|||||||
import type { Moderator } from "@uncaged/nerve-core";
|
import type { Moderator } from "@uncaged/nerve-core";
|
||||||
import type { PlannerMeta } from "./roles/planner/index.js";
|
import type { PlannerMeta } from "./roles/planner/index.js";
|
||||||
import type { CoderMeta } from "./roles/coder/index.js";
|
import type { CoderMeta } from "./roles/coder/index.js";
|
||||||
|
import type { ReviewerMeta } from "./roles/reviewer/index.js";
|
||||||
import type { TesterMeta } from "./roles/tester/index.js";
|
import type { TesterMeta } from "./roles/tester/index.js";
|
||||||
import type { CommitterMeta } from "./roles/committer/index.js";
|
import type { CommitterMeta } from "./roles/committer/index.js";
|
||||||
|
|
||||||
export type SenseMeta = {
|
export type SenseMeta = {
|
||||||
planner: PlannerMeta;
|
planner: PlannerMeta;
|
||||||
coder: CoderMeta;
|
coder: CoderMeta;
|
||||||
|
reviewer: ReviewerMeta;
|
||||||
tester: TesterMeta;
|
tester: TesterMeta;
|
||||||
committer: CommitterMeta;
|
committer: CommitterMeta;
|
||||||
};
|
};
|
||||||
@ -24,7 +26,11 @@ export const moderator: Moderator<SenseMeta> = (context) => {
|
|||||||
const coderCount = context.steps.filter((s) => s.role === "coder").length;
|
const coderCount = context.steps.filter((s) => s.role === "coder").length;
|
||||||
|
|
||||||
if (last.role === "planner") return "coder";
|
if (last.role === "planner") return "coder";
|
||||||
if (last.role === "coder") return "tester";
|
if (last.role === "coder") return "reviewer";
|
||||||
|
if (last.role === "reviewer") {
|
||||||
|
if (last.meta.approved) return "tester";
|
||||||
|
return coderCount < MAX_CODER_ITERATIONS ? "coder" : END;
|
||||||
|
}
|
||||||
if (last.role === "tester") {
|
if (last.role === "tester") {
|
||||||
if (last.meta.passed) return "committer";
|
if (last.meta.passed) return "committer";
|
||||||
const testerCount = countRole(context.steps, "tester");
|
const testerCount = countRole(context.steps, "tester");
|
||||||
|
|||||||
21
workflows/sense-generator/roles/reviewer/index.ts
Normal file
21
workflows/sense-generator/roles/reviewer/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
||||||
|
import { createHermesRole } from "@uncaged/nerve-workflow-utils";
|
||||||
|
import { reviewerPrompt } from "./prompt.js";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const reviewerMetaSchema = z.object({
|
||||||
|
approved: z.boolean().describe("true if the diff is clean and ready for tester validation"),
|
||||||
|
});
|
||||||
|
export type ReviewerMeta = z.infer<typeof reviewerMetaSchema>;
|
||||||
|
|
||||||
|
export type BuildReviewerDeps = {
|
||||||
|
provider: LlmProvider;
|
||||||
|
nerveRoot: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function buildReviewerRole({ provider, nerveRoot }: BuildReviewerDeps) {
|
||||||
|
return createHermesRole<ReviewerMeta>({
|
||||||
|
prompt: async (threadId) => reviewerPrompt({ threadId, nerveRoot }),
|
||||||
|
extract: { provider, schema: reviewerMetaSchema },
|
||||||
|
});
|
||||||
|
}
|
||||||
46
workflows/sense-generator/roles/reviewer/prompt.ts
Normal file
46
workflows/sense-generator/roles/reviewer/prompt.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
export function reviewerPrompt({ threadId, nerveRoot }: { threadId: string; nerveRoot: string }): string {
|
||||||
|
return `You are a **code reviewer** for Nerve workflow changes. You run after the coder and before the tester.
|
||||||
|
|
||||||
|
**IMPORTANT: The Nerve workspace is at \`${nerveRoot}\`. Always \`cd ${nerveRoot}\` first.**
|
||||||
|
|
||||||
|
Read the workflow thread for context: \`nerve thread ${threadId}\`
|
||||||
|
|
||||||
|
## Your job — static analysis of the git diff
|
||||||
|
|
||||||
|
Run these commands and analyze the output:
|
||||||
|
|
||||||
|
1. **\`cd ${nerveRoot} && git diff --stat\`** — see what files changed
|
||||||
|
2. **\`cd ${nerveRoot} && git diff\`** — read the actual diff
|
||||||
|
3. **\`cd ${nerveRoot} && git status --short\`** — check for untracked files
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
### 🔴 Must reject (approved: false)
|
||||||
|
- **Garbage files**: node_modules/, .DS_Store, pnpm cache artifacts (e.g. \`false/\` directory), build outputs (dist/) that shouldn't be committed
|
||||||
|
- **Secrets/credentials**: API keys, tokens, passwords hardcoded in the diff
|
||||||
|
- **Unrelated changes**: files modified outside the scope of the planner's design
|
||||||
|
|
||||||
|
### ⚠️ Flag but may still approve
|
||||||
|
- Debug code left behind (console.log, debugger, TODO/FIXME without context)
|
||||||
|
- Hardcoded paths that should be configurable
|
||||||
|
- Missing type exports that other files might need
|
||||||
|
|
||||||
|
### ✅ Verify
|
||||||
|
- All files from the planner's design are created/modified
|
||||||
|
- Code follows existing patterns (compare with sibling workflows)
|
||||||
|
- Meta types are simple routing signals (single boolean per role)
|
||||||
|
- No dynamic import()
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
Summarize what you found. If garbage files or secrets are present, list them explicitly so the coder knows what to clean up.
|
||||||
|
|
||||||
|
End with:
|
||||||
|
\`\`\`json
|
||||||
|
{ "approved": true }
|
||||||
|
\`\`\`
|
||||||
|
or
|
||||||
|
\`\`\`json
|
||||||
|
{ "approved": false }
|
||||||
|
\`\`\``;
|
||||||
|
}
|
||||||
@ -23,7 +23,7 @@ export function buildWorkflowGenerator({
|
|||||||
roles: {
|
roles: {
|
||||||
planner: buildPlannerRole({ provider, cwd: nerveRoot }),
|
planner: buildPlannerRole({ provider, cwd: nerveRoot }),
|
||||||
coder: buildCoderRole({ provider, cwd: nerveRoot }),
|
coder: buildCoderRole({ provider, cwd: nerveRoot }),
|
||||||
reviewer: buildReviewerRole({ provider, cwd: nerveRoot }),
|
reviewer: buildReviewerRole({ provider, nerveRoot }),
|
||||||
tester: buildTesterRole({ provider, nerveRoot }),
|
tester: buildTesterRole({ provider, nerveRoot }),
|
||||||
committer: buildCommitterRole({ nerveRoot }),
|
committer: buildCommitterRole({ nerveRoot }),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,6 +7,11 @@ Also look at existing workflows in the \`workflows/\` directory for patterns.
|
|||||||
|
|
||||||
Implement the planner's design. This may be **creating a new workflow** or **modifying an existing one**. If there is reviewer, tester, or committer feedback in the thread, fix the issues they identified.
|
Implement the planner's design. This may be **creating a new workflow** or **modifying an existing one**. If there is reviewer, tester, or committer feedback in the thread, fix the issues they identified.
|
||||||
|
|
||||||
|
**IMPORTANT:** The thread contains both the **initial user prompt** (the first message) and the **planner's design**. Read both carefully:
|
||||||
|
- The **initial prompt** contains the user's specific requirements for role behavior, tools to use, and acceptance criteria
|
||||||
|
- The **planner's design** contains the architecture, file structure, and routing logic
|
||||||
|
- When writing role prompts, follow the user's behavioral requirements from the initial prompt — do not invent your own interpretation
|
||||||
|
|
||||||
## Multi-step approach
|
## Multi-step approach
|
||||||
|
|
||||||
You do NOT need to finish everything in one pass. You may return \`done: false\` to continue in the next iteration. For example:
|
You do NOT need to finish everything in one pass. You may return \`done: false\` to continue in the next iteration. For example:
|
||||||
|
|||||||
@ -27,6 +27,12 @@ For **modifications to existing workflows**:
|
|||||||
- Impact on moderator routing logic (this workflow's typical order is planner → coder → reviewer → tester → committer)
|
- Impact on moderator routing logic (this workflow's typical order is planner → coder → reviewer → tester → committer)
|
||||||
- Backward compatibility considerations (if any)
|
- Backward compatibility considerations (if any)
|
||||||
|
|
||||||
|
**For every role (new or modified)**, include a **Role Behavior** section that describes:
|
||||||
|
- What the role should do, check, or produce
|
||||||
|
- What tools or commands it should use
|
||||||
|
- What criteria determine its meta output (e.g. approved/passed/done)
|
||||||
|
- Preserve the user's specific requirements verbatim — do NOT summarize away details
|
||||||
|
|
||||||
If requirements are NOT clear, describe what is missing or ambiguous.
|
If requirements are NOT clear, describe what is missing or ambiguous.
|
||||||
|
|
||||||
End your response with a JSON block:
|
End your response with a JSON block:
|
||||||
|
|||||||
@ -1,23 +1,21 @@
|
|||||||
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
||||||
import { createCursorRole } from "@uncaged/nerve-workflow-utils";
|
import { createHermesRole } from "@uncaged/nerve-workflow-utils";
|
||||||
import { reviewerPrompt } from "./prompt.js";
|
import { reviewerPrompt } from "./prompt.js";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const reviewerMetaSchema = z.object({
|
export const reviewerMetaSchema = z.object({
|
||||||
approved: z.boolean().describe("true if the workflow matches the plan and is ready for tester validation"),
|
approved: z.boolean().describe("true if the diff is clean and ready for tester validation"),
|
||||||
});
|
});
|
||||||
export type ReviewerMeta = z.infer<typeof reviewerMetaSchema>;
|
export type ReviewerMeta = z.infer<typeof reviewerMetaSchema>;
|
||||||
|
|
||||||
export type BuildReviewerDeps = {
|
export type BuildReviewerDeps = {
|
||||||
provider: LlmProvider;
|
provider: LlmProvider;
|
||||||
cwd: string;
|
nerveRoot: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function buildReviewerRole({ provider, cwd }: BuildReviewerDeps) {
|
export function buildReviewerRole({ provider, nerveRoot }: BuildReviewerDeps) {
|
||||||
return createCursorRole<ReviewerMeta>({
|
return createHermesRole<ReviewerMeta>({
|
||||||
cwd,
|
prompt: async (threadId) => reviewerPrompt({ threadId, nerveRoot }),
|
||||||
mode: "ask",
|
|
||||||
prompt: async (threadId) => reviewerPrompt({ threadId }),
|
|
||||||
extract: { provider, schema: reviewerMetaSchema },
|
extract: { provider, schema: reviewerMetaSchema },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,41 @@
|
|||||||
export function reviewerPrompt({ threadId }: { threadId: string }): string {
|
export function reviewerPrompt({ threadId, nerveRoot }: { threadId: string; nerveRoot: string }): string {
|
||||||
return `You are a **reviewer** for Nerve workflow generation. You run **after** the coder and **before** the tester.
|
return `You are a **code reviewer** for Nerve workflow changes. You run after the coder and before the tester.
|
||||||
|
|
||||||
Read the workflow thread for the planner's design and any prior feedback: \`nerve thread ${threadId}\`
|
**IMPORTANT: The Nerve workspace is at \`${nerveRoot}\`. Always \`cd ${nerveRoot}\` first.**
|
||||||
Read workflow conventions: \`cat node_modules/@uncaged/nerve-skills/nerve-dev/SKILL.md\` (from the repo root after \`cd\`).
|
|
||||||
|
|
||||||
## Your job
|
Read the workflow thread for context: \`nerve thread ${threadId}\`
|
||||||
|
|
||||||
1. Identify the target workflow name and paths from the thread (e.g. \`workflows/<name>/\`).
|
## Your job — static analysis of the git diff
|
||||||
2. Read the relevant files: \`moderator.ts\`, \`build.ts\`, \`index.ts\`, and each role under \`roles/\`.
|
|
||||||
3. Check that the implementation matches the planner's plan (roles, routing, meta types, no forbidden patterns like dynamic \`import()\`).
|
|
||||||
4. Do **not** run full build/test here — that is the tester's job. Flag obvious gaps (missing files, wrong exports, broken routing).
|
|
||||||
|
|
||||||
If the work is **ready for the tester** to validate builds and config, set \`approved: true\`.
|
Run these commands and analyze the output:
|
||||||
If the coder must fix issues first, set \`approved: false\` and explain briefly what is wrong.
|
|
||||||
|
|
||||||
End with a JSON block:
|
1. **\`cd ${nerveRoot} && git diff --stat\`** — see what files changed
|
||||||
|
2. **\`cd ${nerveRoot} && git diff\`** — read the actual diff
|
||||||
|
3. **\`cd ${nerveRoot} && git status --short\`** — check for untracked files
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
### 🔴 Must reject (approved: false)
|
||||||
|
- **Garbage files**: node_modules/, .DS_Store, pnpm cache artifacts (e.g. \`false/\` directory), build outputs (dist/) that shouldn't be committed
|
||||||
|
- **Secrets/credentials**: API keys, tokens, passwords hardcoded in the diff
|
||||||
|
- **Unrelated changes**: files modified outside the scope of the planner's design
|
||||||
|
|
||||||
|
### ⚠️ Flag but may still approve
|
||||||
|
- Debug code left behind (console.log, debugger, TODO/FIXME without context)
|
||||||
|
- Hardcoded paths that should be configurable
|
||||||
|
- Missing type exports that other files might need
|
||||||
|
|
||||||
|
### ✅ Verify
|
||||||
|
- All files from the planner's design are created/modified
|
||||||
|
- Code follows existing patterns (compare with sibling workflows)
|
||||||
|
- Meta types are simple routing signals (single boolean per role)
|
||||||
|
- No dynamic import()
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
Summarize what you found. If garbage files or secrets are present, list them explicitly so the coder knows what to clean up.
|
||||||
|
|
||||||
|
End with:
|
||||||
\`\`\`json
|
\`\`\`json
|
||||||
{ "approved": true }
|
{ "approved": true }
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user