
Understand-Anything 语言提取器架构用 LanguageExtractor 接口让 tree-sitter 结构分析支持十余种语言【免费下载链接】Understand-AnythingGraphs that teach graphs that impress. Turn any code into an interactive knowledge graph you can explore, search, and ask questions about. Works with Claude Code, Codex, Cursor, Copilot, Gemini CLI, and more.项目地址: https://gitcode.com/GitHub_Trending/un/Understand-Anything本文基于 Understand-Anything 仓库中的实施计划文档docs/superpowers/plans/2026-04-15-language-extractors-impl.md完整讲解该项目的多语言结构提取language extractor架构从LanguageExtractor接口设计、TreeSitterPlugin分发机制到extract-structure.mjs确定性提取脚本与 file-analyzer agent 的接入方式。读完后你将理解如何把一个只为 TypeScript/JavaScript 硬编码的 AST 解析器重构为可插拔的多语言提取架构并能复现接口定义 → 语言配置 → 逐语言实现 → 打包脚本 → Agent 集成的完整落地路径。背景与目标两个核心动机该计划文档开篇明确了两个目标Goal把 AST 提取逻辑与 TS/JS 专属节点类型解耦让另外 8 种代码语言Python、Go、Rust、Java、Ruby、PHP、C/C、C#获得基于 tree-sitter 的结构化分析能力。计划中明确排除了 Swift 和 Kotlin——当时没有可用的 WASM 语法包注当前仓库后续通过工作区自编译的 WASM 包补上了这些语言见文末仓库现状一节。用确定性的、预构建的 tree-sitter 提取脚本替换 file-analyzer agent 每次临时让 LLM 编写的一次性正则脚本。此前 Phase 1 要求 agent 现场生成正则提取脚本既慢又不确定且完全浪费了 tree-sitter 基础设施。技术栈为web-tree-sitterWASM、TypeScript、Vitest。总体架构与文件布局计划的 Architecture 一节给出了整体设计引入LanguageExtractor接口每种语言实现一份TreeSitterPlugin把提取工作委派给按文件语言注册好的 extractorskills/understand/目录下的extract-structure.mjs脚本通过PluginRegistry同时包含TreeSitterPlugin与非代码文件的正则解析器为 file-analyzer agent 提供确定性的结构提取。计划中的文件结构如下packages/core/src/plugins/ ├── extractors/ │ ├── types.ts # LanguageExtractor 接口 TreeSitterNode 类型再导出 │ ├── base-extractor.ts # 共享工具traverse、getStringValue 等 │ ├── typescript-extractor.ts # TS/JS从 tree-sitter-plugin.ts 迁出 │ ├── python-extractor.ts │ ├── go-extractor.ts │ ├── rust-extractor.ts │ ├── java-extractor.ts │ ├── ruby-extractor.ts │ ├── php-extractor.ts │ ├── cpp-extractor.ts │ ├── csharp-extractor.ts │ └── index.ts # builtinExtractors 数组 再导出 ├── tree-sitter-plugin.ts # 重构为基于 extractor 分发 └── tree-sitter-plugin.test.ts # 既有测试必须保持通过 packages/core/src/plugins/__tests__/ └── extractors.test.ts # 所有新 extractor 的测试 skills/understand/ ├── extract-structure.mjs # 预构建的 tree-sitter 提取脚本新增 └── SKILL.md # 更新为引用 extract-structure.mjs agents/ └── file-analyzer.md # Phase 1 改写为执行预构建脚本路径说明下文所有仓库路径以仓库根目录为起点核心代码位于 packages/core 包内实际路径前缀为understand-anything-plugin/packages/core/。Task 1LanguageExtractor 接口与共享工具库接口定义接口放在 types.ts核心是把 tree-sitter AST 映射到项目统一的StructuralAnalysis/CallGraphEntry类型上// packages/core/src/plugins/extractors/types.ts import type { StructuralAnalysis, CallGraphEntry } from ../../types.js; // Re-export the tree-sitter Node type for use by extractors export type TreeSitterNode import(web-tree-sitter).Node; /** * Language-specific extractor that maps a tree-sitter AST * to the common StructuralAnalysis / CallGraphEntry types. */ export interface LanguageExtractor { /** Language IDs this extractor handles (must match LanguageConfig.id) */ languageIds: string[]; /** Extract functions, classes, imports, exports from the root AST node */ extractStructure(rootNode: TreeSitterNode): StructuralAnalysis; /** Extract caller→callee relationships from the root AST node */ extractCallGraph(rootNode: TreeSitterNode): CallGraphEntry[]; }三个契约点值得注意languageIds必须与LanguageConfig.id严格对齐例如 Python 配置中id: python见 python.ts这样插件注册与查询才能闭环extractStructure只接收 root 节点所有语言都输出同一个StructuralAnalysis结构functions/classes/imports/exports下游图谱构建、指纹、仪表盘无需感知语言差异extractCallGraph提取 caller→callee 关系输出CallGraphEntry[]含行号。共享工具函数base-extractor.ts 把原先散落在tree-sitter-plugin.ts中的 AST 遍历工具抽成共享模块所有语言 extractor 复用import type { TreeSitterNode } from ./types.js; /** Recursively traverse an AST tree, calling the visitor for each node. */ export function traverse( node: TreeSitterNode, visitor: (node: TreeSitterNode) void, ): void { visitor(node); for (let i 0; i node.childCount; i) { const child node.child(i); if (child) traverse(child, visitor); } } /** Extract the unquoted string value from a string-like node. */ export function getStringValue(node: TreeSitterNode): string { for (let i 0; i node.childCount; i) { const child node.child(i); if (child child.type string_fragment) { return child.text; } } return node.text.replace(/^[]|[]$/g, ); } /** Find the first child matching a type. */ export function findChild(node: TreeSitterNode, type: string): TreeSitterNode | null { for (let i 0; i node.childCount; i) { const child node.child(i); if (child child.type type) return child; } return null; } /** Find all children matching a type. */ export function findChildren(node: TreeSitterNode, type: string): TreeSitterNode[] { const result: TreeSitterNode[] []; for (let i 0; i node.childCount; i) { const child node.child(i); if (child child.type type) result.push(child); } return result; } /** Check if a node has a child of the given type (used for export/visibility checks). */ export function hasChildOfType(node: TreeSitterNode, type: string): boolean { for (let i 0; i node.childCount; i) { const child node.child(i); if (child child.type type) return true; } return false; }这些工具覆盖了三类高频操作全树遍历调用图游走、字符串节点取值import/include 路径、子节点查找导出/可见性判断。以 python-extractor.ts 为例findChild被用于extractClass中识别类型注解赋值name: strfindChildren被用于收集dotted_nameimport 说明符——这正是计划共享工具意图的落地证据。Task 2TS/JS 提取逻辑迁入 TypeScriptExtractor 并重构插件分发这是一次纯重构所有既有测试必须零修改通过。TypeScriptExtractor 迁移要点把tree-sitter-plugin.ts中所有 TS/JS 专属提取方法extractFunction、extractClass、extractVariableDeclarations、extractImport、processExportStatement、extractParams、extractReturnType、extractImportSpecifiers及调用图游走器整体迁入typescript-extractor.ts实现LanguageExtractor接口。计划特别强调一个易错点languageIds应为[typescript, javascript]不能包含tsx——tsx是TreeSitterPlugin内部用于选择语法的合成键.tsx文件需要独立的 TSX 语法包不是LanguageConfig.idtsx→typescript 的映射由getExtractor()处理。插件侧的 extractor 分发重构后的 tree-sitter-plugin.ts 维护一张Mapstring, LanguageExtractor注册与查询逻辑见源码 L104-L123// In TreeSitterPlugin private extractors new Mapstring, LanguageExtractor(); registerExtractor(extractor: LanguageExtractor): void { for (const id of extractor.languageIds) { this.extractors.set(id, extractor); } } private getExtractor(langKey: string): LanguageExtractor | null { // tsx is a synthetic grammar key — extraction logic is identical to typescript const key langKey tsx ? typescript : langKey; return this.extractors.get(key) ?? null; }analyzeFile()变为取 parser → 解析 → 按扩展名查语言键 → 分发给 extractor的标准流程analyzeFile(filePath: string, content: string): StructuralAnalysis { const parser this.getParser(filePath); if (!parser) return { functions: [], classes: [], imports: [], exports: [] }; const tree parser.parse(content); if (!tree) { parser.delete(); return { functions: [], classes: [], imports: [], exports: [] }; } const langKey this.languageKeyFromPath(filePath); const extractor langKey ? this.getExtractor(langKey) : null; let result: StructuralAnalysis; if (extractor) { result extractor.extractStructure(tree.rootNode); } else { result { functions: [], classes: [], imports: [], exports: [] }; } tree.delete(); parser.delete(); return result; }extractCallGraph()遵循同样模式且必须保持完全一致的 parser/tree 生命周期管理tree.delete()、parser.delete()避免 WASM 内存泄漏。构造函数接受可选extractors数组若不提供则注册内置TypeScriptExtractor以兼容旧调用方。验证标准运行pnpm --filter understand-anything/core test期望全部 426 个测试通过与重构前一致。值得补充的是当前源码在此基础上的演进TreeSitterPlugin现在按语言键缓存可复用 parser_parsersMap见 L47并新增了单次解析同时返回结构与调用图的analyzeFileFull()L275-L300因为两个 extractor 都是同一rootNode的纯函数单次解析即可得到字节一致的结果把索引热路径上的解析工作量削减约 40%。Task 2.5PluginRegistry 补上 extractCallGraphDEFAULT_PLUGIN_CONFIG 改为动态派生计划指出两个配套缺口PluginRegistry此前只暴露analyzeFile与resolveImports没有extractCallGraph而 Task 13 的脚本需要通过 registry 拿到调用图数据DEFAULT_PLUGIN_CONFIG硬编码了[typescript, javascript]无法反映新增语言。registry.ts 新增方法当前 registry.ts 的实现与计划一致extractCallGraph(filePath: string, content: string): CallGraphEntry[] | null { const plugin this.getPluginForFile(filePath); if (!plugin?.extractCallGraph) return null; return plugin.extractCallGraph(filePath, content); }返回null而非空数组用于区分没有插件支持该文件与支持但无调用两种情况。registry 通过LanguageRegistry完成扩展名→语言→插件的映射而非硬编码查找表。discovery.ts 动态派生语言列表当前 discovery.ts 已按计划落地import { builtinLanguageConfigs } from ../languages/configs/index.js; export const DEFAULT_PLUGIN_CONFIG: PluginConfig { plugins: [ { name: tree-sitter, enabled: true, languages: builtinLanguageConfigs .filter((c) c.treeSitter) .map((c) c.id), }, ], };这意味着新增一种语言只需改语言配置加treeSitter块默认插件配置自动跟随不会再出现支持了 Python 但默认配置里没有 python的不一致。Task 3添加 8 个语法依赖与 treeSitter 语言配置语法包依赖计划要求在 package.json 的dependencies中新增 8 个 tree-sitter 语法包。当前仓库实际版本tree-sitter-c-sharp: ^0.23.1, tree-sitter-cpp: ^0.23.4, tree-sitter-go: ^0.25.0, tree-sitter-java: ^0.23.5, tree-sitter-php: ^0.23.11, tree-sitter-python: ^0.25.0, tree-sitter-ruby: ^0.23.1, tree-sitter-rust: ^0.24.0此外还有tree-sitter-typescript、tree-sitter-javascript、tree-sitter-scala以及tree-sitter-grammars/tree-sitter-kotlin和两个工作区包understand-anything/tree-sitter-dart-wasm、understand-anything/tree-sitter-swift-wasm仓库后续演进见文末。语言配置的 treeSitter 块10 个语言配置各加一个treeSitter字段声明 WASM 包名与文件名。以 python.ts 为例当前仓库实态export const pythonConfig { id: python, displayName: Python, extensions: [.py, .pyi], treeSitter: { wasmPackage: tree-sitter-python, wasmFile: tree-sitter-python.wasm, }, concepts: [ /* 语言概念提示词列表 */ ], filePatterns: { /* 入口/桶文件/测试/配置的 glob 规则 */ }, } satisfies LanguageConfig;计划给出的各语言配置示例// python.ts treeSitter: { wasmPackage: tree-sitter-python, wasmFile: tree-sitter-python.wasm }, // go.ts treeSitter: { wasmPackage: tree-sitter-go, wasmFile: tree-sitter-go.wasm }, // rust.ts treeSitter: { wasmPackage: tree-sitter-rust, wasmFile: tree-sitter-rust.wasm }, // java.ts treeSitter: { wasmPackage: tree-sitter-java, wasmFile: tree-sitter-java.wasm }, // ruby.ts treeSitter: { wasmPackage: tree-sitter-ruby, wasmFile: tree-sitter-ruby.wasm }, // php.ts treeSitter: { wasmPackage: tree-sitter-php, wasmFile: tree-sitter-php.wasm }, // cpp.ts treeSitter: { wasmPackage: tree-sitter-cpp, wasmFile: tree-sitter-cpp.wasm }, // csharp.ts treeSitter: { wasmPackage: tree-sitter-c-sharp, wasmFile: tree-sitter-c_sharp.wasm },注意两个细节C# 的包名是tree-sitter-c-sharp但 wasm 文件名是tree-sitter-c_sharp.wasm下划线包名与文件名不一致是新手容易踩的坑Swift 与 Kotlin 配置在计划执行时不改动当时无 WASM 包可用。验证 WASM 可解析的命令计划 Step 3pnpm install node -e const rrequire(module).createRequire(import.meta.url??__filename); console.log(r.resolve(tree-sitter-python/tree-sitter-python.wasm))加载侧的容错设计在 tree-sitter-plugin.ts 中体现每个语法的加载包在try/catch内执行加载失败只打印 debug 日志并优雅降级——该语言被跳过LLM agent 在 Phase 2 兜底分析而不是让整个插件初始化失败。Task 4–11八种语言提取器的关键 AST 节点与测试用例计划为每种语言列出了 tree-sitter 的关键节点类型并给出代表性测试样例与预期结果。这一节是理解每种语言 extractor 在做什么的核心。PythonTask 4关键节点类型函数function_definitionname、parameters、return_type类class_definitionname、body → 方法 类型注解赋值作为属性导入import_statement、import_from_statement装饰decorated_definition包裹function_definition或class_definition调用callfunction 字段无正式导出语法所有顶层名字视为已导出计划给定的测试样例与断言import os from pathlib import Path from typing import Optional class DataProcessor: name: str def __init__(self, name: str): self.name name def process(self, data: list) - dict: return transform(data) def helper(x: int) - str: return str(x) decorator def decorated_func(): pass预期2 个函数helper、decorated_func、1 个类DataProcessor含方法__init__/process与属性name、3 条 import、调用图process→transform。对照当前实现 python-extractor.ts可以看到这些断言的实现细节unwrapDecorated()负责剥开decorated_definitionL85-L93extractParams()会过滤掉隐式的self/cls并把*args/**kwargs输出为带前缀的参数名extractFromImport()通过节点 id 对比跳过module_name本身支持from foo import bar as baz与from os import *通配导入。调用图提取使用函数栈functionStack跟踪当前所在函数遇到call节点且栈非空时记录 caller→callee 与行号。GoTask 5函数function_declaration方法method_declarationreceiver、name结构体type_declaration→type_spec→struct_type接口interface_type导入import_declaration→import_spec_list→import_spec导出判定首字母大写Go 的可见性约定调用call_expression测试样例与断言package main import ( fmt os ) type Server struct { Host string Port int } func (s *Server) Start() error { fmt.Println(starting) return nil } func NewServer(host string, port int) *Server { return Server{Host: host, Port: port} }预期2 个函数Start、NewServer、1 个 structServer方法 Start、属性 Host/Port、2 条 import、导出Server、Start、NewServer——全部大写开头、调用图Start→fmt.Println。RustTask 6函数function_itemstructstruct_item枚举enum_item实现块impl_item方法体在 impl 内部导入use_declarationscoped_identifier、use_list、use_wildcard导出判定visibility_modifier含pub调用call_expression测试样例Configstruct impl Configpub fn new/fn validatepub fn check_port。预期3 个函数、1 个 struct含 new/validate 方法与 name/port 属性、2 条 import、导出为带pub的项Config、new、check_port、调用图validate→check_port。这里体现了 Rust extractor 的特殊性方法不直接挂在 struct 上必须解析impl_item把方法归并到对应类型。JavaTask 7方法method_declaration构造器constructor_declaration类class_declarationname、class_body接口interface_declaration字段field_declarationdeclarator → variable_declarator → identifier导入import_declarationscoped_identifier导出判定modifiers节点中的public调用method_invocationname、object、argumentsRubyTask 8方法method类class模块module导入call节点且方法名为require或require_relativeRuby 用方法调用代替 import 语句调用callmethod、receiver、arguments无正式导出语法Ruby 的特点是导入与调用共用call节点extractor 需要按方法名区分二者。PHPTask 9函数function_definition方法method_declaration类class_declarationname、declaration_list导入namespace_use_declarationnamespace_use_clause调用function_call_expression/member_call_expression注意PHP AST 把一切包在program→php_tag 语句 之下C/CTask 10函数function_definitiondeclarator → function_declarator → identifier parameter_list类class_specifier结构体struct_specifier包含preproc_includepath → string_literal 或 system_lib_string命名空间namespace_definition调用call_expression两个实现要点C/C 的函数签名是嵌套结构函数名藏在function_declarator内部cppConfig的id: cpp覆盖扩展名[.cpp, .cc, .cxx, .c, .h, .hpp, .hxx]纯 C 文件用 C 语法解析extractor 必须优雅处理 C 专属节点缺失的情况解析纯 C 时类返回空数组。C#Task 11方法method_declaration构造器constructor_declaration类class_declaration接口interface_declaration属性property_declaration导入using_directivequalified_name调用invocation_expressionidentifier/member_access、argument_listTask 12 与 Task 15builtinExtractors 聚合与对外导出extractors/index.tsindex.ts 按计划聚合所有内置 extractorexport const builtinExtractors: LanguageExtractor[] [ new TypeScriptExtractor(), new PythonExtractor(), new GoExtractor(), new RustExtractor(), new JavaExtractor(), new RubyExtractor(), new PhpExtractor(), new CppExtractor(), new CSharpExtractor(), // 仓库后续演进 new DartExtractor(), new KotlinExtractor(), new SwiftExtractor(), new ScalaExtractor(), ];TreeSitterPlugin构造函数中若调用方未提供extractors默认注册builtinExtractors见 tree-sitter-plugin.ts L92-L101这保证什么都不传也能用全部内置语言。Task 15 还要求在 packages/core/src/index.ts 中补上对外导出因为extract-structure.mjs与外部消费者依赖这些导出export type { LanguageExtractor } from ./plugins/extractors/types.js; export { builtinExtractors } from ./plugins/extractors/index.js;最终验证三步pnpm --filter understand-anything/core build→ 全量测试 → 收尾提交。Task 13extract-structure.mjs 确定性提取脚本这是计划中最具实战价值的一环把LLM 每次现场写正则脚本替换为执行预构建脚本。使用方式与 I/O 契约node skills/understand/extract-structure.mjs test-input.json test-output.json输入 JSON{ projectRoot, batchFiles: [{path, language, sizeLines, fileCategory}], batchImportData }输出 JSON{ scriptCompleted, filesAnalyzed, filesSkipped, results: [...] }计划规定脚本的行为契约接收输入/输出 JSON 路径两个参数用createRequire相对于脚本自身位置向上两级到插件根解析understand-anything/core创建PluginRegistry注册TreeSitterPlugin全部内置语言配置 所有非代码解析器对每个文件读取内容、调用registry.analyzeFile()按既有脚本输出 schema 格式化functions、classes、exports、sections、definitions、services 等对支持 tree-sitter 的代码文件额外通过plugin.extractCallGraph()提取调用图对无插件支持的文件Swift、Kotlin、未知语言输出{ path, language, fileCategory, totalLines, nonEmptyLines, metrics }及空结构数据交由 LLM 在 Phase 2 处理写出与既有scriptCompleted/filesAnalyzed/filesSkipped/resultsschema 一致的结果。当前实现的健壮性增强对照仓库中的 extract-structure.mjs实际落地时补上了几处计划未细述的健壮性处理值得工程参考const __dirname dirname(fileURLToPath(import.meta.url)); // skills/understand/ - plugin root is two dirs up const pluginRoot resolve(__dirname, ../..); const require createRequire(resolve(pluginRoot, package.json)); let core; try { core await import(pathToFileURL(require.resolve(understand-anything/core)).href); } catch { // Fallback: direct path for installed plugin cache layouts core await import(pathToFileURL(resolve(pluginRoot, packages/core/dist/index.js)).href); }Windows 兼容Node ESM 的动态import()在 Windows 上要求file://URL裸绝对路径C:\...会抛ERR_UNSUPPORTED_ESM_URL_SCHEMEloader 把C:当 URL scheme因此两处解析都包了pathToFileURL()符号链接安装路径Claude Code / Copilot CLI 的插件缓存默认走符号链接import.meta.url经 symlink 解析而process.argv[1]保留 symlink裸相等判断会让是否 CLI 入口检查静默失效实现中改用realpathSync对两侧规范化后再比较isCliEntry()L152-L161并保证作为模块被 import 时测试场景不触发main()行计数语义对齐POSIX 文本文件以尾换行结尾split(\n)会多出一个空元素实现对totalLines做了与wc -l一致的修正确保与项目扫描器sizeLines的口径吻合结构化校验结果映射逻辑被拆到 extract-structure-result.mjs用纯函数 字段校验器functions必须有name/lineRange/paramsclasses必须有name/lineRange/methods/properties等保证输出 schema 稳定且单测无需 import 带 shebang 的 CLI 脚本结果计数输出中新增analysisOutcomesstructure 与 callGraph 各自的 succeeded/failed/skipped 计数便于诊断批量提取中哪些文件降级了。Task 14重写 file-analyzer.md 的 Phase 1计划要求删除 file-analyzer agent 中约 150 行的现场生成正则脚本指令替换为三步确定性流程。当前 file-analyzer.md 的实态与计划一致准备输入 JSON格式不变cat $PROJECT_ROOT/.understand-anything/tmp/ua-file-analyzer-input-batchIndex.json ENDJSON { projectRoot: project-root, batchFiles: [本批文件含 fileCategory], batchImportData: batchImportData JSON } ENDJSON执行预构建脚本node SKILL_DIR/extract-structure.mjs \ $PROJECT_ROOT/.understand-anything/tmp/ua-file-analyzer-input-batchIndex.json \ $PROJECT_ROOT/.understand-anything/tmp/ua-file-extract-results-batchIndex.json失败即报告禁止回退脚本非零退出时读 stderr 诊断并报告错误不得回退到手写脚本——预构建脚本是唯一提取路径。另外要求验证输出文件存在且非空test -s ...exit 0 但缺输出文件视为硬失败。同时SKILL.md 在派发 file-analyzer 的 prompt 中追加技能目录路径使 agent 能定位脚本 Skill directory (for bundled scripts): SKILL_DIR这复用了既有机制——SKILL.md 对merge-batch-graphs.py、merge-subdomain-graphs.py等脚本已经通过同样的SKILL_DIR方式传参。Phase 2语义分析读取的 JSON 结构不变无需改动。Implementation Notes四条实施备注计划末尾的四条备注包含了不少为什么这里逐条说明测试文件约定每种语言 extractor 有独立测试文件packages/core/src/plugins/extractors/__tests__/language-extractor.test.ts与tree-sitter-plugin.test.ts就近放置的既有模式一致。当前仓库中 extractors 目录 下确实存在 cpp、csharp、dart、go、java、kotlin、php、python、ruby、rust、scala、swift、typescript 十三份 extractor 测试。语法惰性加载未来优化TreeSitterPlugin.init()通过Promise.all预加载全部语法 WASM。10 个语法合计约 12MB初始化延迟可能可感。计划的建议是先测再改——TS/JS 急加载最常见其余延迟到首次使用。这是典型的不要过早优化约束。指纹的免费收益fingerprint.ts中的buildFingerprintStore内部调用PluginRegistry.analyzeFile。新 extractor 接入后Python/Go/Rust 等语言的指纹自动从仅内容哈希升级为结构化指纹零代码改动。这说明统一走 registry 这一抽象层的长期价值任何消费方都自动获得新语言能力。PHP 语法注意tree-sitter-php同时提供tree-sitter-php.wasm完整 PHP 内嵌 HTML/CSS/JS与tree-sitter-php_only.wasm。计划选用完整的tree-sitter-php.wasm因此 PHP extractor 必须对解析内嵌 HTML 模板时出现的非 PHP AST 节点保持鲁棒。仓库现状从计划到实现的完整闭环对照当前仓库该计划的所有环节都已落地且有几处超出原计划范围的演进语言覆盖从 10 种扩展到 13 种计划执行时 Swift/Kotlin 无 WASM 包可用当前仓库通过tree-sitter-grammars/tree-sitter-kotlinnpm 包与两个工作区自编译包tree-sitter-swift-wasm、tree-sitter-dart-wasm补齐了 Kotlin、Swift、Dart并新增了 Scala单次解析优化analyzeFileFull()让脚本一次 parse 同时拿到结构与调用图消除了对同一内容的双重解析降级路径明确TreeSitterPlugin对无 extractor 的语言、语法加载失败、无插件文件三类情况分别返回空结构/跳过/交给非代码解析器整个链路保证任何文件都有确定性的输出形态LLM 只负责结构化数据之上的语义分析。对希望在自己的多语言分析器中复用这一套模式的人该架构给出的可迁移经验可以概括为四条接口先行LanguageExtractor只有两个方法语言差异被锁在实现类内部、配置驱动新语言 一份LanguageConfig 一个 extractor 注册默认配置自动派生、确定性下沉能用预构建脚本做的绝不留给运行时生成、优雅降级缺能力时输出空结构而非报错把不确定性留给上层兜底。参考文件清单实施计划docs/superpowers/plans/2026-04-15-language-extractors-impl.md接口与工具types.ts、base-extractor.ts、index.ts插件与注册表tree-sitter-plugin.ts、registry.ts、discovery.ts语言配置python.ts、configs/index.ts脚本与 Agentextract-structure.mjs、extract-structure-result.mjs、file-analyzer.md、SKILL.md相关测试extractor 测试目录、test_extract_structure_outcomes.test.mjs【免费下载链接】Understand-AnythingGraphs that teach graphs that impress. Turn any code into an interactive knowledge graph you can explore, search, and ask questions about. Works with Claude Code, Codex, Cursor, Copilot, Gemini CLI, and more.项目地址: https://gitcode.com/GitHub_Trending/un/Understand-Anything创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考