refactor: decouple adapters from workflow factories + createXxxRole #16
@ -1,5 +1,4 @@
|
||||
import type { Role, RoleResult, StartStep } from "@uncaged/nerve-core";
|
||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||
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";
|
||||
@ -62,15 +61,18 @@ or
|
||||
\`\`\``;
|
||||
}
|
||||
|
||||
export function buildWorkspaceCommitterRole({
|
||||
extract,
|
||||
nerveRoot,
|
||||
workflowName,
|
||||
conventionalCommitScopeHint,
|
||||
branchCheckoutExample,
|
||||
}: BuildWorkspaceCommitterDeps): Role<CommitterMeta> {
|
||||
export function createWorkspaceCommitterRole(
|
||||
adapter: AgentFn,
|
||||
{
|
||||
extract,
|
||||
nerveRoot,
|
||||
workflowName,
|
||||
conventionalCommitScopeHint,
|
||||
branchCheckoutExample,
|
||||
}: BuildWorkspaceCommitterDeps,
|
||||
): Role<CommitterMeta> {
|
||||
const innerRole = createRole(
|
||||
hermesAdapter,
|
||||
adapter,
|
||||
async (start: StartStep) =>
|
||||
workspaceCommitterPrompt({
|
||||
threadId: start.meta.threadId,
|
||||
|
||||
@ -1,75 +1,34 @@
|
||||
import type { StartStep, WorkflowDefinition } from "@uncaged/nerve-core";
|
||||
import { createCursorAdapter, cursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||
import type { AgentFn, WorkflowDefinition } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
|
||||
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";
|
||||
import { createCoderRole } from "./roles/coder.js";
|
||||
import { createWorkspaceCommitterRole } from "./roles/committer.js";
|
||||
import { createPlannerRole } from "./roles/planner.js";
|
||||
import { createReviewerRole } from "./roles/reviewer.js";
|
||||
import { createTesterRole } from "./roles/tester.js";
|
||||
|
||||
export type BuildDevelopSenseDeps = {
|
||||
export type CreateDevelopSenseDeps = {
|
||||
defaultAdapter: AgentFn;
|
||||
adapters?: Partial<Record<keyof SenseMeta, AgentFn>>;
|
||||
extract: LlmExtractorConfig;
|
||||
cwd: string;
|
||||
};
|
||||
|
||||
const CURSOR_TIMEOUT_MS = 300_000;
|
||||
|
||||
export function buildDevelopSenseWorkflow({
|
||||
export function createDevelopSenseWorkflow({
|
||||
defaultAdapter,
|
||||
adapters,
|
||||
extract,
|
||||
cwd,
|
||||
}: BuildDevelopSenseDeps): WorkflowDefinition<SenseMeta> {
|
||||
}: CreateDevelopSenseDeps): WorkflowDefinition<SenseMeta> {
|
||||
const a = (role: keyof SenseMeta) => adapters?.[role] ?? defaultAdapter;
|
||||
const roles = {
|
||||
planner: createRole(
|
||||
createCursorAdapter({
|
||||
type: "cursor",
|
||||
mode: "ask",
|
||||
model: "auto",
|
||||
timeout: CURSOR_TIMEOUT_MS,
|
||||
}),
|
||||
async (start: StartStep) => plannerPrompt({ threadId: start.meta.threadId }),
|
||||
plannerMetaSchema,
|
||||
extract,
|
||||
),
|
||||
coder: createRole(
|
||||
cursorAdapter,
|
||||
async (start: StartStep) => coderPrompt({ threadId: start.meta.threadId }),
|
||||
coderMetaSchema,
|
||||
extract,
|
||||
),
|
||||
reviewer: createRole(
|
||||
hermesAdapter,
|
||||
async (start: StartStep) =>
|
||||
reviewerPrompt({ threadId: start.meta.threadId, nerveRoot: cwd }),
|
||||
reviewerMetaSchema,
|
||||
extract,
|
||||
),
|
||||
tester: createRole(
|
||||
hermesAdapter,
|
||||
async (start: StartStep) =>
|
||||
testerPrompt({ threadId: start.meta.threadId, nerveRoot: cwd }),
|
||||
testerMetaSchema,
|
||||
extract,
|
||||
),
|
||||
committer: buildWorkspaceCommitterRole({
|
||||
planner: createPlannerRole(a('planner'), extract),
|
||||
coder: createCoderRole(a('coder'), extract),
|
||||
reviewer: createReviewerRole(a('reviewer'), extract, cwd),
|
||||
tester: createTesterRole(a('tester'), extract, cwd),
|
||||
committer: createWorkspaceCommitterRole(a('committer'), {
|
||||
extract,
|
||||
nerveRoot: cwd,
|
||||
workflowName: "develop-sense",
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { join } from "node:path";
|
||||
import { buildDevelopSenseWorkflow } from "./build.js";
|
||||
import { createCursorAdapter, cursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||
import { createDevelopSenseWorkflow } from "./build.js";
|
||||
|
||||
const HOME = process.env.HOME ?? "/home/azureuser";
|
||||
const NERVE_ROOT = join(HOME, ".uncaged-nerve");
|
||||
@ -11,7 +13,19 @@ if (!apiKey || !baseUrl) {
|
||||
throw new Error("Set DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL");
|
||||
}
|
||||
|
||||
const workflow = buildDevelopSenseWorkflow({
|
||||
const CURSOR_TIMEOUT_MS = 300_000;
|
||||
|
||||
const workflow = createDevelopSenseWorkflow({
|
||||
defaultAdapter: hermesAdapter,
|
||||
adapters: {
|
||||
planner: createCursorAdapter({
|
||||
type: "cursor",
|
||||
mode: "ask",
|
||||
model: "auto",
|
||||
timeout: CURSOR_TIMEOUT_MS,
|
||||
}),
|
||||
coder: cursorAdapter,
|
||||
},
|
||||
extract: { provider: { apiKey, baseUrl, model } },
|
||||
cwd: NERVE_ROOT,
|
||||
});
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
|
||||
export const coderMetaSchema = z.object({
|
||||
@ -36,3 +39,12 @@ Return \`done: true\` ONLY when ALL of the following are true:
|
||||
|
||||
Return \`done: false\` if you made progress but there is still work to do.`;
|
||||
}
|
||||
|
||||
export function createCoderRole(adapter: AgentFn, extract: LlmExtractorConfig): Role<CoderMeta> {
|
||||
return createRole(
|
||||
adapter,
|
||||
async (start: StartStep) => coderPrompt({ threadId: start.meta.threadId }),
|
||||
coderMetaSchema,
|
||||
extract,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export {
|
||||
buildWorkspaceCommitterRole,
|
||||
createWorkspaceCommitterRole,
|
||||
committerMetaSchema,
|
||||
type BuildWorkspaceCommitterDeps,
|
||||
type CommitterMeta,
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
|
||||
export const plannerMetaSchema = z.object({
|
||||
@ -22,3 +25,12 @@ Pick a good kebab-case name for this sense. Produce a PLAN (not code) in markdow
|
||||
|
||||
Output ONLY the plan. Be precise and implementation-ready.`;
|
||||
}
|
||||
|
||||
export function createPlannerRole(adapter: AgentFn, extract: LlmExtractorConfig): Role<PlannerMeta> {
|
||||
return createRole(
|
||||
adapter,
|
||||
async (start: StartStep) => plannerPrompt({ threadId: start.meta.threadId }),
|
||||
plannerMetaSchema,
|
||||
extract,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
|
||||
export const reviewerMetaSchema = z.object({
|
||||
@ -43,3 +46,17 @@ or
|
||||
{ "approved": false }
|
||||
\`\`\``;
|
||||
}
|
||||
|
||||
export function createReviewerRole(
|
||||
adapter: AgentFn,
|
||||
extract: LlmExtractorConfig,
|
||||
nerveRoot: string,
|
||||
): Role<ReviewerMeta> {
|
||||
return createRole(
|
||||
adapter,
|
||||
async (start: StartStep) =>
|
||||
reviewerPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||
reviewerMetaSchema,
|
||||
extract,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
|
||||
export const testerMetaSchema = z.object({
|
||||
@ -39,3 +42,17 @@ If any step fails, include the relevant error output.
|
||||
|
||||
Output a clear summary: what you checked, what passed, what failed, and why.`;
|
||||
}
|
||||
|
||||
export function createTesterRole(
|
||||
adapter: AgentFn,
|
||||
extract: LlmExtractorConfig,
|
||||
nerveRoot: string,
|
||||
): Role<TesterMeta> {
|
||||
return createRole(
|
||||
adapter,
|
||||
async (start: StartStep) =>
|
||||
testerPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||
testerMetaSchema,
|
||||
extract,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,75 +1,34 @@
|
||||
import type { StartStep, WorkflowDefinition } from "@uncaged/nerve-core";
|
||||
import { createCursorAdapter, cursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||
import type { AgentFn, WorkflowDefinition } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
|
||||
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";
|
||||
import { createCoderRole } from "./roles/coder.js";
|
||||
import { createWorkspaceCommitterRole } from "./roles/committer.js";
|
||||
import { createPlannerRole } from "./roles/planner.js";
|
||||
import { createReviewerRole } from "./roles/reviewer.js";
|
||||
import { createTesterRole } from "./roles/tester.js";
|
||||
|
||||
export type BuildDevelopWorkflowDeps = {
|
||||
export type CreateDevelopWorkflowDeps = {
|
||||
defaultAdapter: AgentFn;
|
||||
adapters?: Partial<Record<keyof WorkflowMeta, AgentFn>>;
|
||||
extract: LlmExtractorConfig;
|
||||
nerveRoot: string;
|
||||
};
|
||||
|
||||
const CURSOR_TIMEOUT_MS = 300_000;
|
||||
|
||||
export function buildDevelopWorkflow({
|
||||
export function createDevelopWorkflowWorkflow({
|
||||
defaultAdapter,
|
||||
adapters,
|
||||
extract,
|
||||
nerveRoot,
|
||||
}: BuildDevelopWorkflowDeps): WorkflowDefinition<WorkflowMeta> {
|
||||
}: CreateDevelopWorkflowDeps): WorkflowDefinition<WorkflowMeta> {
|
||||
const a = (role: keyof WorkflowMeta) => adapters?.[role] ?? defaultAdapter;
|
||||
const roles = {
|
||||
planner: createRole(
|
||||
createCursorAdapter({
|
||||
type: "cursor",
|
||||
mode: "ask",
|
||||
model: "auto",
|
||||
timeout: CURSOR_TIMEOUT_MS,
|
||||
}),
|
||||
async (start: StartStep) => plannerPrompt({ threadId: start.meta.threadId }),
|
||||
plannerMetaSchema,
|
||||
extract,
|
||||
),
|
||||
coder: createRole(
|
||||
cursorAdapter,
|
||||
async (start: StartStep) => coderPrompt({ threadId: start.meta.threadId }),
|
||||
coderMetaSchema,
|
||||
extract,
|
||||
),
|
||||
reviewer: createRole(
|
||||
hermesAdapter,
|
||||
async (start: StartStep) =>
|
||||
reviewerPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||
reviewerMetaSchema,
|
||||
extract,
|
||||
),
|
||||
tester: createRole(
|
||||
hermesAdapter,
|
||||
async (start: StartStep) =>
|
||||
testerPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||
testerMetaSchema,
|
||||
extract,
|
||||
),
|
||||
committer: buildWorkspaceCommitterRole({
|
||||
planner: createPlannerRole(a('planner'), extract),
|
||||
coder: createCoderRole(a('coder'), extract),
|
||||
reviewer: createReviewerRole(a('reviewer'), extract, nerveRoot),
|
||||
tester: createTesterRole(a('tester'), extract, nerveRoot),
|
||||
committer: createWorkspaceCommitterRole(a('committer'), {
|
||||
extract,
|
||||
nerveRoot,
|
||||
workflowName: "develop-workflow",
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { join } from "node:path";
|
||||
import { buildDevelopWorkflow } from "./build.js";
|
||||
import { createCursorAdapter, cursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||
import { createDevelopWorkflowWorkflow } from "./build.js";
|
||||
|
||||
const HOME = process.env.HOME ?? "/home/azureuser";
|
||||
const NERVE_ROOT = join(HOME, ".uncaged-nerve");
|
||||
@ -12,7 +14,19 @@ if (!apiKey || !baseUrl) {
|
||||
throw new Error("Set DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL");
|
||||
}
|
||||
|
||||
const workflow = buildDevelopWorkflow({
|
||||
const CURSOR_TIMEOUT_MS = 300_000;
|
||||
|
||||
const workflow = createDevelopWorkflowWorkflow({
|
||||
defaultAdapter: hermesAdapter,
|
||||
adapters: {
|
||||
planner: createCursorAdapter({
|
||||
type: "cursor",
|
||||
mode: "ask",
|
||||
model: "auto",
|
||||
timeout: CURSOR_TIMEOUT_MS,
|
||||
}),
|
||||
coder: cursorAdapter,
|
||||
},
|
||||
extract: { provider: { apiKey, baseUrl, model } },
|
||||
nerveRoot: NERVE_ROOT,
|
||||
});
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
|
||||
export const coderMetaSchema = z.object({
|
||||
@ -55,3 +58,12 @@ Return \`done: true\` ONLY when ALL of the following are true:
|
||||
|
||||
Return \`done: false\` if you made progress but there is still work to do, or if build/lint has errors you plan to fix in the next iteration.`;
|
||||
}
|
||||
|
||||
export function createCoderRole(adapter: AgentFn, extract: LlmExtractorConfig): Role<CoderMeta> {
|
||||
return createRole(
|
||||
adapter,
|
||||
async (start: StartStep) => coderPrompt({ threadId: start.meta.threadId }),
|
||||
coderMetaSchema,
|
||||
extract,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export {
|
||||
buildWorkspaceCommitterRole,
|
||||
createWorkspaceCommitterRole,
|
||||
committerMetaSchema,
|
||||
type BuildWorkspaceCommitterDeps,
|
||||
type CommitterMeta,
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
|
||||
export const plannerMetaSchema = z.object({
|
||||
@ -51,3 +54,12 @@ or
|
||||
{ "ready": false }
|
||||
\`\`\``;
|
||||
}
|
||||
|
||||
export function createPlannerRole(adapter: AgentFn, extract: LlmExtractorConfig): Role<PlannerMeta> {
|
||||
return createRole(
|
||||
adapter,
|
||||
async (start: StartStep) => plannerPrompt({ threadId: start.meta.threadId }),
|
||||
plannerMetaSchema,
|
||||
extract,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
|
||||
export const reviewerMetaSchema = z.object({
|
||||
@ -43,3 +46,17 @@ or
|
||||
{ "approved": false }
|
||||
\`\`\``;
|
||||
}
|
||||
|
||||
export function createReviewerRole(
|
||||
adapter: AgentFn,
|
||||
extract: LlmExtractorConfig,
|
||||
nerveRoot: string,
|
||||
): Role<ReviewerMeta> {
|
||||
return createRole(
|
||||
adapter,
|
||||
async (start: StartStep) =>
|
||||
reviewerPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||
reviewerMetaSchema,
|
||||
extract,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
|
||||
export const testerMetaSchema = z.object({
|
||||
@ -40,3 +43,17 @@ If any step fails, include the relevant error output.
|
||||
|
||||
Output a clear summary: what you checked, what passed, what failed, and why.`;
|
||||
}
|
||||
|
||||
export function createTesterRole(
|
||||
adapter: AgentFn,
|
||||
extract: LlmExtractorConfig,
|
||||
nerveRoot: string,
|
||||
): Role<TesterMeta> {
|
||||
return createRole(
|
||||
adapter,
|
||||
async (start: StartStep) =>
|
||||
testerPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||
testerMetaSchema,
|
||||
extract,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,64 +1,42 @@
|
||||
import type { StartStep, WorkflowDefinition } from "@uncaged/nerve-core";
|
||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||
import type { AgentFn, WorkflowDefinition } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
|
||||
import { moderator } from "./moderator.js";
|
||||
import type { WorkflowMeta } from "./moderator.js";
|
||||
import { buildCommitterRole } from "./roles/committer/index.js";
|
||||
import { buildImplementRole } from "./roles/implement/index.js";
|
||||
import { buildPlanRole } from "./roles/plan/index.js";
|
||||
import { prepareMetaSchema } from "./roles/prepare/index.js";
|
||||
import { preparePrompt } from "./roles/prepare/prompt.js";
|
||||
import { buildPublishRole } from "./roles/publish/index.js";
|
||||
import { readIssueMetaSchema } from "./roles/read-issue/index.js";
|
||||
import { readIssuePrompt } from "./roles/read-issue/prompt.js";
|
||||
import { reviewMetaSchema } from "./roles/review/index.js";
|
||||
import { reviewPrompt } from "./roles/review/prompt.js";
|
||||
import { testMetaSchema } from "./roles/test/index.js";
|
||||
import { testPrompt } from "./roles/test/prompt.js";
|
||||
import { createCommitterRole } from "./roles/committer/index.js";
|
||||
import { createImplementRole } from "./roles/implement/index.js";
|
||||
import { createPlanRole } from "./roles/plan/index.js";
|
||||
import { createPrepareRole } from "./roles/prepare/index.js";
|
||||
import { createPublishRole } from "./roles/publish/index.js";
|
||||
import { createReadIssueRole } from "./roles/read-issue/index.js";
|
||||
import { createReviewRole } from "./roles/review/index.js";
|
||||
import { createTestRole } from "./roles/test/index.js";
|
||||
|
||||
export type BuildSolveIssueDeps = {
|
||||
export type CreateSolveIssueDeps = {
|
||||
defaultAdapter: AgentFn;
|
||||
adapters?: Partial<Record<keyof WorkflowMeta, AgentFn>>;
|
||||
nerveRoot: string;
|
||||
extract: LlmExtractorConfig;
|
||||
};
|
||||
|
||||
export function buildSolveIssue({
|
||||
export function createSolveIssueWorkflow({
|
||||
defaultAdapter,
|
||||
adapters,
|
||||
nerveRoot,
|
||||
extract,
|
||||
}: BuildSolveIssueDeps): WorkflowDefinition<WorkflowMeta> {
|
||||
}: CreateSolveIssueDeps): WorkflowDefinition<WorkflowMeta> {
|
||||
const a = (role: keyof WorkflowMeta) => adapters?.[role] ?? defaultAdapter;
|
||||
return {
|
||||
name: "solve-issue",
|
||||
roles: {
|
||||
"read-issue": createRole(
|
||||
hermesAdapter,
|
||||
async (start: StartStep) => readIssuePrompt({ threadId: start.meta.threadId }),
|
||||
readIssueMetaSchema,
|
||||
extract,
|
||||
),
|
||||
prepare: createRole(
|
||||
hermesAdapter,
|
||||
async (start: StartStep) => preparePrompt({ threadId: start.meta.threadId }),
|
||||
prepareMetaSchema,
|
||||
extract,
|
||||
),
|
||||
plan: buildPlanRole({ extract, nerveRoot }),
|
||||
implement: buildImplementRole({ extract, nerveRoot }),
|
||||
committer: buildCommitterRole({ extract, nerveRoot }),
|
||||
review: createRole(
|
||||
hermesAdapter,
|
||||
async (start: StartStep) =>
|
||||
reviewPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||
reviewMetaSchema,
|
||||
extract,
|
||||
),
|
||||
test: createRole(
|
||||
hermesAdapter,
|
||||
async (start: StartStep) => testPrompt({ threadId: start.meta.threadId }),
|
||||
testMetaSchema,
|
||||
extract,
|
||||
),
|
||||
publish: buildPublishRole({ extract, nerveRoot }),
|
||||
"read-issue": createReadIssueRole(a("read-issue"), extract),
|
||||
prepare: createPrepareRole(a("prepare"), extract),
|
||||
plan: createPlanRole(a("plan"), { extract, nerveRoot }),
|
||||
implement: createImplementRole(a("implement"), { extract, nerveRoot }),
|
||||
committer: createCommitterRole(a("committer"), { extract, nerveRoot }),
|
||||
review: createReviewRole(a("review"), extract, nerveRoot),
|
||||
test: createTestRole(a("test"), extract),
|
||||
publish: createPublishRole(a("publish"), { extract, nerveRoot }),
|
||||
},
|
||||
moderator,
|
||||
};
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { join } from "node:path";
|
||||
import { buildSolveIssue } from "./build.js";
|
||||
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||
import { createSolveIssueWorkflow } from "./build.js";
|
||||
import { resolveDashScopeProvider } from "./lib/provider.js";
|
||||
|
||||
const HOME = process.env.HOME ?? "/home/azureuser";
|
||||
@ -11,6 +13,25 @@ if (provider === null) {
|
||||
throw new Error("Set DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL (or cfg get equivalents)");
|
||||
}
|
||||
|
||||
const workflow = buildSolveIssue({ nerveRoot: NERVE_ROOT, extract: { provider } });
|
||||
const CURSOR_TIMEOUT_MS = 300_000;
|
||||
|
||||
const workflow = createSolveIssueWorkflow({
|
||||
defaultAdapter: hermesAdapter,
|
||||
adapters: {
|
||||
plan: createCursorAdapter({
|
||||
type: "cursor",
|
||||
mode: "ask",
|
||||
model: "auto",
|
||||
timeout: CURSOR_TIMEOUT_MS,
|
||||
}),
|
||||
implement: createCursorAdapter({
|
||||
type: "cursor",
|
||||
model: "auto",
|
||||
timeout: CURSOR_TIMEOUT_MS,
|
||||
}),
|
||||
},
|
||||
nerveRoot: NERVE_ROOT,
|
||||
extract: { provider },
|
||||
});
|
||||
|
||||
export default workflow;
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||
import type { AgentFn, Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole, isDryRun } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
@ -14,14 +13,17 @@ export const committerMetaSchema = z.object({
|
||||
});
|
||||
export type CommitterMeta = z.infer<typeof committerMetaSchema>;
|
||||
|
||||
export type BuildCommitterDeps = {
|
||||
export type CreateCommitterRoleDeps = {
|
||||
extract: LlmExtractorConfig;
|
||||
nerveRoot: string;
|
||||
};
|
||||
|
||||
export function buildCommitterRole({ extract, nerveRoot }: BuildCommitterDeps): Role<CommitterMeta> {
|
||||
export function createCommitterRole(
|
||||
adapter: AgentFn,
|
||||
{ extract, nerveRoot }: CreateCommitterRoleDeps,
|
||||
): Role<CommitterMeta> {
|
||||
const innerRole = createRole(
|
||||
hermesAdapter,
|
||||
adapter,
|
||||
async (start: StartStep) =>
|
||||
committerPrompt({
|
||||
threadId: start.meta.threadId,
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
||||
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||
import type { AgentFn, Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
@ -12,17 +11,15 @@ export const implementMetaSchema = z.object({
|
||||
});
|
||||
export type ImplementMeta = z.infer<typeof implementMetaSchema>;
|
||||
|
||||
export type BuildImplementDeps = {
|
||||
export type CreateImplementRoleDeps = {
|
||||
extract: LlmExtractorConfig;
|
||||
nerveRoot: string;
|
||||
};
|
||||
|
||||
const CURSOR_TIMEOUT_MS = 300_000;
|
||||
|
||||
export function buildImplementRole({
|
||||
extract,
|
||||
nerveRoot,
|
||||
}: BuildImplementDeps): Role<ImplementMeta> {
|
||||
export function createImplementRole(
|
||||
adapter: AgentFn,
|
||||
{ extract, nerveRoot }: CreateImplementRoleDeps,
|
||||
): Role<ImplementMeta> {
|
||||
return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<ImplementMeta>> => {
|
||||
const cwd = resolveRepoCwd(messages);
|
||||
if (cwd === null) {
|
||||
@ -33,11 +30,7 @@ export function buildImplementRole({
|
||||
}
|
||||
|
||||
const innerRole = createRole(
|
||||
createCursorAdapter({
|
||||
type: "cursor",
|
||||
model: "auto",
|
||||
timeout: CURSOR_TIMEOUT_MS,
|
||||
}),
|
||||
adapter,
|
||||
async (innerStart: StartStep) =>
|
||||
buildImplementPrompt({
|
||||
threadId: innerStart.meta.threadId,
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
||||
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||
import type { AgentFn, Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
@ -12,14 +11,15 @@ export const planMetaSchema = z.object({
|
||||
});
|
||||
export type PlanMeta = z.infer<typeof planMetaSchema>;
|
||||
|
||||
export type BuildPlanDeps = {
|
||||
export type CreatePlanRoleDeps = {
|
||||
extract: LlmExtractorConfig;
|
||||
nerveRoot: string;
|
||||
};
|
||||
|
||||
const CURSOR_TIMEOUT_MS = 300_000;
|
||||
|
||||
export function buildPlanRole({ extract, nerveRoot }: BuildPlanDeps): Role<PlanMeta> {
|
||||
export function createPlanRole(
|
||||
adapter: AgentFn,
|
||||
{ extract, nerveRoot }: CreatePlanRoleDeps,
|
||||
): Role<PlanMeta> {
|
||||
return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<PlanMeta>> => {
|
||||
const cwd = resolveRepoCwd(messages);
|
||||
if (cwd === null) {
|
||||
@ -30,12 +30,7 @@ export function buildPlanRole({ extract, nerveRoot }: BuildPlanDeps): Role<PlanM
|
||||
}
|
||||
|
||||
const innerRole = createRole(
|
||||
createCursorAdapter({
|
||||
type: "cursor",
|
||||
mode: "ask",
|
||||
model: "auto",
|
||||
timeout: CURSOR_TIMEOUT_MS,
|
||||
}),
|
||||
adapter,
|
||||
async (innerStart: StartStep) =>
|
||||
buildPlanPrompt({
|
||||
threadId: innerStart.meta.threadId,
|
||||
|
||||
@ -1,6 +1,20 @@
|
||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
|
||||
import { preparePrompt } from "./prompt.js";
|
||||
|
||||
export const prepareMetaSchema = z.object({
|
||||
ready: z.boolean().describe("true if repo is ready and baseline build ok"),
|
||||
});
|
||||
export type PrepareMeta = z.infer<typeof prepareMetaSchema>;
|
||||
|
||||
export function createPrepareRole(adapter: AgentFn, extract: LlmExtractorConfig): Role<PrepareMeta> {
|
||||
return createRole(
|
||||
adapter,
|
||||
async (start: StartStep) => preparePrompt({ threadId: start.meta.threadId }),
|
||||
prepareMetaSchema,
|
||||
extract,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||
import type { AgentFn, Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole, isDryRun } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
@ -13,7 +12,7 @@ export const publishMetaSchema = z.object({
|
||||
});
|
||||
export type PublishMeta = z.infer<typeof publishMetaSchema>;
|
||||
|
||||
export type BuildPublishDeps = {
|
||||
export type CreatePublishRoleDeps = {
|
||||
extract: LlmExtractorConfig;
|
||||
nerveRoot: string;
|
||||
};
|
||||
@ -22,9 +21,12 @@ function logPath(nerveRoot: string): string {
|
||||
return join(nerveRoot, "logs", `solve-issue-publish-${Date.now()}.log`);
|
||||
}
|
||||
|
||||
export function buildPublishRole({ extract, nerveRoot }: BuildPublishDeps): Role<PublishMeta> {
|
||||
export function createPublishRole(
|
||||
adapter: AgentFn,
|
||||
{ extract, nerveRoot }: CreatePublishRoleDeps,
|
||||
): Role<PublishMeta> {
|
||||
const innerRole = createRole(
|
||||
hermesAdapter,
|
||||
adapter,
|
||||
async (start: StartStep) =>
|
||||
buildPublishPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||
publishMetaSchema,
|
||||
|
||||
@ -1,6 +1,20 @@
|
||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
|
||||
import { readIssuePrompt } from "./prompt.js";
|
||||
|
||||
export const readIssueMetaSchema = z.object({
|
||||
ready: z.boolean().describe("true if issue content was fetched and markers are present"),
|
||||
});
|
||||
export type ReadIssueMeta = z.infer<typeof readIssueMetaSchema>;
|
||||
|
||||
export function createReadIssueRole(adapter: AgentFn, extract: LlmExtractorConfig): Role<ReadIssueMeta> {
|
||||
return createRole(
|
||||
adapter,
|
||||
async (start: StartStep) => readIssuePrompt({ threadId: start.meta.threadId }),
|
||||
readIssueMetaSchema,
|
||||
extract,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,25 @@
|
||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
|
||||
import { reviewPrompt } from "./prompt.js";
|
||||
|
||||
export const reviewMetaSchema = z.object({
|
||||
approved: z.boolean().describe("true if diff is clean and ready for tests"),
|
||||
});
|
||||
export type ReviewMeta = z.infer<typeof reviewMetaSchema>;
|
||||
|
||||
export function createReviewRole(
|
||||
adapter: AgentFn,
|
||||
extract: LlmExtractorConfig,
|
||||
nerveRoot: string,
|
||||
): Role<ReviewMeta> {
|
||||
return createRole(
|
||||
adapter,
|
||||
async (start: StartStep) =>
|
||||
reviewPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||
reviewMetaSchema,
|
||||
extract,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,20 @@
|
||||
import type { AgentFn, Role, StartStep } from "@uncaged/nerve-core";
|
||||
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||
import { z } from "zod";
|
||||
|
||||
import { testPrompt } from "./prompt.js";
|
||||
|
||||
export const testMetaSchema = z.object({
|
||||
passed: z.boolean().describe("true if all test commands passed"),
|
||||
});
|
||||
export type TestMeta = z.infer<typeof testMetaSchema>;
|
||||
|
||||
export function createTestRole(adapter: AgentFn, extract: LlmExtractorConfig): Role<TestMeta> {
|
||||
return createRole(
|
||||
adapter,
|
||||
async (start: StartStep) => testPrompt({ threadId: start.meta.threadId }),
|
||||
testMetaSchema,
|
||||
extract,
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user