Compare commits

...

2 Commits

Author SHA1 Message Date
a506e5b36b Merge pull request 'refactor: flatten role folders into single .ts files' (#14) from refactor/13-flatten-role-folders into master 2026-04-29 12:23:34 +00:00
42f943c303 refactor: flatten role folders into single .ts files
Each role's index.ts + prompt.ts merged into a single <role>.ts file.
Committer stays as re-export from _shared.
Import paths updated in build.ts and moderator.ts.

Closes #13
2026-04-29 12:21:41 +00:00
22 changed files with 108 additions and 81 deletions

View File

@ -4,15 +4,25 @@ import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
import { createRole } from "@uncaged/nerve-workflow-utils";
import { coderPrompt } from "./roles/coder/prompt.js";
import { coderMetaSchema } from "./roles/coder/index.js";
import { plannerPrompt } from "./roles/planner/prompt.js";
import { plannerMetaSchema } from "./roles/planner/index.js";
import { reviewerPrompt } from "./roles/reviewer/prompt.js";
import { reviewerMetaSchema } from "./roles/reviewer/index.js";
import { testerPrompt } from "./roles/tester/prompt.js";
import { testerMetaSchema } from "./roles/tester/index.js";
import { buildWorkspaceCommitterRole } from "./roles/committer/index.js";
import {
coderMetaSchema,
coderPrompt,
} from "./roles/coder.js";
import {
buildWorkspaceCommitterRole,
} from "./roles/committer.js";
import {
plannerMetaSchema,
plannerPrompt,
} from "./roles/planner.js";
import {
reviewerMetaSchema,
reviewerPrompt,
} from "./roles/reviewer.js";
import {
testerMetaSchema,
testerPrompt,
} from "./roles/tester.js";
import { moderator } from "./moderator.js";
import type { SenseMeta } from "./moderator.js";

View File

@ -1,10 +1,10 @@
import { END } from "@uncaged/nerve-core";
import type { Moderator } from "@uncaged/nerve-core";
import type { PlannerMeta } from "./roles/planner/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 { CommitterMeta } from "./roles/committer/index.js";
import type { PlannerMeta } from "./roles/planner.js";
import type { CoderMeta } from "./roles/coder.js";
import type { ReviewerMeta } from "./roles/reviewer.js";
import type { TesterMeta } from "./roles/tester.js";
import type { CommitterMeta } from "./roles/committer.js";
export type SenseMeta = {
planner: PlannerMeta;

View File

@ -1,3 +1,10 @@
import { z } from "zod";
export const coderMetaSchema = z.object({
filesCreated: z.boolean().describe("true if the sense files were created"),
});
export type CoderMeta = z.infer<typeof coderMetaSchema>;
export function coderPrompt({ threadId }: { threadId: string }): string {
return `Read the workflow thread for the planner's sense design and any tester feedback: \`nerve thread ${threadId}\`
Read the nerve-dev skill for sense file structure and conventions: \`cat node_modules/@uncaged/nerve-skills/nerve-dev/SKILL.md\`

View File

@ -1,6 +0,0 @@
import { z } from "zod";
export const coderMetaSchema = z.object({
filesCreated: z.boolean().describe("true if the sense files were created"),
});
export type CoderMeta = z.infer<typeof coderMetaSchema>;

View File

@ -3,4 +3,4 @@ export {
committerMetaSchema,
type BuildWorkspaceCommitterDeps,
type CommitterMeta,
} from "../../../_shared/workspace-committer.js";
} from "../../_shared/workspace-committer.js";

View File

@ -1,3 +1,10 @@
import { z } from "zod";
export const plannerMetaSchema = z.object({
senseName: z.string().describe("kebab-case sense name from the plan"),
});
export type PlannerMeta = z.infer<typeof plannerMetaSchema>;
export function plannerPrompt({ threadId }: { threadId: string }): string {
return `You are planning a new Nerve sense.

View File

@ -1,6 +0,0 @@
import { z } from "zod";
export const plannerMetaSchema = z.object({
senseName: z.string().describe("kebab-case sense name from the plan"),
});
export type PlannerMeta = z.infer<typeof plannerMetaSchema>;

View File

@ -1,3 +1,10 @@
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 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.

View File

@ -1,6 +0,0 @@
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>;

View File

@ -1,3 +1,10 @@
import { z } from "zod";
export const testerMetaSchema = z.object({
passed: z.boolean().describe("true if all e2e checks passed"),
});
export type TesterMeta = z.infer<typeof testerMetaSchema>;
export function testerPrompt({ threadId, nerveRoot }: { threadId: string; nerveRoot: string }): string {
return `You are testing a newly created Nerve sense end-to-end.

View File

@ -1,6 +0,0 @@
import { z } from "zod";
export const testerMetaSchema = z.object({
passed: z.boolean().describe("true if all e2e checks passed"),
});
export type TesterMeta = z.infer<typeof testerMetaSchema>;

View File

@ -4,15 +4,25 @@ import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
import { createRole } from "@uncaged/nerve-workflow-utils";
import { coderPrompt } from "./roles/coder/prompt.js";
import { coderMetaSchema } from "./roles/coder/index.js";
import { plannerPrompt } from "./roles/planner/prompt.js";
import { plannerMetaSchema } from "./roles/planner/index.js";
import { reviewerPrompt } from "./roles/reviewer/prompt.js";
import { reviewerMetaSchema } from "./roles/reviewer/index.js";
import { testerPrompt } from "./roles/tester/prompt.js";
import { testerMetaSchema } from "./roles/tester/index.js";
import { buildWorkspaceCommitterRole } from "./roles/committer/index.js";
import {
coderMetaSchema,
coderPrompt,
} from "./roles/coder.js";
import {
buildWorkspaceCommitterRole,
} from "./roles/committer.js";
import {
plannerMetaSchema,
plannerPrompt,
} from "./roles/planner.js";
import {
reviewerMetaSchema,
reviewerPrompt,
} from "./roles/reviewer.js";
import {
testerMetaSchema,
testerPrompt,
} from "./roles/tester.js";
import { moderator } from "./moderator.js";
import type { WorkflowMeta } from "./moderator.js";

View File

@ -1,10 +1,10 @@
import { END } from "@uncaged/nerve-core";
import type { Moderator } from "@uncaged/nerve-core";
import type { PlannerMeta } from "./roles/planner/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 { CommitterMeta } from "./roles/committer/index.js";
import type { PlannerMeta } from "./roles/planner.js";
import type { CoderMeta } from "./roles/coder.js";
import type { ReviewerMeta } from "./roles/reviewer.js";
import type { TesterMeta } from "./roles/tester.js";
import type { CommitterMeta } from "./roles/committer.js";
export type WorkflowMeta = {
planner: PlannerMeta;

View File

@ -1,3 +1,10 @@
import { z } from "zod";
export const coderMetaSchema = z.object({
done: z.boolean().describe("true if the workflow files were created and build passes"),
});
export type CoderMeta = z.infer<typeof coderMetaSchema>;
export function coderPrompt({ threadId }: { threadId: string }): string {
return `Read the workflow thread to get the planner's design and any reviewer/tester/committer feedback: \`nerve thread ${threadId}\`
Read the nerve-dev skill for workflow file structure and conventions: \`cat node_modules/@uncaged/nerve-skills/nerve-dev/SKILL.md\`
@ -25,8 +32,7 @@ Each workflow must have:
- \`workflows/<name>/index.ts\` — WorkflowDefinition default export
- \`workflows/<name>/build.ts\` — factory function
- \`workflows/<name>/moderator.ts\` — moderator + meta types
- \`workflows/<name>/roles/<role>/index.ts\` — role build function
- \`workflows/<name>/roles/<role>/prompt.ts\` — prompt pure function
- \`workflows/<name>/roles/<role>.ts\` — meta schema and prompt function per role
- \`workflows/<name>/package.json\` — with esbuild build script
- \`workflows/<name>/tsconfig.json\` — TypeScript config

View File

@ -1,6 +0,0 @@
import { z } from "zod";
export const coderMetaSchema = z.object({
done: z.boolean().describe("true if the workflow files were created and build passes"),
});
export type CoderMeta = z.infer<typeof coderMetaSchema>;

View File

@ -3,4 +3,4 @@ export {
committerMetaSchema,
type BuildWorkspaceCommitterDeps,
type CommitterMeta,
} from "../../../_shared/workspace-committer.js";
} from "../../_shared/workspace-committer.js";

View File

@ -1,3 +1,10 @@
import { z } from "zod";
export const plannerMetaSchema = z.object({
ready: z.boolean().describe("true if requirements are clear and a workflow can be implemented"),
});
export type PlannerMeta = z.infer<typeof plannerMetaSchema>;
export function plannerPrompt({ threadId }: { threadId: string }): string {
return `You are a Nerve workflow planner. You can **create new workflows** or **modify existing ones**.

View File

@ -1,6 +0,0 @@
import { z } from "zod";
export const plannerMetaSchema = z.object({
ready: z.boolean().describe("true if requirements are clear and a workflow can be implemented"),
});
export type PlannerMeta = z.infer<typeof plannerMetaSchema>;

View File

@ -1,3 +1,10 @@
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 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.

View File

@ -1,6 +0,0 @@
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>;

View File

@ -1,3 +1,10 @@
import { z } from "zod";
export const testerMetaSchema = z.object({
passed: z.boolean().describe("true if all validation checks passed"),
});
export type TesterMeta = z.infer<typeof testerMetaSchema>;
export function testerPrompt({ threadId, nerveRoot }: { threadId: string; nerveRoot: string }): string {
return `You are testing a Nerve workflow — either newly created or recently modified.
@ -14,7 +21,7 @@ Verify the full lifecycle in this order:
- \`workflows/<name>/index.ts\`
- \`workflows/<name>/build.ts\`
- \`workflows/<name>/moderator.ts\`
- \`workflows/<name>/roles/\` with subdirectories
- \`workflows/<name>/roles/\` with one \`.ts\` file per role
- \`workflows/<name>/package.json\`
2. **Build** run inside the workflow directory:

View File

@ -1,6 +0,0 @@
import { z } from "zod";
export const testerMetaSchema = z.object({
passed: z.boolean().describe("true if all validation checks passed"),
});
export type TesterMeta = z.infer<typeof testerMetaSchema>;