Compare commits

..

No commits in common. "ac47daa42b18bf7b62e6ff09d09e243b06fe745b" and "a506e5b36bf5b65ba5504e8d32afcf065f2bfa08" have entirely different histories.

25 changed files with 221 additions and 337 deletions

View File

@ -1,4 +1,5 @@
import type { AgentFn, Role, RoleResult, StartStep } from "@uncaged/nerve-core"; import type { Role, RoleResult, StartStep } from "@uncaged/nerve-core";
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, isDryRun } from "@uncaged/nerve-workflow-utils"; import { createRole, isDryRun } from "@uncaged/nerve-workflow-utils";
import { z } from "zod"; import { z } from "zod";
@ -61,18 +62,15 @@ or
\`\`\``; \`\`\``;
} }
export function createWorkspaceCommitterRole( export function buildWorkspaceCommitterRole({
adapter: AgentFn, extract,
{ nerveRoot,
extract, workflowName,
nerveRoot, conventionalCommitScopeHint,
workflowName, branchCheckoutExample,
conventionalCommitScopeHint, }: BuildWorkspaceCommitterDeps): Role<CommitterMeta> {
branchCheckoutExample,
}: BuildWorkspaceCommitterDeps,
): Role<CommitterMeta> {
const innerRole = createRole( const innerRole = createRole(
adapter, hermesAdapter,
async (start: StartStep) => async (start: StartStep) =>
workspaceCommitterPrompt({ workspaceCommitterPrompt({
threadId: start.meta.threadId, threadId: start.meta.threadId,

View File

@ -1,34 +1,75 @@
import type { AgentFn, WorkflowDefinition } from "@uncaged/nerve-core"; 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 { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils"; 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 { moderator } from "./moderator.js";
import type { SenseMeta } 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 CreateDevelopSenseDeps = { export type BuildDevelopSenseDeps = {
defaultAdapter: AgentFn;
adapters?: Partial<Record<keyof SenseMeta, AgentFn>>;
extract: LlmExtractorConfig; extract: LlmExtractorConfig;
cwd: string; cwd: string;
}; };
export function createDevelopSenseWorkflow({ const CURSOR_TIMEOUT_MS = 300_000;
defaultAdapter,
adapters, export function buildDevelopSenseWorkflow({
extract, extract,
cwd, cwd,
}: CreateDevelopSenseDeps): WorkflowDefinition<SenseMeta> { }: BuildDevelopSenseDeps): WorkflowDefinition<SenseMeta> {
const a = (role: keyof SenseMeta) => adapters?.[role] ?? defaultAdapter;
const roles = { const roles = {
planner: createPlannerRole(a('planner'), extract), planner: createRole(
coder: createCoderRole(a('coder'), extract), createCursorAdapter({
reviewer: createReviewerRole(a('reviewer'), extract, cwd), type: "cursor",
tester: createTesterRole(a('tester'), extract, cwd), mode: "ask",
committer: createWorkspaceCommitterRole(a('committer'), { 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({
extract, extract,
nerveRoot: cwd, nerveRoot: cwd,
workflowName: "develop-sense", workflowName: "develop-sense",

View File

@ -1,7 +1,5 @@
import { join } from "node:path"; import { join } from "node:path";
import { createCursorAdapter, cursorAdapter } from "@uncaged/nerve-adapter-cursor"; import { buildDevelopSenseWorkflow } from "./build.js";
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
import { createDevelopSenseWorkflow } from "./build.js";
const HOME = process.env.HOME ?? "/home/azureuser"; const HOME = process.env.HOME ?? "/home/azureuser";
const NERVE_ROOT = join(HOME, ".uncaged-nerve"); const NERVE_ROOT = join(HOME, ".uncaged-nerve");
@ -13,19 +11,7 @@ if (!apiKey || !baseUrl) {
throw new Error("Set DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL"); throw new Error("Set DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL");
} }
const CURSOR_TIMEOUT_MS = 300_000; const workflow = buildDevelopSenseWorkflow({
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 } }, extract: { provider: { apiKey, baseUrl, model } },
cwd: NERVE_ROOT, cwd: NERVE_ROOT,
}); });

View File

@ -1,6 +1,3 @@
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 { z } from "zod";
export const coderMetaSchema = z.object({ export const coderMetaSchema = z.object({
@ -39,12 +36,3 @@ 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.`; 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,
);
}

View File

@ -1,5 +1,5 @@
export { export {
createWorkspaceCommitterRole, buildWorkspaceCommitterRole,
committerMetaSchema, committerMetaSchema,
type BuildWorkspaceCommitterDeps, type BuildWorkspaceCommitterDeps,
type CommitterMeta, type CommitterMeta,

View File

@ -1,6 +1,3 @@
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 { z } from "zod";
export const plannerMetaSchema = z.object({ export const plannerMetaSchema = z.object({
@ -25,12 +22,3 @@ 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.`; 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,
);
}

View File

@ -1,6 +1,3 @@
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 { z } from "zod";
export const reviewerMetaSchema = z.object({ export const reviewerMetaSchema = z.object({
@ -46,17 +43,3 @@ or
{ "approved": false } { "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,
);
}

View File

@ -1,6 +1,3 @@
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 { z } from "zod";
export const testerMetaSchema = z.object({ export const testerMetaSchema = z.object({
@ -42,17 +39,3 @@ If any step fails, include the relevant error output.
Output a clear summary: what you checked, what passed, what failed, and why.`; 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,
);
}

View File

@ -1,34 +1,75 @@
import type { AgentFn, WorkflowDefinition } from "@uncaged/nerve-core"; 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 { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils"; 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 { moderator } from "./moderator.js";
import type { WorkflowMeta } 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 CreateDevelopWorkflowDeps = { export type BuildDevelopWorkflowDeps = {
defaultAdapter: AgentFn;
adapters?: Partial<Record<keyof WorkflowMeta, AgentFn>>;
extract: LlmExtractorConfig; extract: LlmExtractorConfig;
nerveRoot: string; nerveRoot: string;
}; };
export function createDevelopWorkflowWorkflow({ const CURSOR_TIMEOUT_MS = 300_000;
defaultAdapter,
adapters, export function buildDevelopWorkflow({
extract, extract,
nerveRoot, nerveRoot,
}: CreateDevelopWorkflowDeps): WorkflowDefinition<WorkflowMeta> { }: BuildDevelopWorkflowDeps): WorkflowDefinition<WorkflowMeta> {
const a = (role: keyof WorkflowMeta) => adapters?.[role] ?? defaultAdapter;
const roles = { const roles = {
planner: createPlannerRole(a('planner'), extract), planner: createRole(
coder: createCoderRole(a('coder'), extract), createCursorAdapter({
reviewer: createReviewerRole(a('reviewer'), extract, nerveRoot), type: "cursor",
tester: createTesterRole(a('tester'), extract, nerveRoot), mode: "ask",
committer: createWorkspaceCommitterRole(a('committer'), { 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({
extract, extract,
nerveRoot, nerveRoot,
workflowName: "develop-workflow", workflowName: "develop-workflow",

View File

@ -1,7 +1,5 @@
import { join } from "node:path"; import { join } from "node:path";
import { createCursorAdapter, cursorAdapter } from "@uncaged/nerve-adapter-cursor"; import { buildDevelopWorkflow } from "./build.js";
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
import { createDevelopWorkflowWorkflow } from "./build.js";
const HOME = process.env.HOME ?? "/home/azureuser"; const HOME = process.env.HOME ?? "/home/azureuser";
const NERVE_ROOT = join(HOME, ".uncaged-nerve"); const NERVE_ROOT = join(HOME, ".uncaged-nerve");
@ -14,19 +12,7 @@ if (!apiKey || !baseUrl) {
throw new Error("Set DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL"); throw new Error("Set DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL");
} }
const CURSOR_TIMEOUT_MS = 300_000; const workflow = buildDevelopWorkflow({
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 } }, extract: { provider: { apiKey, baseUrl, model } },
nerveRoot: NERVE_ROOT, nerveRoot: NERVE_ROOT,
}); });

View File

@ -1,6 +1,3 @@
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 { z } from "zod";
export const coderMetaSchema = z.object({ export const coderMetaSchema = z.object({
@ -58,12 +55,3 @@ 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.`; 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,
);
}

View File

@ -1,5 +1,5 @@
export { export {
createWorkspaceCommitterRole, buildWorkspaceCommitterRole,
committerMetaSchema, committerMetaSchema,
type BuildWorkspaceCommitterDeps, type BuildWorkspaceCommitterDeps,
type CommitterMeta, type CommitterMeta,

View File

@ -1,6 +1,3 @@
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 { z } from "zod";
export const plannerMetaSchema = z.object({ export const plannerMetaSchema = z.object({
@ -54,12 +51,3 @@ or
{ "ready": false } { "ready": false }
\`\`\``; \`\`\``;
} }
export function createPlannerRole(adapter: AgentFn, extract: LlmExtractorConfig): Role<PlannerMeta> {
return createRole(
adapter,
async (start: StartStep) => plannerPrompt({ threadId: start.meta.threadId }),
plannerMetaSchema,
extract,
);
}

View File

@ -1,6 +1,3 @@
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 { z } from "zod";
export const reviewerMetaSchema = z.object({ export const reviewerMetaSchema = z.object({
@ -46,17 +43,3 @@ or
{ "approved": false } { "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,
);
}

View File

@ -1,6 +1,3 @@
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 { z } from "zod";
export const testerMetaSchema = z.object({ export const testerMetaSchema = z.object({
@ -43,17 +40,3 @@ If any step fails, include the relevant error output.
Output a clear summary: what you checked, what passed, what failed, and why.`; 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,
);
}

View File

@ -1,42 +1,64 @@
import type { AgentFn, WorkflowDefinition } from "@uncaged/nerve-core"; import type { StartStep, WorkflowDefinition } from "@uncaged/nerve-core";
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 { moderator } from "./moderator.js"; import { moderator } from "./moderator.js";
import type { WorkflowMeta } from "./moderator.js"; import type { WorkflowMeta } from "./moderator.js";
import { createCommitterRole } from "./roles/committer/index.js"; import { buildCommitterRole } from "./roles/committer/index.js";
import { createImplementRole } from "./roles/implement/index.js"; import { buildImplementRole } from "./roles/implement/index.js";
import { createPlanRole } from "./roles/plan/index.js"; import { buildPlanRole } from "./roles/plan/index.js";
import { createPrepareRole } from "./roles/prepare/index.js"; import { prepareMetaSchema } from "./roles/prepare/index.js";
import { createPublishRole } from "./roles/publish/index.js"; import { preparePrompt } from "./roles/prepare/prompt.js";
import { createReadIssueRole } from "./roles/read-issue/index.js"; import { buildPublishRole } from "./roles/publish/index.js";
import { createReviewRole } from "./roles/review/index.js"; import { readIssueMetaSchema } from "./roles/read-issue/index.js";
import { createTestRole } from "./roles/test/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";
export type CreateSolveIssueDeps = { export type BuildSolveIssueDeps = {
defaultAdapter: AgentFn;
adapters?: Partial<Record<keyof WorkflowMeta, AgentFn>>;
nerveRoot: string; nerveRoot: string;
extract: LlmExtractorConfig; extract: LlmExtractorConfig;
}; };
export function createSolveIssueWorkflow({ export function buildSolveIssue({
defaultAdapter,
adapters,
nerveRoot, nerveRoot,
extract, extract,
}: CreateSolveIssueDeps): WorkflowDefinition<WorkflowMeta> { }: BuildSolveIssueDeps): WorkflowDefinition<WorkflowMeta> {
const a = (role: keyof WorkflowMeta) => adapters?.[role] ?? defaultAdapter;
return { return {
name: "solve-issue", name: "solve-issue",
roles: { roles: {
"read-issue": createReadIssueRole(a("read-issue"), extract), "read-issue": createRole(
prepare: createPrepareRole(a("prepare"), extract), hermesAdapter,
plan: createPlanRole(a("plan"), { extract, nerveRoot }), async (start: StartStep) => readIssuePrompt({ threadId: start.meta.threadId }),
implement: createImplementRole(a("implement"), { extract, nerveRoot }), readIssueMetaSchema,
committer: createCommitterRole(a("committer"), { extract, nerveRoot }), extract,
review: createReviewRole(a("review"), extract, nerveRoot), ),
test: createTestRole(a("test"), extract), prepare: createRole(
publish: createPublishRole(a("publish"), { extract, nerveRoot }), 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 }),
}, },
moderator, moderator,
}; };

View File

@ -1,7 +1,5 @@
import { join } from "node:path"; import { join } from "node:path";
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor"; import { buildSolveIssue } from "./build.js";
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
import { createSolveIssueWorkflow } from "./build.js";
import { resolveDashScopeProvider } from "./lib/provider.js"; import { resolveDashScopeProvider } from "./lib/provider.js";
const HOME = process.env.HOME ?? "/home/azureuser"; const HOME = process.env.HOME ?? "/home/azureuser";
@ -13,25 +11,6 @@ if (provider === null) {
throw new Error("Set DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL (or cfg get equivalents)"); throw new Error("Set DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL (or cfg get equivalents)");
} }
const CURSOR_TIMEOUT_MS = 300_000; const workflow = buildSolveIssue({ nerveRoot: NERVE_ROOT, extract: { provider } });
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; export default workflow;

View File

@ -1,4 +1,5 @@
import type { AgentFn, Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core"; import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
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, isDryRun } from "@uncaged/nerve-workflow-utils"; import { createRole, isDryRun } from "@uncaged/nerve-workflow-utils";
import { z } from "zod"; import { z } from "zod";
@ -13,17 +14,14 @@ export const committerMetaSchema = z.object({
}); });
export type CommitterMeta = z.infer<typeof committerMetaSchema>; export type CommitterMeta = z.infer<typeof committerMetaSchema>;
export type CreateCommitterRoleDeps = { export type BuildCommitterDeps = {
extract: LlmExtractorConfig; extract: LlmExtractorConfig;
nerveRoot: string; nerveRoot: string;
}; };
export function createCommitterRole( export function buildCommitterRole({ extract, nerveRoot }: BuildCommitterDeps): Role<CommitterMeta> {
adapter: AgentFn,
{ extract, nerveRoot }: CreateCommitterRoleDeps,
): Role<CommitterMeta> {
const innerRole = createRole( const innerRole = createRole(
adapter, hermesAdapter,
async (start: StartStep) => async (start: StartStep) =>
committerPrompt({ committerPrompt({
threadId: start.meta.threadId, threadId: start.meta.threadId,

View File

@ -1,4 +1,5 @@
import type { AgentFn, Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core"; import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
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 { z } from "zod"; import { z } from "zod";
@ -11,15 +12,17 @@ export const implementMetaSchema = z.object({
}); });
export type ImplementMeta = z.infer<typeof implementMetaSchema>; export type ImplementMeta = z.infer<typeof implementMetaSchema>;
export type CreateImplementRoleDeps = { export type BuildImplementDeps = {
extract: LlmExtractorConfig; extract: LlmExtractorConfig;
nerveRoot: string; nerveRoot: string;
}; };
export function createImplementRole( const CURSOR_TIMEOUT_MS = 300_000;
adapter: AgentFn,
{ extract, nerveRoot }: CreateImplementRoleDeps, export function buildImplementRole({
): Role<ImplementMeta> { extract,
nerveRoot,
}: BuildImplementDeps): Role<ImplementMeta> {
return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<ImplementMeta>> => { return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<ImplementMeta>> => {
const cwd = resolveRepoCwd(messages); const cwd = resolveRepoCwd(messages);
if (cwd === null) { if (cwd === null) {
@ -30,7 +33,11 @@ export function createImplementRole(
} }
const innerRole = createRole( const innerRole = createRole(
adapter, createCursorAdapter({
type: "cursor",
model: "auto",
timeout: CURSOR_TIMEOUT_MS,
}),
async (innerStart: StartStep) => async (innerStart: StartStep) =>
buildImplementPrompt({ buildImplementPrompt({
threadId: innerStart.meta.threadId, threadId: innerStart.meta.threadId,

View File

@ -1,4 +1,5 @@
import type { AgentFn, Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core"; import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
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 { z } from "zod"; import { z } from "zod";
@ -11,15 +12,14 @@ export const planMetaSchema = z.object({
}); });
export type PlanMeta = z.infer<typeof planMetaSchema>; export type PlanMeta = z.infer<typeof planMetaSchema>;
export type CreatePlanRoleDeps = { export type BuildPlanDeps = {
extract: LlmExtractorConfig; extract: LlmExtractorConfig;
nerveRoot: string; nerveRoot: string;
}; };
export function createPlanRole( const CURSOR_TIMEOUT_MS = 300_000;
adapter: AgentFn,
{ extract, nerveRoot }: CreatePlanRoleDeps, export function buildPlanRole({ extract, nerveRoot }: BuildPlanDeps): Role<PlanMeta> {
): Role<PlanMeta> {
return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<PlanMeta>> => { return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<PlanMeta>> => {
const cwd = resolveRepoCwd(messages); const cwd = resolveRepoCwd(messages);
if (cwd === null) { if (cwd === null) {
@ -30,7 +30,12 @@ export function createPlanRole(
} }
const innerRole = createRole( const innerRole = createRole(
adapter, createCursorAdapter({
type: "cursor",
mode: "ask",
model: "auto",
timeout: CURSOR_TIMEOUT_MS,
}),
async (innerStart: StartStep) => async (innerStart: StartStep) =>
buildPlanPrompt({ buildPlanPrompt({
threadId: innerStart.meta.threadId, threadId: innerStart.meta.threadId,

View File

@ -1,20 +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"; import { z } from "zod";
import { preparePrompt } from "./prompt.js";
export const prepareMetaSchema = z.object({ export const prepareMetaSchema = z.object({
ready: z.boolean().describe("true if repo is ready and baseline build ok"), ready: z.boolean().describe("true if repo is ready and baseline build ok"),
}); });
export type PrepareMeta = z.infer<typeof prepareMetaSchema>; 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,
);
}

View File

@ -1,6 +1,7 @@
import { mkdirSync, writeFileSync } from "node:fs"; import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path"; import { join } from "node:path";
import type { AgentFn, Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core"; import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
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, isDryRun } from "@uncaged/nerve-workflow-utils"; import { createRole, isDryRun } from "@uncaged/nerve-workflow-utils";
import { z } from "zod"; import { z } from "zod";
@ -12,7 +13,7 @@ export const publishMetaSchema = z.object({
}); });
export type PublishMeta = z.infer<typeof publishMetaSchema>; export type PublishMeta = z.infer<typeof publishMetaSchema>;
export type CreatePublishRoleDeps = { export type BuildPublishDeps = {
extract: LlmExtractorConfig; extract: LlmExtractorConfig;
nerveRoot: string; nerveRoot: string;
}; };
@ -21,12 +22,9 @@ function logPath(nerveRoot: string): string {
return join(nerveRoot, "logs", `solve-issue-publish-${Date.now()}.log`); return join(nerveRoot, "logs", `solve-issue-publish-${Date.now()}.log`);
} }
export function createPublishRole( export function buildPublishRole({ extract, nerveRoot }: BuildPublishDeps): Role<PublishMeta> {
adapter: AgentFn,
{ extract, nerveRoot }: CreatePublishRoleDeps,
): Role<PublishMeta> {
const innerRole = createRole( const innerRole = createRole(
adapter, hermesAdapter,
async (start: StartStep) => async (start: StartStep) =>
buildPublishPrompt({ threadId: start.meta.threadId, nerveRoot }), buildPublishPrompt({ threadId: start.meta.threadId, nerveRoot }),
publishMetaSchema, publishMetaSchema,

View File

@ -1,20 +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"; import { z } from "zod";
import { readIssuePrompt } from "./prompt.js";
export const readIssueMetaSchema = z.object({ export const readIssueMetaSchema = z.object({
ready: z.boolean().describe("true if issue content was fetched and markers are present"), ready: z.boolean().describe("true if issue content was fetched and markers are present"),
}); });
export type ReadIssueMeta = z.infer<typeof readIssueMetaSchema>; 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,
);
}

View File

@ -1,25 +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"; import { z } from "zod";
import { reviewPrompt } from "./prompt.js";
export const reviewMetaSchema = z.object({ export const reviewMetaSchema = z.object({
approved: z.boolean().describe("true if diff is clean and ready for tests"), approved: z.boolean().describe("true if diff is clean and ready for tests"),
}); });
export type ReviewMeta = z.infer<typeof reviewMetaSchema>; 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,
);
}

View File

@ -1,20 +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"; import { z } from "zod";
import { testPrompt } from "./prompt.js";
export const testMetaSchema = z.object({ export const testMetaSchema = z.object({
passed: z.boolean().describe("true if all test commands passed"), passed: z.boolean().describe("true if all test commands passed"),
}); });
export type TestMeta = z.infer<typeof testMetaSchema>; 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,
);
}