Merge pull request 'refactor: flatten role folders into single .ts files' (#14) from refactor/13-flatten-role-folders into master

This commit is contained in:
xiaoju 2026-04-29 12:23:34 +00:00
commit a506e5b36b
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 type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
import { createRole } from "@uncaged/nerve-workflow-utils"; import { createRole } from "@uncaged/nerve-workflow-utils";
import { coderPrompt } from "./roles/coder/prompt.js"; import {
import { coderMetaSchema } from "./roles/coder/index.js"; coderMetaSchema,
import { plannerPrompt } from "./roles/planner/prompt.js"; coderPrompt,
import { plannerMetaSchema } from "./roles/planner/index.js"; } from "./roles/coder.js";
import { reviewerPrompt } from "./roles/reviewer/prompt.js"; import {
import { reviewerMetaSchema } from "./roles/reviewer/index.js"; buildWorkspaceCommitterRole,
import { testerPrompt } from "./roles/tester/prompt.js"; } from "./roles/committer.js";
import { testerMetaSchema } from "./roles/tester/index.js"; import {
import { buildWorkspaceCommitterRole } from "./roles/committer/index.js"; 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 { moderator } from "./moderator.js";
import type { SenseMeta } from "./moderator.js"; import type { SenseMeta } from "./moderator.js";

View File

@ -1,10 +1,10 @@
import { END } from "@uncaged/nerve-core"; 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.js";
import type { CoderMeta } from "./roles/coder/index.js"; import type { CoderMeta } from "./roles/coder.js";
import type { ReviewerMeta } from "./roles/reviewer/index.js"; import type { ReviewerMeta } from "./roles/reviewer.js";
import type { TesterMeta } from "./roles/tester/index.js"; import type { TesterMeta } from "./roles/tester.js";
import type { CommitterMeta } from "./roles/committer/index.js"; import type { CommitterMeta } from "./roles/committer.js";
export type SenseMeta = { export type SenseMeta = {
planner: PlannerMeta; 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 { 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}\` 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\` 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, committerMetaSchema,
type BuildWorkspaceCommitterDeps, type BuildWorkspaceCommitterDeps,
type CommitterMeta, 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 { export function plannerPrompt({ threadId }: { threadId: string }): string {
return `You are planning a new Nerve sense. 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 { 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. 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 { export function testerPrompt({ threadId, nerveRoot }: { threadId: string; nerveRoot: string }): string {
return `You are testing a newly created Nerve sense end-to-end. 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 type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
import { createRole } from "@uncaged/nerve-workflow-utils"; import { createRole } from "@uncaged/nerve-workflow-utils";
import { coderPrompt } from "./roles/coder/prompt.js"; import {
import { coderMetaSchema } from "./roles/coder/index.js"; coderMetaSchema,
import { plannerPrompt } from "./roles/planner/prompt.js"; coderPrompt,
import { plannerMetaSchema } from "./roles/planner/index.js"; } from "./roles/coder.js";
import { reviewerPrompt } from "./roles/reviewer/prompt.js"; import {
import { reviewerMetaSchema } from "./roles/reviewer/index.js"; buildWorkspaceCommitterRole,
import { testerPrompt } from "./roles/tester/prompt.js"; } from "./roles/committer.js";
import { testerMetaSchema } from "./roles/tester/index.js"; import {
import { buildWorkspaceCommitterRole } from "./roles/committer/index.js"; 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 { moderator } from "./moderator.js";
import type { WorkflowMeta } from "./moderator.js"; import type { WorkflowMeta } from "./moderator.js";

View File

@ -1,10 +1,10 @@
import { END } from "@uncaged/nerve-core"; 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.js";
import type { CoderMeta } from "./roles/coder/index.js"; import type { CoderMeta } from "./roles/coder.js";
import type { ReviewerMeta } from "./roles/reviewer/index.js"; import type { ReviewerMeta } from "./roles/reviewer.js";
import type { TesterMeta } from "./roles/tester/index.js"; import type { TesterMeta } from "./roles/tester.js";
import type { CommitterMeta } from "./roles/committer/index.js"; import type { CommitterMeta } from "./roles/committer.js";
export type WorkflowMeta = { export type WorkflowMeta = {
planner: PlannerMeta; 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 { 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}\` 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\` 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>/index.ts\` — WorkflowDefinition default export
- \`workflows/<name>/build.ts\` — factory function - \`workflows/<name>/build.ts\` — factory function
- \`workflows/<name>/moderator.ts\` — moderator + meta types - \`workflows/<name>/moderator.ts\` — moderator + meta types
- \`workflows/<name>/roles/<role>/index.ts\` — role build function - \`workflows/<name>/roles/<role>.ts\` — meta schema and prompt function per role
- \`workflows/<name>/roles/<role>/prompt.ts\` — prompt pure function
- \`workflows/<name>/package.json\` — with esbuild build script - \`workflows/<name>/package.json\` — with esbuild build script
- \`workflows/<name>/tsconfig.json\` — TypeScript config - \`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, committerMetaSchema,
type BuildWorkspaceCommitterDeps, type BuildWorkspaceCommitterDeps,
type CommitterMeta, 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 { export function plannerPrompt({ threadId }: { threadId: string }): string {
return `You are a Nerve workflow planner. You can **create new workflows** or **modify existing ones**. 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 { 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. 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 { export function testerPrompt({ threadId, nerveRoot }: { threadId: string; nerveRoot: string }): string {
return `You are testing a Nerve workflow — either newly created or recently modified. 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>/index.ts\`
- \`workflows/<name>/build.ts\` - \`workflows/<name>/build.ts\`
- \`workflows/<name>/moderator.ts\` - \`workflows/<name>/moderator.ts\`
- \`workflows/<name>/roles/\` with subdirectories - \`workflows/<name>/roles/\` with one \`.ts\` file per role
- \`workflows/<name>/package.json\` - \`workflows/<name>/package.json\`
2. **Build** run inside the workflow directory: 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>;