mcpField
C
cartograph

cartograph

github.com/noodleflowlabs开发3190 次安装15 周前更新
代码仓库

关于 cartograph

cartograph 是用于代码库结构分析的技能,面向希望了解产品功能与代码组织关系的开发者。它提取页面、实体、操作、流程、技术栈等信息,并生成结构映射及代码健康分析。

主要功能

  1. 01提取页面、功能与实体
  2. 02映射实体关系和操作
  3. 03追踪用户流程
  4. 04按功能组织代码分区
  5. 05分析代码健康与不变量
  6. 06汇总技术栈与文件权重

适用场景

  1. 01梳理大型代码库的产品结构
  2. 02定位功能对应的代码文件
  3. 03检查代码组织与不变量
  4. 04整合项目结构映射

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/noodleflowlabs/cartograph --skill cartograph

安装后在 Claude Code 中输入/cartograph确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

.DS_Store
app/
package-lock.json
package.json
server.mjs
app/checks/
check-no-cdn.mjs
check-syntax.mjs
app/ui/
cartograph-logo.svg
favicon.svg
index.html
README.md
app/ui/src/
app.js
config.js
main.js
app/ui/src/components/
common.js
sessions.js
sidebar.js
welcome.js
app/ui/src/lib/
data.js
html.js
prompts.js
app/ui/src/views/
details.js
engineering.js
overview.js
panel.js
app/ui/styles/
base.css
content.css
layout.css
navigation.css
responsive.css
views.css

详细介绍

Cartograph

提取代码库的结构地图:界面、功能、实体、关系、操作、流程、分区和技术栈。四个正交维度——界面是你前往的地方(页面/入口点),功能是你可以执行的操作(独立能力),实体是应用处理的对象(数据),分区是代码的组织方式(连接产品概念与底层代码库的逻辑文件分组)。技术栈则全面列出项目使用的所有技术、框架和库。

工作流

意图检测

默认模式为全面扫描——生成完整的代码库结构地图和代码健康状况评估。除非用户的消息明确要求以下特定不变量模式之一,否则都运行此模式。

以下两种范围更窄的模式会覆盖默认模式:

  1. 添加不变量——消息包含“add invariant”“new invariant”或“add this invariant: '...'” → 添加不变量流程。
  2. 独立验证——消息包含“verify”“check invariants”“run invariants”或类似内容 → 独立验证流程。

全面扫描

扫描分为四个步骤:

  1. 发现——读取代码库,构建每项提取任务所需的上下文。
  2. 提取——生成结构地图。
  3. 分析——评估汇总后的地图,检查代码健康状况和不变量。
  4. 汇总——将所有内容合并到 .cartograph/mapping.json。

在提取和分析过程中,只要输入已就绪,就通过子 Agent 并行执行任务——不要在编排器中按顺序运行,因为运行时间很重要。自然的并行批次如下:

{ Surfaces, Entities }
  → { Features, Operations }
  → { Flows, Compartments, File Tree Weights }
  → { Compartment Dependencies }
  → { Co-location, DRYness, Dead Code, Invariants }

下方每项任务都会说明其输入和返回内容。所有返回结构都符合 references/json-schema.md。


1. 发现

在启动并行任务前自行完成此步骤——它很快,而且每项提取任务都需要它。

  1. 读取 package.json,获取项目名称和依赖项(用于检测框架)。
  2. 使用 Glob 查找关键结构文件:
    • 架构:**/*.prisma、**/schema.*、**/models/**
    • 路由/页面:app/**/page.{tsx,ts,jsx,js}、app/api/**/*.{ts,js}、pages/**/*.{tsx,ts}
    • 服务端操作:搜索 "use server"
    • 组件:components/**/*.{tsx,jsx}
    • 库/服务:lib/**/*.{ts,js}、services/**/*.{ts,js}
  3. 阅读目录树,了解整体结构。
  4. 检测技术栈。 对于正在使用的每项技术,记录名称、版本、类别、来源(发现位置)和置信度(高 = 明确的依赖项 + 匹配的配置;中 = 仅依赖项;低 = 根据模式推断)。完整的检测信号目录——配置文件、文件模式、导入模式——见 references/tech-stack-detection.md。类别列表见 references/json-schema.md。
  5. 收集完整的非生成文件清单。

返回发现信息包:文件清单 + 技术栈数组。将其传递给每项提取和分析任务。


2. 提取
界面

