@didnhdj/fnmap 0.1.10 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ import type { AnalyzeResult } from '../types';
2
+ import { extractJSDocDescription } from './jsdoc';
3
+ /**
4
+ * 分析JS/TS文件,提取结构信息
5
+ */
6
+ export declare function analyzeFile(code: unknown, filePath: string | null): AnalyzeResult;
7
+ export { extractJSDocDescription };
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analyzer/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,aAAa,EAMd,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAKlD;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,aAAa,CAyZjF;AAoFD,OAAO,EAAE,uBAAuB,EAAE,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Comment } from '@babel/types';
2
+ /**
3
+ * 从JSDoc注释中提取描述
4
+ */
5
+ export declare function extractJSDocDescription(comment: Comment | null | undefined): string;
6
+ //# sourceMappingURL=jsdoc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsdoc.d.ts","sourceRoot":"","sources":["../../src/analyzer/jsdoc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAqBnF"}
@@ -0,0 +1,25 @@
1
+ import { Command } from 'commander';
2
+ export declare const logger: {
3
+ error: (msg: string) => void;
4
+ success: (msg: string) => void;
5
+ info: (msg: string) => void;
6
+ warn: (msg: string) => void;
7
+ title: (msg: string) => void;
8
+ };
9
+ export declare function setQuietMode(quiet: boolean): void;
10
+ export declare function isQuietMode(): boolean;
11
+ /**
12
+ * 获取package.json中的版本号
13
+ */
14
+ export declare function getVersion(): string;
15
+ /**
16
+ * 配置CLI命令
17
+ */
18
+ export declare function setupCLI(): Command;
19
+ export declare function getProgram(): Command;
20
+ export declare const program: {
21
+ readonly opts: <T extends import("commander").OptionValues>() => T;
22
+ readonly args: string[];
23
+ readonly parse: (argv?: readonly string[], parseOptions?: import("commander").ParseOptions) => Command;
24
+ };
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,eAAO,MAAM,MAAM;iBACJ,MAAM,KAAG,IAAI;mBAGX,MAAM,KAAG,IAAI;gBAGhB,MAAM,KAAG,IAAI;gBAGb,MAAM,KAAG,IAAI;iBAGZ,MAAM,KAAG,IAAI;CAG3B,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAEjD;AAED,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAQnC;AAED;;GAEG;AACH,wBAAgB,QAAQ,IAAI,OAAO,CAgDlC;AAGD,wBAAgB,UAAU,IAAI,OAAO,CAKpC;AAGD,eAAO,MAAM,OAAO;;;;CAUnB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { FnmapConfig, LoadedConfig } from '../types';
2
+ /**
3
+ * 加载配置文件
4
+ * 优先级: .fnmaprc > .fnmaprc.json > package.json#fnmap > 默认配置
5
+ */
6
+ export declare function loadConfig(projectDir: string): LoadedConfig;
7
+ /**
8
+ * 合并配置
9
+ */
10
+ export declare function mergeConfig(userConfig: FnmapConfig | null): Required<FnmapConfig>;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAM1D;;;GAGG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CA4E3D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,WAAW,GAAG,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,CAOjF"}
@@ -0,0 +1,17 @@
1
+ import type { FnmapConfig } from '../types';
2
+ export declare const COLORS: {
3
+ readonly reset: "\u001B[0m";
4
+ readonly red: "\u001B[31m";
5
+ readonly green: "\u001B[32m";
6
+ readonly yellow: "\u001B[33m";
7
+ readonly blue: "\u001B[34m";
8
+ readonly gray: "\u001B[90m";
9
+ readonly bold: "\u001B[1m";
10
+ };
11
+ export declare const MAX_FILE_SIZE: number;
12
+ export declare const MAX_DIR_DEPTH = 50;
13
+ export declare const SUPPORTED_EXTENSIONS: readonly [".js", ".ts", ".jsx", ".tsx", ".mjs"];
14
+ export type SupportedExtension = (typeof SUPPORTED_EXTENSIONS)[number];
15
+ export declare const DEFAULT_EXCLUDES: readonly ["node_modules", ".git", "dist", "build", ".next", "coverage", "__pycache__", ".cache"];
16
+ export declare const DEFAULT_CONFIG: Required<FnmapConfig>;
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,eAAO,MAAM,MAAM;;;;;;;;CAQT,CAAC;AAGX,eAAO,MAAM,aAAa,QAAmB,CAAC;AAG9C,eAAO,MAAM,aAAa,KAAK,CAAC;AAGhC,eAAO,MAAM,oBAAoB,iDAAkD,CAAC;AACpF,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAGvE,eAAO,MAAM,gBAAgB,kGASnB,CAAC;AAGX,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,WAAW,CAIhD,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { FileInfoEntry } from '../types';
2
+ /**
3
+ * 生成目录级 .fnmap 索引文件(包含完整信息)
4
+ */
5
+ export declare function generateAiMap(dirPath: string, filesInfo: FileInfoEntry[]): string;
6
+ //# sourceMappingURL=fnmap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fnmap.d.ts","sourceRoot":"","sources":["../../src/generator/fnmap.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,MAAM,CA2DjF"}
@@ -0,0 +1,10 @@
1
+ import type { FileInfo } from '../types';
2
+ /**
3
+ * 生成紧凑格式AI注释头
4
+ */
5
+ export declare function generateHeader(info: FileInfo, fileName: string): string;
6
+ /**
7
+ * 移除现有的AI注释头
8
+ */
9
+ export declare function removeExistingHeaders(code: string): string;
10
+ //# sourceMappingURL=header.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../src/generator/header.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAoDvE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM1D"}
@@ -0,0 +1,4 @@
1
+ export { generateHeader, removeExistingHeaders } from './header';
2
+ export { generateAiMap } from './fnmap';
3
+ export { generateFileMermaid, generateProjectMermaid } from './mermaid';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { FileInfo, FileInfoEntry } from '../types';
2
+ /**
3
+ * 生成单文件的mermaid调用图
4
+ */
5
+ export declare function generateFileMermaid(fileName: string, info: FileInfo): string | null;
6
+ /**
7
+ * 生成项目级mermaid调用图
8
+ */
9
+ export declare function generateProjectMermaid(_projectDir: string, allFilesInfo: FileInfoEntry[]): string;
10
+ //# sourceMappingURL=mermaid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../../src/generator/mermaid.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAExD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAgDnF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,GAAG,MAAM,CAqFjG"}
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * fnmap - AI Code Indexing Tool
4
+ * Analyzes JS/TS code structure and generates structured code maps to help AI understand code quickly
5
+ */
6
+ export type { ErrorType, ValidationResult, ValidationSuccess, ValidationFailure, FnmapConfig, LoadedConfig, ImportInfo, FunctionInfo, MethodInfo, ClassInfo, ConstantInfo, CallGraph, FileInfo, ParseErrorResult, AnalyzeResult, ProcessResult, ProcessSuccess, ProcessFailure, CLIOptions, ErrorContext, FileInfoEntry } from './types';
7
+ export { ErrorTypes, isParseError, isProcessSuccess, isProcessFailure, isValidationSuccess, isValidationFailure } from './types';
8
+ export { COLORS, SUPPORTED_EXTENSIONS, DEFAULT_EXCLUDES, DEFAULT_CONFIG, MAX_FILE_SIZE, MAX_DIR_DEPTH } from './constants';
9
+ export { validateFilePath, validateConfig, formatError } from './validation';
10
+ export { loadConfig, mergeConfig } from './config';
11
+ export { setupCLI, getVersion, logger, setQuietMode, isQuietMode, program } from './cli';
12
+ export { scanDirectory, getGitChangedFiles } from './scanner';
13
+ export { analyzeFile, extractJSDocDescription } from './analyzer';
14
+ export { generateHeader, removeExistingHeaders, generateAiMap, generateFileMermaid, generateProjectMermaid } from './generator';
15
+ export { processFile, processCode } from './processor';
16
+ export type { ProcessCodeOptions } from './processor';
17
+ import { main } from './main';
18
+ export { main };
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAGH,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,UAAU,EACV,SAAS,EACT,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,cAAc,EACd,cAAc,EACd,UAAU,EACV,YAAY,EACZ,aAAa,EACd,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAGjI,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG3H,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG7E,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGnD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAGzF,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAG9D,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAGlE,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,aAAa,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGhI,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGtD,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,IAAI,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const x=require("fs"),E=require("path"),Re=require("commander"),P=require("child_process"),Fe=require("@babel/parser"),B=require("@babel/traverse"),C={FILE_NOT_FOUND:"FILE_NOT_FOUND",FILE_READ_ERROR:"FILE_READ_ERROR",PARSE_ERROR:"PARSE_ERROR",CONFIG_ERROR:"CONFIG_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",PERMISSION_ERROR:"PERMISSION_ERROR",FILE_TOO_LARGE:"FILE_TOO_LARGE"};function ie(e){return"parseError"in e}function be(e){return e.success===!0}function xe(e){return e.success===!1}function Ae(e){return e.valid===!0}function Ce(e){return e.valid===!1}const A={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",gray:"\x1B[90m",bold:"\x1B[1m"},K=10*1024*1024,X=50,z=[".js",".ts",".jsx",".tsx",".mjs"],Z=["node_modules",".git","dist","build",".next","coverage","__pycache__",".cache"],H={enable:!0,include:["**/*.js","**/*.ts","**/*.jsx","**/*.tsx","**/*.mjs"],exclude:[]};function ae(e){if(!e||typeof e!="string")return{valid:!1,error:"File path is required and must be a string / 文件路径必须是字符串",errorType:C.VALIDATION_ERROR};if(!x.existsSync(e))return{valid:!1,error:`File not found: ${e} / 文件不存在: ${e}`,errorType:C.FILE_NOT_FOUND};try{const s=x.statSync(e);if(!s.isFile())return{valid:!1,error:`Path is not a file: ${e} / 路径不是文件: ${e}`,errorType:C.VALIDATION_ERROR};if(s.size>K)return{valid:!1,error:`File too large (${(s.size/1024/1024).toFixed(2)}MB > ${K/1024/1024}MB): ${e} / 文件过大`,errorType:C.FILE_TOO_LARGE}}catch(s){return{valid:!1,error:`Cannot access file: ${e}. Reason: ${s.message} / 无法访问文件`,errorType:C.PERMISSION_ERROR}}return{valid:!0}}function Q(e){if(!e||typeof e!="object")return{valid:!1,error:"Config must be an object / 配置必须是对象"};const s=e;return s.enable!==void 0&&typeof s.enable!="boolean"?{valid:!1,error:"Config.enable must be a boolean / enable 字段必须是布尔值"}:s.include!==void 0&&!Array.isArray(s.include)?{valid:!1,error:"Config.include must be an array / include 字段必须是数组"}:s.exclude!==void 0&&!Array.isArray(s.exclude)?{valid:!1,error:"Config.exclude must be an array / exclude 字段必须是数组"}:{valid:!0}}function V(e,s,r={}){const n=[s];return r.file&&n.push(`File: ${r.file}`),r.line!==void 0&&r.column!==void 0&&n.push(`Location: Line ${r.line}, Column ${r.column}`),r.suggestion&&n.push(`Suggestion: ${r.suggestion}`),n.join(`
3
- `)}let T=!1,q=null;const $={error:e=>{T||console.error(`${A.red}✗${A.reset} ${e}`)},success:e=>{T||console.log(`${A.green}✓${A.reset} ${e}`)},info:e=>{T||console.log(e)},warn:e=>{T||console.warn(`${A.yellow}!${A.reset} ${e}`)},title:e=>{T||console.log(`${A.bold}${e}${A.reset}`)}};function ce(e){T=e}function ve(){return T}function le(){try{return require("../../package.json").version}catch{return"0.1.0"}}function Y(){return q=new Re.Command,q.name("fnmap").description("AI code indexing tool - Analyzes JS/TS code structure and generates structured code maps").version(le(),"-v, --version","Show version number").option("-f, --files <files>","Process specified files (comma-separated)",e=>e.split(",").map(s=>s.trim()).filter(Boolean)).option("-d, --dir <dir>","Process all code files in directory").option("-p, --project <dir>","Specify project root directory",process.env.CLAUDE_PROJECT_DIR??process.cwd()).option("-c, --changed","Process only git changed files (staged + modified + untracked)").option("-s, --staged","Process only git staged files (for pre-commit hook)").option("-m, --mermaid [mode]","Generate Mermaid call graph (file=file-level, project=project-level)").option("-q, --quiet","Quiet mode").option("--init","Create default config file .fnmaprc").argument("[files...]","Directly specify file paths").allowUnknownOption(!1).addHelpText("after",`
2
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const b=require("fs"),h=require("path"),Fe=require("commander"),k=require("child_process"),Ie=require("@babel/parser"),B=require("@babel/traverse"),x={FILE_NOT_FOUND:"FILE_NOT_FOUND",FILE_READ_ERROR:"FILE_READ_ERROR",PARSE_ERROR:"PARSE_ERROR",CONFIG_ERROR:"CONFIG_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",PERMISSION_ERROR:"PERMISSION_ERROR",FILE_TOO_LARGE:"FILE_TOO_LARGE"};function Y(e){return"parseError"in e}function be(e){return e.success===!0}function Ae(e){return e.success===!1}function Ce(e){return e.valid===!0}function xe(e){return e.valid===!1}const C={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",gray:"\x1B[90m",bold:"\x1B[1m"},K=10*1024*1024,X=50,z=[".js",".ts",".jsx",".tsx",".mjs"],ee=["node_modules",".git","dist","build",".next","coverage","__pycache__",".cache"],H={enable:!0,include:["**/*.js","**/*.ts","**/*.jsx","**/*.tsx","**/*.mjs"],exclude:[]};function le(e){if(!e||typeof e!="string")return{valid:!1,error:"File path is required and must be a string / 文件路径必须是字符串",errorType:x.VALIDATION_ERROR};if(!b.existsSync(e))return{valid:!1,error:`File not found: ${e} / 文件不存在: ${e}`,errorType:x.FILE_NOT_FOUND};try{const o=b.statSync(e);if(!o.isFile())return{valid:!1,error:`Path is not a file: ${e} / 路径不是文件: ${e}`,errorType:x.VALIDATION_ERROR};if(o.size>K)return{valid:!1,error:`File too large (${(o.size/1024/1024).toFixed(2)}MB > ${K/1024/1024}MB): ${e} / 文件过大`,errorType:x.FILE_TOO_LARGE}}catch(o){return{valid:!1,error:`Cannot access file: ${e}. Reason: ${o.message} / 无法访问文件`,errorType:x.PERMISSION_ERROR}}return{valid:!0}}function Q(e){if(!e||typeof e!="object")return{valid:!1,error:"Config must be an object / 配置必须是对象"};const o=e;return o.enable!==void 0&&typeof o.enable!="boolean"?{valid:!1,error:"Config.enable must be a boolean / enable 字段必须是布尔值"}:o.include!==void 0&&!Array.isArray(o.include)?{valid:!1,error:"Config.include must be an array / include 字段必须是数组"}:o.exclude!==void 0&&!Array.isArray(o.exclude)?{valid:!1,error:"Config.exclude must be an array / exclude 字段必须是数组"}:{valid:!0}}function V(e,o,s={}){const n=[o];return s.file&&n.push(`File: ${s.file}`),s.line!==void 0&&s.column!==void 0&&n.push(`Location: Line ${s.line}, Column ${s.column}`),s.suggestion&&n.push(`Suggestion: ${s.suggestion}`),n.join(`
3
+ `)}let w=!1,q=null;const $={error:e=>{w||console.error(`${C.red}✗${C.reset} ${e}`)},success:e=>{w||console.log(`${C.green}✓${C.reset} ${e}`)},info:e=>{w||console.log(e)},warn:e=>{w||console.warn(`${C.yellow}!${C.reset} ${e}`)},title:e=>{w||console.log(`${C.bold}${e}${C.reset}`)}};function fe(e){w=e}function ve(){return w}function de(){try{return require("../../package.json").version}catch{return"0.1.0"}}function ne(){return q=new Fe.Command,q.name("fnmap").description("AI code indexing tool - Analyzes JS/TS code structure and generates structured code maps").version(de(),"-v, --version","Show version number").option("-f, --files <files>","Process specified files (comma-separated)",e=>e.split(",").map(o=>o.trim()).filter(Boolean)).option("-d, --dir <dir>","Process all code files in directory").option("-p, --project <dir>","Specify project root directory",process.env.CLAUDE_PROJECT_DIR??process.cwd()).option("-c, --changed","Process only git changed files (staged + modified + untracked)").option("-s, --staged","Process only git staged files (for pre-commit hook)").option("-m, --mermaid [mode]","Generate Mermaid call graph (file=file-level, project=project-level)").option("-q, --quiet","Quiet mode").option("--init","Create default config file .fnmaprc").argument("[files...]","Directly specify file paths").allowUnknownOption(!1).addHelpText("after",`
4
4
  Configuration files (by priority):
5
5
  .fnmaprc JSON config file
6
6
  .fnmaprc.json JSON config file
@@ -19,16 +19,16 @@ Examples:
19
19
  $ fnmap --mermaid file --dir src Generate file-level call graphs
20
20
  $ fnmap --mermaid project Generate project-level call graph
21
21
  $ fnmap --init Create config file
22
- `),q}function k(){return q||Y()}const _e={get opts(){return k().opts.bind(k())},get args(){return k().args},get parse(){return k().parse.bind(k())}};function fe(e){const s=[".fnmaprc",".fnmaprc.json"];for(const n of s){const i=E.join(e,n);if(x.existsSync(i))try{const l=x.readFileSync(i,"utf-8");if(!l.trim()){$.warn(`Config file is empty: ${n}. Using default config / 配置文件为空,使用默认配置`);continue}let g;try{g=JSON.parse(l)}catch(d){const a=d,o=V(C.CONFIG_ERROR,`Failed to parse config file: ${n} / 配置文件解析失败`,{file:i,line:a.lineNumber,column:a.columnNumber,suggestion:"Check JSON syntax, ensure proper quotes and commas / 检查 JSON 语法,确保引号和逗号正确"});$.warn(o);continue}const m=Q(g);if(!m.valid){$.warn(`Invalid config in ${n}: ${m.error}`);continue}return{config:g,source:n}}catch(l){const g=l,m=V(C.FILE_READ_ERROR,`Failed to read config file: ${n} / 配置文件读取失败`,{file:i,suggestion:g.message});$.warn(m)}}const r=E.join(e,"package.json");if(x.existsSync(r))try{const n=JSON.parse(x.readFileSync(r,"utf-8"));if(n.fnmap){const i=Q(n.fnmap);if(!i.valid)$.warn(`Invalid fnmap config in package.json: ${i.error}`);else return{config:n.fnmap,source:"package.json#fnmap"}}}catch{}return{config:null,source:null}}function de(e){return e?{...H,...e,exclude:[...e.exclude??[]]}:H}function ee(e){return e.endsWith(".d.ts")||e.endsWith(".d.tsx")||e.endsWith(".d.mts")}function Ne(e){try{return P.execSync("git rev-parse --show-toplevel",{cwd:e,encoding:"utf-8"}).trim()}catch{return null}}function pe(e,s=!1){const r=[];try{const n=Ne(e);if(!n)return[];let i;if(s)i=P.execSync("git diff --cached --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"});else{const m=P.execSync("git diff --cached --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"}),d=P.execSync("git diff --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"}),a=P.execSync("git ls-files --others --exclude-standard",{cwd:e,encoding:"utf-8"});i=`${m}
22
+ `),q}function P(){return q||ne()}const _e={get opts(){return P().opts.bind(P())},get args(){return P().args},get parse(){return P().parse.bind(P())}};function ue(e){const o=[".fnmaprc",".fnmaprc.json"];for(const n of o){const i=h.join(e,n);if(b.existsSync(i))try{const l=b.readFileSync(i,"utf-8");if(!l.trim()){$.warn(`Config file is empty: ${n}. Using default config / 配置文件为空,使用默认配置`);continue}let g;try{g=JSON.parse(l)}catch(d){const m=d,r=V(x.CONFIG_ERROR,`Failed to parse config file: ${n} / 配置文件解析失败`,{file:i,line:m.lineNumber,column:m.columnNumber,suggestion:"Check JSON syntax, ensure proper quotes and commas / 检查 JSON 语法,确保引号和逗号正确"});$.warn(r);continue}const p=Q(g);if(!p.valid){$.warn(`Invalid config in ${n}: ${p.error}`);continue}return{config:g,source:n}}catch(l){const g=l,p=V(x.FILE_READ_ERROR,`Failed to read config file: ${n} / 配置文件读取失败`,{file:i,suggestion:g.message});$.warn(p)}}const s=h.join(e,"package.json");if(b.existsSync(s))try{const n=JSON.parse(b.readFileSync(s,"utf-8"));if(n.fnmap){const i=Q(n.fnmap);if(!i.valid)$.warn(`Invalid fnmap config in package.json: ${i.error}`);else return{config:n.fnmap,source:"package.json#fnmap"}}}catch{}return{config:null,source:null}}function pe(e){return e?{...H,...e,exclude:[...e.exclude??[]]}:H}function te(e){return e.endsWith(".d.ts")||e.endsWith(".d.tsx")||e.endsWith(".d.mts")}function Ne(e){try{return k.execSync("git rev-parse --show-toplevel",{cwd:e,encoding:"utf-8"}).trim()}catch{return null}}function me(e,o=!1){const s=[];try{const n=Ne(e);if(!n)return[];let i;if(o)i=k.execSync("git diff --cached --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"});else{const p=k.execSync("git diff --cached --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"}),d=k.execSync("git diff --name-only --diff-filter=ACMR",{cwd:e,encoding:"utf-8"}),m=k.execSync("git ls-files --others --exclude-standard",{cwd:e,encoding:"utf-8"});i=`${p}
23
23
  ${d}
24
- ${a}`}const l=i.split(`
25
- `).map(m=>m.trim()).filter(Boolean).filter(m=>{const d=E.extname(m);return z.includes(d)&&!ee(m)}),g=[...new Set(l)];for(const m of g){const d=E.resolve(n,m);x.existsSync(d)&&d.startsWith(E.resolve(e))&&r.push(d)}}catch{return[]}return r}function Oe(e){const s=[];if(!x.existsSync(e))return s;try{const r=x.readdirSync(e,{withFileTypes:!0});for(const n of r)if(n.isFile()){const i=E.extname(n.name);z.includes(i)&&!ee(n.name)&&s.push(E.join(e,n.name))}}catch{return s}return s}function W(e,s=e,r=Z,n=0,i=new Set){const l=[];if(!x.existsSync(e))return $.warn(`Directory does not exist: ${e} / 目录不存在`),l;if(n>X)return $.warn(`Max directory depth (${X}) exceeded: ${e} / 超过最大目录深度`),l;let g;try{g=x.realpathSync(e)}catch(d){const a=d;return $.warn(`Cannot resolve real path: ${e}. Reason: ${a.message} / 无法解析真实路径`),l}if(i.has(g))return $.warn(`Circular reference detected, skipping: ${e} / 检测到循环引用`),l;i.add(g);let m;try{m=x.readdirSync(e,{withFileTypes:!0})}catch(d){const a=d;return a.code==="EACCES"||a.code==="EPERM"?$.warn(`Permission denied: ${e} / 权限不足`):$.warn(`Failed to read directory: ${e}. Reason: ${a.message} / 读取目录失败`),l}for(const d of m)try{const a=E.join(e,d.name);if(d.isDirectory())r.includes(d.name)||l.push(...W(a,s,r,n+1,i));else if(d.isFile()){const o=E.extname(d.name);z.includes(o)&&!ee(d.name)&&l.push(E.relative(s,a))}}catch(a){const o=a;$.warn(`Error processing entry: ${d.name}. Reason: ${o.message} / 处理条目出错`)}return l}function G(e){if(!e)return"";const r=e.value.split(`
26
- `).map(n=>n.replace(/^\s*\*\s?/,"").trim());for(const n of r)if(n.startsWith("@description "))return n.slice(13).trim().slice(0,60);for(const n of r)if(n&&!n.startsWith("@")&&!n.startsWith("/"))return n.slice(0,60);return""}const J=typeof B=="function"?B:B.default;function ue(e,s){var u,R;if(e==null)return{parseError:"Code content is null or undefined / 代码内容为空",errorType:C.VALIDATION_ERROR};if(typeof e!="string")return{parseError:"Code must be a string / 代码必须是字符串类型",errorType:C.VALIDATION_ERROR};if(!e.trim())return{description:"",imports:[],functions:[],classes:[],constants:[],callGraph:{}};const r={description:"",imports:[],functions:[],classes:[],constants:[],callGraph:{}},n=e.match(/^\/\*\*[\s\S]*?\*\//);if(n){const t=n[0].split(`
27
- `).map(c=>c.replace(/^\s*\*\s?/,"").trim()).filter(c=>c&&!c.startsWith("/")&&!c.startsWith("@ai"));for(const c of t)if(c.startsWith("@description ")){r.description=c.slice(13).trim();break}if(!r.description&&t.length>0){const c=t.find(y=>!y.startsWith("@"));c&&(r.description=c)}}let i;try{const f=s&&(s.endsWith(".ts")||s.endsWith(".tsx"));i=Fe.parse(e,{sourceType:"unambiguous",plugins:["jsx","classPrivateProperties","classPrivateMethods",...f?["typescript"]:[]]})}catch(f){const t=f;return{parseError:V(C.PARSE_ERROR,`Syntax error: ${t.message} / 语法错误`,{file:s??void 0,line:(u=t.loc)==null?void 0:u.line,column:(R=t.loc)==null?void 0:R.column,suggestion:"Check syntax errors in the file / 检查文件中的语法错误"}),loc:t.loc,errorType:C.PARSE_ERROR}}const l=new Map,g=new Map,m=new Map;function d(f){var c,y,F;let t=f;for(;t;){if(t.node.type==="FunctionDeclaration"){const h=t.node;if(h.id)return h.id.name}if(t.node.type==="ClassMethod"){const h=t.node,I=(c=t.parentPath)==null?void 0:c.parentPath,S=I==null?void 0:I.node,b=((y=S==null?void 0:S.id)==null?void 0:y.name)??"",v=((F=h.key)==null?void 0:F.name)??"";return b?`${b}.${v}`:v}if(t.node.type==="ArrowFunctionExpression"||t.node.type==="FunctionExpression"){const h=t.parent;if((h==null?void 0:h.type)==="VariableDeclarator"){const S=h.id;if(S!=null&&S.name)return S.name}}t=t.parentPath}return null}J(i,{VariableDeclarator(f){var c,y,F,h;const t=f.node;if(((c=t.init)==null?void 0:c.type)==="CallExpression"&&((y=t.init.callee)==null?void 0:y.type)==="Identifier"&&t.init.callee.name==="require"&&((h=(F=t.init.arguments)==null?void 0:F[0])==null?void 0:h.type)==="StringLiteral"){const I=t.init.arguments[0].value;if(l.has(I)||l.set(I,new Set),t.id.type==="Identifier"){const S=t.id.name;l.get(I).add(S),g.set(S,I),m.set(S,new Set)}else if(t.id.type==="ObjectPattern"){for(const S of t.id.properties)if(S.type==="ObjectProperty"&&S.key.type==="Identifier"){const b=S.value.type==="Identifier"?S.value.name:S.key.name;l.get(I).add(S.key.name),g.set(b,I),m.set(b,new Set)}}}},CallExpression(f){var c,y,F,h,I,S;const t=f.node;if(((c=t.callee)==null?void 0:c.type)==="Identifier"&&t.callee.name==="require"&&((F=(y=t.arguments)==null?void 0:y[0])==null?void 0:F.type)==="StringLiteral"){const b=f.parent;if((b==null?void 0:b.type)==="MemberExpression"&&((h=b.property)==null?void 0:h.type)==="Identifier"){const v=t.arguments[0].value;l.has(v)||l.set(v,new Set),l.get(v).add(b.property.name);const N=(I=f.parentPath)==null?void 0:I.parent;if((N==null?void 0:N.type)==="VariableDeclarator"){const O=N;((S=O.id)==null?void 0:S.type)==="Identifier"&&(g.set(O.id.name,v),m.set(O.id.name,new Set))}}}},ImportDeclaration(f){const t=f.node,c=t.source.value;l.has(c)||l.set(c,new Set);for(const y of t.specifiers){let F,h;if(y.type==="ImportDefaultSpecifier")F="default",h=y.local.name;else if(y.type==="ImportNamespaceSpecifier")F="*",h=y.local.name;else if(y.type==="ImportSpecifier"){const I=y.imported;F=I.type==="Identifier"?I.name:I.value,h=y.local.name}F&&h&&(l.get(c).add(F),g.set(h,c),m.set(h,new Set))}}}),J(i,{Identifier(f){const t=f.node.name;if(m.has(t)){const c=f.parent;if((c==null?void 0:c.type)==="VariableDeclarator"&&c.id===f.node||(c==null?void 0:c.type)==="ImportSpecifier"||(c==null?void 0:c.type)==="ImportDefaultSpecifier")return;const y=d(f);y&&m.get(t).add(y)}},FunctionDeclaration(f){var b,v,N,O,L;const t=f.node,c=((b=t.id)==null?void 0:b.name)??"[anonymous]",y=t.params.map(_=>{var D,j;return _.type==="Identifier"?_.name:_.type==="AssignmentPattern"&&((D=_.left)==null?void 0:D.type)==="Identifier"?_.left.name+"?":_.type==="RestElement"&&((j=_.argument)==null?void 0:j.type)==="Identifier"?"..."+_.argument.name:"?"}),F=((N=(v=t.loc)==null?void 0:v.start)==null?void 0:N.line)??0,h=((L=(O=t.loc)==null?void 0:O.end)==null?void 0:L.line)??0;let I="";const S=t.leadingComments;S&&S.length>0&&(I=G(S[S.length-1])),r.functions.push({name:c,params:y.join(","),startLine:F,endLine:h,description:I})},ClassDeclaration(f){var v,N,O,L,_,D,j,ne,te,re;const t=f.node,c=((v=t.id)==null?void 0:v.name)??"[anonymous]",y=((O=(N=t.loc)==null?void 0:N.start)==null?void 0:O.line)??0,F=((_=(L=t.loc)==null?void 0:L.end)==null?void 0:_.line)??0,h=((D=t.superClass)==null?void 0:D.type)==="Identifier"?t.superClass.name:null;let I="";const S=t.leadingComments;S&&S.length>0&&(I=G(S[S.length-1]));const b=[];if((j=t.body)!=null&&j.body){for(const w of t.body.body)if(w.type==="ClassMethod"){const Se=((ne=w.key)==null?void 0:ne.type)==="Identifier"?w.key.name:"[computed]",Ee=w.params.map(M=>{var oe;return M.type==="Identifier"?M.name:M.type==="AssignmentPattern"&&((oe=M.left)==null?void 0:oe.type)==="Identifier"?M.left.name+"?":"?"}),Ie=((re=(te=w.loc)==null?void 0:te.start)==null?void 0:re.line)??0;let se="";const U=w.leadingComments;U&&U.length>0&&(se=G(U[U.length-1])),b.push({name:Se,params:Ee.join(","),line:Ie,static:w.static,kind:w.kind,description:se})}}r.classes.push({name:c,superClass:h,startLine:y,endLine:F,methods:b,description:I})},VariableDeclaration(f){var c,y,F;if(f.parent.type!=="Program")return;const t=f.node;if(t.kind==="const"){let h="";const I=t.leadingComments;I&&I.length>0&&(h=G(I[I.length-1]));for(const S of t.declarations){const b=((c=S.id)==null?void 0:c.type)==="Identifier"?S.id.name:void 0;if(b&&b===b.toUpperCase()&&b.length>2){const v=((F=(y=t.loc)==null?void 0:y.start)==null?void 0:F.line)??0;r.constants.push({name:b,line:v,description:h})}}}}});for(const[f,t]of l){const c=new Set;for(const y of g.keys())if(g.get(y)===f&&m.has(y))for(const F of m.get(y))c.add(F);r.imports.push({module:f,members:Array.from(t),usedIn:Array.from(c)})}const a=new Set;for(const f of r.functions)a.add(f.name);for(const f of r.classes)for(const t of f.methods)a.add(t.name),a.add(`${f.name}.${t.name}`);const o=new Set(g.keys()),p=new Map;J(i,{CallExpression(f){var y,F;const t=f.node;let c=null;if(t.callee.type==="Identifier")c=t.callee.name;else if(t.callee.type==="MemberExpression"&&((y=t.callee.property)==null?void 0:y.type)==="Identifier"){const h=((F=t.callee.object)==null?void 0:F.type)==="Identifier"?t.callee.object.name:void 0,I=t.callee.property.name;h&&o.has(h)?c=`${h}.${I}`:c=I}if(c){const h=d(f);if(h&&h!==c){const I=a.has(c),S=o.has(c)||c.includes(".")&&o.has(c.split(".")[0]);(I||S)&&(p.has(h)||p.set(h,new Set),p.get(h).add(c))}}}}),r.callGraph={};for(const[f,t]of p)r.callGraph[f]=Array.from(t);return r.isPureType=we(i),r}function we(e){let s=!1;for(const r of e.program.body){switch(r.type){case"TSTypeAliasDeclaration":case"TSInterfaceDeclaration":case"TSEnumDeclaration":break;case"ImportDeclaration":r.importKind!=="type"&&r.specifiers.some(i=>i.type==="ImportSpecifier"?i.importKind!=="type":!0)&&r.specifiers.length>0&&(s=!0);break;case"ExportNamedDeclaration":if(r.exportKind==="type")break;if(r.declaration){const n=r.declaration.type;n!=="TSTypeAliasDeclaration"&&n!=="TSInterfaceDeclaration"&&(s=!0)}else r.specifiers&&r.specifiers.length>0&&r.specifiers.some(i=>i.type==="ExportSpecifier"?i.exportKind!=="type":!0)&&(s=!0);break;case"ExportDefaultDeclaration":s=!0;break;case"ExportAllDeclaration":r.exportKind!=="type"&&(s=!0);break;default:s=!0;break}if(s)break}return!s}function Te(e,s){const r=[];let n=`/*@AI ${s}`;e.description&&(n+=` - ${e.description.slice(0,50)}`),r.push(n);for(const i of e.imports){const l=Array.isArray(i.members)?i.members.join(","):"";let g=`<${i.module}:${l}`;Array.isArray(i.usedIn)&&i.usedIn.length>0&&(g+=` ->${i.usedIn.join(",")}`),r.push(g)}for(const i of e.classes){let l=i.name;i.superClass&&(l+=`:${i.superClass}`),l+=` ${i.startLine}-${i.endLine}`,i.description&&(l+=` ${i.description}`),r.push(l);for(const g of i.methods){const m=g.static?" +":" .",d=g.kind==="get"?"get:":g.kind==="set"?"set:":"";let a=`${m}${d}${g.name}(${g.params}) ${g.line}`;g.description&&(a+=` ${g.description}`),r.push(a)}}for(const i of e.functions){let l=`${i.name}(${i.params}) ${i.startLine}-${i.endLine}`;i.description&&(l+=` ${i.description}`),r.push(l)}for(const i of e.constants){let l=`${i.name} ${i.line}`;i.description&&(l+=` ${i.description}`),r.push(l)}return r.push("@AI*/"),r.join(`
28
- `)}function Le(e){let s=e;return s=s.replace(/\/\*@AI[\s\S]*?@AI\*\/\s*/g,""),s=s.replace(/\/\*\*[\s\S]*?@ai-context-end[\s\S]*?\*\/\s*/g,""),s=s.replace(/^\/\*\*[\s\S]*?\*\/\s*\n?/,""),s}function me(e,s){var n,i,l;const r=[`@FNMAP ${E.basename(e)}/`];for(const{relativePath:g,info:m}of s){let a=`#${E.basename(g)}`;m.description&&(a+=` ${m.description.slice(0,50)}`),r.push(a);for(const o of m.imports){const p=Array.isArray(o.members)?o.members.join(","):"";r.push(` <${o.module}:${p}`)}for(const o of m.classes){let p=` ${o.name}`;o.superClass&&(p+=`:${o.superClass}`),p+=` ${o.startLine}-${o.endLine}`,o.description&&(p+=` ${o.description}`),r.push(p);for(const u of o.methods){const R=u.static?" +":" .",f=u.kind==="get"?"get:":u.kind==="set"?"set:":"";let t=`${R}${f}${u.name}(${u.params}) ${u.line}`;u.description&&(t+=` ${u.description}`);const c=`${o.name}.${u.name}`,y=((n=m.callGraph)==null?void 0:n[c])??((i=m.callGraph)==null?void 0:i[u.name]);Array.isArray(y)&&y.length>0&&(t+=` →${y.join(",")}`),r.push(t)}}for(const o of m.functions){let p=` ${o.name}(${o.params}) ${o.startLine}-${o.endLine}`;o.description&&(p+=` ${o.description}`);const u=(l=m.callGraph)==null?void 0:l[o.name];Array.isArray(u)&&u.length>0&&(p+=` →${u.join(",")}`),r.push(p)}for(const o of m.constants){let p=` ${o.name} ${o.line}`;o.description&&(p+=` ${o.description}`),r.push(p)}}return r.push("@FNMAP"),r.join(`
29
- `)}function ge(e,s){const r=["flowchart TD"],n=o=>"id_"+o.replace(/[^a-zA-Z0-9]/g,p=>`_${p.charCodeAt(0)}_`),i=o=>o.replace(/"/g,"#quot;"),l=s.functions.map(o=>o.name),g=[];for(const o of s.classes)for(const p of o.methods)g.push(`${o.name}.${p.name}`);const m=[...l,...g];if(m.length===0)return null;const d=E.basename(e,E.extname(e));r.push(` subgraph ${n(d)}["${d}"]`);for(const o of m)r.push(` ${n(o)}["${i(o)}"]`);r.push(" end");const a=s.callGraph??{};for(const[o,p]of Object.entries(a))if(Array.isArray(p)){for(const u of p)if(m.includes(u)||u.includes(".")){const R=m.includes(u)?u:u.split(".").pop();(m.includes(u)||m.some(f=>f.endsWith(R)))&&r.push(` ${n(o)} --> ${n(u)}`)}}return r.join(`
30
- `)}function ye(e,s){const r=["flowchart TD"],n=d=>"id_"+d.replace(/[^a-zA-Z0-9]/g,a=>`_${a.charCodeAt(0)}_`),i=d=>d.replace(/"/g,"#quot;"),l=new Map,g=[];for(const{relativePath:d,info:a}of s){const o=E.basename(d,E.extname(d)),p=a.functions.map(t=>t.name),u=[];for(const t of a.classes)for(const c of t.methods)u.push(`${t.name}.${c.name}`);const R=[...p,...u];l.set(d,{fileName:o,functions:R});const f=a.callGraph??{};for(const[t,c]of Object.entries(f))if(Array.isArray(c))for(const y of c)g.push({file:d,fileName:o,caller:t,callee:y})}for(const[,{fileName:d,functions:a}]of l)if(a.length!==0){r.push(` subgraph ${n(d)}["${i(d)}"]`);for(const o of a)r.push(` ${n(d)}_${n(o)}["${i(o)}"]`);r.push(" end")}const m=new Set;for(const{fileName:d,caller:a,callee:o}of g){const p=`${n(d)}_${n(a)}`;let u=null;for(const[,{fileName:R,functions:f}]of l)if(f.includes(o)){u=`${n(R)}_${n(o)}`;break}if(!u){const R=[...l.keys()].find(f=>{var t;return((t=l.get(f))==null?void 0:t.fileName)===d});if(R){const f=l.get(R);f!=null&&f.functions.includes(o)&&(u=`${n(d)}_${n(o)}`)}}if(u){const R=`${p}-->${u}`;m.has(R)||(r.push(` ${p} --> ${u}`),m.add(R))}}return r.join(`
31
- `)}function he(e){const s=ae(e);if(!s.valid)return{success:!1,error:s.error,errorType:s.errorType??C.VALIDATION_ERROR};try{const r=x.readFileSync(e,"utf-8"),n=ue(r,e);return n?ie(n)?{success:!1,error:n.parseError,errorType:n.errorType,loc:n.loc}:{success:!0,info:n}:{success:!1,error:"Analysis returned null / 分析返回空值",errorType:C.PARSE_ERROR}}catch(r){const n=r;return{success:!1,error:V(C.FILE_READ_ERROR,"Failed to read or process file / 读取或处理文件失败",{file:e,suggestion:n.message}),errorType:C.FILE_READ_ERROR}}}const De=`
24
+ ${m}`}const l=i.split(`
25
+ `).map(p=>p.trim()).filter(Boolean).filter(p=>{const d=h.extname(p);return z.includes(d)&&!te(p)}),g=[...new Set(l)];for(const p of g){const d=h.resolve(n,p);b.existsSync(d)&&d.startsWith(h.resolve(e))&&s.push(d)}}catch{return[]}return s}function Oe(e){const o=[];if(!b.existsSync(e))return o;try{const s=b.readdirSync(e,{withFileTypes:!0});for(const n of s)if(n.isFile()){const i=h.extname(n.name);z.includes(i)&&!te(n.name)&&o.push(h.join(e,n.name))}}catch{return o}return o}function W(e,o=e,s=ee,n=0,i=new Set){const l=[];if(!b.existsSync(e))return $.warn(`Directory does not exist: ${e} / 目录不存在`),l;if(n>X)return $.warn(`Max directory depth (${X}) exceeded: ${e} / 超过最大目录深度`),l;let g;try{g=b.realpathSync(e)}catch(d){const m=d;return $.warn(`Cannot resolve real path: ${e}. Reason: ${m.message} / 无法解析真实路径`),l}if(i.has(g))return $.warn(`Circular reference detected, skipping: ${e} / 检测到循环引用`),l;i.add(g);let p;try{p=b.readdirSync(e,{withFileTypes:!0})}catch(d){const m=d;return m.code==="EACCES"||m.code==="EPERM"?$.warn(`Permission denied: ${e} / 权限不足`):$.warn(`Failed to read directory: ${e}. Reason: ${m.message} / 读取目录失败`),l}for(const d of p)try{const m=h.join(e,d.name);if(d.isDirectory())s.includes(d.name)||l.push(...W(m,o,s,n+1,i));else if(d.isFile()){const r=h.extname(d.name);z.includes(r)&&!te(d.name)&&l.push(h.relative(o,m))}}catch(m){const r=m;$.warn(`Error processing entry: ${d.name}. Reason: ${r.message} / 处理条目出错`)}return l}function G(e){if(!e)return"";const s=e.value.split(`
26
+ `).map(n=>n.replace(/^\s*\*\s?/,"").trim());for(const n of s)if(n.startsWith("@description "))return n.slice(13).trim().slice(0,60);for(const n of s)if(n&&!n.startsWith("@")&&!n.startsWith("/"))return n.slice(0,60);return""}const J=typeof B=="function"?B:B.default;function re(e,o){var f,S;if(e==null)return{parseError:"Code content is null or undefined / 代码内容为空",errorType:x.VALIDATION_ERROR};if(typeof e!="string")return{parseError:"Code must be a string / 代码必须是字符串类型",errorType:x.VALIDATION_ERROR};if(!e.trim())return{description:"",imports:[],functions:[],classes:[],constants:[],callGraph:{}};const s={description:"",imports:[],functions:[],classes:[],constants:[],callGraph:{}},n=e.match(/^\/\*\*[\s\S]*?\*\//);if(n){const t=n[0].split(`
27
+ `).map(a=>a.replace(/^\s*\*\s?/,"").trim()).filter(a=>a&&!a.startsWith("/")&&!a.startsWith("@ai"));for(const a of t)if(a.startsWith("@description ")){s.description=a.slice(13).trim();break}if(!s.description&&t.length>0){const a=t.find(y=>!y.startsWith("@"));a&&(s.description=a)}}let i;try{const c=o&&(o.endsWith(".ts")||o.endsWith(".tsx"));i=Ie.parse(e,{sourceType:"unambiguous",plugins:["jsx","classPrivateProperties","classPrivateMethods",...c?["typescript"]:[]]})}catch(c){const t=c;return{parseError:V(x.PARSE_ERROR,`Syntax error: ${t.message} / 语法错误`,{file:o??void 0,line:(f=t.loc)==null?void 0:f.line,column:(S=t.loc)==null?void 0:S.column,suggestion:"Check syntax errors in the file / 检查文件中的语法错误"}),loc:t.loc,errorType:x.PARSE_ERROR}}const l=new Map,g=new Map,p=new Map;function d(c){var a,y,I;let t=c;for(;t;){if(t.node.type==="FunctionDeclaration"){const E=t.node;if(E.id)return E.id.name}if(t.node.type==="ClassMethod"){const E=t.node,F=(a=t.parentPath)==null?void 0:a.parentPath,R=F==null?void 0:F.node,A=((y=R==null?void 0:R.id)==null?void 0:y.name)??"",v=((I=E.key)==null?void 0:I.name)??"";return A?`${A}.${v}`:v}if(t.node.type==="ArrowFunctionExpression"||t.node.type==="FunctionExpression"){const E=t.parent;if((E==null?void 0:E.type)==="VariableDeclarator"){const R=E.id;if(R!=null&&R.name)return R.name}}t=t.parentPath}return null}J(i,{VariableDeclarator(c){var a,y,I,E;const t=c.node;if(((a=t.init)==null?void 0:a.type)==="CallExpression"&&((y=t.init.callee)==null?void 0:y.type)==="Identifier"&&t.init.callee.name==="require"&&((E=(I=t.init.arguments)==null?void 0:I[0])==null?void 0:E.type)==="StringLiteral"){const F=t.init.arguments[0].value;if(l.has(F)||l.set(F,new Set),t.id.type==="Identifier"){const R=t.id.name;l.get(F).add(R),g.set(R,F),p.set(R,new Set)}else if(t.id.type==="ObjectPattern"){for(const R of t.id.properties)if(R.type==="ObjectProperty"&&R.key.type==="Identifier"){const A=R.value.type==="Identifier"?R.value.name:R.key.name;l.get(F).add(R.key.name),g.set(A,F),p.set(A,new Set)}}}},CallExpression(c){var a,y,I,E,F,R;const t=c.node;if(((a=t.callee)==null?void 0:a.type)==="Identifier"&&t.callee.name==="require"&&((I=(y=t.arguments)==null?void 0:y[0])==null?void 0:I.type)==="StringLiteral"){const A=c.parent;if((A==null?void 0:A.type)==="MemberExpression"&&((E=A.property)==null?void 0:E.type)==="Identifier"){const v=t.arguments[0].value;l.has(v)||l.set(v,new Set),l.get(v).add(A.property.name);const N=(F=c.parentPath)==null?void 0:F.parent;if((N==null?void 0:N.type)==="VariableDeclarator"){const O=N;((R=O.id)==null?void 0:R.type)==="Identifier"&&(g.set(O.id.name,v),p.set(O.id.name,new Set))}}}},ImportDeclaration(c){const t=c.node,a=t.source.value;l.has(a)||l.set(a,new Set);for(const y of t.specifiers){let I,E;if(y.type==="ImportDefaultSpecifier")I="default",E=y.local.name;else if(y.type==="ImportNamespaceSpecifier")I="*",E=y.local.name;else if(y.type==="ImportSpecifier"){const F=y.imported;I=F.type==="Identifier"?F.name:F.value,E=y.local.name}I&&E&&(l.get(a).add(I),g.set(E,a),p.set(E,new Set))}}}),J(i,{Identifier(c){const t=c.node.name;if(p.has(t)){const a=c.parent;if((a==null?void 0:a.type)==="VariableDeclarator"&&a.id===c.node||(a==null?void 0:a.type)==="ImportSpecifier"||(a==null?void 0:a.type)==="ImportDefaultSpecifier")return;const y=d(c);y&&p.get(t).add(y)}},FunctionDeclaration(c){var A,v,N,O,L;const t=c.node,a=((A=t.id)==null?void 0:A.name)??"[anonymous]",y=t.params.map(_=>{var D,j;return _.type==="Identifier"?_.name:_.type==="AssignmentPattern"&&((D=_.left)==null?void 0:D.type)==="Identifier"?_.left.name+"?":_.type==="RestElement"&&((j=_.argument)==null?void 0:j.type)==="Identifier"?"..."+_.argument.name:"?"}),I=((N=(v=t.loc)==null?void 0:v.start)==null?void 0:N.line)??0,E=((L=(O=t.loc)==null?void 0:O.end)==null?void 0:L.line)??0;let F="";const R=t.leadingComments;R&&R.length>0&&(F=G(R[R.length-1])),s.functions.push({name:a,params:y.join(","),startLine:I,endLine:E,description:F})},ClassDeclaration(c){var v,N,O,L,_,D,j,se,oe,ie;const t=c.node,a=((v=t.id)==null?void 0:v.name)??"[anonymous]",y=((O=(N=t.loc)==null?void 0:N.start)==null?void 0:O.line)??0,I=((_=(L=t.loc)==null?void 0:L.end)==null?void 0:_.line)??0,E=((D=t.superClass)==null?void 0:D.type)==="Identifier"?t.superClass.name:null;let F="";const R=t.leadingComments;R&&R.length>0&&(F=G(R[R.length-1]));const A=[];if((j=t.body)!=null&&j.body){for(const T of t.body.body)if(T.type==="ClassMethod"){const Ee=((se=T.key)==null?void 0:se.type)==="Identifier"?T.key.name:"[computed]",Se=T.params.map(M=>{var ce;return M.type==="Identifier"?M.name:M.type==="AssignmentPattern"&&((ce=M.left)==null?void 0:ce.type)==="Identifier"?M.left.name+"?":"?"}),Re=((ie=(oe=T.loc)==null?void 0:oe.start)==null?void 0:ie.line)??0;let ae="";const U=T.leadingComments;U&&U.length>0&&(ae=G(U[U.length-1])),A.push({name:Ee,params:Se.join(","),line:Re,static:T.static,kind:T.kind,description:ae})}}s.classes.push({name:a,superClass:E,startLine:y,endLine:I,methods:A,description:F})},VariableDeclaration(c){var a,y,I;if(c.parent.type!=="Program")return;const t=c.node;if(t.kind==="const"){let E="";const F=t.leadingComments;F&&F.length>0&&(E=G(F[F.length-1]));for(const R of t.declarations){const A=((a=R.id)==null?void 0:a.type)==="Identifier"?R.id.name:void 0;if(A&&A===A.toUpperCase()&&A.length>2){const v=((I=(y=t.loc)==null?void 0:y.start)==null?void 0:I.line)??0;s.constants.push({name:A,line:v,description:E})}}}}});for(const[c,t]of l){const a=new Set;for(const y of g.keys())if(g.get(y)===c&&p.has(y))for(const I of p.get(y))a.add(I);s.imports.push({module:c,members:Array.from(t),usedIn:Array.from(a)})}const m=new Set;for(const c of s.functions)m.add(c.name);for(const c of s.classes)for(const t of c.methods)m.add(t.name),m.add(`${c.name}.${t.name}`);const r=new Set(g.keys()),u=new Map;J(i,{CallExpression(c){var y,I;const t=c.node;let a=null;if(t.callee.type==="Identifier")a=t.callee.name;else if(t.callee.type==="MemberExpression"&&((y=t.callee.property)==null?void 0:y.type)==="Identifier"){const E=((I=t.callee.object)==null?void 0:I.type)==="Identifier"?t.callee.object.name:void 0,F=t.callee.property.name;E&&r.has(E)?a=`${E}.${F}`:a=F}if(a){const E=d(c);if(E&&E!==a){const F=m.has(a),R=r.has(a)||a.includes(".")&&r.has(a.split(".")[0]);(F||R)&&(u.has(E)||u.set(E,new Set),u.get(E).add(a))}}}}),s.callGraph={};for(const[c,t]of u)s.callGraph[c]=Array.from(t);return s.isPureType=Te(i),s}function Te(e){let o=!1;for(const s of e.program.body){switch(s.type){case"TSTypeAliasDeclaration":case"TSInterfaceDeclaration":case"TSEnumDeclaration":break;case"ImportDeclaration":s.importKind!=="type"&&s.specifiers.some(i=>i.type==="ImportSpecifier"?i.importKind!=="type":!0)&&s.specifiers.length>0&&(o=!0);break;case"ExportNamedDeclaration":if(s.exportKind==="type")break;if(s.declaration){const n=s.declaration.type;n!=="TSTypeAliasDeclaration"&&n!=="TSInterfaceDeclaration"&&(o=!0)}else s.specifiers&&s.specifiers.length>0&&s.specifiers.some(i=>i.type==="ExportSpecifier"?i.exportKind!=="type":!0)&&(o=!0);break;case"ExportDefaultDeclaration":o=!0;break;case"ExportAllDeclaration":s.exportKind!=="type"&&(o=!0);break;default:o=!0;break}if(o)break}return!o}function we(e,o){const s=[];let n=`/*@AI ${o}`;e.description&&(n+=` - ${e.description.slice(0,50)}`),s.push(n);for(const i of e.imports){const l=Array.isArray(i.members)?i.members.join(","):"";let g=`<${i.module}:${l}`;Array.isArray(i.usedIn)&&i.usedIn.length>0&&(g+=` ->${i.usedIn.join(",")}`),s.push(g)}for(const i of e.classes){let l=i.name;i.superClass&&(l+=`:${i.superClass}`),l+=` ${i.startLine}-${i.endLine}`,i.description&&(l+=` ${i.description}`),s.push(l);for(const g of i.methods){const p=g.static?" +":" .",d=g.kind==="get"?"get:":g.kind==="set"?"set:":"";let m=`${p}${d}${g.name}(${g.params}) ${g.line}`;g.description&&(m+=` ${g.description}`),s.push(m)}}for(const i of e.functions){let l=`${i.name}(${i.params}) ${i.startLine}-${i.endLine}`;i.description&&(l+=` ${i.description}`),s.push(l)}for(const i of e.constants){let l=`${i.name} ${i.line}`;i.description&&(l+=` ${i.description}`),s.push(l)}return s.push("@AI*/"),s.join(`
28
+ `)}function Le(e){let o=e;return o=o.replace(/\/\*@AI[\s\S]*?@AI\*\/\s*/g,""),o=o.replace(/\/\*\*[\s\S]*?@ai-context-end[\s\S]*?\*\/\s*/g,""),o=o.replace(/^\/\*\*[\s\S]*?\*\/\s*\n?/,""),o}function Z(e,o){var n,i,l;const s=[`@FNMAP ${h.basename(e)}/`];for(const{relativePath:g,info:p}of o){let m=`#${h.basename(g)}`;p.description&&(m+=` ${p.description.slice(0,50)}`),s.push(m);for(const r of p.imports){const u=Array.isArray(r.members)?r.members.join(","):"";s.push(` <${r.module}:${u}`)}for(const r of p.classes){let u=` ${r.name}`;r.superClass&&(u+=`:${r.superClass}`),u+=` ${r.startLine}-${r.endLine}`,r.description&&(u+=` ${r.description}`),s.push(u);for(const f of r.methods){const S=f.static?" +":" .",c=f.kind==="get"?"get:":f.kind==="set"?"set:":"";let t=`${S}${c}${f.name}(${f.params}) ${f.line}`;f.description&&(t+=` ${f.description}`);const a=`${r.name}.${f.name}`,y=((n=p.callGraph)==null?void 0:n[a])??((i=p.callGraph)==null?void 0:i[f.name]);Array.isArray(y)&&y.length>0&&(t+=` →${y.join(",")}`),s.push(t)}}for(const r of p.functions){let u=` ${r.name}(${r.params}) ${r.startLine}-${r.endLine}`;r.description&&(u+=` ${r.description}`);const f=(l=p.callGraph)==null?void 0:l[r.name];Array.isArray(f)&&f.length>0&&(u+=` →${f.join(",")}`),s.push(u)}for(const r of p.constants){let u=` ${r.name} ${r.line}`;r.description&&(u+=` ${r.description}`),s.push(u)}}return s.push("@FNMAP"),s.join(`
29
+ `)}function ge(e,o){const s=["flowchart TD"],n=r=>"id_"+r.replace(/[^a-zA-Z0-9]/g,u=>`_${u.charCodeAt(0)}_`),i=r=>r.replace(/"/g,"#quot;"),l=o.functions.map(r=>r.name),g=[];for(const r of o.classes)for(const u of r.methods)g.push(`${r.name}.${u.name}`);const p=[...l,...g];if(p.length===0)return null;const d=h.basename(e,h.extname(e));s.push(` subgraph ${n(d)}["${d}"]`);for(const r of p)s.push(` ${n(r)}["${i(r)}"]`);s.push(" end");const m=o.callGraph??{};for(const[r,u]of Object.entries(m))if(Array.isArray(u)){for(const f of u)if(p.includes(f)||f.includes(".")){const S=p.includes(f)?f:f.split(".").pop();(p.includes(f)||p.some(c=>c.endsWith(S)))&&s.push(` ${n(r)} --> ${n(f)}`)}}return s.join(`
30
+ `)}function ye(e,o){const s=["flowchart TD"],n=d=>"id_"+d.replace(/[^a-zA-Z0-9]/g,m=>`_${m.charCodeAt(0)}_`),i=d=>d.replace(/"/g,"#quot;"),l=new Map,g=[];for(const{relativePath:d,info:m}of o){const r=h.basename(d,h.extname(d)),u=m.functions.map(t=>t.name),f=[];for(const t of m.classes)for(const a of t.methods)f.push(`${t.name}.${a.name}`);const S=[...u,...f];l.set(d,{fileName:r,functions:S});const c=m.callGraph??{};for(const[t,a]of Object.entries(c))if(Array.isArray(a))for(const y of a)g.push({file:d,fileName:r,caller:t,callee:y})}for(const[,{fileName:d,functions:m}]of l)if(m.length!==0){s.push(` subgraph ${n(d)}["${i(d)}"]`);for(const r of m)s.push(` ${n(d)}_${n(r)}["${i(r)}"]`);s.push(" end")}const p=new Set;for(const{fileName:d,caller:m,callee:r}of g){const u=`${n(d)}_${n(m)}`;let f=null;for(const[,{fileName:S,functions:c}]of l)if(c.includes(r)){f=`${n(S)}_${n(r)}`;break}if(!f){const S=[...l.keys()].find(c=>{var t;return((t=l.get(c))==null?void 0:t.fileName)===d});if(S){const c=l.get(S);c!=null&&c.functions.includes(r)&&(f=`${n(d)}_${n(r)}`)}}if(f){const S=`${u}-->${f}`;p.has(S)||(s.push(` ${u} --> ${f}`),p.add(S))}}return s.join(`
31
+ `)}function De(e,o){const s=(o==null?void 0:o.filePath)??null;try{const n=re(e,s);return n?Y(n)?{success:!1,error:n.parseError,errorType:n.errorType,loc:n.loc}:{success:!0,info:n}:{success:!1,error:"Analysis returned null / 分析返回空值",errorType:x.PARSE_ERROR}}catch(n){return{success:!1,error:`Failed to process code / 处理代码失败: ${n.message}`,errorType:x.PARSE_ERROR}}}function he(e){const o=le(e);if(!o.valid)return{success:!1,error:o.error,errorType:o.errorType??x.VALIDATION_ERROR};try{const s=b.readFileSync(e,"utf-8"),n=re(s,e);return n?Y(n)?{success:!1,error:n.parseError,errorType:n.errorType,loc:n.loc}:{success:!0,info:n}:{success:!1,error:"Analysis returned null / 分析返回空值",errorType:x.PARSE_ERROR}}catch(s){const n=s;return{success:!1,error:V(x.FILE_READ_ERROR,"Failed to read or process file / 读取或处理文件失败",{file:e,suggestion:n.message}),errorType:x.FILE_READ_ERROR}}}const je=`
32
32
 
33
33
  ## .fnmap Code Index Format
34
34
 
@@ -53,8 +53,8 @@ The \`.fnmap\` file provides a structured code index for quick navigation. Read
53
53
  1. Every global variable, function, class, and file module must have a **concise comment describing its purpose or functionality** - avoid describing anything else
54
54
  2. When updating code, always update related comments to reflect the changes
55
55
  3. Prefer encapsulating logic in functions rather than writing flat, sequential code
56
- `;function je(e){const s=["CLAUDE.md","AGENTS.md"],r=x.readdirSync(e);for(const n of s){const i=r.find(l=>l.toLowerCase()===n.toLowerCase());if(i){const l=E.join(e,i);if(x.readFileSync(l,"utf-8").includes(".fnmap Code Index Format")){console.log(`${A.yellow}!${A.reset} ${i} already contains fnmap documentation`);continue}x.appendFileSync(l,De),console.log(`${A.green}✓${A.reset} Appended fnmap documentation to ${i}`)}}}function $e(){const e=Y();e.parse(process.argv);const s=e.opts(),r=e.args;s.quiet&&ce(!0);const n=E.resolve(s.project);if(s.init){const a=E.join(n,".fnmaprc");if(x.existsSync(a))console.log(`${A.yellow}!${A.reset} Config file already exists: .fnmaprc`);else{const o={enable:!0,include:["src/**/*.js","src/**/*.ts","src/**/*.jsx","src/**/*.tsx"],exclude:["node_modules","dist","build",".next","coverage","__pycache__",".cache"]};x.writeFileSync(a,JSON.stringify(o,null,2)),console.log(`${A.green}✓${A.reset} Created config file: .fnmaprc`)}je(n);return}const i=[...s.files??[],...r].filter(a=>x.existsSync(a));let l=[];if(s.changed||s.staged){const a=pe(n,s.staged);if(a.length===0){$.info("No git changed code files detected");return}const o=new Set;for(const p of a)o.add(E.dirname(p));for(const p of o){const u=Oe(p);l.push(...u)}}else if(i.length>0)l=i.map(a=>E.isAbsolute(a)?a:E.resolve(n,a));else if(s.dir){const a=E.resolve(n,s.dir);l=W(a,n).map(p=>E.join(n,p))}else{const{config:a,source:o}=fe(n);if(a){if($.info(`Using config: ${o}`),a.enable===!1){$.info("Config file has enable set to false, skipping processing");return}const p=de(a),u=[...Z,...p.exclude];if(p.include)for(const R of p.include){const f=R.replace(/\/\*\*\/.*$/,"").replace(/\*\*\/.*$/,""),t=f?E.resolve(n,f):n;if(x.existsSync(t)){const c=W(t,n,u);l.push(...c.map(y=>E.join(n,y)))}}}else{$.warn("No config file found. Use fnmap init to create config, or use --dir/--files to specify scope"),$.info(""),$.info("Supported config files: .fnmaprc, .fnmaprc.json, package.json#fnmap");return}}if(l.length===0){$.info("No files found to process");return}l=[...new Set(l)],$.info("=".repeat(50)),$.title("fnmap - AI Code Indexing Tool"),$.info("=".repeat(50));let g=0,m=0;const d=new Map;for(const a of l){const o=E.relative(n,a);$.info(`
57
- Analyzing: ${o}`);const p=he(a);if(p.success){g++;const u=p.info;if(u.isPureType){$.info("Skipped (pure type file) / 跳过纯类型文件");continue}$.success(`Imports: ${u.imports.length}, Functions: ${u.functions.length}, Classes: ${u.classes.length}, Constants: ${u.constants.length}`);const R=E.dirname(a);d.has(R)||d.set(R,[]),d.get(R).push({relativePath:o,info:u})}else m++,$.error(p.error)}if(d.size>0){$.info(`
58
- Generating .fnmap index...`);for(const[a,o]of d)try{const p=me(a,o),u=E.join(a,".fnmap");x.writeFileSync(u,p),$.success(E.relative(n,u))}catch(p){const u=p;$.error(`Failed to generate .fnmap for ${E.relative(n,a)}: ${u.message}`)}}if(s.mermaid&&d.size>0){if($.info(`
59
- Generating Mermaid call graphs...`),s.mermaid==="file"||s.mermaid===!0)for(const[a,o]of d)for(const{relativePath:p,info:u}of o)try{const R=ge(p,u);if(R){const f=E.basename(p,E.extname(p)),t=E.join(a,`${f}.mermaid`);x.writeFileSync(t,R),$.success(E.relative(n,t))}}catch(R){const f=R;$.error(`Failed to generate mermaid for ${p}: ${f.message}`)}else if(s.mermaid==="project")try{const a=[];for(const[,u]of d)a.push(...u);const o=ye(n,a),p=E.join(n,".fnmap.mermaid");x.writeFileSync(p,o),$.success(E.relative(n,p))}catch(a){const o=a;$.error(`Failed to generate project mermaid: ${o.message}`)}}$.info(`
60
- `+"=".repeat(50)),$.info(`Complete! Analyzed: ${A.green}${g}${A.reset}, Failed: ${m>0?A.red:""}${m}${A.reset}`),$.info("=".repeat(50))}require.main===module&&$e();exports.COLORS=A;exports.DEFAULT_CONFIG=H;exports.DEFAULT_EXCLUDES=Z;exports.ErrorTypes=C;exports.MAX_DIR_DEPTH=X;exports.MAX_FILE_SIZE=K;exports.SUPPORTED_EXTENSIONS=z;exports.analyzeFile=ue;exports.extractJSDocDescription=G;exports.formatError=V;exports.generateAiMap=me;exports.generateFileMermaid=ge;exports.generateHeader=Te;exports.generateProjectMermaid=ye;exports.getGitChangedFiles=pe;exports.getVersion=le;exports.isParseError=ie;exports.isProcessFailure=xe;exports.isProcessSuccess=be;exports.isQuietMode=ve;exports.isValidationFailure=Ce;exports.isValidationSuccess=Ae;exports.loadConfig=fe;exports.logger=$;exports.main=$e;exports.mergeConfig=de;exports.processFile=he;exports.program=_e;exports.removeExistingHeaders=Le;exports.scanDirectory=W;exports.setQuietMode=ce;exports.setupCLI=Y;exports.validateConfig=Q;exports.validateFilePath=ae;
56
+ `;function Me(e){const o=["CLAUDE.md","AGENTS.md"],s=b.readdirSync(e);for(const n of o){const i=s.find(l=>l.toLowerCase()===n.toLowerCase());if(i){const l=h.join(e,i);if(b.readFileSync(l,"utf-8").includes(".fnmap Code Index Format")){console.log(`${C.yellow}!${C.reset} ${i} already contains fnmap documentation`);continue}b.appendFileSync(l,je),console.log(`${C.green}✓${C.reset} Appended fnmap documentation to ${i}`)}}}function $e(){const e=ne();e.parse(process.argv);const o=e.opts(),s=e.args;o.quiet&&fe(!0);const n=h.resolve(o.project);if(o.init){const r=h.join(n,".fnmaprc");if(b.existsSync(r))console.log(`${C.yellow}!${C.reset} Config file already exists: .fnmaprc`);else{const u={enable:!0,include:["src/**/*.js","src/**/*.ts","src/**/*.jsx","src/**/*.tsx"],exclude:["node_modules","dist","build",".next","coverage","__pycache__",".cache"]};b.writeFileSync(r,JSON.stringify(u,null,2)),console.log(`${C.green}✓${C.reset} Created config file: .fnmaprc`)}Me(n);return}const i=[...o.files??[],...s].filter(r=>b.existsSync(r));let l=[],g=!1;if(o.changed||o.staged){const r=me(n,o.staged);if(r.length===0){$.info("No git changed code files detected");return}const u=new Set;for(const f of r)u.add(h.dirname(f));for(const f of u){const S=Oe(f);l.push(...S)}}else if(i.length>0)g=!0,l=i.map(r=>h.isAbsolute(r)?r:h.resolve(n,r));else if(o.dir){const r=h.resolve(n,o.dir);l=W(r,n).map(f=>h.join(n,f))}else{const{config:r,source:u}=ue(n);if(r){if($.info(`Using config: ${u}`),r.enable===!1){$.info("Config file has enable set to false, skipping processing");return}const f=pe(r),S=[...ee,...f.exclude];if(f.include)for(const c of f.include){const t=c.replace(/\/\*\*\/.*$/,"").replace(/\*\*\/.*$/,""),a=t?h.resolve(n,t):n;if(b.existsSync(a)){const y=W(a,n,S);l.push(...y.map(I=>h.join(n,I)))}}}else{$.warn("No config file found. Use fnmap init to create config, or use --dir/--files to specify scope"),$.info(""),$.info("Supported config files: .fnmaprc, .fnmaprc.json, package.json#fnmap");return}}if(l.length===0){$.info("No files found to process");return}l=[...new Set(l)],$.info("=".repeat(50)),$.title("fnmap - AI Code Indexing Tool"),$.info("=".repeat(50));let p=0,d=0;const m=new Map;for(const r of l){const u=h.relative(n,r);$.info(`
57
+ Analyzing: ${u}`);const f=he(r);if(f.success){p++;const S=f.info;if(S.isPureType){$.info("Skipped (pure type file) / 跳过纯类型文件");continue}$.success(`Imports: ${S.imports.length}, Functions: ${S.functions.length}, Classes: ${S.classes.length}, Constants: ${S.constants.length}`);const c=h.dirname(r);m.has(c)||m.set(c,[]),m.get(c).push({relativePath:u,info:S})}else d++,$.error(f.error)}if(m.size>0)if($.info(`
58
+ Generating .fnmap index...`),g)for(const[r,u]of m)for(const{relativePath:f,info:S}of u)try{const c=Z(r,[{relativePath:f,info:S}]),t=h.basename(f,h.extname(f)),a=h.join(r,`${t}.fnmap`);b.writeFileSync(a,c),$.success(h.relative(n,a))}catch(c){const t=c;$.error(`Failed to generate .fnmap for ${f}: ${t.message}`)}else for(const[r,u]of m)try{const f=Z(r,u),S=h.join(r,".fnmap");b.writeFileSync(S,f),$.success(h.relative(n,S))}catch(f){const S=f;$.error(`Failed to generate .fnmap for ${h.relative(n,r)}: ${S.message}`)}if(o.mermaid&&m.size>0){if($.info(`
59
+ Generating Mermaid call graphs...`),o.mermaid==="file"||o.mermaid===!0)for(const[r,u]of m)for(const{relativePath:f,info:S}of u)try{const c=ge(f,S);if(c){const t=h.basename(f,h.extname(f)),a=h.join(r,`${t}.mermaid`);b.writeFileSync(a,c),$.success(h.relative(n,a))}}catch(c){const t=c;$.error(`Failed to generate mermaid for ${f}: ${t.message}`)}else if(o.mermaid==="project")try{const r=[];for(const[,S]of m)r.push(...S);const u=ye(n,r),f=h.join(n,".fnmap.mermaid");b.writeFileSync(f,u),$.success(h.relative(n,f))}catch(r){const u=r;$.error(`Failed to generate project mermaid: ${u.message}`)}}$.info(`
60
+ `+"=".repeat(50)),$.info(`Complete! Analyzed: ${C.green}${p}${C.reset}, Failed: ${d>0?C.red:""}${d}${C.reset}`),$.info("=".repeat(50))}require.main===module&&$e();exports.COLORS=C;exports.DEFAULT_CONFIG=H;exports.DEFAULT_EXCLUDES=ee;exports.ErrorTypes=x;exports.MAX_DIR_DEPTH=X;exports.MAX_FILE_SIZE=K;exports.SUPPORTED_EXTENSIONS=z;exports.analyzeFile=re;exports.extractJSDocDescription=G;exports.formatError=V;exports.generateAiMap=Z;exports.generateFileMermaid=ge;exports.generateHeader=we;exports.generateProjectMermaid=ye;exports.getGitChangedFiles=me;exports.getVersion=de;exports.isParseError=Y;exports.isProcessFailure=Ae;exports.isProcessSuccess=be;exports.isQuietMode=ve;exports.isValidationFailure=xe;exports.isValidationSuccess=Ce;exports.loadConfig=ue;exports.logger=$;exports.main=$e;exports.mergeConfig=pe;exports.processCode=De;exports.processFile=he;exports.program=_e;exports.removeExistingHeaders=Le;exports.scanDirectory=W;exports.setQuietMode=fe;exports.setupCLI=ne;exports.validateConfig=Q;exports.validateFilePath=le;
package/dist/main.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 主函数
3
+ */
4
+ export declare function main(): void;
5
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAkEA;;GAEG;AACH,wBAAgB,IAAI,IAAI,IAAI,CA4O3B"}
@@ -0,0 +1,14 @@
1
+ import type { ProcessResult } from '../types';
2
+ export interface ProcessCodeOptions {
3
+ /** 文件路径(用于判断是否为 TS 文件,可选) */
4
+ filePath?: string;
5
+ }
6
+ /**
7
+ * 处理代码字符串,返回分析结果
8
+ */
9
+ export declare function processCode(code: string, options?: ProcessCodeOptions): ProcessResult;
10
+ /**
11
+ * 处理单个文件(只分析,不修改文件)
12
+ */
13
+ export declare function processFile(filePath: string): ProcessResult;
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/processor/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAK9C,MAAM,WAAW,kBAAkB;IACjC,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,aAAa,CAgCrF;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CA+C3D"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 获取 git 改动的文件列表
3
+ */
4
+ export declare function getGitChangedFiles(projectDir: string, stagedOnly?: boolean): string[];
5
+ /**
6
+ * 扫描单个目录下的代码文件(不递归)
7
+ */
8
+ export declare function scanSingleDirectory(dir: string): string[];
9
+ /**
10
+ * 递归扫描目录获取所有代码文件
11
+ */
12
+ export declare function scanDirectory(dir: string, baseDir?: string, excludes?: readonly string[], depth?: number, visited?: Set<string>): string[];
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"AA6BA;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,MAAM,EAAE,CA2DnF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAsBzD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAY,EACrB,QAAQ,GAAE,SAAS,MAAM,EAAqB,EAC9C,KAAK,SAAI,EACT,OAAO,GAAE,GAAG,CAAC,MAAM,CAAa,GAC/B,MAAM,EAAE,CAkEV"}
@@ -0,0 +1,120 @@
1
+ export declare const ErrorTypes: {
2
+ readonly FILE_NOT_FOUND: "FILE_NOT_FOUND";
3
+ readonly FILE_READ_ERROR: "FILE_READ_ERROR";
4
+ readonly PARSE_ERROR: "PARSE_ERROR";
5
+ readonly CONFIG_ERROR: "CONFIG_ERROR";
6
+ readonly VALIDATION_ERROR: "VALIDATION_ERROR";
7
+ readonly PERMISSION_ERROR: "PERMISSION_ERROR";
8
+ readonly FILE_TOO_LARGE: "FILE_TOO_LARGE";
9
+ };
10
+ export type ErrorType = (typeof ErrorTypes)[keyof typeof ErrorTypes];
11
+ export interface ValidationSuccess {
12
+ valid: true;
13
+ }
14
+ export interface ValidationFailure {
15
+ valid: false;
16
+ error: string;
17
+ errorType?: ErrorType;
18
+ }
19
+ export type ValidationResult = ValidationSuccess | ValidationFailure;
20
+ export interface FnmapConfig {
21
+ enable?: boolean;
22
+ include?: string[];
23
+ exclude?: string[];
24
+ }
25
+ export interface LoadedConfig {
26
+ config: FnmapConfig | null;
27
+ source: string | null;
28
+ }
29
+ export interface ImportInfo {
30
+ module: string;
31
+ members: string[];
32
+ usedIn: string[];
33
+ }
34
+ export interface FunctionInfo {
35
+ name: string;
36
+ params: string;
37
+ startLine: number;
38
+ endLine: number;
39
+ description: string;
40
+ }
41
+ export interface MethodInfo {
42
+ name: string;
43
+ params: string;
44
+ line: number;
45
+ static: boolean;
46
+ kind: 'method' | 'get' | 'set' | 'constructor';
47
+ description: string;
48
+ }
49
+ export interface ClassInfo {
50
+ name: string;
51
+ superClass: string | null;
52
+ startLine: number;
53
+ endLine: number;
54
+ methods: MethodInfo[];
55
+ description: string;
56
+ }
57
+ export interface ConstantInfo {
58
+ name: string;
59
+ line: number;
60
+ description: string;
61
+ }
62
+ export type CallGraph = Record<string, string[]>;
63
+ export interface FileInfo {
64
+ description: string;
65
+ imports: ImportInfo[];
66
+ functions: FunctionInfo[];
67
+ classes: ClassInfo[];
68
+ constants: ConstantInfo[];
69
+ callGraph: CallGraph;
70
+ isPureType?: boolean;
71
+ }
72
+ export interface ParseErrorResult {
73
+ parseError: string;
74
+ errorType: ErrorType;
75
+ loc?: {
76
+ line: number;
77
+ column: number;
78
+ };
79
+ }
80
+ export type AnalyzeResult = FileInfo | ParseErrorResult;
81
+ export interface ProcessSuccess {
82
+ success: true;
83
+ info: FileInfo;
84
+ }
85
+ export interface ProcessFailure {
86
+ success: false;
87
+ error: string;
88
+ errorType: ErrorType;
89
+ loc?: {
90
+ line: number;
91
+ column: number;
92
+ };
93
+ }
94
+ export type ProcessResult = ProcessSuccess | ProcessFailure;
95
+ export interface CLIOptions {
96
+ files?: string[];
97
+ dir?: string;
98
+ project: string;
99
+ changed?: boolean;
100
+ staged?: boolean;
101
+ mermaid?: boolean | 'file' | 'project';
102
+ quiet?: boolean;
103
+ init?: boolean;
104
+ }
105
+ export interface ErrorContext {
106
+ file?: string;
107
+ line?: number;
108
+ column?: number;
109
+ suggestion?: string;
110
+ }
111
+ export interface FileInfoEntry {
112
+ relativePath: string;
113
+ info: FileInfo;
114
+ }
115
+ export declare function isParseError(result: AnalyzeResult): result is ParseErrorResult;
116
+ export declare function isProcessSuccess(result: ProcessResult): result is ProcessSuccess;
117
+ export declare function isProcessFailure(result: ProcessResult): result is ProcessFailure;
118
+ export declare function isValidationSuccess(result: ValidationResult): result is ValidationSuccess;
119
+ export declare function isValidationFailure(result: ValidationResult): result is ValidationFailure;
120
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { ValidationResult, ErrorContext, ErrorType } from '../types';
2
+ /**
3
+ * 验证文件路径
4
+ */
5
+ export declare function validateFilePath(filePath: unknown): ValidationResult;
6
+ /**
7
+ * 验证配置对象
8
+ */
9
+ export declare function validateConfig(config: unknown): ValidationResult;
10
+ /**
11
+ * 格式化错误信息
12
+ */
13
+ export declare function formatError(_errorType: ErrorType | string, message: string, context?: ErrorContext): string;
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validation/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAe,SAAS,EAAE,MAAM,UAAU,CAAC;AAIvF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,gBAAgB,CA6CpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAgChE;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,SAAS,GAAG,MAAM,EAC9B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,YAAiB,GACzB,MAAM,CAgBR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@didnhdj/fnmap",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "AI code indexing tool for analyzing JS/TS code structure and generating structured code maps to help AI understand code quickly",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  "LICENSE"
15
15
  ],
16
16
  "scripts": {
17
- "build": "tsc && vite build",
17
+ "build": "vite build && tsc --emitDeclarationOnly",
18
18
  "dev": "tsc --watch",
19
19
  "typecheck": "tsc --noEmit",
20
20
  "test": "vitest run",