From 9c11cd53e611792b05bd50f4dd03fc19089da790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=A9=98?= Date: Wed, 29 Apr 2026 13:27:08 +0000 Subject: [PATCH] =?UTF-8?q?rename=20with-dry-run=20=E2=86=92=20role-decora?= =?UTF-8?q?tors,=20remove=20RFC-004=20from=20this=20PR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/rfc-004-package-architecture.md | 168 ------------------ ...ry-run.test.ts => role-decorators.test.ts} | 2 +- packages/workflow-utils/src/index.ts | 2 +- .../{with-dry-run.ts => role-decorators.ts} | 0 4 files changed, 2 insertions(+), 170 deletions(-) delete mode 100644 docs/rfc-004-package-architecture.md rename packages/workflow-utils/src/__tests__/{with-dry-run.test.ts => role-decorators.test.ts} (97%) rename packages/workflow-utils/src/{with-dry-run.ts => role-decorators.ts} (100%) diff --git a/docs/rfc-004-package-architecture.md b/docs/rfc-004-package-architecture.md deleted file mode 100644 index 90eb8ff..0000000 --- a/docs/rfc-004-package-architecture.md +++ /dev/null @@ -1,168 +0,0 @@ -# RFC-004: Package Architecture β€” Shareable Workflows, Roles & Senses - -**Author:** 小橘 🍊(NEKO TeamοΌ‰ -**Status:** Draft -**Created:** 2026-04-29 - -## Summary - -Make workflows, roles, and senses publishable as lightweight npm packages. Workspaces become pure configuration β€” selecting packages, wiring adapters, and providing credentials. No builtin workflows in the nerve core. - -## Motivation - -Currently, workflows like `develop-sense` and `develop-workflow` live inside the workspace (`~/.uncaged-nerve/workflows/`). This creates problems: - -1. **No sharing** β€” every workspace duplicates the same workflow code -2. **No versioning** β€” upgrading a workflow means manual file edits -3. **Builtin is a trap** β€” if we bake workflows into nerve core, they require adapters and LLM providers that may not be installed. A fresh `nerve` install on a bare machine would fail to load builtins. -4. **Roles are already shared** β€” `_shared/workspace-committer.ts` proves the pattern works; we just need to formalize it as packages - -The adapter pattern (`@uncaged/nerve-adapter-hermes`, `@uncaged/nerve-adapter-cursor`) already established the precedent: infrastructure as packages, workspace as wiring. - -## Design - -### Package Taxonomy - -``` -@uncaged/nerve-core # types, engine -@uncaged/nerve-daemon # runtime -@uncaged/nerve-workflow-utils # createRole, withDryRun, etc. - -# Adapters (existing) -@uncaged/nerve-adapter-hermes -@uncaged/nerve-adapter-cursor - -# Workflows (new) -@uncaged/nerve-workflow-solve-issue -@uncaged/nerve-workflow-develop-sense -@uncaged/nerve-workflow-develop-workflow - -# Shared Roles (new) -@uncaged/nerve-role-committer # workspace committer (branch, commit, push) -@uncaged/nerve-role-reviewer # code review role -@uncaged/nerve-role-publisher # PR creation role - -# Senses (existing pattern, formalized) -@uncaged/nerve-sense-cpu-usage -@uncaged/nerve-sense-disk-usage -``` - -### Package Contract - -Each package type exports a factory function: - -#### Workflow Package - -```ts -// @uncaged/nerve-workflow-develop-sense -import type { AgentFn, WorkflowDefinition } from "@uncaged/nerve-core"; -import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils"; - -export type SenseMeta = { /* ... */ }; - -export type CreateDevelopSenseDeps = { - defaultAdapter: AgentFn; - adapters?: Partial>; - extract: LlmExtractorConfig; - cwd: string; -}; - -export function createDevelopSenseWorkflow(deps: CreateDevelopSenseDeps): WorkflowDefinition; -``` - -#### Role Package - -```ts -// @uncaged/nerve-role-committer -import type { AgentFn, Role } from "@uncaged/nerve-core"; -import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils"; - -export type CommitterMeta = { committed: boolean }; - -export function createCommitterRole(adapter: AgentFn, extract: LlmExtractorConfig): Role; -``` - -#### Sense Package - -```ts -// @uncaged/nerve-sense-cpu-usage -export const senseName = "cpu-usage"; -export const schema = { /* drizzle schema */ }; -export async function compute(ctx: SenseContext): Promise; -``` - -### Workspace as Configuration - -The workspace becomes a thin wiring layer: - -``` -~/.uncaged-nerve/ - nerve.yaml # senses, extract config - package.json # depends on workflow/role/adapter packages - workflows/ - develop-sense/ - index.ts # 10 lines: import package, wire adapters, export - solve-issue/ - index.ts # same pattern -``` - -A typical `index.ts`: - -```ts -import { createDevelopSenseWorkflow } from "@uncaged/nerve-workflow-develop-sense"; -import { hermesAdapter } from "@uncaged/nerve-adapter-hermes"; -import { cursorAdapter } from "@uncaged/nerve-adapter-cursor"; - -export default createDevelopSenseWorkflow({ - defaultAdapter: hermesAdapter, - adapters: { planner: cursorAdapter, coder: cursorAdapter }, - extract: { provider: { apiKey, baseUrl, model } }, - cwd: nerveRoot, -}); -``` - -### What Stays in Workspace - -- **Custom workflows** β€” project-specific workflows that aren't general enough to share -- **Custom senses** β€” project-specific metrics -- **Configuration** β€” adapter selection, credentials, `nerve.yaml` -- **Overrides** β€” a workspace can always write its own role/workflow instead of using a package - -### Dependency Rules - -``` -nerve-core ← no deps on other nerve packages -nerve-workflow-utils ← depends on nerve-core -nerve-adapter-* ← depends on nerve-core -nerve-role-* ← depends on nerve-core, nerve-workflow-utils -nerve-workflow-* ← depends on nerve-core, nerve-workflow-utils, may depend on nerve-role-* -nerve-sense-* ← depends on nerve-core -nerve-daemon ← depends on nerve-core, nerve-store -``` - -Workflow packages depend on role packages (not adapters). Adapters are injected at the workspace level. - -### Migration Path - -1. **Phase 1: Extract role packages** β€” Start with `@uncaged/nerve-role-committer` (already `_shared/workspace-committer.ts`). Publish, update workspace to import from package. -2. **Phase 2: Extract workflow packages** β€” Move `develop-sense` and `develop-workflow` to packages. Workspace `index.ts` becomes pure wiring. -3. **Phase 3: Sense packages** β€” Formalize sense packaging (lower priority, senses are already self-contained directories). -4. **Phase 4: Community** β€” Document the package contract so others can publish workflows/roles/senses. - -### Not in Scope - -- **No builtin workflows** β€” nerve core ships zero workflows. All workflows are packages installed by the workspace. -- **No workflow marketplace/registry** β€” just npm packages. `pnpm add @uncaged/nerve-workflow-solve-issue`. -- **No nerve.yaml workflow declaration** β€” workflows are still TypeScript entry points. The daemon discovers them the same way it does today. - -## Open Questions - -1. **Monorepo vs separate repos?** β€” Should workflow/role packages live in the nerve monorepo or separate repos? Monorepo is easier for coordinated releases; separate repos allow independent versioning. -2. **Sense package format** β€” Senses currently bundle with esbuild. Should sense packages ship pre-bundled or as TypeScript source? -3. **Version coupling** β€” How tightly should workflow packages pin `nerve-core`? Peer deps with semver range? - -## Prior Art - -- Adapter packages (`@uncaged/nerve-adapter-*`) β€” established the factory + injection pattern -- `_shared/workspace-committer.ts` β€” proved roles can be shared across workflows -- `createRole` / `withDryRun` in `workflow-utils` β€” building blocks that role packages compose diff --git a/packages/workflow-utils/src/__tests__/with-dry-run.test.ts b/packages/workflow-utils/src/__tests__/role-decorators.test.ts similarity index 97% rename from packages/workflow-utils/src/__tests__/with-dry-run.test.ts rename to packages/workflow-utils/src/__tests__/role-decorators.test.ts index fa7e1ff..30c19f2 100644 --- a/packages/workflow-utils/src/__tests__/with-dry-run.test.ts +++ b/packages/workflow-utils/src/__tests__/role-decorators.test.ts @@ -7,7 +7,7 @@ import type { WorkflowMessage, } from "@uncaged/nerve-core"; -import { decorateRole, onFail, withDryRun } from "../with-dry-run.js"; +import { decorateRole, onFail, withDryRun } from "../role-decorators.js"; type TestMeta = { ok: boolean }; diff --git a/packages/workflow-utils/src/index.ts b/packages/workflow-utils/src/index.ts index bd90aca..56bc046 100644 --- a/packages/workflow-utils/src/index.ts +++ b/packages/workflow-utils/src/index.ts @@ -27,7 +27,7 @@ export { type RoleDecorator, type WithDryRunOptions, type OnFailOptions, -} from "./with-dry-run.js"; +} from "./role-decorators.js"; export { nerveCommandEnv, spawnSafe, diff --git a/packages/workflow-utils/src/with-dry-run.ts b/packages/workflow-utils/src/role-decorators.ts similarity index 100% rename from packages/workflow-utils/src/with-dry-run.ts rename to packages/workflow-utils/src/role-decorators.ts