需要: 发现信息包。

界面是入口点——独立的、面向用户的体验。每个应用本质上都是一组界面的集合。

  1. 遍历路由树(app/**/page.tsx),识别每种不同的面向用户的体验。
  2. 将相关路由归为同一界面(例如,/create + /create/[id]/edit = 一个“创作工作室”界面)。
  3. 查找仅限管理员使用的区域、独立工具、仪表板和引导流程。
  4. 对于每个界面,确定:入口点(主页面文件和路由)、使用者(用户/管理员/系统)、描述(该界面作为独立体验的功能)。

返回: 界面数组(不包含 entityIds、operationIds、flowIds 或 compartmentIds——这些会由后续任务和汇总步骤补充)。

实体 + 关系

依赖: discover。

实体 — 阅读架构/类型定义并提取领域对象:

  1. 数据库模型(高置信度)— Prisma 模型、TypeORM 实体、Mongoose 架构。
  2. TypeScript 类型/接口(中置信度)— 用作 API 负载、表单数据、状态的类型。
  3. 枚举(高置信度)— 表示领域概念的枚举定义。
  4. 派生类型(中置信度)— 类似 PostWithAuthor 的转换版本。

对于每个实体:记录 id、名称、种类、描述、源位置、关键字段(最重要的 3–8 个)和置信度。

关系 — 映射实体间的连接:

  1. 架构中的外键和引用 → has-many、belongs-to、has-one。
  2. 嵌套的 includes/joins → 确认关系。
  3. 类型组合 → derives-from。
  4. 较宽松的引用 → references。

返回: 两个数组 — entities 和 relationships。

功能

依赖: discover、surfaces、entities。

功能是嵌入在界面中的独立能力 — 即你在应用中能做什么,而不是去哪里。点赞按钮、提示词向导、积分购买、年龄门禁都是功能。它们组合成界面;它们本身不是页面。

如果描述某项功能时离不开具体页面的名称,那么它很可能属于界面,而不是功能。

六种类型 — 工具、交互、交易、门禁、基础设施、工作流 — 以及标志每种类型的代码模式,都列在 references/feature-kinds.md 中。将其用作扫描清单。

不同实现应视为不同功能。 同一概念能力通常会在不同界面中以独立实现的形式存在 — 例如聊天中的面向用户的“提示词向导”模态框,以及帖子管理区域中的管理端“提示词改编向导”。务必分别创建功能条目,并为它们指定不同的名称。从一个界面提取功能后,还要扫描其他界面的组件树,查找类似模式,并 grep 共享服务导入 — 这是最容易遗漏的一类功能。

对于每项功能,记录:名称、描述、种类、surfaceIds、entityIds、实现(2–5 个最重要的文件,而非全部文件)。

返回: features 数组(不含 compartmentIds — 由 Compartment Dependencies 填充)。

操作

依赖: discover、entities。

对于每个入口点(路由处理程序、服务端操作、API 端点):

  1. 它针对哪个实体。
  2. 操作类型:create、read、update、delete 或 domain。
  3. 描述性名称(例如,“发布帖子”“生成预览”)。
  4. 对其他实体的副作用。
  5. 实现位置(文件 + 函数)。

返回: operations 数组。

流程

依赖: discover、surfaces、entities、features、operations。

  1. 从 UI 页面入手 — 用户在每个页面上能做什么?
  2. 追踪:UI 操作 → 处理程序 → 服务 → DB。
  3. 按用户可见的目标为每个流程命名。
  4. 确定触发条件和执行者(用户/管理员/系统)。
  5. 按顺序列出步骤,并关联到操作和实体。

返回: flows 数组。

功能模块

需要: 发现、界面、功能、实体、操作。

