refactor: use @uncaged/nerve-role-committer package #19

Merged
xiaoju merged 1 commits from feat/use-role-committer-package into master 2026-04-29 14:20:02 +00:00
6 changed files with 20 additions and 93 deletions

View File

@ -13,7 +13,8 @@
"@uncaged/nerve-daemon": "link:../repos/nerve/packages/daemon",
"@uncaged/nerve-workflow-utils": "link:../repos/nerve/packages/workflow-utils",
"drizzle-orm": "latest",
"zod": "^4.3.6"
"zod": "^4.3.6",
"@uncaged/nerve-role-committer": "link:../repos/nerve/packages/role-committer"
},
"devDependencies": {
"drizzle-kit": "latest"
@ -27,7 +28,8 @@
"@uncaged/nerve-adapter-hermes": "link:../repos/nerve/packages/adapter-hermes",
"@uncaged/nerve-daemon": "link:../repos/nerve/packages/daemon",
"@uncaged/nerve-core": "link:../repos/nerve/packages/core",
"@uncaged/nerve-workflow-utils": "link:../repos/nerve/packages/workflow-utils"
"@uncaged/nerve-workflow-utils": "link:../repos/nerve/packages/workflow-utils",
"@uncaged/nerve-role-committer": "link:../repos/nerve/packages/role-committer"
}
}
}
}

4
pnpm-lock.yaml generated
View File

@ -10,6 +10,7 @@ overrides:
'@uncaged/nerve-daemon': link:../repos/nerve/packages/daemon
'@uncaged/nerve-core': link:../repos/nerve/packages/core
'@uncaged/nerve-workflow-utils': link:../repos/nerve/packages/workflow-utils
'@uncaged/nerve-role-committer': link:../repos/nerve/packages/role-committer
importers:
@ -27,6 +28,9 @@ importers:
'@uncaged/nerve-daemon':
specifier: link:../repos/nerve/packages/daemon
version: link:../repos/nerve/packages/daemon
'@uncaged/nerve-role-committer':
specifier: link:../repos/nerve/packages/role-committer
version: link:../repos/nerve/packages/role-committer
'@uncaged/nerve-workflow-utils':
specifier: link:../repos/nerve/packages/workflow-utils
version: link:../repos/nerve/packages/workflow-utils

View File

@ -1,65 +0,0 @@
import type { AgentFn, Role, RoleResult, StartStep } from "@uncaged/nerve-core";
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
import { createRole, isDryRun } from "@uncaged/nerve-workflow-utils";
import { z } from "zod";
export const committerMetaSchema = z.object({
committed: z
.boolean()
.describe("true if branch created, changes committed, and pushed successfully"),
});
export type CommitterMeta = z.infer<typeof committerMetaSchema>;
function workspaceCommitterPrompt(threadId: string): string {
return `You are the committer agent. The coder finished with a passing build; your job is to branch, commit, and push.
1. Read the workflow thread: \`nerve thread show ${threadId}\` — understand what was planned, coded, and reviewed.
2. Run \`git status\`. If nothing to commit, set committed=false.
3. Create a feature branch: infer a good \`fix/<slug>\` or \`feat/<slug>\` name from the thread context.
4. \`git add -A\`
5. Write a conventional commit message based on the thread context.
6. \`git commit -m "<message>"\` — do NOT pass \`--author\`, use repo git config.
7. \`git push -u origin <branch>\`
**committed=true** only if branch was created, commit succeeded, and **push** succeeded.
End your reply with a JSON line:
\`\`\`json
{ "committed": true }
\`\`\`
or
\`\`\`json
{ "committed": false }
\`\`\``;
}
export function createWorkspaceCommitterRole(
adapter: AgentFn,
extract: LlmExtractorConfig,
): Role<CommitterMeta> {
const innerRole = createRole(
adapter,
async (start: StartStep) => workspaceCommitterPrompt(start.meta.threadId),
committerMetaSchema,
extract,
);
return async (start, _messages): Promise<RoleResult<CommitterMeta>> => {
if (isDryRun(start)) {
return {
content: "[dry-run] committer skipped (no git branch/commit/push)",
meta: { committed: true },
};
}
try {
return await innerRole(start, _messages);
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
return {
content: `committer failed: ${msg}`,
meta: { committed: false },
};
}
};
}

View File

@ -1,5 +1,5 @@
export {
createWorkspaceCommitterRole,
createCommitterRole as createWorkspaceCommitterRole,
committerMetaSchema,
type CommitterMeta,
} from "../../_shared/workspace-committer.js";
} from "@uncaged/nerve-role-committer";

View File

@ -1,5 +1,5 @@
export {
createWorkspaceCommitterRole,
createCommitterRole as createWorkspaceCommitterRole,
committerMetaSchema,
type CommitterMeta,
} from "../../_shared/workspace-committer.js";
} from "@uncaged/nerve-role-committer";

View File

@ -1,6 +1,6 @@
import type { AgentFn, Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
import { createRole, isDryRun } from "@uncaged/nerve-workflow-utils";
import { createRole, decorateRole, withDryRun, onFail } from "@uncaged/nerve-workflow-utils";
import { z } from "zod";
import { committerPrompt } from "./prompt.js";
@ -16,29 +16,15 @@ export function createCommitterRole(
adapter: AgentFn,
extract: LlmExtractorConfig,
): Role<CommitterMeta> {
const innerRole = createRole(
const inner = createRole(
adapter,
async (start: StartStep) => committerPrompt({ threadId: start.meta.threadId }),
committerMetaSchema,
extract,
);
return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<CommitterMeta>> => {
if (isDryRun(start)) {
return {
content: "[dry-run] committer skipped (no git branch/commit/push)",
meta: { committed: true },
};
}
try {
return await innerRole(start, messages);
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
return {
content: `committer failed: ${msg}`,
meta: { committed: false },
};
}
};
return decorateRole(inner, [
withDryRun({ label: "committer", meta: { committed: true } as CommitterMeta }),
onFail({ label: "committer", meta: { committed: false } as CommitterMeta }),
]) as Role<CommitterMeta>;
}