From 49f3d91d1bd37b92c1aca710c03be417b6049a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=A2=A8?= Date: Thu, 30 Apr 2026 14:29:45 +0000 Subject: [PATCH] =?UTF-8?q?chore:=20dead=20code=20cleanup=20=E2=80=94=20re?= =?UTF-8?q?move=20unused=20exports=20and=20fix=20stale=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Delete createEchoAgent (daemon, never referenced) - Delete isDryRun (workflow-utils, deprecated, always false) - Delete KNOWN_AGENT_ADAPTER_IDS (core, never referenced) - Remove parseDurationStringToMs, labelSenseTrigger from core public API - Remove spawnSafe re-export from workflow-utils - Fix core/README.md stale API names - Clean stale hermes-options.ts comment Closes #302 --- docs/dead-code-analysis.md | 146 ++++++++++++++++++ packages/core/README.md | 36 +++-- packages/core/src/agent.ts | 6 - packages/core/src/index.ts | 4 +- packages/daemon/src/agent-adapters/echo.ts | 9 -- packages/daemon/src/index.ts | 2 - packages/workflow-utils/src/index.ts | 2 - packages/workflow-utils/src/role-types.ts | 10 +- .../workflow-utils/src/shared/hermes-agent.ts | 2 +- 9 files changed, 171 insertions(+), 46 deletions(-) create mode 100644 docs/dead-code-analysis.md delete mode 100644 packages/daemon/src/agent-adapters/echo.ts diff --git a/docs/dead-code-analysis.md b/docs/dead-code-analysis.md new file mode 100644 index 0000000..6ec0955 --- /dev/null +++ b/docs/dead-code-analysis.md @@ -0,0 +1,146 @@ +# Nerve 单仓死代码分析报告 + +**分析日期**: 2026-04-30 +**范围**: `packages/core`, `daemon`, `store`, `cli`, `khala`, `workflow-utils`, `workflow-meta`, `adapter-cursor`, `adapter-hermes` 的 TypeScript 源码与 `package.json` 依赖。 +**方法**: 全仓 `ripgrep` 交叉验证 `import` / `export` 路径;未运行 Knip/TS 死代码专项工具。 +**说明**: 未包含 `role-reviewer` / `role-committer` / `skills` 等包(你列出的范围外),但 `workflow-meta` 对其中部分有依赖,相关结论已注明。 + +--- + +## 方法限制(读前必读) + +| 限制 | 影响 | +|------|------| +| **动态 `import(url)`** | `cli` 的 `loadDaemonModule` 在运行时加载 `@uncaged/nerve-daemon`,静态分析无法把 `createKernel` 等映射到具体导出。 | +| **已发布包的公共 API** | 大量导出仅被仓外消费者使用;本报告中的「仓内未引用」≠「应删除」。 | +| **构建入口** | Rslib 多入口(如 `sense-worker`、`workflow-worker`、`daemon-bootstrap`)视为存活入口,不作为孤儿文件。 | +| **内部未导出函数** | 未做逐函数调用图;「死函数」仅列出高置信个例。 | + +--- + +## 1. 未使用导出(仓内无 `import`) + +以下符号在 **monorepo 内** 没有任何文件从 `@uncaged/nerve-core` / `@uncaged/nerve-workflow-utils` / `@uncaged/nerve-daemon` 等包根导出处引用(测试与定义自身除外)。 + +### 1.1 `@uncaged/nerve-core` + +| 路径 | 未使用项 | 置信度 | 建议 | +|------|----------|--------|------| +| `packages/core/src/index.ts` → `agent.js` | `KNOWN_AGENT_ADAPTER_IDS` | **高** | **调查**:若 CLI/校验应约束 adapter id,可接入;否则可从公共 API 移除或保留作文档性常量并注明。 | +| `packages/core/src/index.ts` → `sense.js` | `labelSenseTrigger`(`senseTriggerLabels` 在 `cli`/`daemon` 有使用) | **高(相对仓内)** | **保留或收敛导出**:仅 `senseTriggerLabels` 对外即可;`labelSenseTrigger` 可作为内部函数若不需要单独暴露。 | +| `packages/core/src/index.ts` → `util.js` | `parseDurationStringToMs`(仅 `config.ts` 内部使用) | **高** | **调查**:无需在 `index` 再导出则改为非导出或移除 re-export,减少 API 面。 | +| `packages/core/src/index.ts` → `sense.js` | 类型 `SenseModule` | **中** | **保留**:用户文档/外部 Sense 模块常用;仓内未按名引用属正常。 | +| `packages/core/src/index.ts` → `config.js` | 单独导出的类型 `NerveApiConfig`(仅 `config.ts` 与 `index` 出现) | **低** | **保留**:通常随 `NerveConfig` 一并使用;单独 export 多为 TS 公共类型便利。 | + +### 1.2 `@uncaged/nerve-workflow-utils` + +下列项在 **`packages/*/src/**/*.ts` 中无人从包入口导入**(`workflow-meta`、`role-committer` 等主要只用 `createRole`、`decorateRole`、`onFail`、`withDryRun`、`LlmExtractorConfig`)。 + +| 路径 | 未使用项 | 置信度 | 建议 | +|------|----------|--------|------| +| `packages/workflow-utils/src/index.ts` | `createLlmAdapter` | **高** | **保留(对外 API)** 或标注文档;仓内无调用。 | +| 同上 | `llmExtract`、`llmExtractWithRetry` | **高** | **保留**:高级用法 / 文档示例;内部与测试使用。 | +| 同上 | `mergeExtractConfig`、`ExtractConfigLayer` | **高** | **调查**:若 RFC 分层配置仍在推进则保留;否则评估移除导出。 | +| 同上 | `assertZodMetaSchemas`、`createLlmExtractFn`、`extractMetaOrThrow`、`zodMeta`、`ZodMetaSchema` | **高** | **保留(对外 API)**;当前仅 `workflow-utils` 测试与内部 `create-role` 链路使用 `extractMetaOrThrow`。 | +| 同上 | `readNerveYaml`、`nerveAgentContext` 及相关类型 | **高** | **调查**:若已无 YAML 上下文注入场景可删导出;否则保留给 Agent 工具链。 | +| 同上 | `spawnSafe`、`nerveCommandEnv` 及 `Spawn*` 类型(从 core 再导出) | **高** | **删除再导出或改为文档链接**:仓内均直接从 `@uncaged/nerve-core` 引用 spawn。 | +| 同上 | `isDryRun` | **高** | **删除或实现**:见 §3「死函数」。 | +| 同上 | `LlmMessage`、`MetaExtractConfig`、`LlmChatError`、`LlmError`、`LlmProvider` 等类型再导出 | **中** | **保留**:供外部精细类型标注;仓内未单独 import。 | + +### 1.3 `@uncaged/nerve-daemon` + +| 路径 | 未使用项 | 置信度 | 建议 | +|------|----------|--------|------| +| `packages/daemon/src/index.ts` → `agent-adapters/echo.js` | `createEchoAgent` | **高** | **调查**:无任何测试或运行时引用;若设计保留 `type: "echo"` 适配器,应在 workflow/agent 装载路径接线或删除导出与文件。 | +| `packages/daemon/src/index.ts`(及其它导出) | 多数 IPC / runtime 符号 | **低** | **保留**:动态加载与外部集成无法静态判定;仅 `createEchoAgent` 可确定为当前静态图下的死角。 | + +### 1.4 `@uncaged/nerve-adapter-cursor` / `@uncaged/nerve-adapter-hermes` + +| 路径 | 未使用项 | 置信度 | 建议 | +|------|----------|--------|------| +| `packages/adapter-cursor/src/index.ts` | `cursorAdapter`、`createCursorAdapter`(以及 `cursorAgent` 仅被 `workflow-utils/src/role-cursor.ts` 使用) | **中** | **保留**:默认实例与工厂供下游与工作流仓使用;仓内业务包未直接 import `cursorAdapter` 属预期。 | +| `packages/adapter-hermes/src/index.ts` | `hermesAdapter`、`createHermesAdapter`(`hermesAgent` 经 `workflow-utils` 间接使用) | **中** | 同上。 | + +### 1.5 `@uncaged/nerve-cli` + +| 路径 | 未使用项 | 置信度 | 建议 | +|------|----------|--------|------| +| `packages/cli/src/index.ts` | `getNerveRoot`、`loadDaemonModule` 等程序化导出 | **高(仓内)** | **保留**:无任何 workspace 包引用 `@uncaged/nerve-cli`;面向 CLI 二次开发者。 | + +--- + +## 2. 孤儿文件(非入口、未被其它源码引用) + +在当前 Rslib / `cli` 入口定义下,**未发现**「零 import、且非 entry / 非测试」的 `.ts` 业务文件: + +- `workflow-utils` 下的 `role-cursor.ts`、`role-hermes.ts`、`role-llm.ts`、`role-react.ts` 虽**未出现在包 `index.ts`**,但被 `packages/workflow-utils/src/__tests__/role-factories.test.ts` 直接引用,**不是孤儿文件**。 +- `daemon` 的 `sense-worker.ts`、`workflow-worker.ts` 等为 **显式构建入口**。 + +**置信度**: 对全表扫描为 **中**(未跑依赖图工具;动态路径可能掩盖极少数脚本引用)。 + +--- + +## 3. 死函数(内部未导出且未被调用) + +| 路径 | 说明 | 置信度 | 建议 | +|------|------|--------|------| +| `packages/workflow-utils/src/role-types.ts` | `isDryRun(_start: StartStep)`:导出在 `index.ts`,但全仓无调用(函数体恒返回 `false` 且标注 deprecated) | **高** | **删除**或改为内部常量;若需向后兼容则保留并文档标注「保留占位」。 | + +其它内部函数未系统逐文件枚举。 + +--- + +## 4. 未使用的 npm 依赖(package.json) + +在声明范围内通过源码 `import` 抽查:**未发现明显「完全未使用」的生产依赖**。 + +| 包 | 依赖 | 结论 | +|----|------|------| +| `cli` | `citty`、`yaml`、`picomatch` | 均有对应源码引用。 | +| `khala` | `hono`、`jsonata`、`ulidx`、`@uncaged/nerve-core` | 均有引用。 | +| `core` / `daemon` | `yaml`、`drizzle-orm` | 均有引用。 | +| `workflow-utils` / `workflow-meta` | `zod`、workspace 包 | 均有引用。 | + +**置信度**: **中**(未对 peer、可选路径、CLI `bin` 专用依赖做自动化扫描)。 + +--- + +## 5. 陈旧测试夹具 / 未引用辅助文件 + +未发现独立的「fixture 目录」明显失联;`cli` 下 `e2e-harness`、`__tests__` 内 helper 均有对应测试引用。 + +**置信度**: **低**(未枚举每个 `__tests__` 资源文件)。 + +--- + +## 6. 重构遗留 / 文档漂移 + +| 项目 | 位置 | 说明 | 置信度 | 建议 | +|------|------|------|--------|------| +| 已更名 API 仍出现在 README | `packages/core/README.md` | 仍描述 `parseSenseWorkflowDirective`、`ParsedSenseWorkflowDirective`、`SenseComputeRoute`;源码已为 `parseWorkflowTrigger` / `routeSenseComputeOutput` / `RoutedSenseOutput` | **高** | **更新文档**(本次分析不改代码,仅记录)。 | +| Hermes 选项合并注释 | `packages/workflow-utils/src/shared/hermes-agent.ts` | 注释称 absorbed from `hermes-options.ts`,该文件已不存在 | **中** | **清理注释**,避免误导。 | +| `KNOWN_AGENT_ADAPTER_IDS` 含 `codex` | `packages/core/src/agent.ts` | 仓内无 `codex` 适配器包;与常量未被引用叠加 | **中** | **对齐产品**:实现适配器或从列表移除。 | + +未发现用户提到的 `worker-fork-support` 等字符串副本(全仓无匹配)。 + +--- + +## 7. 汇总建议优先级 + +1. **高优先级调查**: `createEchoAgent` 与 `KNOWN_AGENT_ADAPTER_IDS` — 要么接入运行时,要么删减以免维护假象。 +2. **API 面收敛**: `parseDurationStringToMs`、`labelSenseTrigger` 若无意对外,可从 `core` 公共导出移除。 +3. **`workflow-utils`**: 评估 `isDryRun` 删除;`spawnSafe` 等从 `workflow-utils` 再导出是否仍有必要。 +4. **文档**: 修正 `packages/core/README.md` 中 Sense→Workflow 路由 API 名称。 + +--- + +## 8. 重新验证命令示例 + +后续可在本地采用工具增强置信度(非本次执行): + +```bash +pnpm add -D -w knip +pnpm exec knip +``` + +或在各包对 `@uncaged/nerve-*` 的导出做面向消费者的契约测试,避免误删对外 API。 diff --git a/packages/core/README.md b/packages/core/README.md index f88a572..95abf44 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -4,11 +4,11 @@ Shared types and configuration parser for the [nerve](../../README.md) observati ## What's Inside -- **Type definitions** — `Signal`, `SenseConfig`, `SenseInfo`, `SenseReflexConfig`, `ReflexConfig` (sense-only), `WorkflowConfig`, `NerveConfig`, and related types +- **Type definitions** — `Signal`, `SenseConfig`, `SenseInfo`, `WorkflowConfig`, `NerveConfig`, and related types - **Config parser** — `parseNerveConfig(yaml)` validates and parses `nerve.yaml` into `NerveConfig` (rejects reflex entries that declare a `workflow` key; reflexes only schedule senses) -- **Sense → workflow routing** — `parseSenseWorkflowDirective`, `routeSenseComputeOutput`, and types `ParsedSenseWorkflowDirective`, `SenseComputeRoute` +- **Sense → workflow routing** — `parseWorkflowTrigger`, `routeSenseComputeOutput`, and types `WorkflowTrigger`, `RoutedSenseOutput` - **Daemon IPC protocol** — request/response types (`DaemonIpcRequest`, `DaemonIpcResponse`, …) and `parseDaemonIpcRequest` for newline-delimited JSON on the CLI ↔ daemon socket -- **Workflow automaton types** — `START` / `END` sentinel constants, `WorkflowMessage`, `StartStep`, `RoleStep`, `ModeratorContext` (`start` + `steps`; empty `steps` on first moderator call), `Moderator` (single `context` argument), `WorkflowDefinition`, `Role`, `SenseResult`, plus `DEFAULT_ENGINE_MAX_ROUNDS` +- **Workflow automaton types** — `START` / `END` sentinel constants, `WorkflowMessage`, `StartStep`, `RoleStep`, `ModeratorContext` (`start` + `steps`; empty `steps` on first moderator call), `Moderator` (single `context` argument), `WorkflowDefinition`, `Role`, `RoleResult`, plus `DEFAULT_ENGINE_MAX_ROUNDS` - **Result type** — `Result` with `ok()` / `err()` helpers for explicit error handling (no thrown exceptions for parse paths) ## Usage @@ -26,23 +26,31 @@ if (result.ok) { ### Sense return → signal vs workflow ```typescript -import { parseSenseWorkflowDirective, routeSenseComputeOutput } from "@uncaged/nerve-core"; +import { parseWorkflowTrigger, routeSenseComputeOutput } from "@uncaged/nerve-core"; -const directive = parseSenseWorkflowDirective("my-workflow|8|Hello from sense"); +const directive = parseWorkflowTrigger({ + name: "my-workflow", + maxRounds: 8, + prompt: "Hello from sense", + dryRun: false, +}); if (directive.ok) { - console.log(directive.value.workflowName, directive.value.maxRounds, directive.value.prompt); + console.log(directive.value.name, directive.value.maxRounds, directive.value.prompt); } const route = routeSenseComputeOutput({ - metric: 42, - workflow: "my-workflow|8|Run now", + signal: { metric: 42 }, + workflow: { + name: "my-workflow", + maxRounds: 8, + prompt: "Run now", + dryRun: false, + }, }); -if (route.kind === "launch") { - // engine starts workflow; no Signal to the bus for this return - console.log(route.launch); -} else { - // normal signal with payload - console.log(route.payload); +if (route.ok && route.value.workflow !== null) { + console.log(route.value.workflow); +} else if (route.ok) { + console.log(route.value.signal); } ``` diff --git a/packages/core/src/agent.ts b/packages/core/src/agent.ts index 21aff3f..51a9e56 100644 --- a/packages/core/src/agent.ts +++ b/packages/core/src/agent.ts @@ -21,9 +21,3 @@ export class ExtractError extends Error { Object.setPrototypeOf(this, new.target.prototype); } } - -/** - * Agent adapter ids referenced by tooling / docs (RFC-003). - * Workflows import adapter packages directly; echo may be used in tests via a small factory. - */ -export const KNOWN_AGENT_ADAPTER_IDS = ["echo", "cursor", "hermes", "codex"] as const; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index affc366..3076306 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -13,7 +13,7 @@ export type { } from "./config.js"; export type { Signal, SenseInfo } from "./sense.js"; export type { SenseComputeFn, SenseModule } from "./sense.js"; -export { labelSenseTrigger, senseTriggerLabels } from "./sense.js"; +export { senseTriggerLabels } from "./sense.js"; export type { WorkflowMessage, RoleResult, @@ -29,7 +29,6 @@ export type { WorkflowDefinition, } from "./workflow.js"; export { START, END, DEFAULT_ENGINE_MAX_ROUNDS } from "./workflow.js"; -export { parseDurationStringToMs } from "./util.js"; export type { Schema, ExtractFn } from "./agent.js"; export { ExtractError } from "./agent.js"; export type { Result } from "./util.js"; @@ -46,7 +45,6 @@ export { parseNerveConfig } from "./config.js"; export type { KnowledgeConfig } from "./config.js"; export { parseKnowledgeYaml } from "./config.js"; export { isPlainRecord } from "./util.js"; -export { KNOWN_AGENT_ADAPTER_IDS } from "./agent.js"; export type { RoutedSenseOutput } from "./sense.js"; export { parseWorkflowTrigger, routeSenseComputeOutput } from "./sense.js"; diff --git a/packages/daemon/src/agent-adapters/echo.ts b/packages/daemon/src/agent-adapters/echo.ts deleted file mode 100644 index 5a21a84..0000000 --- a/packages/daemon/src/agent-adapters/echo.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { AgentConfig, AgentFn, ThreadContext } from "@uncaged/nerve-core"; - -/** - * Echo adapter (`type: "echo"`) — returns the assembled prompt unchanged. - * Used for tests and dry-run wiring before real adapters exist. - */ -export function createEchoAgent(_config: AgentConfig): AgentFn { - return async (_ctx: ThreadContext, prompt: string) => prompt; -} diff --git a/packages/daemon/src/index.ts b/packages/daemon/src/index.ts index 86f3fd4..e418cfe 100644 --- a/packages/daemon/src/index.ts +++ b/packages/daemon/src/index.ts @@ -56,5 +56,3 @@ export type { export { createWorkflowManager } from "./workflow-manager.js"; export type { WorkflowManager } from "./workflow-manager.js"; - -export { createEchoAgent } from "./agent-adapters/echo.js"; diff --git a/packages/workflow-utils/src/index.ts b/packages/workflow-utils/src/index.ts index f3251bb..8e685f6 100644 --- a/packages/workflow-utils/src/index.ts +++ b/packages/workflow-utils/src/index.ts @@ -26,13 +26,11 @@ export { } from "./role-decorators.js"; export { nerveCommandEnv, - spawnSafe, type SpawnEnv, type SpawnError, type SpawnResult, type SpawnSafeOptions, } from "@uncaged/nerve-core"; export type { LlmError, LlmProvider } from "./shared/llm-extract.js"; -export { isDryRun } from "./role-types.js"; export type { LlmMessage, MetaExtractConfig } from "./role-types.js"; export type { LlmChatError } from "./shared/llm-chat.js"; diff --git a/packages/workflow-utils/src/role-types.ts b/packages/workflow-utils/src/role-types.ts index 3b0c284..be540c8 100644 --- a/packages/workflow-utils/src/role-types.ts +++ b/packages/workflow-utils/src/role-types.ts @@ -1,16 +1,8 @@ -import type { SpawnEnv, StartStep, ThreadContext } from "@uncaged/nerve-core"; +import type { SpawnEnv, ThreadContext } from "@uncaged/nerve-core"; import type { z } from "zod"; import type { LlmProvider } from "./shared/llm-extract.js"; -/** - * @deprecated `dryRun` has been removed from `StartStep.meta` (RFC-005). - * Use adapter/role-level `dryRun` config instead. - */ -export function isDryRun(_start: StartStep): boolean { - return false; -} - export type CliPromptFn = (ctx: ThreadContext) => Promise; export type LlmMessage = { role: "system" | "user" | "assistant"; content: string }; diff --git a/packages/workflow-utils/src/shared/hermes-agent.ts b/packages/workflow-utils/src/shared/hermes-agent.ts index fc0b24c..35ef9f8 100644 --- a/packages/workflow-utils/src/shared/hermes-agent.ts +++ b/packages/workflow-utils/src/shared/hermes-agent.ts @@ -3,7 +3,7 @@ import type { HermesRoleDefaults, HermesRoleRequired } from "../role-types.js"; export { hermesAgent } from "@uncaged/nerve-adapter-hermes"; export type { HermesAgentOptions } from "@uncaged/nerve-adapter-hermes"; -// --- Hermes options resolution (absorbed from hermes-options.ts) --- +// --- Hermes role defaults resolution --- const HERMES_DEFAULTS: HermesRoleDefaults = { model: null, -- 2.43.0