功能模块是将相关文件归为一组的逻辑单元,构成连贯的功能单元。它们连接了产品视角(界面、功能)与底层代码结构,让开发者能够从“这项功能有什么作用?”找到“相关代码在哪里?”。

  1. 扫描文件树,并结合界面、功能、实体和操作作为上下文。
  2. 使用多种信号对文件进行分组:
    • 文件夹结构——同一目录中的文件通常彼此相关。
    • 导入关系图——相互导入频繁的文件可能属于同一功能模块。
    • 功能对应关系——属于某项功能的文件应归入对应的功能模块。
    • 领域关联度——处理同一实体或业务概念的文件应归为一组。
    • 命名约定——名称相关的文件(例如,image-*.ts、*-generation.*)可能属于同一功能模块。
    • 共享基础设施——供 3 项或更多功能使用的文件可能需要单独设立功能模块,也可能出现在多个功能模块中。
  3. 功能模块可以嵌套——子模块可按任意深度嵌套。典型 Web 应用通常有 2–3 层。
  4. 文件可以属于多个模块——同一文件可以出现在多个功能模块中(例如,lib/prisma.ts 同时属于“数据库访问”和“共享基础设施”)。
  5. **每个非生成文件都必须至少属于一个功能模块。**配置文件、构建工具等应归入“项目基础设施”模块。排除 generated/、node_modules/、.next/、dist/。
  6. 对每个功能模块,提供:名称和描述(根据其功能命名,而非文件夹名称——“图像生成流水线”,而非“app/chat/actions”)、标签(从 references/json-schema.md 中的词汇表选择,必要时可自定义)、文件(并标注角色:component、hook、action、api、lib、type、config、style、test、other)、parentId(顶层为 null)、featureIds、surfaceIds。

让功能模块保持实用的指南:

  • 除非某模块确实是独立模块,否则不要创建仅含 1 个文件的功能模块——将小型分组并入上级模块。
  • 对典型 Web 应用,顶层功能模块保持在 8–15 个。子模块可以更多。
  • 优先采用有意义的分组,而不是与文件夹一一对应。如果文件夹中包含无关文件,应拆分处理。如果相关文件分布在多个文件夹中,则应归为一组。

**返回:**功能模块数组(不含 dependsOn——该字段由 Compartment Dependencies 填充)。

文件树权重

需要: 发现、功能。

为每个非生成文件估算其用途中服务于各项功能的比例。

  1. 从发现结果包中获取完整文件列表。
  2. 阅读每个文件(或抽样阅读特别大的文件),并估算各项比例。
  3. 不属于任何产品功能的文件,其唯一功能权重应设为 "__infrastructure__"。
  4. 服务于多项功能的文件应按比例分配权重(例如,共享 hook → 各占 50%)。
  5. 每个文件的所有权重之和必须为 1.0。

估算指南:

  • 查看导入项、函数名称、组件名称以及文件的整体用途。
  • 完全专用于一项功能的文件 → [{featureId: "that-feature", weight: 1.0}]。
  • 被多项功能使用的共享工具 → 按比例拆分。
  • 配置文件、通用类型定义、构建配置、中间件 → __infrastructure__。
  • 优先为每个文件分配较少的功能及较高的权重,避免分配给过多功能且权重极低。

返回:{file, featureWeights: [{featureId, weight}]} 条目数组,每个文件对应一项。

组件依赖关系

需要: 组件(以及用于回填的界面和功能)。

  1. 遍历每个组件中每个文件的导入项。
  2. 将每个导入文件映射到它所属的组件。
  3. 将这些依赖记录为每个组件上的 dependsOn 边(仅限组件间依赖,不包括自身引用)。
  4. 在功能上填充 compartmentIds — 确定每项功能由哪些组件实现。
  5. 在界面上填充 compartmentIds — 确定每个界面由哪些组件提供服务。

返回: 更新后的组件数组(已填充 dependsOn),以及 featureCompartmentIds 映射和 surfaceCompartmentIds 映射。


3. 分析

对组装完成的提取结果运行分析。

分析任务的报告方式。 每个分析任务都会输出一条指标记录,其中包含分数、阈值对象、一行 summary,以及一个 findings[] 数组。发现项用于解释分数 — 每个导致分数低于 100 的文件或条目都必须作为发现项列出,并提供具体建议。摘要必须包含准确数量(“103 个受评估文件中有 8 个位置不当”),不能使用含糊的描述(“大多数文件的位置都正确”)。

分数低于 100 却没有发现项,就无法采取行动 — 请将其视为输出中的错误,而不是有效结果。

每项任务的具体发现项结构见 references/json-schema.md 中对应指标的说明。

位置共置

