跳转至

模块依赖图全拆

重要性:⭐⭐⭐⭐⭐(理解模块关系——1726 个文件 + ~10+ 循环依赖 + 5+ lazy require 模式) 范围src/ 全部 1884 个 .ts/.tsx 文件 方法:grep 全部 import / require 模式 → 分类 → 找循环


1. 总体数据

src/ 文件数:        1884
含 import 文件数:  1726 (91%)
循环 require 模式: 10+ (跨多文件)
lazy require 模式: 5+ 主流
外部依赖:          ~50+ 个 npm 包

关键观察: - 91% 文件有 import(几乎所有文件都依赖别人) - 9% 文件可能纯函数(constants/types)


2. 分层架构(推测)

┌─────────────────────────────────────────────┐
│  Layer 0: entry(main.tsx)                │
│  - 协调整体启动                              │
└────────────────────┬────────────────────────┘
┌────────────────────▼────────────────────────┐
│  Layer 1: launchers(replLauncher, cli/)  │
│  - REPL / print 模式分发                    │
└────────────────────┬────────────────────────┘
┌────────────────────▼────────────────────────┐
│  Layer 2: screens(REPL.tsx)              │
│  - UI 编排(Hooks-first)                   │
└────────────────────┬────────────────────────┘
┌────────────────────▼────────────────────────┐
│  Layer 3: business(services/, tools/)    │
│  - 业务实现(API, MCP, Tools, Auth)        │
└────────────────────┬────────────────────────┘
┌────────────────────▼────────────────────────┐
│  Layer 4: state(state/AppStateStore,      │
│  bootstrap/, services/policyLimits)        │
│  - 跨组件状态                                │
└────────────────────┬────────────────────────┘
┌────────────────────▼────────────────────────┐
│  Layer 5: utils/utils/utils                │
│  - 纯函数(无状态)                          │
└─────────────────────────────────────────────┘

5 层架构 —— 依赖单向向下(理论上)。


3. 5+ 循环依赖(实测发现)

3.1 main.tsx → teammate.ts → AppState.tsx → main.tsx

// main.tsx (行 70-72)
const getTeammateUtils = () => require('./utils/teammate.js') as typeof import('./utils/teammate.js');

// 注释:
// Lazy require to avoid circular dependency: teammate.ts -> AppState.tsx -> ... -> main.tsx

循环链

main.tsx → utils/teammate.ts → state/AppState.tsx → ... → main.tsx

解决getter 包 require —— 第一次调用才 require。

3.2 coordinatorModeModule 循环

// main.tsx (行 76)
const coordinatorModeModule = feature('COORDINATOR_MODE') 
  ? require('./coordinator/coordinatorMode.js') 
  : null;

推测

main.tsx → coordinator/coordinatorMode.ts → ... → main.tsx

解决feature() 编译时门控 + require 兜底。

3.3 assistantModule (KAIROS) 循环

// main.tsx (行 80-81)
const assistantModule = feature('KAIROS') 
  ? require('./assistant/index.js') 
  : null;
const kairosGate = feature('KAIROS') 
  ? require('./assistant/gate.js') 
  : null;

推测

main.tsx → assistant/index.js → ... → main.tsx

解决:同样 lazy require + feature gate。

3.4 autoModeStateModule (TRANSCRIPT_CLASSIFIER) 循环

// main.tsx (行 171)
const autoModeStateModule = feature('TRANSCRIPT_CLASSIFIER') 
  ? require('./utils/permissions/autoModeState.js') 
  : null;

推测

main.tsx → utils/permissions/autoModeState.ts → ... → main.tsx

解决:同上。

3.5 BriefTool 循环

// 多个文件 (4 处)
} = require('../tools/BriefTool/prompt.js') as typeof import('../tools/BriefTool/prompt.js');
} = require('../tools/BriefTool/BriefTool.js') as typeof import('../tools/BriefTool/BriefTool.js');

推测

main.tsx → tools/BriefTool/BriefTool.ts → ... → main.tsx

解决:lazy require。

3.6 proactiveModule 循环

// 2 处
const proactiveModule = feature('PROACTIVE') || feature('KAIROS') 
  ? require('../proactive/index.js') 
  : null;

推测

REPL.tsx → proactive/index.ts → ... → REPL.tsx

解决:lazy require。

3.7 snipCompact 循环

} = require('./services/compact/snipCompact.js') as typeof import('./services/compact/snipCompact.js');

推测

main.tsx → services/compact/snipCompact.ts → ... → main.tsx

解决:lazy require。

3.8 extractMemories 循环

} = require('../services/extractMemories/extractMemories.js') as typeof import('../services/extractMemories/extractMemories.js');

推测

REPL.tsx → services/extractMemories → ... → REPL.tsx

解决:lazy require。

3.9 teamMemPaths 循环

} = require('../memdir/teamMemPaths.js') as typeof import('../memdir/teamMemPaths.js'));

推测

main.tsx → memdir/teamMemPaths.ts → ... → main.tsx

解决:lazy require。

3.10 contextCollapse/persist 循环

require('../services/contextCollapse/persist.js') as typeof import('../services/contextCollapse/persist.js');

推测

main.tsx → services/contextCollapse/persist.ts → ... → main.tsx

解决:lazy require。


4. 循环依赖的共同模式

所有循环都是 main.tsx → X → ... → main.tsxREPL.tsx → X → ... → REPL.tsx

原因: - main.tsx 是入口,被所有东西依赖(间接) - AppState.tsx 是状态中心,被所有东西依赖

