Compare commits
No commits in common. "6a2dbb7335d287e634a5b95ec3da26219e4baa2f" and "59b8f033baaeba64bde452cccbdcaa80b0993106" have entirely different histories.
6a2dbb7335
...
59b8f033ba
@ -13,8 +13,7 @@
|
|||||||
"@uncaged/nerve-daemon": "link:../repos/nerve/packages/daemon",
|
"@uncaged/nerve-daemon": "link:../repos/nerve/packages/daemon",
|
||||||
"@uncaged/nerve-workflow-utils": "link:../repos/nerve/packages/workflow-utils",
|
"@uncaged/nerve-workflow-utils": "link:../repos/nerve/packages/workflow-utils",
|
||||||
"drizzle-orm": "latest",
|
"drizzle-orm": "latest",
|
||||||
"zod": "^4.3.6",
|
"zod": "^4.3.6"
|
||||||
"@uncaged/nerve-role-committer": "link:../repos/nerve/packages/role-committer"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"drizzle-kit": "latest"
|
"drizzle-kit": "latest"
|
||||||
@ -28,8 +27,7 @@
|
|||||||
"@uncaged/nerve-adapter-hermes": "link:../repos/nerve/packages/adapter-hermes",
|
"@uncaged/nerve-adapter-hermes": "link:../repos/nerve/packages/adapter-hermes",
|
||||||
"@uncaged/nerve-daemon": "link:../repos/nerve/packages/daemon",
|
"@uncaged/nerve-daemon": "link:../repos/nerve/packages/daemon",
|
||||||
"@uncaged/nerve-core": "link:../repos/nerve/packages/core",
|
"@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
4
pnpm-lock.yaml
generated
@ -10,7 +10,6 @@ overrides:
|
|||||||
'@uncaged/nerve-daemon': link:../repos/nerve/packages/daemon
|
'@uncaged/nerve-daemon': link:../repos/nerve/packages/daemon
|
||||||
'@uncaged/nerve-core': link:../repos/nerve/packages/core
|
'@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
|
|
||||||
|
|
||||||
importers:
|
importers:
|
||||||
|
|
||||||
@ -28,9 +27,6 @@ importers:
|
|||||||
'@uncaged/nerve-daemon':
|
'@uncaged/nerve-daemon':
|
||||||
specifier: link:../repos/nerve/packages/daemon
|
specifier: link:../repos/nerve/packages/daemon
|
||||||
version: 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':
|
'@uncaged/nerve-workflow-utils':
|
||||||
specifier: link:../repos/nerve/packages/workflow-utils
|
specifier: link:../repos/nerve/packages/workflow-utils
|
||||||
version: link:../repos/nerve/packages/workflow-utils
|
version: link:../repos/nerve/packages/workflow-utils
|
||||||
|
|||||||
65
workflows/_shared/workspace-committer.ts
Normal file
65
workflows/_shared/workspace-committer.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
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 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
export {
|
export {
|
||||||
createCommitterRole as createWorkspaceCommitterRole,
|
createWorkspaceCommitterRole,
|
||||||
committerMetaSchema,
|
committerMetaSchema,
|
||||||
type CommitterMeta,
|
type CommitterMeta,
|
||||||
} from "@uncaged/nerve-role-committer";
|
} from "../../_shared/workspace-committer.js";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export {
|
export {
|
||||||
createCommitterRole as createWorkspaceCommitterRole,
|
createWorkspaceCommitterRole,
|
||||||
committerMetaSchema,
|
committerMetaSchema,
|
||||||
type CommitterMeta,
|
type CommitterMeta,
|
||||||
} from "@uncaged/nerve-role-committer";
|
} from "../../_shared/workspace-committer.js";
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
import type { AgentFn, Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
||||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||||
import { createRole, decorateRole, withDryRun, onFail } from "@uncaged/nerve-workflow-utils";
|
import { createRole, isDryRun } from "@uncaged/nerve-workflow-utils";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { committerPrompt } from "./prompt.js";
|
import { committerPrompt } from "./prompt.js";
|
||||||
@ -16,15 +16,29 @@ export function createCommitterRole(
|
|||||||
adapter: AgentFn,
|
adapter: AgentFn,
|
||||||
extract: LlmExtractorConfig,
|
extract: LlmExtractorConfig,
|
||||||
): Role<CommitterMeta> {
|
): Role<CommitterMeta> {
|
||||||
const inner = createRole(
|
const innerRole = createRole(
|
||||||
adapter,
|
adapter,
|
||||||
async (start: StartStep) => committerPrompt({ threadId: start.meta.threadId }),
|
async (start: StartStep) => committerPrompt({ threadId: start.meta.threadId }),
|
||||||
committerMetaSchema,
|
committerMetaSchema,
|
||||||
extract,
|
extract,
|
||||||
);
|
);
|
||||||
|
|
||||||
return decorateRole(inner, [
|
return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<CommitterMeta>> => {
|
||||||
withDryRun({ label: "committer", meta: { committed: true } as CommitterMeta }),
|
if (isDryRun(start)) {
|
||||||
onFail({ label: "committer", meta: { committed: false } as CommitterMeta }),
|
return {
|
||||||
]) as Role<CommitterMeta>;
|
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 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user