读取项目说明文件(AGENTS.md、CLAUDE.md 或同等文件),提取明确的位置共置约定。如果没有找到,则使用以下默认规则:

  • 仅供单个界面使用的文件应放在该界面的目录中。
  • 供多个界面使用、但代表一项功能的文件应放在 features/<capability>/ 中。
  • 根目录下的 components/、lib/ 和 actions/ 仅用于供 3 个或更多界面/功能使用的全局代码。
  • components/ui/* 不受此规则限制,视为位置正确。

评估每个非生成、非基础设施文件:

  1. 追踪导入该文件的所有文件。
  2. 确定实际使用该文件的界面/功能。
  3. 将其当前位置与规则要求的位置进行比较。
  4. 判定为二元 pass/fail。

对于每个未通过的文件,输出一条发现项,并将 action 设为 "move"(移入界面或功能目录以实现共置)或 "promote"(将其移至 features/,因为它服务于多个界面)。包含 file、verdict、reason、consumers[] 和 recommendation。

分数:(通过的文件数 / 受评估文件总数) * 100。

返回: 一个 id: "co-location" 的指标对象。

DRY 程度
  1. 以功能和组件作为起始映射。
  2. 在读取文件之前查找可能的重复项:
    • 不同界面中 kind 相同且 entityIds 有重叠的功能。
    • 位于不同界面中、名称或导入模式相似的文件。
    • 描述、标签相似或 featureIds 有重叠的组件。
    • 封装相同外部 API 或工作流的钩子/操作/客户端。
  3. 阅读候选项以确认是否确实重叠 — 同时考虑功能重叠(重复解决相同的产品问题)和结构相似性(以少量变化重复相同的技术模式)。
  4. 对每项已确认的重复,指出哪些内容确实可以共享、哪些必须保留为特定实现,以及共享逻辑应放置在哪里(遵循位置共置规则)。

每条发现项都包含 id、title、severity、implementations[]、sharedLogic[] 和 recommendation。

分数:K = 200 / totalNonInfrastructureFiles;score = max(0, 100 - (findingCount * K))。

返回: 一个 id: "dryness" 且包含 scalingFactor 的指标对象。

死代码
  1. 为每个非生成文件构建导入映射(解析相对导入和 @/ 路径别名)。
  2. 使用 references/dead-code-detection.md 中的始终有效入口点列表——即使没有任何导入者,这些入口点也永远不算死代码。
  3. 遍历代码库,查找 references/dead-code-detection.md 中记录的五种发现类型:
    • dead-file — 非入口点且没有导入者。
    • test-only-file — 仅被测试导入的非测试文件(仅供参考;不影响分数)。
    • orphaned-surface — 没有入站导航引用的界面。
    • orphaned-feature — surfaceIds 为空或所有界面均为孤立界面的功能。
    • dead-entity — 没有操作、功能或查询引用的数据库模型或 DTO。

每项发现都包含 id、kind、severity、target、reason、evidence、recommendation。

分数:totalEvaluated = filesEvaluated + surfacesEvaluated + featuresEvaluated + entitiesEvaluated;deadItems = deadFiles + orphanedSurfaces + orphanedFeatures + deadEntities;score = ((totalEvaluated - deadItems) / totalEvaluated) * 100。从分子和分母中排除仅供测试使用的文件。

返回: 一个 id: "dead-code" 的指标对象。

不变量

仅当仓库根目录存在 cartograph-invariants.md 时才运行。否则返回 null,并且 Assemble 会完全省略 invariants 键。

  1. 读取 cartograph-invariants.md。
  2. 对每个不变量,执行下文的 验证不变量 流程。
  3. 计算汇总数量(总数、通过、失败、跳过)。
  4. 将 verifiedAt 设为当前 ISO 8601 时间戳,并将 definitionsFile 设为 "cartograph-invariants.md"。

返回: 符合 references/json-schema.md 中架构的 invariants 对象,或 null。


4. Assemble

自行运行。将所有内容合并到 .cartograph/mapping.json:

  1. 根据操作、流程和 Compartment Dependencies 输出,为每个界面填充 entityIds、operationIds、flowIds 和 compartmentIds。
  2. 根据 Compartment Dependencies 输出,为每个功能填充 compartmentIds。设置 "files": [](空数组是向后兼容的格式;compartmentIds 是主要的代码映射)。
  3. 原样包含 Discover 中的 techStack 数组。
  4. 在 fileTree 下原样包含 File Tree Weights 数组。
  5. 添加顶层 codeHealth 对象:analyzedAt = ISO timestamp,metrics = [coLocationMetric, drynessMetric, deadCodeMetric]。
  6. 如果不变量分析返回了非空结果,则包含 "invariants": <result>。如果结果为 null,则省略此键。
  7. 在仓库根目录写入 .cartograph/mapping.json,必要时创建 .cartograph/ 目录。最终结构见 references/json-schema.md。
  8. 告知用户:“在项目根目录运行 npm --prefix skills/cartograph/app install 一次以启动 Cartograph UI,然后运行 npm --prefix skills/cartograph/app start。”如果已验证不变量,还要打印不变量摘要(格式与独立验证相同)。

添加不变量流程

当用户想要添加新不变量时:

  1. 提取用户的断言文本(“add invariant:”或类似表述后面的自然语言声明)。
  2. 阅读代码库以理解该断言:
    • 找出与该断言相关的文件、函数和模式。
    • 确定涉及哪些界面和功能(如果之前已有 .cartograph/mapping.json,则参考其中的 ID 填写 surfaceIds 和 featureIds)。
    • 梳理验证方法。
  3. 按照 references/invariant-definitions-format.md,将单行描述扩展为完整的不变量定义:
    • 编写 YAML frontmatter:生成唯一的 kebab-case id;设置 severity(涉及资金/安全/数据完整性时为 critical,涉及核心产品逻辑时为 high,涉及约定时为 low);添加相关的 tags;可选择添加 surfaceIds/featureIds。
    • 编写所有正文部分:断言、验证步骤、通过标准、已知范围、验证提示词。
  4. 将不变量追加到仓库根目录的 cartograph-invariants.md。如果文件不存在,则创建该文件并添加 # Cartograph Invariants 标题。
  5. 使用下方的验证不变量流程进行初始验证。
  6. 报告结果:
    • 通过:“Invariant added and verified. Definition saved to cartograph-invariants.md.”
    • 未通过:“Invariant added but does NOT currently hold — definition saved anyway. Violations: [details]. Fix the code to make it pass, or edit the definition if the assertion needs adjusting.”

如果断言过于模糊,无法确定验证步骤,请先提出澄清问题,再编写定义。


独立验证流程

当用户想要验证现有不变量,但不进行完整扫描时:

  1. 从仓库根目录读取 cartograph-invariants.md。如果文件不存在,则回复:“No invariant definitions found. Add one with: /cartograph add this invariant: '...'”。

  2. 对文件中的每个不变量运行下方的验证不变量流程。

  3. 在控制台打印通过/未通过摘要:

    Invariant Results (N checked)
    ──────────────────────────────────
    ✓ CRITICAL  Invariant name
      Summary of passing result
    
    ✗ HIGH  Invariant name
      Violation in file:line
      Brief description of violation
    
    N of M invariants passing.
    
  4. 如果 .cartograph/mapping.json 存在,则仅更新 invariants 键(保留所有其他数据不变)。按照 references/json-schema.md 中的架构编写 invariants 对象。

  5. 如果 .cartograph/mapping.json 不存在,则按需创建 .cartograph/ 目录,并写入仅包含 meta 和 invariants 键的最小 JSON。


验证不变量流程

添加不变量流程、独立验证流程和完整扫描中的不变量分析任务共用此流程。

  1. 解析不变量:提取 frontmatter 字段和正文部分(参见 references/invariant-definitions-format.md)。
  2. 如果 enabled: false,则输出 "skipped" 结果并停止。
  3. 以验证步骤部分为指南;读取已知范围中列出的文件,以及步骤中引用的任何文件。
  4. 评估通过标准是否满足:
    • 通过:记录已检查的文件,使用空的 violations 数组,并将 fixPrompt 设为 null。
    • 未通过:记录具体违规项,包括文件路径、行号、预期内容、实际内容和建议。生成一条完整自足的 fixPrompt,供 AI Agent 修复具体违规项使用;其中须包含不变量名称、违规详情、受影响的路径/行号以及需要修改的内容。
  5. 对每个结果(通过或未通过)都设置 verificationPrompt,其值为不变量定义中的验证提示词。

结果结构位于 references/json-schema.md 的 invariants 键下。


重要说明

  • 对分析中的代码库只读 — 切勿修改。仅写入 .cartograph/mapping.json,以及(根据用户要求)cartograph-invariants.md。
  • 不确定时,优先纳入并降低置信度,而不是省略。
  • 使用通俗易懂的描述 — 产品经理也应能读懂。
  • 使用相对路径 — 所有文件路径均相对于仓库根目录。
  • 大型仓库 — 按功能或路由目录进行分析,然后合并。

评论

支持 Markdown