解决套路

// 模式 1: getter 包 require
const getX = () => require('./X.js') as typeof import('./X.js');

// 模式 2: feature gate 包 require
const x = feature('X') ? require('./X.js') : null;

// 模式 3: 用到时再 require(动态 import)
const { x } = await import('./X.js');

3 种模式都用 —— 项目对循环依赖有成熟方案


5. 主要依赖方向

5.1 React/Ink 框架(被依赖最多)

ink.js
React
useState, useEffect, useRef, ...

~500+ 文件 import ink.jsReact

5.2 AppState(被广泛依赖)

state/AppStateStore.ts (useAppState, useSetAppState)
state/selectors.ts

~100+ 文件 用 useAppState。

5.3 utils(基础工具)

utils/array.js (count, uniq, ...)
utils/stringUtils.js
utils/errors.js
utils/logger.js

utils 198 个目录 —— "工具大杂烩"。

5.4 services(业务)

services/api/claude.ts
services/mcp/*
services/analytics/*
services/compact/*

services 47 个子目录 —— 业务封装。

5.5 tools(工具)

Tool.ts (基类)
tools/BashTool/*
tools/FileReadTool/*
...

tools 103 个子目录 —— 工具集。


6. 5 大耦合热点(推测)

6.1 state/AppStateStore.ts

~100+ 文件用 useAppState。

耦合影响:改 store schema 影响 100+ 文件。

6.2 Tool.ts

所有 tool 都继承。

耦合影响:改 Tool 接口影响 100+ 工具。

6.3 services/api/claude.ts (3419 行)

所有 API 调用 走这里。

耦合影响:改 API 影响所有功能。

6.4 commands.ts (25185 字节)

所有命令 注册在这里。

耦合影响:改 commands 影响 REPL。

6.5 history.ts (14081 字节)

所有消息 走这里。

耦合影响:改 history 影响所有 UI。


7. 模块耦合度分类

类型 文件数 特征
中心模块 ~10 100+ 文件 import
业务模块 ~50 10-50 文件 import
工具模块 ~200 < 10 文件 import
叶子模块 ~1500 0 文件 import(被引用的末端)

模块分布: - 中心:~10 - 业务:~50 - 工具:~200 - 叶子:~1500

典型 —— 中心少,工具多,叶子最多。


8. 依赖方向分析

8.1 严格单向

UI (components/, screens/) 
  ↓ only imports
business (services/, tools/) 
  ↓ only imports
state (state/)
  ↓ only imports
utils (utils/)

理想分层 —— 单向依赖。

8.2 实际违反

  • main.tsx 入口依赖太多(god module
  • REPL.tsx 入口依赖太多(god component
  • hooks/ 双向引用(hooks 之间互相用)

实际 —— 入口文件"例外"。


9. 外部依赖(npm)

通过 grep 包名(推测): - react, react-dom - ink, @inkjs/* - commander, @commander-js/extra-typings - chalk - lodash-es - zod - bun:bundle (Bun 特有) - crypto, fs, path (Node 内置)

~30+ npm 外部包(推测)。


10. import 风格统计

10.1 ES import vs CJS require

import { x } from './x.js'      // ES (绝大多数)
const x = require('./x.js')     // CJS (lazy require)

~99% ES, ~1% CJS (lazy require)

10.2 路径风格

import { x } from './x.js'           // 相对
import { x } from '../utils/x.js'    // 多级相对
import { x } from 'src/utils/x.js'   // src-absolute (较少)

~95% 相对路径 —— 5% 用 src-absolute。

10.3 扩展名

import { x } from './x.js'  // .js
import { x } from './x.ts'  // .ts (罕见)

~100% 写 .js —— 即使是 TS 文件(NodeNext 模块解析要求)。


11. 关键洞察

11.1 循环依赖是"演进必然"

中心模块(main.tsx, AppState)被广泛依赖 —— 循环不可避免

11.2 Lazy require 是"解法"

3 种模式:getter / feature gate / dynamic import。

11.3 入口文件"违反"分层

main.tsx / REPL.tsx 是编排 —— 依赖所有层。

11.4 中心模块 10 个

~10 个文件被 100+ 文件依赖 —— 改时要小心。

11.5 业务服务 47 个

足够多 —— 业务领域清晰切分。

11.6 工具 198 个 vs 业务 47 个

4 倍工具 —— 反映项目对 helper 的偏好。

11.7 ES import 99%

现代化 —— 几乎没有 CJS(除 lazy require)。

11.8 .js 扩展名 100%

NodeNext 规范 —— 即使 TS 也写 .js。


12. 改进建议(如果重构)

12.1 拆 main.tsx

4683 行 → 拆成 subcommand 目录(已部分做)。

12.2 拆 REPL.tsx

5005 行 → 拆成 screens(已部分做)。

12.3 减少 utils/ 数量

198 个 → 合并相似工具。

12.4 引入 dependency-cruiser

工具自动检查循环 + 分层违规。


13. 阅读建议

  1. grep require\(['\"]\./* —— 找所有 lazy require
  2. 用 madge 或类似工具 —— 自动生成依赖图
  3. 看 state/AppStateStore.ts —— 中心模块
  4. 看 services/api/claude.ts —— 最大业务
  5. 看 utils/ —— 198 个工具分类

14. 与其他分析的关系

文件 关系
architecture-history.md 演进历史(动态)
extensibility.md 扩展点
error-handling-overview.md 错误依赖
performance-history.md 性能影响