@ev3lynx/md-analyzer
Advanced tools
| #!/usr/bin/env node | ||
| export {}; |
| #!/usr/bin/env node | ||
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| const commander_1 = require("commander"); | ||
| const path = __importStar(require("path")); | ||
| const fs = __importStar(require("fs")); | ||
| const zod_1 = require("zod"); | ||
| const schema_js_1 = require("../core/schema.js"); | ||
| const config_js_1 = require("../utils/config.js"); | ||
| const analyzer_js_1 = require("../core/analyzer.js"); | ||
| const graph_js_1 = require("../core/graph.js"); | ||
| const search_js_1 = require("../core/search.js"); | ||
| const health_js_1 = require("../core/health.js"); | ||
| const session_js_1 = require("../core/session.js"); | ||
| const output_js_1 = require("./output.js"); | ||
| const pkgVersion = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf-8')).version; | ||
| const program = new commander_1.Command(); | ||
| program | ||
| .name('md-analyzer') | ||
| .description('Markdown document analyzer for AI agents - extract metadata, headings, links, tables, tokens, formatting counts, and key points from .md files') | ||
| .version(pkgVersion, '--version, -v', 'Show version number') | ||
| .arguments('[directory]') | ||
| .option('--json', 'Output as JSON') | ||
| .option('--search <kw>', 'Search keyword in content') | ||
| .option('--filter <k=v>', 'Filter by metadata field') | ||
| .option('--rank', 'Rank results by relevance') | ||
| .option('--graph', 'Document relationship graph') | ||
| .option('--deps', 'Dependency graph (DAG order + levels)') | ||
| .option('--orphans', 'Find unreferenced docs') | ||
| .option('--backlinks <doc>', 'Find docs linking to <doc>') | ||
| .option('--keypoints', 'Quick overview (single-shot)') | ||
| .option('--lint-fragments', 'Fragment health check') | ||
| .option('--session', 'Token budget report') | ||
| .option('--budget <n>', 'Set token budget limit', parseInt, 100000) | ||
| .option('--max-results <n>', 'Limit output', parseInt, 0) | ||
| .addHelpText('after', ` | ||
| Examples: | ||
| md-analyzer /path/to/docs --keypoints --json | ||
| md-analyzer . --search "task" --rank --json | ||
| md-analyzer . --session --budget 50000 --json | ||
| md-analyzer . --orphans --json | ||
| md-analyzer . --lint-fragments --json | ||
| md-analyzer . --deps --json`); | ||
| program.action(async (directory, options) => { | ||
| const startTime = Date.now(); | ||
| let parsed; | ||
| try { | ||
| parsed = schema_js_1.CliOptions.parse({ directory, ...options }); | ||
| } | ||
| catch (e) { | ||
| if (e instanceof zod_1.z.ZodError) { | ||
| console.error('Invalid arguments:'); | ||
| for (const issue of e.issues) { | ||
| console.error(' ' + issue.path.join('.') + ': ' + issue.message); | ||
| } | ||
| } | ||
| else { | ||
| console.error('Unexpected error:', e instanceof Error ? e.message : e); | ||
| } | ||
| process.exit(1); | ||
| } | ||
| const configPath = (0, config_js_1.resolveConfigPath)(); | ||
| const config = (0, config_js_1.getTomlConfig)(configPath); | ||
| const targetArg = parsed.directory || process.env['MD_ANALYZER_DEFAULT_DIR'] || config.default_directory || process.cwd(); | ||
| let mdFiles = []; | ||
| let scanErrors = []; | ||
| try { | ||
| const stat = fs.statSync(targetArg); | ||
| if (stat.isFile()) { | ||
| if (targetArg.endsWith('.md')) | ||
| mdFiles = [targetArg]; | ||
| else { | ||
| scanErrors.push('not_a_markdown_file: ' + targetArg); | ||
| if (!parsed.json) | ||
| console.log('Not a .md file: ' + targetArg + '\n'); | ||
| } | ||
| } | ||
| else if (stat.isDirectory()) { | ||
| if (!parsed.json) | ||
| console.log('Scanning: ' + targetArg + '\n'); | ||
| const scanned = (0, analyzer_js_1.scanMarkdownFiles)(targetArg); | ||
| mdFiles = scanned.files; | ||
| scanErrors = scanned.errors; | ||
| if (!parsed.json) { | ||
| console.log('Found ' + mdFiles.length + ' .md files\n'); | ||
| if (scanErrors.length > 0) | ||
| console.log('Warnings: ' + scanErrors.length + ' directories skipped\n'); | ||
| } | ||
| } | ||
| else { | ||
| scanErrors.push('unsupported_path_type: ' + targetArg); | ||
| } | ||
| } | ||
| catch { | ||
| scanErrors.push('path_not_found: ' + targetArg); | ||
| } | ||
| let results = await Promise.all(mdFiles.map(file => (0, analyzer_js_1.analyzeFileWithMicromark)(file).catch(() => (0, analyzer_js_1.analyzeFile)(file)))); | ||
| if (scanErrors.length > 0 && results.length > 0) { | ||
| if (!results[0].stats.errors) | ||
| results[0].stats.errors = []; | ||
| results[0].stats.errors.push(...scanErrors); | ||
| } | ||
| if (parsed.filter && parsed.filter.includes('=')) { | ||
| const [key, value] = parsed.filter.split('='); | ||
| results = (0, search_js_1.filterByMetadata)(results, key, value); | ||
| if (!parsed.json) | ||
| console.log('Filtered by ' + key + '=' + value + ': ' + results.length + ' results\n'); | ||
| } | ||
| if (parsed.search) { | ||
| results = (0, search_js_1.searchContent)(results, parsed.search); | ||
| if (!parsed.json) | ||
| console.log('Search "' + parsed.search + '": ' + results.length + ' results\n'); | ||
| } | ||
| if (parsed.rank && parsed.search) { | ||
| results = (0, search_js_1.rankByRelevance)(results, parsed.search); | ||
| if (!parsed.json) | ||
| console.log('Ranked by relevance to "' + parsed.search + '"\n'); | ||
| } | ||
| let limitedResults = results; | ||
| if (parsed.maxResults > 0 && results.length > parsed.maxResults) { | ||
| if (!parsed.json) | ||
| console.log('Warning: Limiting output to ' + parsed.maxResults + ' of ' + results.length + ' results\n'); | ||
| limitedResults = results.slice(0, parsed.maxResults); | ||
| } | ||
| const session = (0, session_js_1.loadSession)(); | ||
| const updatedSession = (0, session_js_1.updateSessionStats)(results, session); | ||
| (0, session_js_1.saveSession)(updatedSession); | ||
| const tokensThisCall = results.reduce((sum, r) => sum + r.stats.tokens, 0); | ||
| if (parsed.session) | ||
| console.log(JSON.stringify((0, session_js_1.getTokenBudgetReport)(updatedSession, parsed.budget), null, 2)); | ||
| else if (parsed.keypoints) | ||
| console.log(JSON.stringify(limitedResults.map(doc => (0, output_js_1.extractKeyPoints)(doc)), null, 2)); | ||
| else if (parsed.lintFragments) | ||
| console.log(JSON.stringify((0, health_js_1.getFragmentHealth)(limitedResults), null, 2)); | ||
| else if (parsed.deps) { | ||
| const graph = (0, graph_js_1.buildGraph)(limitedResults); | ||
| console.log(JSON.stringify({ nodes: Object.keys(graph.nodes), edges: graph.edges, tokensThisCall }, null, 2)); | ||
| } | ||
| else if (parsed.orphans) { | ||
| const orphans = (0, graph_js_1.findOrphans)((0, graph_js_1.buildGraph)(limitedResults)); | ||
| console.log(JSON.stringify({ orphans, count: orphans.length, tokensThisCall }, null, 2)); | ||
| } | ||
| else if (parsed.backlinks) { | ||
| const backlinks = (0, graph_js_1.findBacklinks)(limitedResults, parsed.backlinks); | ||
| console.log(JSON.stringify({ target: parsed.backlinks, backlinks, count: backlinks.length, tokensThisCall }, null, 2)); | ||
| } | ||
| else if (parsed.graph) | ||
| console.log(JSON.stringify((0, graph_js_1.buildGraph)(limitedResults), null, 2)); | ||
| else { | ||
| if (!parsed.json) { | ||
| console.log('\nTokens this call: ' + tokensThisCall); | ||
| console.log('Total session tokens: ' + updatedSession.totalTokens + '\n'); | ||
| } | ||
| console.log(JSON.stringify(limitedResults, null, 2)); | ||
| } | ||
| const usedFlags = process.argv.slice(2).filter(a => a.startsWith('--')).map(a => a.replace(/=.*/, '')); | ||
| const mode = parsed.deps ? 'deps' : parsed.lintFragments ? 'lint-fragments' : parsed.session ? 'session' : parsed.keypoints ? 'keypoints' : parsed.orphans ? 'orphans' : parsed.backlinks ? 'backlinks' : parsed.graph ? 'graph' : parsed.search ? 'search' : 'default'; | ||
| (0, output_js_1.writeRunLog)({ | ||
| timestamp: new Date().toISOString(), | ||
| sessionId: updatedSession.sessionId, | ||
| directory: targetArg, | ||
| flags: usedFlags, | ||
| filesFound: mdFiles.length, | ||
| filesProcessed: results.length, | ||
| tokensThisCall, | ||
| totalSessionTokens: updatedSession.totalTokens, | ||
| errors: scanErrors, | ||
| durationMs: Date.now() - startTime, | ||
| mode | ||
| }); | ||
| }); | ||
| program.parse(); | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"} |
| import type { AnalysisResult, RunLog } from '../types/index.js'; | ||
| export declare function extractKeyPoints(doc: AnalysisResult): object; | ||
| export declare function writeRunLog(log: RunLog): void; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.extractKeyPoints = extractKeyPoints; | ||
| exports.writeRunLog = writeRunLog; | ||
| const fs = __importStar(require("fs")); | ||
| const path = __importStar(require("path")); | ||
| const constants_js_1 = require("../utils/constants.js"); | ||
| function extractKeyPoints(doc) { | ||
| return { | ||
| fileName: doc.fileName, title: doc.headings[0]?.text || doc.fileName, level: doc.headings[0]?.level || 1, | ||
| summary: { | ||
| totalHeadings: doc.stats.totalHeadings, totalLinks: doc.stats.totalLinks, totalWikilinks: doc.stats.totalWikilinks, | ||
| totalTokens: doc.stats.tokens, wordCount: doc.stats.wordCount, | ||
| boldCount: doc.stats.boldCount ?? 0, italicCount: doc.stats.italicCount ?? 0, bulletCount: doc.stats.bulletCount ?? 0 | ||
| }, | ||
| keyHeadings: doc.headings.slice(0, 10).map((h, i) => ({ | ||
| level: h.level, text: h.text, line: h.line, | ||
| tokens: doc.sections?.[i]?.tokens ?? 0 | ||
| })), | ||
| importantLinks: doc.links.filter(l => !l.isInternal).slice(0, 3).map(l => ({ text: l.text, url: l.url })), | ||
| internalReferences: doc.links.filter(l => l.isInternal && l.fileName).slice(0, 5).map(l => l.fileName), | ||
| metadata: doc.metadata, readingTime: Math.ceil(doc.stats.wordCount / 200) + ' min' | ||
| }; | ||
| } | ||
| function writeRunLog(log) { | ||
| try { | ||
| if (!fs.existsSync(constants_js_1.LOG_DIR)) | ||
| fs.mkdirSync(constants_js_1.LOG_DIR, { recursive: true }); | ||
| const logFile = path.join(constants_js_1.LOG_DIR, `${log.sessionId}.json`); | ||
| const existing = fs.existsSync(logFile) ? JSON.parse(fs.readFileSync(logFile, 'utf-8')) : []; | ||
| existing.push(log); | ||
| fs.writeFileSync(logFile, JSON.stringify(existing, null, 2)); | ||
| } | ||
| catch (e) { | ||
| console.error('run_log_write_error:', e instanceof Error ? e.message : e); | ||
| } | ||
| } | ||
| //# sourceMappingURL=output.js.map |
| {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"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"} |
| import type { AnalysisResult } from '../types/index.js'; | ||
| export declare function scanMarkdownFiles(dir: string): { | ||
| files: string[]; | ||
| errors: string[]; | ||
| }; | ||
| export declare function analyzeFile(filePath: string): AnalysisResult; | ||
| export declare function analyzeFileWithMicromark(filePath: string): Promise<AnalysisResult>; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.scanMarkdownFiles = scanMarkdownFiles; | ||
| exports.analyzeFile = analyzeFile; | ||
| exports.analyzeFileWithMicromark = analyzeFileWithMicromark; | ||
| const fs = __importStar(require("fs")); | ||
| const path = __importStar(require("path")); | ||
| const js_tiktoken_1 = require("js-tiktoken"); | ||
| const constants_js_1 = require("../utils/constants.js"); | ||
| const extractors_js_1 = require("./extractors.js"); | ||
| const counters_js_1 = require("./counters.js"); | ||
| const micromark_walk_js_1 = require("./micromark-walk.js"); | ||
| const hybrid_merge_js_1 = require("./hybrid-merge.js"); | ||
| function scanMarkdownFiles(dir) { | ||
| const files = [], errors = []; | ||
| function walk(dir) { | ||
| let entries; | ||
| try { | ||
| entries = fs.readdirSync(dir, { withFileTypes: true }); | ||
| } | ||
| catch (e) { | ||
| errors.push(`permission_denied: ${dir}`); | ||
| console.error('scan_error:', e instanceof Error ? e.message : e); | ||
| return; | ||
| } | ||
| for (const entry of entries) { | ||
| if (entry.name.startsWith('.') || constants_js_1.SKIP_DIRS.has(entry.name)) | ||
| continue; | ||
| const fullPath = path.join(dir, entry.name); | ||
| try { | ||
| if (entry.isDirectory()) | ||
| walk(fullPath); | ||
| else if (entry.isFile() && entry.name.endsWith('.md')) | ||
| files.push(fullPath); | ||
| } | ||
| catch (e) { | ||
| errors.push(`access_error: ${fullPath}`); | ||
| console.error('access_error:', e instanceof Error ? e.message : e); | ||
| } | ||
| } | ||
| } | ||
| try { | ||
| walk(dir); | ||
| } | ||
| catch (e) { | ||
| errors.push(`scan_error: ${e instanceof Error ? e.message : 'unknown'}`); | ||
| console.error('walk_error:', e instanceof Error ? e.message : e); | ||
| } | ||
| return { files, errors }; | ||
| } | ||
| function computeSections(content, headings) { | ||
| const bodyLines = content.split('\n'); | ||
| return headings.map((h, i) => { | ||
| const startIdx = h.line - 1; | ||
| const endIdx = i + 1 < headings.length ? headings[i + 1].line - 1 : bodyLines.length; | ||
| const sectionText = bodyLines.slice(startIdx, endIdx).join('\n'); | ||
| let tokens = 0; | ||
| try { | ||
| tokens = (0, js_tiktoken_1.encodingForModel)('gpt-4').encode(sectionText).length; | ||
| } | ||
| catch (e) { | ||
| console.error('section_token_fallback:', e instanceof Error ? e.message : e); | ||
| tokens = Math.ceil(sectionText.length / 4); | ||
| } | ||
| return { line: h.line, tokens }; | ||
| }); | ||
| } | ||
| function buildBaseResult(filePath, content, errors) { | ||
| const { metadata, content: markdownContent } = (0, extractors_js_1.extractFrontmatter)(content); | ||
| const fragmentMeta = (0, extractors_js_1.extractFragmentMeta)(metadata); | ||
| const headings = (0, extractors_js_1.extractHeadings)(markdownContent); | ||
| const links = (0, extractors_js_1.extractLinks)(markdownContent); | ||
| const wikilinks = (0, extractors_js_1.extractWikilinks)(markdownContent); | ||
| const tables = (0, extractors_js_1.extractTables)(markdownContent); | ||
| const counts = (0, counters_js_1.countStats)(markdownContent); | ||
| if (counts.tokens === 0 && !errors.includes('token_count_fallback')) { | ||
| errors.push('token_count_fallback: tiktoken unavailable'); | ||
| } | ||
| const sections = computeSections(markdownContent, headings); | ||
| const fileName = path.basename(filePath, '.md'); | ||
| return { | ||
| file: filePath, fileName, metadata, fragmentMeta, headings, sections, links, wikilinks, tables, | ||
| stats: { | ||
| totalHeadings: headings.length, totalLinks: links.length, | ||
| internalLinks: links.filter(l => l.isInternal).length, | ||
| externalLinks: links.filter(l => !l.isInternal).length, | ||
| totalWikilinks: wikilinks.length, wordCount: counts.wordCount, | ||
| charCount: counts.charCount, lineCount: counts.lineCount, | ||
| codeBlocks: counts.codeBlocks, tables: tables.length, | ||
| tokens: counts.tokens, errors: errors.length > 0 ? errors : undefined | ||
| } | ||
| }; | ||
| } | ||
| function analyzeFile(filePath) { | ||
| const errors = []; | ||
| let content = ''; | ||
| try { | ||
| content = fs.readFileSync(filePath, 'utf-8'); | ||
| } | ||
| catch (e) { | ||
| errors.push(`file_read_error: ${e instanceof Error ? e.message : 'unknown'}`); | ||
| return { | ||
| file: filePath, fileName: path.basename(filePath, '.md'), metadata: null, fragmentMeta: null, | ||
| headings: [], sections: [], links: [], wikilinks: [], tables: [], | ||
| stats: { totalHeadings: 0, totalLinks: 0, internalLinks: 0, externalLinks: 0, totalWikilinks: 0, wordCount: 0, charCount: 0, lineCount: 0, codeBlocks: 0, tables: 0, tokens: 0, errors } | ||
| }; | ||
| } | ||
| return buildBaseResult(filePath, content, errors); | ||
| } | ||
| async function analyzeFileWithMicromark(filePath) { | ||
| const errors = []; | ||
| let content = ''; | ||
| try { | ||
| content = fs.readFileSync(filePath, 'utf-8'); | ||
| } | ||
| catch (e) { | ||
| errors.push(`file_read_error: ${e instanceof Error ? e.message : 'unknown'}`); | ||
| return { | ||
| file: filePath, fileName: path.basename(filePath, '.md'), metadata: null, fragmentMeta: null, | ||
| headings: [], sections: [], links: [], wikilinks: [], tables: [], | ||
| stats: { totalHeadings: 0, totalLinks: 0, internalLinks: 0, externalLinks: 0, totalWikilinks: 0, wordCount: 0, charCount: 0, lineCount: 0, codeBlocks: 0, tables: 0, tokens: 0, errors } | ||
| }; | ||
| } | ||
| const { content: markdownContent } = (0, extractors_js_1.extractFrontmatter)(content); | ||
| if (!(await (0, micromark_walk_js_1.isMicromarkAvailable)())) { | ||
| errors.push('micromark_unavailable'); | ||
| return buildBaseResult(filePath, content, errors); | ||
| } | ||
| try { | ||
| const [regions, mmLinks, mmHeadings, mmTables, mmFormatting] = await Promise.all([ | ||
| (0, micromark_walk_js_1.walkCodeBlocks)(markdownContent), | ||
| (0, micromark_walk_js_1.walkLinks)(markdownContent), | ||
| (0, micromark_walk_js_1.walkHeadings)(markdownContent), | ||
| (0, micromark_walk_js_1.walkTables)(markdownContent), | ||
| (0, micromark_walk_js_1.walkFormatting)(markdownContent) | ||
| ]); | ||
| if (mmLinks === null) | ||
| errors.push('walkLinks_failed'); | ||
| if (mmHeadings === null) | ||
| errors.push('walkHeadings_failed'); | ||
| if (mmTables === null) | ||
| errors.push('walkTables_failed'); | ||
| if (mmFormatting === null) | ||
| errors.push('walkFormatting_failed'); | ||
| const safeRegions = regions ?? []; | ||
| const result = buildBaseResult(filePath, content, errors); | ||
| result.links = (0, hybrid_merge_js_1.filterMicromarkLinks)(mmLinks ?? [], safeRegions); | ||
| result.stats.totalLinks = result.links.length; | ||
| result.stats.internalLinks = result.links.filter(l => l.isInternal).length; | ||
| result.stats.externalLinks = result.links.filter(l => !l.isInternal).length; | ||
| result.headings = (0, hybrid_merge_js_1.filterMicromarkHeadings)(mmHeadings ?? [], safeRegions, markdownContent); | ||
| result.sections = computeSections(markdownContent, result.headings); | ||
| result.stats.totalHeadings = result.headings.length; | ||
| result.tables = (0, hybrid_merge_js_1.filterMicromarkTables)(mmTables ?? [], safeRegions, markdownContent); | ||
| result.stats.tables = result.tables.length; | ||
| result.stats.codeBlocks = (0, hybrid_merge_js_1.countCodeBlocks)(regions); | ||
| const fmt = (0, hybrid_merge_js_1.countFormatting)(mmFormatting, markdownContent, safeRegions); | ||
| result.stats.boldCount = fmt.boldCount; | ||
| result.stats.italicCount = fmt.italicCount; | ||
| result.stats.bulletCount = fmt.bulletCount; | ||
| result.stats.errors = errors.length > 0 ? errors : undefined; | ||
| return result; | ||
| } | ||
| catch (e) { | ||
| errors.push(`micromark_pipeline_failed: ${e instanceof Error ? e.message : 'unknown'}`); | ||
| return buildBaseResult(filePath, content, errors); | ||
| } | ||
| } | ||
| //# sourceMappingURL=analyzer.js.map |
| {"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../../src/core/analyzer.ts"],"names":[],"mappings":"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"} |
| export declare function countStats(content: string): { | ||
| wordCount: number; | ||
| charCount: number; | ||
| lineCount: number; | ||
| codeBlocks: number; | ||
| tokens: number; | ||
| }; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.countStats = countStats; | ||
| const js_tiktoken_1 = require("js-tiktoken"); | ||
| function countStats(content) { | ||
| const wordCount = content.split(/\s+/).filter(w => w.length > 0).length; | ||
| const charCount = content.length; | ||
| const lineCount = content.split('\n').length; | ||
| const codeBlocks = (content.match(/```[\s\S]*?```/g) || []).length; | ||
| let tokens = 0; | ||
| try { | ||
| tokens = (0, js_tiktoken_1.encodingForModel)('gpt-4').encode(content).length; | ||
| } | ||
| catch (e) { | ||
| console.error('token_count_fallback: tiktoken unavailable, using estimate:', e instanceof Error ? e.message : e); | ||
| tokens = Math.ceil(charCount / 4); | ||
| } | ||
| return { wordCount, charCount, lineCount, codeBlocks, tokens }; | ||
| } | ||
| //# sourceMappingURL=counters.js.map |
| {"version":3,"file":"counters.js","sourceRoot":"","sources":["../../src/core/counters.ts"],"names":[],"mappings":";;AAEA,gCAaC;AAfD,6CAA8C;AAE9C,SAAgB,UAAU,CAAC,OAAe;IACxC,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAA;IACvE,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAA;IAChC,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAA;IAC5C,MAAM,UAAU,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAA;IAClE,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,IAAI,CAAC;QACH,MAAM,GAAG,IAAA,8BAAgB,EAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAA;IAC3D,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,6DAA6D,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAChH,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAA;IACnC,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,CAAA;AAChE,CAAC"} |
| import type { Link, Wikilink, Heading, Table, FragmentMeta } from '../types/index.js'; | ||
| export declare function extractFrontmatter(content: string): { | ||
| metadata: Record<string, unknown> | null; | ||
| content: string; | ||
| }; | ||
| export declare function extractFragmentMeta(metadata: Record<string, unknown> | null): FragmentMeta | null; | ||
| /** @deprecated Use walkHeadings from micromark-walk.js instead (token-accurate) */ | ||
| export declare function extractHeadings(content: string): Heading[]; | ||
| /** @deprecated Use walkLinks from micromark-walk.js instead (token-accurate) */ | ||
| export declare function extractLinks(content: string): Link[]; | ||
| export declare function extractWikilinks(content: string): Wikilink[]; | ||
| /** @deprecated Use walkTables from micromark-walk.js instead (uses GFM spec) */ | ||
| export declare function extractTables(content: string): Table[]; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.extractFrontmatter = extractFrontmatter; | ||
| exports.extractFragmentMeta = extractFragmentMeta; | ||
| exports.extractHeadings = extractHeadings; | ||
| exports.extractLinks = extractLinks; | ||
| exports.extractWikilinks = extractWikilinks; | ||
| exports.extractTables = extractTables; | ||
| const path = __importStar(require("path")); | ||
| const yaml = __importStar(require("js-yaml")); | ||
| function extractFrontmatter(content) { | ||
| const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n/; | ||
| const match = content.match(frontmatterRegex); | ||
| if (!match) | ||
| return { metadata: null, content }; | ||
| try { | ||
| const parsed = yaml.load(match[1]); | ||
| const metadata = parsed && typeof parsed === 'object' ? parsed : null; | ||
| return { metadata, content: content.substring(match[0].length) }; | ||
| } | ||
| catch (e) { | ||
| console.error('frontmatter_parse_error:', e instanceof Error ? e.message : e); | ||
| return { metadata: null, content }; | ||
| } | ||
| } | ||
| function extractFragmentMeta(metadata) { | ||
| if (!metadata) | ||
| return null; | ||
| const dependsRaw = metadata.depends_on; | ||
| const depends = Array.isArray(dependsRaw) ? dependsRaw.map(String) : (typeof dependsRaw === 'string' ? [dependsRaw] : []); | ||
| const tags = Array.isArray(metadata.tags) ? metadata.tags.map(String) : (typeof metadata.tags === 'string' ? [metadata.tags] : []); | ||
| return { | ||
| title: String(metadata.title || ''), | ||
| description: metadata.description ? String(metadata.description) : null, | ||
| tags, | ||
| depends_on: depends, | ||
| status: metadata.status ? String(metadata.status) : null, | ||
| source: metadata.source ? String(metadata.source) : null, | ||
| order: metadata.order != null ? Number(metadata.order) : null, | ||
| date_iso: metadata.date_iso ? String(metadata.date_iso) : null, | ||
| }; | ||
| } | ||
| /** @deprecated Use walkHeadings from micromark-walk.js instead (token-accurate) */ | ||
| function extractHeadings(content) { | ||
| const headings = []; | ||
| const headingRegex = /^(#{1,6})\s+(.+)$/gm; | ||
| let match; | ||
| while ((match = headingRegex.exec(content)) !== null) { | ||
| const line = content.substring(0, match.index).split('\n').length; | ||
| headings.push({ level: match[1].length, text: match[2].trim(), line }); | ||
| } | ||
| return headings; | ||
| } | ||
| /** @deprecated Use walkLinks from micromark-walk.js instead (token-accurate) */ | ||
| function extractLinks(content) { | ||
| const links = []; | ||
| const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g; | ||
| let match; | ||
| while ((match = linkRegex.exec(content)) !== null) { | ||
| const url = match[2].trim(); | ||
| const isInternal = url.startsWith('#') || (!url.startsWith('http') && !url.startsWith('//')); | ||
| let fileName = null; | ||
| if (isInternal && !url.startsWith('#')) { | ||
| const baseName = path.basename(url, '.md'); | ||
| if (baseName && baseName !== url) | ||
| fileName = baseName; | ||
| } | ||
| links.push({ text: match[1].trim(), url, isInternal, fileName }); | ||
| } | ||
| return links; | ||
| } | ||
| function extractWikilinks(content) { | ||
| const wikilinks = []; | ||
| const wikiRegex = /\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g; | ||
| let match; | ||
| while ((match = wikiRegex.exec(content)) !== null) { | ||
| wikilinks.push({ target: match[1].trim(), display: match[2]?.trim() || null }); | ||
| } | ||
| return wikilinks; | ||
| } | ||
| /** @deprecated Use walkTables from micromark-walk.js instead (uses GFM spec) */ | ||
| function extractTables(content) { | ||
| const tables = []; | ||
| const tableRegex = /\|(.+)\|\n\|[-:\s|]+\|\n((?:\|.+\|\n?)+)/g; | ||
| let match; | ||
| while ((match = tableRegex.exec(content)) !== null) { | ||
| const headers = match[1].split('|').map(h => h.trim()).filter(h => h); | ||
| const rows = []; | ||
| match[2].trim().split('\n').forEach(row => { | ||
| const cells = row.split('|').map(c => c.trim()).filter(c => c); | ||
| if (cells.length > 0) | ||
| rows.push(cells); | ||
| }); | ||
| tables.push({ headers, rows }); | ||
| } | ||
| return tables; | ||
| } | ||
| //# sourceMappingURL=extractors.js.map |
| {"version":3,"file":"extractors.js","sourceRoot":"","sources":["../../src/core/extractors.ts"],"names":[],"mappings":"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"} |
| import type { AnalysisResult, Graph } from '../types/index.js'; | ||
| export declare function buildGraph(results: AnalysisResult[]): Graph; | ||
| export declare function findOrphans(graph: Graph, excludeOrphansWithDeps?: Set<string>): string[]; | ||
| export declare function findBacklinks(results: AnalysisResult[], targetFileName: string): string[]; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.buildGraph = buildGraph; | ||
| exports.findOrphans = findOrphans; | ||
| exports.findBacklinks = findBacklinks; | ||
| function addEdge(graph, edges, source, target, type) { | ||
| if (!graph[source]) | ||
| graph[source] = { inbound: [], outbound: [] }; | ||
| if (!graph[target]) | ||
| graph[target] = { inbound: [], outbound: [] }; | ||
| if (!graph[source].outbound.includes(target)) | ||
| graph[source].outbound.push(target); | ||
| if (!graph[target].inbound.includes(source)) | ||
| graph[target].inbound.push(source); | ||
| edges.push({ source, target, type }); | ||
| } | ||
| function buildGraph(results) { | ||
| const graph = {}, edges = []; | ||
| results.forEach(doc => { | ||
| const source = doc.fileName; | ||
| if (!graph[source]) | ||
| graph[source] = { inbound: [], outbound: [] }; | ||
| doc.links.forEach(link => { | ||
| if (link.isInternal && link.fileName) | ||
| addEdge(graph, edges, source, link.fileName, 'link'); | ||
| }); | ||
| doc.wikilinks.forEach(w => { | ||
| const slug = w.target.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''); | ||
| if (slug !== source && results.some(r => r.fileName === slug)) | ||
| addEdge(graph, edges, source, slug, 'wikilink'); | ||
| }); | ||
| if (doc.fragmentMeta) { | ||
| doc.fragmentMeta.depends_on.forEach(dep => { | ||
| const depName = dep.replace(/\.md$/, ''); | ||
| if (depName !== source && results.some(r => r.fileName === depName)) | ||
| addEdge(graph, edges, source, depName, 'depends_on'); | ||
| }); | ||
| } | ||
| }); | ||
| return { nodes: graph, edges }; | ||
| } | ||
| function findOrphans(graph, excludeOrphansWithDeps) { | ||
| return Object.keys(graph.nodes).filter(node => { | ||
| if (excludeOrphansWithDeps?.has(node)) | ||
| return false; | ||
| return graph.nodes[node].inbound.length === 0 && graph.nodes[node].outbound.length === 0; | ||
| }); | ||
| } | ||
| function findBacklinks(results, targetFileName) { | ||
| const backlinks = []; | ||
| results.forEach(doc => { | ||
| doc.links.forEach(link => { | ||
| if (link.isInternal && link.fileName === targetFileName && !backlinks.includes(doc.fileName)) { | ||
| backlinks.push(doc.fileName); | ||
| } | ||
| }); | ||
| doc.wikilinks.forEach(w => { | ||
| const slug = w.target.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''); | ||
| if (slug === targetFileName && !backlinks.includes(doc.fileName)) | ||
| backlinks.push(doc.fileName); | ||
| }); | ||
| }); | ||
| return backlinks; | ||
| } | ||
| //# sourceMappingURL=graph.js.map |
| {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/core/graph.ts"],"names":[],"mappings":"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"} |
| import type { AnalysisResult } from '../types/index.js'; | ||
| export declare function getFragmentHealth(results: AnalysisResult[]): object; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.getFragmentHealth = getFragmentHealth; | ||
| function getFragmentHealth(results) { | ||
| const total = results.length; | ||
| const withFrontmatter = results.filter(r => r.fragmentMeta).length; | ||
| const withDeps = results.filter(r => r.fragmentMeta && r.fragmentMeta.depends_on.length > 0).length; | ||
| const withWikilinks = results.filter(r => r.stats.totalWikilinks > 0).length; | ||
| const withStatus = results.filter(r => r.fragmentMeta && r.fragmentMeta.status).length; | ||
| const withDescription = results.filter(r => r.fragmentMeta && r.fragmentMeta.description).length; | ||
| const withSource = results.filter(r => r.fragmentMeta && r.fragmentMeta.source).length; | ||
| const noTitle = results.filter(r => r.fragmentMeta && !r.fragmentMeta.title).length; | ||
| const issues = []; | ||
| for (const r of results) { | ||
| const fileIssues = []; | ||
| if (!r.fragmentMeta) | ||
| fileIssues.push('no_frontmatter'); | ||
| else { | ||
| if (!r.fragmentMeta.title) | ||
| fileIssues.push('empty_title'); | ||
| if (!r.fragmentMeta.source) | ||
| fileIssues.push('no_source'); | ||
| if (r.fragmentMeta.depends_on.length === 0 && r.stats.totalWikilinks > 0) | ||
| fileIssues.push('wikilinks_no_depends_on'); | ||
| } | ||
| if (fileIssues.length > 0) | ||
| issues.push({ file: r.fileName, issues: fileIssues }); | ||
| } | ||
| return { total, withFrontmatter, withDeps, withWikilinks, withStatus, withDescription, withSource, noTitle, filesWithIssues: issues.length, issues }; | ||
| } | ||
| //# sourceMappingURL=health.js.map |
| {"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/core/health.ts"],"names":[],"mappings":";;AAEA,8CAqBC;AArBD,SAAgB,iBAAiB,CAAC,OAAyB;IACzD,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAA;IAC5B,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,MAAM,CAAA;IAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAA;IACnG,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,MAAM,CAAA;IAC5E,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,MAAM,CAAA;IACtF,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,MAAM,CAAA;IAChG,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,MAAM,CAAA;IACtF,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,CAAA;IACnF,MAAM,MAAM,GAAyC,EAAE,CAAA;IACvD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,UAAU,GAAa,EAAE,CAAA;QAC/B,IAAI,CAAC,CAAC,CAAC,YAAY;YAAE,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;aACjD,CAAC;YACJ,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK;gBAAE,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;YACzD,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM;gBAAE,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;YACxD,IAAI,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC;gBAAE,UAAU,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAA;QACtH,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;IAClF,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAA;AACtJ,CAAC"} |
| import type { CodeBlockRegion, FormattingCounts, MicromarkLink } from './micromark-walk.js'; | ||
| import type { Link, Heading, Table } from '../types/index.js'; | ||
| export declare function filterMicromarkLinks(links: MicromarkLink[], regions: CodeBlockRegion[]): Link[]; | ||
| export declare function filterMicromarkHeadings(headings: Heading[], regions: CodeBlockRegion[], content: string): Heading[]; | ||
| export declare function filterMicromarkTables(tables: Table[], regions: CodeBlockRegion[], content: string): Table[]; | ||
| export declare function countCodeBlocks(regions: CodeBlockRegion[] | null): number; | ||
| export declare function countFormatting(counts: FormattingCounts | null, content: string, regions: CodeBlockRegion[]): { | ||
| boldCount: number; | ||
| italicCount: number; | ||
| bulletCount: number; | ||
| }; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.filterMicromarkLinks = filterMicromarkLinks; | ||
| exports.filterMicromarkHeadings = filterMicromarkHeadings; | ||
| exports.filterMicromarkTables = filterMicromarkTables; | ||
| exports.countCodeBlocks = countCodeBlocks; | ||
| exports.countFormatting = countFormatting; | ||
| const path = __importStar(require("path")); | ||
| function isInsideBlock(offset, regions) { | ||
| if (regions.length === 0) | ||
| return false; | ||
| return regions.some(r => offset >= r.start && offset < r.end); | ||
| } | ||
| function filterMicromarkLinks(links, regions) { | ||
| if (!links || links.length === 0) | ||
| return []; | ||
| return links | ||
| .filter(ml => { | ||
| if (regions.length === 0) | ||
| return true; | ||
| return !isInsideBlock(ml.start, regions); | ||
| }) | ||
| .map(ml => ({ | ||
| text: ml.text, | ||
| url: ml.url, | ||
| isInternal: ml.url.startsWith('#') || (!ml.url.startsWith('http') && !ml.url.startsWith('//')), | ||
| fileName: (() => { | ||
| if (!ml.url.startsWith('#') && !ml.url.startsWith('http') && !ml.url.startsWith('//')) { | ||
| const baseName = path.basename(ml.url, '.md'); | ||
| if (baseName && baseName !== ml.url) | ||
| return baseName; | ||
| } | ||
| return null; | ||
| })(), | ||
| isImage: ml.isImage || undefined | ||
| })); | ||
| } | ||
| function filterMicromarkHeadings(headings, regions, content) { | ||
| if (!headings || headings.length === 0) | ||
| return []; | ||
| if (regions.length === 0) | ||
| return headings; | ||
| return headings.filter(h => { | ||
| const idx = content.indexOf(h.text); | ||
| return idx === -1 || !isInsideBlock(idx, regions); | ||
| }); | ||
| } | ||
| function filterMicromarkTables(tables, regions, content) { | ||
| if (!tables || tables.length === 0) | ||
| return []; | ||
| if (regions.length === 0) | ||
| return tables; | ||
| return tables.filter(t => { | ||
| const idx = content.indexOf(t.headers.join('|')); | ||
| return idx === -1 || !isInsideBlock(idx, regions); | ||
| }); | ||
| } | ||
| function countCodeBlocks(regions) { | ||
| return regions ? regions.length : -1; | ||
| } | ||
| function isInsideAnyBlock(offset, regions) { | ||
| return regions.some(r => offset >= r.start && offset < r.end); | ||
| } | ||
| function countFormatting(counts, content, regions) { | ||
| if (!counts) | ||
| return { boldCount: 0, italicCount: 0, bulletCount: 0 }; | ||
| if (regions.length === 0) { | ||
| return { | ||
| boldCount: counts.bold + counts.boldItalic, | ||
| italicCount: counts.italic, | ||
| bulletCount: counts.bullet | ||
| }; | ||
| } | ||
| const boldItalicRe = /\*\*\*(.+?)\*\*\*/g; | ||
| const boldRe = /(?<!\*)\*\*(?!\*)(.+?)\*\*/g; | ||
| const italicRe = /(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g; | ||
| const bulletRe = /^[ \t]*[-*+][ \t]/gm; | ||
| const isInCode = (offset) => isInsideAnyBlock(offset, regions); | ||
| const boldItalicCount = [...content.matchAll(boldItalicRe)].filter(m => !isInCode(m.index)).length; | ||
| const boldCount = [...content.matchAll(boldRe)].filter(m => !isInCode(m.index)).length; | ||
| const italicCount = [...content.matchAll(italicRe)].filter(m => !isInCode(m.index)).length; | ||
| const bulletCount = [...content.matchAll(bulletRe)].filter(m => !isInCode(m.index)).length; | ||
| return { | ||
| boldCount: boldCount + boldItalicCount, | ||
| italicCount, | ||
| bulletCount | ||
| }; | ||
| } | ||
| //# sourceMappingURL=hybrid-merge.js.map |
| {"version":3,"file":"hybrid-merge.js","sourceRoot":"","sources":["../../src/core/hybrid-merge.ts"],"names":[],"mappings":"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"} |
| export interface CodeBlockRegion { | ||
| start: number; | ||
| end: number; | ||
| } | ||
| export interface FormattingCounts { | ||
| bold: number; | ||
| italic: number; | ||
| boldItalic: number; | ||
| bullet: number; | ||
| } | ||
| export interface MicromarkLink { | ||
| text: string; | ||
| url: string; | ||
| start: number; | ||
| end: number; | ||
| isImage: boolean; | ||
| isAutolink: boolean; | ||
| isReference: boolean; | ||
| } | ||
| import type { Heading, Table } from '../types/index.js'; | ||
| export declare function isMicromarkAvailable(): Promise<boolean>; | ||
| export declare function walkCodeBlocks(content: string): Promise<CodeBlockRegion[] | null>; | ||
| export declare function walkLinks(content: string): Promise<MicromarkLink[] | null>; | ||
| export declare function walkHeadings(content: string): Promise<Heading[] | null>; | ||
| export declare function walkFormatting(content: string): Promise<FormattingCounts | null>; | ||
| export declare function walkTables(content: string): Promise<Table[] | null>; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.isMicromarkAvailable = isMicromarkAvailable; | ||
| exports.walkCodeBlocks = walkCodeBlocks; | ||
| exports.walkLinks = walkLinks; | ||
| exports.walkHeadings = walkHeadings; | ||
| exports.walkFormatting = walkFormatting; | ||
| exports.walkTables = walkTables; | ||
| let micromarkModule = null; | ||
| async function getMicromark() { | ||
| if (micromarkModule === null) { | ||
| try { | ||
| micromarkModule = await Promise.resolve().then(() => __importStar(require('micromark'))); | ||
| } | ||
| catch { | ||
| micromarkModule = false; | ||
| } | ||
| } | ||
| return micromarkModule; | ||
| } | ||
| async function isMicromarkAvailable() { | ||
| const mm = await getMicromark(); | ||
| return mm !== false; | ||
| } | ||
| function parseEvents(content, extensions) { | ||
| const mm = micromarkModule; | ||
| if (!mm) | ||
| throw new Error('micromark not loaded'); | ||
| if (!content) | ||
| return []; | ||
| const opts = extensions && extensions.length > 0 ? { extensions } : {}; | ||
| return mm.postprocess(mm.parse(opts).document().write(mm.preprocess()(content, 'utf-8', true))); | ||
| } | ||
| async function walkCodeBlocks(content) { | ||
| const mm = await getMicromark(); | ||
| if (!mm) | ||
| return null; | ||
| try { | ||
| const events = parseEvents(content); | ||
| const regions = []; | ||
| let depth = 0; | ||
| let currentStart = 0; | ||
| for (const ev of events) { | ||
| const token = ev[1]; | ||
| if (token.type === 'codeFenced' || token.type === 'codeIndented') { | ||
| if (ev[0] === 'enter') { | ||
| if (depth === 0) | ||
| currentStart = token.start.offset; | ||
| depth++; | ||
| } | ||
| else { | ||
| depth--; | ||
| if (depth === 0 && token.end) { | ||
| regions.push({ start: currentStart, end: token.end.offset }); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return regions; | ||
| } | ||
| catch (e) { | ||
| console.error('walkCodeBlocks_error:', e instanceof Error ? e.message : e); | ||
| return null; | ||
| } | ||
| } | ||
| async function walkLinks(content) { | ||
| const mm = await getMicromark(); | ||
| if (!mm) | ||
| return null; | ||
| try { | ||
| const events = parseEvents(content); | ||
| const definitions = new Map(); | ||
| for (const ev of events) { | ||
| if (ev[0] === 'enter' && ev[1].type === 'definitionLabelString') { | ||
| const name = content.slice(ev[1].start.offset, ev[1].end?.offset ?? ev[1].start.offset).toLowerCase(); | ||
| definitions.set(name, ''); | ||
| } | ||
| else if (ev[0] === 'enter' && ev[1].type === 'definitionDestinationString') { | ||
| const url = content.slice(ev[1].start.offset, ev[1].end?.offset ?? ev[1].start.offset); | ||
| let lastName = ''; | ||
| for (const [k, v] of definitions) { | ||
| if (!v) { | ||
| lastName = k; | ||
| break; | ||
| } | ||
| } | ||
| if (lastName) | ||
| definitions.set(lastName, url); | ||
| } | ||
| } | ||
| const links = []; | ||
| let current = null; | ||
| for (const ev of events) { | ||
| const token = ev[1]; | ||
| const slice = (t) => content.slice(t.start.offset, t.end?.offset ?? t.start.offset); | ||
| if (ev[0] === 'enter' && (token.type === 'link' || token.type === 'image' || token.type === 'autolink')) { | ||
| current = { type: token.type, start: token.start.offset, text: '', url: '', hasRef: false }; | ||
| } | ||
| if (current && ev[0] === 'enter') { | ||
| if (token.type === 'labelText') { | ||
| current.text = slice(token); | ||
| } | ||
| else if (token.type === 'resourceDestinationString') { | ||
| current.url = slice(token); | ||
| } | ||
| else if (token.type === 'referenceString') { | ||
| current.hasRef = true; | ||
| current.url = definitions.get(slice(token).toLowerCase()) ?? slice(token); | ||
| } | ||
| else if (token.type === 'autolinkEmail' || token.type === 'autolinkProtocol') { | ||
| current.url = slice(token); | ||
| if (!current.text) | ||
| current.text = current.url; | ||
| } | ||
| } | ||
| if (ev[0] === 'exit' && current && token.type === current.type) { | ||
| if (token.end && (current.text || current.url)) { | ||
| links.push({ | ||
| text: current.text || current.url, | ||
| url: current.url || current.text, | ||
| start: current.start, | ||
| end: token.end.offset, | ||
| isImage: current.type === 'image', | ||
| isAutolink: current.type === 'autolink', | ||
| isReference: current.hasRef | ||
| }); | ||
| } | ||
| current = null; | ||
| } | ||
| } | ||
| return links; | ||
| } | ||
| catch (e) { | ||
| console.error('walkLinks_error:', e instanceof Error ? e.message : e); | ||
| return null; | ||
| } | ||
| } | ||
| function offsetToLine(content, offset) { | ||
| let line = 1; | ||
| for (let i = 0; i < offset && i < content.length; i++) { | ||
| if (content[i] === '\n') | ||
| line++; | ||
| } | ||
| return line; | ||
| } | ||
| async function walkHeadings(content) { | ||
| const mm = await getMicromark(); | ||
| if (!mm) | ||
| return null; | ||
| try { | ||
| const events = parseEvents(content); | ||
| const headings = []; | ||
| let current = null; | ||
| for (const ev of events) { | ||
| const token = ev[1]; | ||
| const slice = (t) => content.slice(t.start.offset, t.end?.offset ?? t.start.offset); | ||
| if (ev[0] === 'enter') { | ||
| if (token.type === 'setextHeadingText') { | ||
| current = { level: 0, text: slice(token), start: token.start.offset }; | ||
| } | ||
| else if (token.type === 'setextHeadingLineSequence') { | ||
| if (current) | ||
| current.level = slice(token).startsWith('=') ? 1 : 2; | ||
| } | ||
| else if (token.type === 'atxHeadingSequence') { | ||
| current = { level: slice(token).length, text: '', start: token.start.offset }; | ||
| } | ||
| else if (token.type === 'atxHeadingText') { | ||
| if (current) | ||
| current.text = slice(token); | ||
| } | ||
| } | ||
| if (ev[0] === 'exit' && current && (token.type === 'atxHeading' || token.type === 'setextHeading')) { | ||
| headings.push({ | ||
| level: current.level, | ||
| text: current.text.trim(), | ||
| line: offsetToLine(content, current.start) | ||
| }); | ||
| current = null; | ||
| } | ||
| } | ||
| return headings.length > 0 ? headings : null; | ||
| } | ||
| catch (e) { | ||
| console.error('walkHeadings_error:', e instanceof Error ? e.message : e); | ||
| return null; | ||
| } | ||
| } | ||
| async function walkFormatting(content) { | ||
| const mm = await getMicromark(); | ||
| if (!mm) | ||
| return null; | ||
| try { | ||
| const boldItalicRe = /\*\*\*(.+?)\*\*\*/g; | ||
| const boldRe = /(?<!\*)\*\*(?!\*)(.+?)\*\*/g; | ||
| const italicRe = /(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g; | ||
| const bulletRe = /^[ \t]*[-*+][ \t]/gm; | ||
| const boldItalicMatches = [...content.matchAll(boldItalicRe)]; | ||
| const boldMatches = [...content.matchAll(boldRe)]; | ||
| const italicMatches = [...content.matchAll(italicRe)]; | ||
| const bulletMatches = [...content.matchAll(bulletRe)]; | ||
| return { | ||
| bold: boldMatches.length, | ||
| italic: italicMatches.length, | ||
| boldItalic: boldItalicMatches.length, | ||
| bullet: bulletMatches.length | ||
| }; | ||
| } | ||
| catch (e) { | ||
| console.error('walkFormatting_error:', e instanceof Error ? e.message : e); | ||
| return null; | ||
| } | ||
| } | ||
| async function walkTables(content) { | ||
| const mm = await getMicromark(); | ||
| if (!mm) | ||
| return null; | ||
| try { | ||
| const gfm = await Promise.resolve().then(() => __importStar(require('micromark-extension-gfm'))); | ||
| const events = parseEvents(content, [gfm.gfm()]); | ||
| const tables = []; | ||
| let currentTable = null; | ||
| let inHead = false; | ||
| let inBody = false; | ||
| let currentRow = null; | ||
| let currentCell = null; | ||
| for (const ev of events) { | ||
| const token = ev[1]; | ||
| const slice = (t) => content.slice(t.start.offset, t.end?.offset ?? t.start.offset); | ||
| if (ev[0] === 'enter') { | ||
| if (token.type === 'table') { | ||
| currentTable = { headers: [], rows: [] }; | ||
| } | ||
| else if (token.type === 'tableHead') { | ||
| inHead = true; | ||
| } | ||
| else if (token.type === 'tableBody') { | ||
| inBody = true; | ||
| } | ||
| else if (token.type === 'tableRow' && (inHead || inBody)) { | ||
| currentRow = []; | ||
| } | ||
| else if (token.type === 'tableHeader' && inHead) { | ||
| currentCell = ''; | ||
| } | ||
| else if (token.type === 'tableData' && inBody) { | ||
| currentCell = ''; | ||
| } | ||
| else if (token.type === 'tableContent' && currentCell !== null) { | ||
| currentCell = slice(token); | ||
| } | ||
| } | ||
| if (ev[0] === 'exit') { | ||
| if (token.type === 'tableHeader' && inHead && currentCell !== null) { | ||
| if (currentRow) | ||
| currentRow.push(currentCell); | ||
| currentCell = null; | ||
| } | ||
| else if (token.type === 'tableData' && inBody && currentCell !== null) { | ||
| if (currentRow) | ||
| currentRow.push(currentCell); | ||
| currentCell = null; | ||
| } | ||
| else if (token.type === 'tableRow' && currentRow !== null) { | ||
| if (inHead) | ||
| currentTable.headers = currentRow; | ||
| else if (inBody) | ||
| currentTable.rows.push(currentRow); | ||
| currentRow = null; | ||
| } | ||
| else if (token.type === 'tableHead') { | ||
| inHead = false; | ||
| } | ||
| else if (token.type === 'tableBody') { | ||
| inBody = false; | ||
| } | ||
| else if (token.type === 'table' && currentTable) { | ||
| tables.push(currentTable); | ||
| currentTable = null; | ||
| } | ||
| } | ||
| } | ||
| return tables.length > 0 ? tables : null; | ||
| } | ||
| catch (e) { | ||
| console.error('walkTables_error:', e instanceof Error ? e.message : e); | ||
| return null; | ||
| } | ||
| } | ||
| //# sourceMappingURL=micromark-walk.js.map |
| {"version":3,"file":"micromark-walk.js","sourceRoot":"","sources":["../../src/core/micromark-walk.ts"],"names":[],"mappings":"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"} |
| import { z } from 'zod'; | ||
| export declare const CliOptions: z.ZodObject<{ | ||
| directory: z.ZodOptional<z.ZodString>; | ||
| json: z.ZodDefault<z.ZodBoolean>; | ||
| search: z.ZodOptional<z.ZodString>; | ||
| filter: z.ZodOptional<z.ZodString>; | ||
| rank: z.ZodDefault<z.ZodBoolean>; | ||
| graph: z.ZodDefault<z.ZodBoolean>; | ||
| deps: z.ZodDefault<z.ZodBoolean>; | ||
| orphans: z.ZodDefault<z.ZodBoolean>; | ||
| backlinks: z.ZodOptional<z.ZodString>; | ||
| keypoints: z.ZodDefault<z.ZodBoolean>; | ||
| lintFragments: z.ZodDefault<z.ZodBoolean>; | ||
| session: z.ZodDefault<z.ZodBoolean>; | ||
| budget: z.ZodDefault<z.ZodNumber>; | ||
| maxResults: z.ZodDefault<z.ZodNumber>; | ||
| }, z.core.$strip>; | ||
| export type CliOptions = z.infer<typeof CliOptions>; | ||
| export declare const AnalyzerConfigSchema: z.ZodObject<{ | ||
| default_directory: z.ZodDefault<z.ZodString>; | ||
| default_budget: z.ZodDefault<z.ZodNumber>; | ||
| max_tokens: z.ZodDefault<z.ZodNumber>; | ||
| max_results_default: z.ZodDefault<z.ZodNumber>; | ||
| session_file: z.ZodDefault<z.ZodString>; | ||
| }, z.core.$strip>; | ||
| export type AnalyzerConfig = z.infer<typeof AnalyzerConfigSchema>; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.AnalyzerConfigSchema = exports.CliOptions = void 0; | ||
| const zod_1 = require("zod"); | ||
| exports.CliOptions = zod_1.z.object({ | ||
| directory: zod_1.z.string().optional(), | ||
| json: zod_1.z.boolean().default(false), | ||
| search: zod_1.z.string().min(1, 'Search keyword cannot be empty').optional(), | ||
| filter: zod_1.z.string().regex(/^[^=]+=.+$/, 'Filter must be in format key=value').optional(), | ||
| rank: zod_1.z.boolean().default(false), | ||
| graph: zod_1.z.boolean().default(false), | ||
| deps: zod_1.z.boolean().default(false), | ||
| orphans: zod_1.z.boolean().default(false), | ||
| backlinks: zod_1.z.string().min(1, 'Backlinks target cannot be empty').optional(), | ||
| keypoints: zod_1.z.boolean().default(false), | ||
| lintFragments: zod_1.z.boolean().default(false), | ||
| session: zod_1.z.boolean().default(false), | ||
| budget: zod_1.z.number().int().positive('Budget must be a positive integer').default(100000), | ||
| maxResults: zod_1.z.number().int().nonnegative('max-results must be non-negative').default(0), | ||
| }); | ||
| exports.AnalyzerConfigSchema = zod_1.z.object({ | ||
| default_directory: zod_1.z.string().default(''), | ||
| default_budget: zod_1.z.number().int().positive().default(100000), | ||
| max_tokens: zod_1.z.number().int().positive().default(200000), | ||
| max_results_default: zod_1.z.number().int().nonnegative().default(20), | ||
| session_file: zod_1.z.string().default('/tmp/md-analyzer-session.json'), | ||
| }); | ||
| //# sourceMappingURL=schema.js.map |
| {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":";;;AAAA,6BAAuB;AAEV,QAAA,UAAU,GAAG,OAAC,CAAC,MAAM,CAAC;IACjC,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,IAAI,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAChC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,gCAAgC,CAAC,CAAC,QAAQ,EAAE;IACtE,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,oCAAoC,CAAC,CAAC,QAAQ,EAAE;IACvF,IAAI,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAChC,KAAK,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IACjC,IAAI,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAChC,OAAO,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IACnC,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,kCAAkC,CAAC,CAAC,QAAQ,EAAE;IAC3E,SAAS,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IACrC,aAAa,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IACzC,OAAO,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IACnC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IACtF,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,kCAAkC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;CACxF,CAAC,CAAA;AAIW,QAAA,oBAAoB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3C,iBAAiB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IACzC,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IAC3D,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IACvD,mBAAmB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC/D,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,+BAA+B,CAAC;CAClE,CAAC,CAAA"} |
| import type { AnalysisResult } from '../types/index.js'; | ||
| export declare function searchContent(results: AnalysisResult[], keyword: string): AnalysisResult[]; | ||
| export declare function filterByMetadata(results: AnalysisResult[], key: string, value: string): AnalysisResult[]; | ||
| export declare function rankByRelevance(results: AnalysisResult[], keyword: string): AnalysisResult[]; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.searchContent = searchContent; | ||
| exports.filterByMetadata = filterByMetadata; | ||
| exports.rankByRelevance = rankByRelevance; | ||
| const fs = __importStar(require("fs")); | ||
| function searchContent(results, keyword) { | ||
| const kw = keyword.toLowerCase(); | ||
| return results.filter(doc => { | ||
| const content = fs.readFileSync(doc.file, 'utf-8').toLowerCase(); | ||
| return content.includes(kw); | ||
| }); | ||
| } | ||
| function filterByMetadata(results, key, value) { | ||
| return results.filter(doc => doc.metadata && String(doc.metadata[key] || '') === value); | ||
| } | ||
| function rankByRelevance(results, keyword) { | ||
| const kw = keyword.toLowerCase(); | ||
| return [...results].sort((a, b) => { | ||
| const countA = (fs.readFileSync(a.file, 'utf-8').toLowerCase().match(new RegExp(kw, 'g')) || []).length; | ||
| const countB = (fs.readFileSync(b.file, 'utf-8').toLowerCase().match(new RegExp(kw, 'g')) || []).length; | ||
| return countB - countA; | ||
| }); | ||
| } | ||
| //# sourceMappingURL=search.js.map |
| {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,sCAMC;AAED,4CAEC;AAED,0CAOC;AAtBD,uCAAwB;AAGxB,SAAgB,aAAa,CAAC,OAAyB,EAAE,OAAe;IACtE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAA;IAChC,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;QAC1B,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAA;QAChE,OAAO,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,SAAgB,gBAAgB,CAAC,OAAyB,EAAE,GAAW,EAAE,KAAa;IACpF,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,IAAI,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,CAAA;AACzF,CAAC;AAED,SAAgB,eAAe,CAAC,OAAyB,EAAE,OAAe;IACxE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAA;IAChC,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAChC,MAAM,MAAM,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAA;QACvG,MAAM,MAAM,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAA;QACvG,OAAO,MAAM,GAAG,MAAM,CAAA;IACxB,CAAC,CAAC,CAAA;AACJ,CAAC"} |
| import type { AnalysisResult, SessionStats } from '../types/index.js'; | ||
| export declare function loadSession(): SessionStats; | ||
| export declare function saveSession(session: SessionStats): void; | ||
| export declare function updateSessionStats(results: AnalysisResult[], session: SessionStats): SessionStats; | ||
| export declare function getTokenBudgetReport(session: SessionStats, budget: number): object; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.loadSession = loadSession; | ||
| exports.saveSession = saveSession; | ||
| exports.updateSessionStats = updateSessionStats; | ||
| exports.getTokenBudgetReport = getTokenBudgetReport; | ||
| const fs = __importStar(require("fs")); | ||
| const constants_js_1 = require("../utils/constants.js"); | ||
| function loadSession() { | ||
| try { | ||
| return JSON.parse(fs.readFileSync(constants_js_1.SESSION_FILE, 'utf-8')); | ||
| } | ||
| catch (e) { | ||
| console.error('session_load: new session created'); | ||
| return { sessionId: `session-${Date.now()}`, calls: 0, totalTokens: 0, filesProcessed: 0, startTime: new Date().toISOString() }; | ||
| } | ||
| } | ||
| function saveSession(session) { | ||
| fs.writeFileSync(constants_js_1.SESSION_FILE, JSON.stringify(session, null, 2)); | ||
| } | ||
| function updateSessionStats(results, session) { | ||
| const tokensThisCall = results.reduce((sum, r) => sum + r.stats.tokens, 0); | ||
| return { ...session, calls: session.calls + 1, totalTokens: session.totalTokens + tokensThisCall, filesProcessed: session.filesProcessed + results.length }; | ||
| } | ||
| function getTokenBudgetReport(session, budget) { | ||
| const remaining = budget - session.totalTokens; | ||
| const percentUsed = Math.round((session.totalTokens / budget) * 100); | ||
| return { | ||
| sessionId: session.sessionId, totalCalls: session.calls, totalTokens: session.totalTokens, budget, remaining, | ||
| percentUsed: percentUsed + '%', status: percentUsed >= 100 ? 'EXCEEDED' : percentUsed >= 80 ? 'WARNING' : 'OK' | ||
| }; | ||
| } | ||
| //# sourceMappingURL=session.js.map |
| {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,kCAOC;AAED,kCAEC;AAED,gDAGC;AAED,oDAOC;AA7BD,uCAAwB;AACxB,wDAAoD;AAGpD,SAAgB,WAAW;IACzB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,2BAAY,EAAE,OAAO,CAAC,CAAC,CAAA;IAC3D,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAA;QAClD,OAAO,EAAE,SAAS,EAAE,WAAW,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAA;IACjI,CAAC;AACH,CAAC;AAED,SAAgB,WAAW,CAAC,OAAqB;IAC/C,EAAE,CAAC,aAAa,CAAC,2BAAY,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;AAClE,CAAC;AAED,SAAgB,kBAAkB,CAAC,OAAyB,EAAE,OAAqB;IACjF,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAC1E,OAAO,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,GAAG,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,GAAG,cAAc,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAA;AAC7J,CAAC;AAED,SAAgB,oBAAoB,CAAC,OAAqB,EAAE,MAAc;IACxE,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC,WAAW,CAAA;IAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,WAAW,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,CAAA;IACpE,OAAO;QACL,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS;QAC5G,WAAW,EAAE,WAAW,GAAG,GAAG,EAAE,MAAM,EAAE,WAAW,IAAI,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;KAC/G,CAAA;AACH,CAAC"} |
| export { analyzeFile, analyzeFileWithMicromark, scanMarkdownFiles } from './core/analyzer.js'; | ||
| export { buildGraph, findOrphans, findBacklinks } from './core/graph.js'; | ||
| export { searchContent, filterByMetadata, rankByRelevance } from './core/search.js'; | ||
| export { getFragmentHealth } from './core/health.js'; | ||
| export { loadSession, saveSession, updateSessionStats, getTokenBudgetReport } from './core/session.js'; | ||
| export { countStats } from './core/counters.js'; | ||
| export { extractFrontmatter, extractFragmentMeta, extractHeadings, extractLinks, extractWikilinks, extractTables } from './core/extractors.js'; | ||
| export { CliOptions, AnalyzerConfigSchema } from './core/schema.js'; | ||
| export type { AnalyzerConfig } from './core/schema.js'; | ||
| export type { Link, Wikilink, Heading, Table, FragmentMeta, Stats, SectionInfo, AnalysisResult, GraphNode, Graph, SessionStats, RunLog } from './types/index.js'; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.AnalyzerConfigSchema = exports.CliOptions = exports.extractTables = exports.extractWikilinks = exports.extractLinks = exports.extractHeadings = exports.extractFragmentMeta = exports.extractFrontmatter = exports.countStats = exports.getTokenBudgetReport = exports.updateSessionStats = exports.saveSession = exports.loadSession = exports.getFragmentHealth = exports.rankByRelevance = exports.filterByMetadata = exports.searchContent = exports.findBacklinks = exports.findOrphans = exports.buildGraph = exports.scanMarkdownFiles = exports.analyzeFileWithMicromark = exports.analyzeFile = void 0; | ||
| var analyzer_js_1 = require("./core/analyzer.js"); | ||
| Object.defineProperty(exports, "analyzeFile", { enumerable: true, get: function () { return analyzer_js_1.analyzeFile; } }); | ||
| Object.defineProperty(exports, "analyzeFileWithMicromark", { enumerable: true, get: function () { return analyzer_js_1.analyzeFileWithMicromark; } }); | ||
| Object.defineProperty(exports, "scanMarkdownFiles", { enumerable: true, get: function () { return analyzer_js_1.scanMarkdownFiles; } }); | ||
| var graph_js_1 = require("./core/graph.js"); | ||
| Object.defineProperty(exports, "buildGraph", { enumerable: true, get: function () { return graph_js_1.buildGraph; } }); | ||
| Object.defineProperty(exports, "findOrphans", { enumerable: true, get: function () { return graph_js_1.findOrphans; } }); | ||
| Object.defineProperty(exports, "findBacklinks", { enumerable: true, get: function () { return graph_js_1.findBacklinks; } }); | ||
| var search_js_1 = require("./core/search.js"); | ||
| Object.defineProperty(exports, "searchContent", { enumerable: true, get: function () { return search_js_1.searchContent; } }); | ||
| Object.defineProperty(exports, "filterByMetadata", { enumerable: true, get: function () { return search_js_1.filterByMetadata; } }); | ||
| Object.defineProperty(exports, "rankByRelevance", { enumerable: true, get: function () { return search_js_1.rankByRelevance; } }); | ||
| var health_js_1 = require("./core/health.js"); | ||
| Object.defineProperty(exports, "getFragmentHealth", { enumerable: true, get: function () { return health_js_1.getFragmentHealth; } }); | ||
| var session_js_1 = require("./core/session.js"); | ||
| Object.defineProperty(exports, "loadSession", { enumerable: true, get: function () { return session_js_1.loadSession; } }); | ||
| Object.defineProperty(exports, "saveSession", { enumerable: true, get: function () { return session_js_1.saveSession; } }); | ||
| Object.defineProperty(exports, "updateSessionStats", { enumerable: true, get: function () { return session_js_1.updateSessionStats; } }); | ||
| Object.defineProperty(exports, "getTokenBudgetReport", { enumerable: true, get: function () { return session_js_1.getTokenBudgetReport; } }); | ||
| var counters_js_1 = require("./core/counters.js"); | ||
| Object.defineProperty(exports, "countStats", { enumerable: true, get: function () { return counters_js_1.countStats; } }); | ||
| var extractors_js_1 = require("./core/extractors.js"); | ||
| Object.defineProperty(exports, "extractFrontmatter", { enumerable: true, get: function () { return extractors_js_1.extractFrontmatter; } }); | ||
| Object.defineProperty(exports, "extractFragmentMeta", { enumerable: true, get: function () { return extractors_js_1.extractFragmentMeta; } }); | ||
| Object.defineProperty(exports, "extractHeadings", { enumerable: true, get: function () { return extractors_js_1.extractHeadings; } }); | ||
| Object.defineProperty(exports, "extractLinks", { enumerable: true, get: function () { return extractors_js_1.extractLinks; } }); | ||
| Object.defineProperty(exports, "extractWikilinks", { enumerable: true, get: function () { return extractors_js_1.extractWikilinks; } }); | ||
| Object.defineProperty(exports, "extractTables", { enumerable: true, get: function () { return extractors_js_1.extractTables; } }); | ||
| var schema_js_1 = require("./core/schema.js"); | ||
| Object.defineProperty(exports, "CliOptions", { enumerable: true, get: function () { return schema_js_1.CliOptions; } }); | ||
| Object.defineProperty(exports, "AnalyzerConfigSchema", { enumerable: true, get: function () { return schema_js_1.AnalyzerConfigSchema; } }); | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,kDAA6F;AAApF,0GAAA,WAAW,OAAA;AAAE,uHAAA,wBAAwB,OAAA;AAAE,gHAAA,iBAAiB,OAAA;AACjE,4CAAwE;AAA/D,sGAAA,UAAU,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,yGAAA,aAAa,OAAA;AAC/C,8CAAmF;AAA1E,0GAAA,aAAa,OAAA;AAAE,6GAAA,gBAAgB,OAAA;AAAE,4GAAA,eAAe,OAAA;AACzD,8CAAoD;AAA3C,8GAAA,iBAAiB,OAAA;AAC1B,gDAAsG;AAA7F,yGAAA,WAAW,OAAA;AAAE,yGAAA,WAAW,OAAA;AAAE,gHAAA,kBAAkB,OAAA;AAAE,kHAAA,oBAAoB,OAAA;AAC3E,kDAA+C;AAAtC,yGAAA,UAAU,OAAA;AACnB,sDAA8I;AAArI,mHAAA,kBAAkB,OAAA;AAAE,oHAAA,mBAAmB,OAAA;AAAE,gHAAA,eAAe,OAAA;AAAE,6GAAA,YAAY,OAAA;AAAE,iHAAA,gBAAgB,OAAA;AAAE,8GAAA,aAAa,OAAA;AAEhH,8CAAmE;AAA1D,uGAAA,UAAU,OAAA;AAAE,iHAAA,oBAAoB,OAAA"} |
| export interface Link { | ||
| text: string; | ||
| url: string; | ||
| isInternal: boolean; | ||
| fileName: string | null; | ||
| isImage?: boolean; | ||
| } | ||
| export interface Wikilink { | ||
| target: string; | ||
| display: string | null; | ||
| } | ||
| export interface Heading { | ||
| level: number; | ||
| text: string; | ||
| line: number; | ||
| } | ||
| export interface Table { | ||
| headers: string[]; | ||
| rows: string[][]; | ||
| } | ||
| export interface FragmentMeta { | ||
| title: string; | ||
| description: string | null; | ||
| tags: string[]; | ||
| depends_on: string[]; | ||
| status: string | null; | ||
| source: string | null; | ||
| order: number | null; | ||
| date_iso: string | null; | ||
| } | ||
| export interface Stats { | ||
| totalHeadings: number; | ||
| totalLinks: number; | ||
| internalLinks: number; | ||
| externalLinks: number; | ||
| totalWikilinks: number; | ||
| wordCount: number; | ||
| charCount: number; | ||
| lineCount: number; | ||
| codeBlocks: number; | ||
| tables: number; | ||
| tokens: number; | ||
| boldCount?: number; | ||
| italicCount?: number; | ||
| bulletCount?: number; | ||
| errors?: string[]; | ||
| } | ||
| export interface SectionInfo { | ||
| line: number; | ||
| tokens: number; | ||
| } | ||
| export interface AnalysisResult { | ||
| file: string; | ||
| fileName: string; | ||
| metadata: Record<string, unknown> | null; | ||
| fragmentMeta: FragmentMeta | null; | ||
| headings: Heading[]; | ||
| sections: SectionInfo[]; | ||
| links: Link[]; | ||
| wikilinks: Wikilink[]; | ||
| tables: Table[]; | ||
| stats: Stats; | ||
| } | ||
| export interface GraphNode { | ||
| inbound: string[]; | ||
| outbound: string[]; | ||
| } | ||
| export interface Graph { | ||
| nodes: Record<string, GraphNode>; | ||
| edges: { | ||
| source: string; | ||
| target: string; | ||
| type: string; | ||
| }[]; | ||
| } | ||
| export interface SessionStats { | ||
| sessionId: string; | ||
| calls: number; | ||
| totalTokens: number; | ||
| filesProcessed: number; | ||
| startTime: string; | ||
| } | ||
| export interface RunLog { | ||
| timestamp: string; | ||
| sessionId: string; | ||
| directory: string; | ||
| flags: string[]; | ||
| filesFound: number; | ||
| filesProcessed: number; | ||
| tokensThisCall: number; | ||
| totalSessionTokens: number; | ||
| errors: string[]; | ||
| durationMs: number; | ||
| mode: string; | ||
| } | ||
| export interface AnalyzerConfig { | ||
| default_directory: string; | ||
| default_budget: number; | ||
| max_tokens: number; | ||
| max_results_default: number; | ||
| session_file: string; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""} |
| import type { AnalyzerConfig } from '../core/schema.js'; | ||
| export declare function getTomlConfig(tomlPath: string): AnalyzerConfig; | ||
| export declare function resolveConfigPath(): string; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.getTomlConfig = getTomlConfig; | ||
| exports.resolveConfigPath = resolveConfigPath; | ||
| const fs = __importStar(require("fs")); | ||
| const path = __importStar(require("path")); | ||
| const schema_js_1 = require("../core/schema.js"); | ||
| const DEFAULT_CONFIG = schema_js_1.AnalyzerConfigSchema.parse({}); | ||
| function getTomlConfig(tomlPath) { | ||
| try { | ||
| const content = fs.readFileSync(tomlPath, 'utf-8'); | ||
| const config = {}; | ||
| let inConfigSection = false; | ||
| content.split('\n').forEach(line => { | ||
| const trimmed = line.trim(); | ||
| if (trimmed === '[tool.md-analyzer.config]') { | ||
| inConfigSection = true; | ||
| return; | ||
| } | ||
| if (inConfigSection) { | ||
| if (trimmed.startsWith('[')) { | ||
| inConfigSection = false; | ||
| return; | ||
| } | ||
| if (trimmed.includes('=')) { | ||
| const separatorIdx = trimmed.indexOf('='); | ||
| const key = trimmed.substring(0, separatorIdx).trim(); | ||
| const value = trimmed.substring(separatorIdx + 1).trim().replace(/^["']|["']$/g, ''); | ||
| config[key] = value; | ||
| } | ||
| } | ||
| }); | ||
| return { | ||
| default_directory: config['default_directory'] || DEFAULT_CONFIG.default_directory, | ||
| default_budget: parseInt(config['default_budget'] || '', 10) || DEFAULT_CONFIG.default_budget, | ||
| max_tokens: parseInt(config['max_tokens'] || '', 10) || DEFAULT_CONFIG.max_tokens, | ||
| max_results_default: parseInt(config['max_results_default'] || '', 10) || DEFAULT_CONFIG.max_results_default, | ||
| session_file: config['session_file'] || DEFAULT_CONFIG.session_file, | ||
| }; | ||
| } | ||
| catch (e) { | ||
| console.error('config_load_error: could not read hooks.toml:', e instanceof Error ? e.message : e); | ||
| return { ...DEFAULT_CONFIG }; | ||
| } | ||
| } | ||
| function resolveConfigPath() { | ||
| const candidates = [ | ||
| path.join(__dirname, '..', '..', 'hooks.toml'), | ||
| path.join(__dirname, '..', 'hooks.toml'), | ||
| path.join(process.cwd(), 'hooks.toml'), | ||
| ]; | ||
| for (const candidate of candidates) { | ||
| if (fs.existsSync(candidate)) | ||
| return candidate; | ||
| } | ||
| return candidates[0]; | ||
| } | ||
| //# sourceMappingURL=config.js.map |
| {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"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"} |
| export declare const SKIP_DIRS: Set<string>; | ||
| export declare const SESSION_FILE = "/tmp/md-analyzer-session.json"; | ||
| export declare const LOG_DIR: string; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.LOG_DIR = exports.SESSION_FILE = exports.SKIP_DIRS = void 0; | ||
| const path = __importStar(require("path")); | ||
| exports.SKIP_DIRS = new Set([ | ||
| 'node_modules', '.git', '.svn', '.hg', | ||
| 'services', 'data', 'appendonlydir', | ||
| 'dist', 'build', '__pycache__', '.next', 'coverage' | ||
| ]); | ||
| exports.SESSION_FILE = '/tmp/md-analyzer-session.json'; | ||
| exports.LOG_DIR = path.join(__dirname, '..', '..', 'log'); | ||
| //# sourceMappingURL=constants.js.map |
| {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAA4B;AAEf,QAAA,SAAS,GAAG,IAAI,GAAG,CAAC;IAC/B,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK;IACrC,UAAU,EAAE,MAAM,EAAE,eAAe;IACnC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU;CACpD,CAAC,CAAA;AAEW,QAAA,YAAY,GAAG,+BAA+B,CAAA;AAE9C,QAAA,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA"} |
+150
| #!/usr/bin/env node | ||
| import { Command } from 'commander' | ||
| import * as path from 'path' | ||
| import * as fs from 'fs' | ||
| import { z } from 'zod' | ||
| import { CliOptions } from '../core/schema.js' | ||
| import { getTomlConfig, resolveConfigPath } from '../utils/config.js' | ||
| import { scanMarkdownFiles, analyzeFile } from '../core/analyzer.js' | ||
| import { buildGraph, findOrphans, findBacklinks } from '../core/graph.js' | ||
| import { searchContent, filterByMetadata, rankByRelevance } from '../core/search.js' | ||
| import { getFragmentHealth } from '../core/health.js' | ||
| import { loadSession, saveSession, updateSessionStats, getTokenBudgetReport } from '../core/session.js' | ||
| import { extractKeyPoints, writeRunLog } from './output.js' | ||
| const pkgVersion: string = JSON.parse( | ||
| fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf-8') | ||
| ).version | ||
| const program = new Command() | ||
| program | ||
| .name('md-analyzer') | ||
| .description('Markdown document analyzer for AI agents - extract metadata, headings, links, tables, tokens, and key points from .md files') | ||
| .version(pkgVersion, '--version, -v', 'Show version number') | ||
| .arguments('[directory]') | ||
| .option('--json', 'Output as JSON') | ||
| .option('--search <kw>', 'Search keyword in content') | ||
| .option('--filter <k=v>', 'Filter by metadata field') | ||
| .option('--rank', 'Rank results by relevance') | ||
| .option('--graph', 'Document relationship graph') | ||
| .option('--deps', 'Dependency graph (DAG order + levels)') | ||
| .option('--orphans', 'Find unreferenced docs') | ||
| .option('--backlinks <doc>', 'Find docs linking to <doc>') | ||
| .option('--keypoints', 'Quick overview (single-shot)') | ||
| .option('--lint-fragments', 'Fragment health check') | ||
| .option('--session', 'Token budget report') | ||
| .option('--budget <n>', 'Set token budget limit', parseInt, 100000) | ||
| .option('--max-results <n>', 'Limit output', parseInt, 0) | ||
| .addHelpText('after', ` | ||
| Examples: | ||
| md-analyzer /path/to/docs --keypoints --json | ||
| md-analyzer . --search "task" --rank --json | ||
| md-analyzer . --session --budget 50000 --json | ||
| md-analyzer . --orphans --json | ||
| md-analyzer . --lint-fragments --json | ||
| md-analyzer . --deps --json`) | ||
| program.action((directory: string | undefined, options: Record<string, unknown>) => { | ||
| const startTime = Date.now() | ||
| let parsed: CliOptions | ||
| try { | ||
| parsed = CliOptions.parse({ directory, ...options }) | ||
| } catch (e: unknown) { | ||
| if (e instanceof z.ZodError) { | ||
| console.error('Invalid arguments:') | ||
| for (const issue of e.issues) { | ||
| console.error(' ' + issue.path.join('.') + ': ' + issue.message) | ||
| } | ||
| } else { | ||
| console.error('Unexpected error:', e instanceof Error ? e.message : e) | ||
| } | ||
| process.exit(1) | ||
| } | ||
| const configPath = resolveConfigPath() | ||
| const config = getTomlConfig(configPath) | ||
| const targetDir = parsed.directory || process.env['MD_ANALYZER_DEFAULT_DIR'] || config.default_directory || process.cwd() | ||
| if (!parsed.json) console.log('Scanning: ' + targetDir + '\n') | ||
| const { files: mdFiles, errors: scanErrors } = scanMarkdownFiles(targetDir) | ||
| if (!parsed.json) { | ||
| console.log('Found ' + mdFiles.length + ' .md files\n') | ||
| if (scanErrors.length > 0) console.log('Warnings: ' + scanErrors.length + ' directories skipped\n') | ||
| } | ||
| let results = mdFiles.map(file => analyzeFile(file)) | ||
| if (scanErrors.length > 0 && results.length > 0) { | ||
| if (!results[0].stats.errors) results[0].stats.errors = [] | ||
| results[0].stats.errors.push(...scanErrors) | ||
| } | ||
| if (parsed.filter && parsed.filter.includes('=')) { | ||
| const [key, value] = parsed.filter.split('=') | ||
| results = filterByMetadata(results, key, value) | ||
| if (!parsed.json) console.log('Filtered by ' + key + '=' + value + ': ' + results.length + ' results\n') | ||
| } | ||
| if (parsed.search) { | ||
| results = searchContent(results, parsed.search) | ||
| if (!parsed.json) console.log('Search "' + parsed.search + '": ' + results.length + ' results\n') | ||
| } | ||
| if (parsed.rank && parsed.search) { | ||
| results = rankByRelevance(results, parsed.search) | ||
| if (!parsed.json) console.log('Ranked by relevance to "' + parsed.search + '"\n') | ||
| } | ||
| let limitedResults = results | ||
| if (parsed.maxResults > 0 && results.length > parsed.maxResults) { | ||
| if (!parsed.json) console.log('Warning: Limiting output to ' + parsed.maxResults + ' of ' + results.length + ' results\n') | ||
| limitedResults = results.slice(0, parsed.maxResults) | ||
| } | ||
| const session = loadSession() | ||
| const updatedSession = updateSessionStats(results, session) | ||
| saveSession(updatedSession) | ||
| const tokensThisCall = results.reduce((sum, r) => sum + r.stats.tokens, 0) | ||
| if (parsed.session) console.log(JSON.stringify(getTokenBudgetReport(updatedSession, parsed.budget), null, 2)) | ||
| else if (parsed.keypoints) console.log(JSON.stringify(limitedResults.map(doc => extractKeyPoints(doc)), null, 2)) | ||
| else if (parsed.lintFragments) console.log(JSON.stringify(getFragmentHealth(limitedResults), null, 2)) | ||
| else if (parsed.deps) { | ||
| const graph = buildGraph(limitedResults) | ||
| console.log(JSON.stringify({ nodes: Object.keys(graph.nodes), edges: graph.edges, tokensThisCall }, null, 2)) | ||
| } else if (parsed.orphans) { | ||
| const orphans = findOrphans(buildGraph(limitedResults)) | ||
| console.log(JSON.stringify({ orphans, count: orphans.length, tokensThisCall }, null, 2)) | ||
| } else if (parsed.backlinks) { | ||
| const backlinks = findBacklinks(limitedResults, parsed.backlinks) | ||
| console.log(JSON.stringify({ target: parsed.backlinks, backlinks, count: backlinks.length, tokensThisCall }, null, 2)) | ||
| } else if (parsed.graph) console.log(JSON.stringify(buildGraph(limitedResults), null, 2)) | ||
| else { | ||
| if (!parsed.json) { | ||
| console.log('\nTokens this call: ' + tokensThisCall) | ||
| console.log('Total session tokens: ' + updatedSession.totalTokens + '\n') | ||
| } | ||
| console.log(JSON.stringify(limitedResults, null, 2)) | ||
| } | ||
| const usedFlags = process.argv.slice(2).filter(a => a.startsWith('--')).map(a => a.replace(/=.*/, '')) | ||
| const mode = parsed.deps ? 'deps' : parsed.lintFragments ? 'lint-fragments' : parsed.session ? 'session' : parsed.keypoints ? 'keypoints' : parsed.orphans ? 'orphans' : parsed.backlinks ? 'backlinks' : parsed.graph ? 'graph' : parsed.search ? 'search' : 'default' | ||
| writeRunLog({ | ||
| timestamp: new Date().toISOString(), | ||
| sessionId: updatedSession.sessionId, | ||
| directory: targetDir, | ||
| flags: usedFlags, | ||
| filesFound: mdFiles.length, | ||
| filesProcessed: results.length, | ||
| tokensThisCall, | ||
| totalSessionTokens: updatedSession.totalTokens, | ||
| errors: scanErrors, | ||
| durationMs: Date.now() - startTime, | ||
| mode | ||
| }) | ||
| }) | ||
| program.parse() |
| import * as fs from 'fs' | ||
| import * as path from 'path' | ||
| import { LOG_DIR } from '../utils/constants.js' | ||
| import type { AnalysisResult, RunLog } from '../types/index.js' | ||
| export function extractKeyPoints(doc: AnalysisResult): object { | ||
| return { | ||
| fileName: doc.fileName, title: doc.headings[0]?.text || doc.fileName, level: doc.headings[0]?.level || 1, | ||
| summary: { | ||
| totalHeadings: doc.stats.totalHeadings, totalLinks: doc.stats.totalLinks, totalWikilinks: doc.stats.totalWikilinks, | ||
| totalTokens: doc.stats.tokens, wordCount: doc.stats.wordCount | ||
| }, | ||
| keyHeadings: doc.headings.slice(0, 10).map((h, i) => ({ | ||
| level: h.level, text: h.text, line: h.line, | ||
| tokens: doc.sections?.[i]?.tokens ?? 0 | ||
| })), | ||
| importantLinks: doc.links.filter(l => !l.isInternal).slice(0, 3).map(l => ({ text: l.text, url: l.url })), | ||
| internalReferences: doc.links.filter(l => l.isInternal && l.fileName).slice(0, 5).map(l => l.fileName), | ||
| metadata: doc.metadata, readingTime: Math.ceil(doc.stats.wordCount / 200) + ' min' | ||
| } | ||
| } | ||
| export function writeRunLog(log: RunLog): void { | ||
| try { | ||
| if (!fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true }) | ||
| const logFile = path.join(LOG_DIR, `${log.sessionId}.json`) | ||
| const existing: RunLog[] = fs.existsSync(logFile) ? JSON.parse(fs.readFileSync(logFile, 'utf-8')) : [] | ||
| existing.push(log) | ||
| fs.writeFileSync(logFile, JSON.stringify(existing, null, 2)) | ||
| } catch (e: unknown) { | ||
| console.error('run_log_write_error:', e instanceof Error ? e.message : e) | ||
| } | ||
| } |
| import * as fs from 'fs' | ||
| import * as path from 'path' | ||
| import { encodingForModel } from 'js-tiktoken' | ||
| import { SKIP_DIRS } from '../utils/constants.js' | ||
| import { extractFrontmatter, extractFragmentMeta, extractHeadings, extractLinks, extractWikilinks, extractTables } from './extractors.js' | ||
| import { countStats } from './counters.js' | ||
| import type { AnalysisResult, SectionInfo } from '../types/index.js' | ||
| export function scanMarkdownFiles(dir: string): { files: string[]; errors: string[] } { | ||
| const files: string[] = [], errors: string[] = [] | ||
| function walk(dir: string): void { | ||
| let entries | ||
| try { entries = fs.readdirSync(dir, { withFileTypes: true }) } | ||
| catch (e: unknown) { errors.push(`permission_denied: ${dir}`); console.error('scan_error:', e instanceof Error ? e.message : e); return } | ||
| for (const entry of entries) { | ||
| if (entry.name.startsWith('.') || SKIP_DIRS.has(entry.name)) continue | ||
| const fullPath = path.join(dir, entry.name) | ||
| try { | ||
| if (entry.isDirectory()) walk(fullPath) | ||
| else if (entry.isFile() && entry.name.endsWith('.md')) files.push(fullPath) | ||
| } catch (e: unknown) { errors.push(`access_error: ${fullPath}`); console.error('access_error:', e instanceof Error ? e.message : e) } | ||
| } | ||
| } | ||
| try { walk(dir) } catch (e: unknown) { errors.push(`scan_error: ${e instanceof Error ? e.message : 'unknown'}`); console.error('walk_error:', e instanceof Error ? e.message : e) } | ||
| return { files, errors } | ||
| } | ||
| function computeSections(content: string, headings: { line: number }[]): SectionInfo[] { | ||
| const bodyLines = content.split('\n') | ||
| return headings.map((h, i) => { | ||
| const startIdx = h.line - 1 | ||
| const endIdx = i + 1 < headings.length ? headings[i + 1].line - 1 : bodyLines.length | ||
| const sectionText = bodyLines.slice(startIdx, endIdx).join('\n') | ||
| let tokens = 0 | ||
| try { tokens = encodingForModel('gpt-4').encode(sectionText).length } | ||
| catch (e: unknown) { | ||
| console.error('section_token_fallback:', e instanceof Error ? e.message : e) | ||
| tokens = Math.ceil(sectionText.length / 4) | ||
| } | ||
| return { line: h.line, tokens } | ||
| }) | ||
| } | ||
| export function analyzeFile(filePath: string): AnalysisResult { | ||
| const errors: string[] = [] | ||
| let content = '' | ||
| try { content = fs.readFileSync(filePath, 'utf-8') } | ||
| catch (e: unknown) { | ||
| errors.push(`file_read_error: ${e instanceof Error ? e.message : 'unknown'}`) | ||
| return { | ||
| file: filePath, fileName: path.basename(filePath, '.md'), metadata: null, fragmentMeta: null, | ||
| headings: [], sections: [], links: [], wikilinks: [], tables: [], | ||
| stats: { totalHeadings: 0, totalLinks: 0, internalLinks: 0, externalLinks: 0, totalWikilinks: 0, wordCount: 0, charCount: 0, lineCount: 0, codeBlocks: 0, tables: 0, tokens: 0, errors } | ||
| } | ||
| } | ||
| const { metadata, content: markdownContent } = extractFrontmatter(content) | ||
| const fragmentMeta = extractFragmentMeta(metadata) | ||
| const headings = extractHeadings(markdownContent) | ||
| const links = extractLinks(markdownContent) | ||
| const wikilinks = extractWikilinks(markdownContent) | ||
| const tables = extractTables(markdownContent) | ||
| const counts = countStats(markdownContent) | ||
| if (counts.tokens === 0) errors.push('token_count_fallback: tiktoken unavailable') | ||
| const sections = computeSections(markdownContent, headings) | ||
| return { | ||
| file: filePath, fileName: path.basename(filePath, '.md'), metadata, fragmentMeta, headings, sections, links, wikilinks, tables, | ||
| stats: { | ||
| totalHeadings: headings.length, totalLinks: links.length, internalLinks: links.filter(l => l.isInternal).length, | ||
| externalLinks: links.filter(l => !l.isInternal).length, totalWikilinks: wikilinks.length, wordCount: counts.wordCount, | ||
| charCount: counts.charCount, lineCount: counts.lineCount, codeBlocks: counts.codeBlocks, tables: tables.length, | ||
| tokens: counts.tokens, errors: errors.length > 0 ? errors : undefined | ||
| } | ||
| } | ||
| } |
| import { encodingForModel } from 'js-tiktoken' | ||
| export function countStats(content: string): { wordCount: number; charCount: number; lineCount: number; codeBlocks: number; tokens: number } { | ||
| const wordCount = content.split(/\s+/).filter(w => w.length > 0).length | ||
| const charCount = content.length | ||
| const lineCount = content.split('\n').length | ||
| const codeBlocks = (content.match(/```[\s\S]*?```/g) || []).length | ||
| let tokens = 0 | ||
| try { | ||
| tokens = encodingForModel('gpt-4').encode(content).length | ||
| } catch (e: unknown) { | ||
| console.error('token_count_fallback: tiktoken unavailable, using estimate:', e instanceof Error ? e.message : e) | ||
| tokens = Math.ceil(charCount / 4) | ||
| } | ||
| return { wordCount, charCount, lineCount, codeBlocks, tokens } | ||
| } |
| import * as path from 'path' | ||
| import * as yaml from 'js-yaml' | ||
| import type { Link, Wikilink, Heading, Table, FragmentMeta } from '../types/index.js' | ||
| export function extractFrontmatter(content: string): { metadata: Record<string, unknown> | null; content: string } { | ||
| const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n/ | ||
| const match = content.match(frontmatterRegex) | ||
| if (!match) return { metadata: null, content } | ||
| try { | ||
| const parsed = yaml.load(match[1]) | ||
| const metadata = parsed && typeof parsed === 'object' ? parsed as Record<string, unknown> : null | ||
| return { metadata, content: content.substring(match[0].length) } | ||
| } catch (e: unknown) { | ||
| console.error('frontmatter_parse_error:', e instanceof Error ? e.message : e) | ||
| return { metadata: null, content } | ||
| } | ||
| } | ||
| export function extractFragmentMeta(metadata: Record<string, unknown> | null): FragmentMeta | null { | ||
| if (!metadata) return null | ||
| const dependsRaw = metadata.depends_on | ||
| const depends: string[] = Array.isArray(dependsRaw) ? dependsRaw.map(String) : (typeof dependsRaw === 'string' ? [dependsRaw] : []) | ||
| const tags: string[] = Array.isArray(metadata.tags) ? metadata.tags.map(String) : (typeof metadata.tags === 'string' ? [metadata.tags] : []) | ||
| return { | ||
| title: String(metadata.title || ''), | ||
| description: metadata.description ? String(metadata.description) : null, | ||
| tags, | ||
| depends_on: depends, | ||
| status: metadata.status ? String(metadata.status) : null, | ||
| source: metadata.source ? String(metadata.source) : null, | ||
| order: metadata.order != null ? Number(metadata.order) : null, | ||
| date_iso: metadata.date_iso ? String(metadata.date_iso) : null, | ||
| } | ||
| } | ||
| export function extractHeadings(content: string): Heading[] { | ||
| const headings: Heading[] = [] | ||
| const headingRegex = /^(#{1,6})\s+(.+)$/gm | ||
| let match: RegExpExecArray | null | ||
| while ((match = headingRegex.exec(content)) !== null) { | ||
| const line = content.substring(0, match.index).split('\n').length | ||
| headings.push({ level: match[1].length, text: match[2].trim(), line }) | ||
| } | ||
| return headings | ||
| } | ||
| export function extractLinks(content: string): Link[] { | ||
| const links: Link[] = [] | ||
| const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g | ||
| let match: RegExpExecArray | null | ||
| while ((match = linkRegex.exec(content)) !== null) { | ||
| const url = match[2].trim() | ||
| const isInternal = url.startsWith('#') || (!url.startsWith('http') && !url.startsWith('//')) | ||
| let fileName: string | null = null | ||
| if (isInternal && !url.startsWith('#')) { | ||
| const baseName = path.basename(url, '.md') | ||
| if (baseName && baseName !== url) fileName = baseName | ||
| } | ||
| links.push({ text: match[1].trim(), url, isInternal, fileName }) | ||
| } | ||
| return links | ||
| } | ||
| export function extractWikilinks(content: string): Wikilink[] { | ||
| const wikilinks: Wikilink[] = [] | ||
| const wikiRegex = /\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g | ||
| let match: RegExpExecArray | null | ||
| while ((match = wikiRegex.exec(content)) !== null) { | ||
| wikilinks.push({ target: match[1].trim(), display: match[2]?.trim() || null }) | ||
| } | ||
| return wikilinks | ||
| } | ||
| export function extractTables(content: string): Table[] { | ||
| const tables: Table[] = [] | ||
| const tableRegex = /\|(.+)\|\n\|[-:\s|]+\|\n((?:\|.+\|\n?)+)/g | ||
| let match: RegExpExecArray | null | ||
| while ((match = tableRegex.exec(content)) !== null) { | ||
| const headers = match[1].split('|').map(h => h.trim()).filter(h => h) | ||
| const rows: string[][] = [] | ||
| match[2].trim().split('\n').forEach(row => { | ||
| const cells = row.split('|').map(c => c.trim()).filter(c => c) | ||
| if (cells.length > 0) rows.push(cells) | ||
| }) | ||
| tables.push({ headers, rows }) | ||
| } | ||
| return tables | ||
| } |
| import type { AnalysisResult, Graph, GraphNode } from '../types/index.js' | ||
| function addEdge(graph: Record<string, GraphNode>, edges: { source: string; target: string; type: string }[], source: string, target: string, type: string): void { | ||
| if (!graph[source]) graph[source] = { inbound: [], outbound: [] } | ||
| if (!graph[target]) graph[target] = { inbound: [], outbound: [] } | ||
| if (!graph[source].outbound.includes(target)) graph[source].outbound.push(target) | ||
| if (!graph[target].inbound.includes(source)) graph[target].inbound.push(source) | ||
| edges.push({ source, target, type }) | ||
| } | ||
| export function buildGraph(results: AnalysisResult[]): Graph { | ||
| const graph: Record<string, GraphNode> = {}, edges: { source: string; target: string; type: string }[] = [] | ||
| results.forEach(doc => { | ||
| const source = doc.fileName | ||
| if (!graph[source]) graph[source] = { inbound: [], outbound: [] } | ||
| doc.links.forEach(link => { | ||
| if (link.isInternal && link.fileName) addEdge(graph, edges, source, link.fileName, 'link') | ||
| }) | ||
| doc.wikilinks.forEach(w => { | ||
| const slug = w.target.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') | ||
| if (slug !== source && results.some(r => r.fileName === slug)) addEdge(graph, edges, source, slug, 'wikilink') | ||
| }) | ||
| if (doc.fragmentMeta) { | ||
| doc.fragmentMeta.depends_on.forEach(dep => { | ||
| const depName = dep.replace(/\.md$/, '') | ||
| if (depName !== source && results.some(r => r.fileName === depName)) addEdge(graph, edges, source, depName, 'depends_on') | ||
| }) | ||
| } | ||
| }) | ||
| return { nodes: graph, edges } | ||
| } | ||
| export function findOrphans(graph: Graph, excludeOrphansWithDeps?: Set<string>): string[] { | ||
| return Object.keys(graph.nodes).filter(node => { | ||
| if (excludeOrphansWithDeps?.has(node)) return false | ||
| return graph.nodes[node].inbound.length === 0 && graph.nodes[node].outbound.length === 0 | ||
| }) | ||
| } | ||
| export function findBacklinks(results: AnalysisResult[], targetFileName: string): string[] { | ||
| const backlinks: string[] = [] | ||
| results.forEach(doc => { | ||
| doc.links.forEach(link => { | ||
| if (link.isInternal && link.fileName === targetFileName && !backlinks.includes(doc.fileName)) { | ||
| backlinks.push(doc.fileName) | ||
| } | ||
| }) | ||
| doc.wikilinks.forEach(w => { | ||
| const slug = w.target.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') | ||
| if (slug === targetFileName && !backlinks.includes(doc.fileName)) backlinks.push(doc.fileName) | ||
| }) | ||
| }) | ||
| return backlinks | ||
| } |
| import type { AnalysisResult } from '../types/index.js' | ||
| export function getFragmentHealth(results: AnalysisResult[]): object { | ||
| const total = results.length | ||
| const withFrontmatter = results.filter(r => r.fragmentMeta).length | ||
| const withDeps = results.filter(r => r.fragmentMeta && r.fragmentMeta.depends_on.length > 0).length | ||
| const withWikilinks = results.filter(r => r.stats.totalWikilinks > 0).length | ||
| const withStatus = results.filter(r => r.fragmentMeta && r.fragmentMeta.status).length | ||
| const withDescription = results.filter(r => r.fragmentMeta && r.fragmentMeta.description).length | ||
| const withSource = results.filter(r => r.fragmentMeta && r.fragmentMeta.source).length | ||
| const noTitle = results.filter(r => r.fragmentMeta && !r.fragmentMeta.title).length | ||
| const issues: { file: string; issues: string[] }[] = [] | ||
| for (const r of results) { | ||
| const fileIssues: string[] = [] | ||
| if (!r.fragmentMeta) fileIssues.push('no_frontmatter') | ||
| else { | ||
| if (!r.fragmentMeta.title) fileIssues.push('empty_title') | ||
| if (!r.fragmentMeta.source) fileIssues.push('no_source') | ||
| if (r.fragmentMeta.depends_on.length === 0 && r.stats.totalWikilinks > 0) fileIssues.push('wikilinks_no_depends_on') | ||
| } | ||
| if (fileIssues.length > 0) issues.push({ file: r.fileName, issues: fileIssues }) | ||
| } | ||
| return { total, withFrontmatter, withDeps, withWikilinks, withStatus, withDescription, withSource, noTitle, filesWithIssues: issues.length, issues } | ||
| } |
| import { z } from 'zod' | ||
| export const CliOptions = z.object({ | ||
| directory: z.string().optional(), | ||
| json: z.boolean().default(false), | ||
| search: z.string().min(1, 'Search keyword cannot be empty').optional(), | ||
| filter: z.string().regex(/^[^=]+=.+$/, 'Filter must be in format key=value').optional(), | ||
| rank: z.boolean().default(false), | ||
| graph: z.boolean().default(false), | ||
| deps: z.boolean().default(false), | ||
| orphans: z.boolean().default(false), | ||
| backlinks: z.string().min(1, 'Backlinks target cannot be empty').optional(), | ||
| keypoints: z.boolean().default(false), | ||
| lintFragments: z.boolean().default(false), | ||
| session: z.boolean().default(false), | ||
| budget: z.number().int().positive('Budget must be a positive integer').default(100000), | ||
| maxResults: z.number().int().nonnegative('max-results must be non-negative').default(0), | ||
| }) | ||
| export type CliOptions = z.infer<typeof CliOptions> | ||
| export const AnalyzerConfigSchema = z.object({ | ||
| default_directory: z.string().default(''), | ||
| default_budget: z.number().int().positive().default(100000), | ||
| max_tokens: z.number().int().positive().default(200000), | ||
| max_results_default: z.number().int().nonnegative().default(20), | ||
| session_file: z.string().default('/tmp/md-analyzer-session.json'), | ||
| }) | ||
| export type AnalyzerConfig = z.infer<typeof AnalyzerConfigSchema> |
| import * as fs from 'fs' | ||
| import type { AnalysisResult } from '../types/index.js' | ||
| export function searchContent(results: AnalysisResult[], keyword: string): AnalysisResult[] { | ||
| const kw = keyword.toLowerCase() | ||
| return results.filter(doc => { | ||
| const content = fs.readFileSync(doc.file, 'utf-8').toLowerCase() | ||
| return content.includes(kw) | ||
| }) | ||
| } | ||
| export function filterByMetadata(results: AnalysisResult[], key: string, value: string): AnalysisResult[] { | ||
| return results.filter(doc => doc.metadata && String(doc.metadata[key] || '') === value) | ||
| } | ||
| export function rankByRelevance(results: AnalysisResult[], keyword: string): AnalysisResult[] { | ||
| const kw = keyword.toLowerCase() | ||
| return [...results].sort((a, b) => { | ||
| const countA = (fs.readFileSync(a.file, 'utf-8').toLowerCase().match(new RegExp(kw, 'g')) || []).length | ||
| const countB = (fs.readFileSync(b.file, 'utf-8').toLowerCase().match(new RegExp(kw, 'g')) || []).length | ||
| return countB - countA | ||
| }) | ||
| } |
| import * as fs from 'fs' | ||
| import { SESSION_FILE } from '../utils/constants.js' | ||
| import type { AnalysisResult, SessionStats } from '../types/index.js' | ||
| export function loadSession(): SessionStats { | ||
| try { | ||
| return JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8')) | ||
| } catch (e: unknown) { | ||
| console.error('session_load: new session created') | ||
| return { sessionId: `session-${Date.now()}`, calls: 0, totalTokens: 0, filesProcessed: 0, startTime: new Date().toISOString() } | ||
| } | ||
| } | ||
| export function saveSession(session: SessionStats): void { | ||
| fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2)) | ||
| } | ||
| export function updateSessionStats(results: AnalysisResult[], session: SessionStats): SessionStats { | ||
| const tokensThisCall = results.reduce((sum, r) => sum + r.stats.tokens, 0) | ||
| return { ...session, calls: session.calls + 1, totalTokens: session.totalTokens + tokensThisCall, filesProcessed: session.filesProcessed + results.length } | ||
| } | ||
| export function getTokenBudgetReport(session: SessionStats, budget: number): object { | ||
| const remaining = budget - session.totalTokens | ||
| const percentUsed = Math.round((session.totalTokens / budget) * 100) | ||
| return { | ||
| sessionId: session.sessionId, totalCalls: session.calls, totalTokens: session.totalTokens, budget, remaining, | ||
| percentUsed: percentUsed + '%', status: percentUsed >= 100 ? 'EXCEEDED' : percentUsed >= 80 ? 'WARNING' : 'OK' | ||
| } | ||
| } |
+15
| export { analyzeFile, scanMarkdownFiles } from './core/analyzer.js' | ||
| export { buildGraph, findOrphans, findBacklinks } from './core/graph.js' | ||
| export { searchContent, filterByMetadata, rankByRelevance } from './core/search.js' | ||
| export { getFragmentHealth } from './core/health.js' | ||
| export { loadSession, saveSession, updateSessionStats, getTokenBudgetReport } from './core/session.js' | ||
| export { countStats } from './core/counters.js' | ||
| export { extractFrontmatter, extractFragmentMeta, extractHeadings, extractLinks, extractWikilinks, extractTables } from './core/extractors.js' | ||
| export { CliOptions, AnalyzerConfigSchema } from './core/schema.js' | ||
| export type { AnalyzerConfig } from './core/schema.js' | ||
| export type { | ||
| Link, Wikilink, Heading, Table, FragmentMeta, Stats, SectionInfo, | ||
| AnalysisResult, GraphNode, Graph, SessionStats, RunLog | ||
| } from './types/index.js' |
| export interface Link { | ||
| text: string | ||
| url: string | ||
| isInternal: boolean | ||
| fileName: string | null | ||
| } | ||
| export interface Wikilink { | ||
| target: string | ||
| display: string | null | ||
| } | ||
| export interface Heading { | ||
| level: number | ||
| text: string | ||
| line: number | ||
| } | ||
| export interface Table { | ||
| headers: string[] | ||
| rows: string[][] | ||
| } | ||
| export interface FragmentMeta { | ||
| title: string | ||
| description: string | null | ||
| tags: string[] | ||
| depends_on: string[] | ||
| status: string | null | ||
| source: string | null | ||
| order: number | null | ||
| date_iso: string | null | ||
| } | ||
| export interface Stats { | ||
| totalHeadings: number | ||
| totalLinks: number | ||
| internalLinks: number | ||
| externalLinks: number | ||
| totalWikilinks: number | ||
| wordCount: number | ||
| charCount: number | ||
| lineCount: number | ||
| codeBlocks: number | ||
| tables: number | ||
| tokens: number | ||
| errors?: string[] | ||
| } | ||
| export interface SectionInfo { | ||
| line: number | ||
| tokens: number | ||
| } | ||
| export interface AnalysisResult { | ||
| file: string | ||
| fileName: string | ||
| metadata: Record<string, unknown> | null | ||
| fragmentMeta: FragmentMeta | null | ||
| headings: Heading[] | ||
| sections: SectionInfo[] | ||
| links: Link[] | ||
| wikilinks: Wikilink[] | ||
| tables: Table[] | ||
| stats: Stats | ||
| } | ||
| export interface GraphNode { | ||
| inbound: string[] | ||
| outbound: string[] | ||
| } | ||
| export interface Graph { | ||
| nodes: Record<string, GraphNode> | ||
| edges: { source: string; target: string; type: string }[] | ||
| } | ||
| export interface SessionStats { | ||
| sessionId: string | ||
| calls: number | ||
| totalTokens: number | ||
| filesProcessed: number | ||
| startTime: string | ||
| } | ||
| export interface RunLog { | ||
| timestamp: string | ||
| sessionId: string | ||
| directory: string | ||
| flags: string[] | ||
| filesFound: number | ||
| filesProcessed: number | ||
| tokensThisCall: number | ||
| totalSessionTokens: number | ||
| errors: string[] | ||
| durationMs: number | ||
| mode: string | ||
| } | ||
| export interface AnalyzerConfig { | ||
| default_directory: string | ||
| default_budget: number | ||
| max_tokens: number | ||
| max_results_default: number | ||
| session_file: string | ||
| } |
| import * as fs from 'fs' | ||
| import * as path from 'path' | ||
| import { AnalyzerConfigSchema } from '../core/schema.js' | ||
| import type { AnalyzerConfig } from '../core/schema.js' | ||
| const DEFAULT_CONFIG: AnalyzerConfig = AnalyzerConfigSchema.parse({}) | ||
| export function getTomlConfig(tomlPath: string): AnalyzerConfig { | ||
| try { | ||
| const content = fs.readFileSync(tomlPath, 'utf-8') | ||
| const config: Record<string, string> = {} | ||
| let inConfigSection = false | ||
| content.split('\n').forEach(line => { | ||
| const trimmed = line.trim() | ||
| if (trimmed === '[tool.md-analyzer.config]') { inConfigSection = true; return } | ||
| if (inConfigSection) { | ||
| if (trimmed.startsWith('[')) { inConfigSection = false; return } | ||
| if (trimmed.includes('=')) { | ||
| const separatorIdx = trimmed.indexOf('=') | ||
| const key = trimmed.substring(0, separatorIdx).trim() | ||
| const value = trimmed.substring(separatorIdx + 1).trim().replace(/^["']|["']$/g, '') | ||
| config[key] = value | ||
| } | ||
| } | ||
| }) | ||
| return { | ||
| default_directory: config['default_directory'] || DEFAULT_CONFIG.default_directory, | ||
| default_budget: parseInt(config['default_budget'] || '', 10) || DEFAULT_CONFIG.default_budget, | ||
| max_tokens: parseInt(config['max_tokens'] || '', 10) || DEFAULT_CONFIG.max_tokens, | ||
| max_results_default: parseInt(config['max_results_default'] || '', 10) || DEFAULT_CONFIG.max_results_default, | ||
| session_file: config['session_file'] || DEFAULT_CONFIG.session_file, | ||
| } | ||
| } catch (e: unknown) { | ||
| console.error('config_load_error: could not read hooks.toml:', e instanceof Error ? e.message : e) | ||
| return { ...DEFAULT_CONFIG } | ||
| } | ||
| } | ||
| export function resolveConfigPath(): string { | ||
| const candidates = [ | ||
| path.join(__dirname, '..', '..', 'hooks.toml'), | ||
| path.join(__dirname, '..', 'hooks.toml'), | ||
| path.join(process.cwd(), 'hooks.toml'), | ||
| ] | ||
| for (const candidate of candidates) { | ||
| if (fs.existsSync(candidate)) return candidate | ||
| } | ||
| return candidates[0] | ||
| } |
| import * as path from 'path' | ||
| export const SKIP_DIRS = new Set([ | ||
| 'node_modules', '.git', '.svn', '.hg', | ||
| 'services', 'data', 'appendonlydir', | ||
| 'dist', 'build', '__pycache__', '.next', 'coverage' | ||
| ]) | ||
| export const SESSION_FILE = '/tmp/md-analyzer-session.json' | ||
| export const LOG_DIR = path.join(__dirname, '..', '..', 'log') |
+9
-6
| { | ||
| "name": "@ev3lynx/md-analyzer", | ||
| "version": "0.1.4", | ||
| "version": "0.1.6", | ||
| "description": "Markdown document analyzer for AI agents - extract metadata, headings, links, tables, tokens, and key points", | ||
| "main": "md-analyzer.js", | ||
| "main": "dist/index.js", | ||
| "types": "dist/index.d.ts", | ||
| "bin": { | ||
| "md-analyzer": "md-analyzer.js" | ||
| "md-analyzer": "dist/cli/index.js" | ||
| }, | ||
@@ -15,3 +16,3 @@ "scripts": { | ||
| "prepublish": "npm run build", | ||
| "test": "node md-analyzer.js . --json" | ||
| "test": "node dist/cli/index.js . --json" | ||
| }, | ||
@@ -41,5 +42,7 @@ "keywords": [ | ||
| "dependencies": { | ||
| "commander": "^15.0.0", | ||
| "js-tiktoken": "^1.0.0", | ||
| "js-yaml": "^4.2.0", | ||
| "micromark": "^4.0.0" | ||
| "micromark": "^4.0.0", | ||
| "zod": "^4.4.3" | ||
| }, | ||
@@ -55,3 +58,3 @@ "devDependencies": { | ||
| "files": [ | ||
| "md-analyzer.js", | ||
| "dist/", | ||
| "src/", | ||
@@ -58,0 +61,0 @@ "hooks.toml", |
-538
| #!/usr/bin/env node | ||
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| } | ||
| Object.defineProperty(o, k2, desc); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || (function () { | ||
| var ownKeys = function(o) { | ||
| ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| var ar = []; | ||
| for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| return ar; | ||
| }; | ||
| return ownKeys(o); | ||
| }; | ||
| return function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| })(); | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| const fs = __importStar(require("fs")); | ||
| const path = __importStar(require("path")); | ||
| const js_tiktoken_1 = require("js-tiktoken"); | ||
| const yaml = __importStar(require("js-yaml")); | ||
| const SKIP_DIRS = new Set([ | ||
| 'node_modules', '.git', '.svn', '.hg', | ||
| 'services', 'data', 'appendonlydir', | ||
| 'dist', 'build', '__pycache__', '.next', 'coverage' | ||
| ]); | ||
| function getTomlConfig(tomlPath) { | ||
| try { | ||
| const content = fs.readFileSync(tomlPath, 'utf-8'); | ||
| const config = {}; | ||
| let inConfigSection = false; | ||
| content.split('\n').forEach(line => { | ||
| const trimmed = line.trim(); | ||
| if (trimmed === '[tool.md-analyzer.config]') { | ||
| inConfigSection = true; | ||
| return; | ||
| } | ||
| if (inConfigSection) { | ||
| if (trimmed.startsWith('[')) { | ||
| inConfigSection = false; | ||
| return; | ||
| } | ||
| if (trimmed.includes('=')) { | ||
| const [key, ...valueParts] = trimmed.split('='); | ||
| const value = valueParts.join('=').trim().replace(/^["']|["']$/g, ''); | ||
| config[key.trim()] = value; | ||
| } | ||
| } | ||
| }); | ||
| return config; | ||
| } | ||
| catch { | ||
| return {}; | ||
| } | ||
| } | ||
| function extractFrontmatter(content) { | ||
| const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n/; | ||
| const match = content.match(frontmatterRegex); | ||
| if (!match) | ||
| return { metadata: null, content }; | ||
| try { | ||
| const parsed = yaml.load(match[1]); | ||
| const metadata = parsed && typeof parsed === 'object' ? parsed : null; | ||
| return { metadata, content: content.substring(match[0].length) }; | ||
| } | ||
| catch { | ||
| return { metadata: null, content }; | ||
| } | ||
| } | ||
| function extractHeadings(content) { | ||
| const headings = []; | ||
| const headingRegex = /^(#{1,6})\s+(.+)$/gm; | ||
| let match; | ||
| while ((match = headingRegex.exec(content)) !== null) { | ||
| const line = content.substring(0, match.index).split('\n').length; | ||
| headings.push({ level: match[1].length, text: match[2].trim(), line }); | ||
| } | ||
| return headings; | ||
| } | ||
| function extractLinks(content) { | ||
| const links = []; | ||
| const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g; | ||
| let match; | ||
| while ((match = linkRegex.exec(content)) !== null) { | ||
| const url = match[2].trim(); | ||
| const isInternal = url.startsWith('#') || (!url.startsWith('http') && !url.startsWith('//')); | ||
| let fileName = null; | ||
| if (isInternal && !url.startsWith('#')) { | ||
| const baseName = path.basename(url, '.md'); | ||
| if (baseName && baseName !== url) | ||
| fileName = baseName; | ||
| } | ||
| links.push({ text: match[1].trim(), url, isInternal, fileName }); | ||
| } | ||
| return links; | ||
| } | ||
| function extractWikilinks(content) { | ||
| const wikilinks = []; | ||
| const wikiRegex = /\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g; | ||
| let match; | ||
| while ((match = wikiRegex.exec(content)) !== null) { | ||
| wikilinks.push({ target: match[1].trim(), display: match[2]?.trim() || null }); | ||
| } | ||
| return wikilinks; | ||
| } | ||
| function extractFragmentMeta(metadata) { | ||
| if (!metadata) | ||
| return null; | ||
| const dependsRaw = metadata.depends_on; | ||
| const depends = Array.isArray(dependsRaw) ? dependsRaw.map(String) : (typeof dependsRaw === 'string' ? [dependsRaw] : []); | ||
| const tags = Array.isArray(metadata.tags) ? metadata.tags.map(String) : (typeof metadata.tags === 'string' ? [metadata.tags] : []); | ||
| return { | ||
| title: String(metadata.title || ''), | ||
| description: metadata.description ? String(metadata.description) : null, | ||
| tags, | ||
| depends_on: depends, | ||
| status: metadata.status ? String(metadata.status) : null, | ||
| source: metadata.source ? String(metadata.source) : null, | ||
| order: metadata.order != null ? Number(metadata.order) : null, | ||
| date_iso: metadata.date_iso ? String(metadata.date_iso) : null, | ||
| }; | ||
| } | ||
| function extractTables(content) { | ||
| const tables = []; | ||
| const tableRegex = /\|(.+)\|\n\|[-:\s|]+\|\n((?:\|.+\|\n?)+)/g; | ||
| let match; | ||
| while ((match = tableRegex.exec(content)) !== null) { | ||
| const headers = match[1].split('|').map(h => h.trim()).filter(h => h); | ||
| const rows = []; | ||
| match[2].trim().split('\n').forEach(row => { | ||
| const cells = row.split('|').map(c => c.trim()).filter(c => c); | ||
| if (cells.length > 0) | ||
| rows.push(cells); | ||
| }); | ||
| tables.push({ headers, rows }); | ||
| } | ||
| return tables; | ||
| } | ||
| function countStats(content) { | ||
| const wordCount = content.split(/\s+/).filter(w => w.length > 0).length; | ||
| const charCount = content.length; | ||
| const lineCount = content.split('\n').length; | ||
| const codeBlocks = (content.match(/```[\s\S]*?```/g) || []).length; | ||
| let tokens = 0; | ||
| try { | ||
| tokens = (0, js_tiktoken_1.encodingForModel)('gpt-4').encode(content).length; | ||
| } | ||
| catch { | ||
| tokens = Math.ceil(charCount / 4); | ||
| } | ||
| return { wordCount, charCount, lineCount, codeBlocks, tokens }; | ||
| } | ||
| function scanMarkdownFiles(dir) { | ||
| const files = [], errors = []; | ||
| function walk(dir) { | ||
| let entries; | ||
| try { | ||
| entries = fs.readdirSync(dir, { withFileTypes: true }); | ||
| } | ||
| catch (e) { | ||
| errors.push(`permission_denied: ${dir}`); | ||
| return; | ||
| } | ||
| for (const entry of entries) { | ||
| if (entry.name.startsWith('.') || SKIP_DIRS.has(entry.name)) | ||
| continue; | ||
| const fullPath = path.join(dir, entry.name); | ||
| try { | ||
| if (entry.isDirectory()) | ||
| walk(fullPath); | ||
| else if (entry.isFile() && entry.name.endsWith('.md')) | ||
| files.push(fullPath); | ||
| } | ||
| catch (e) { | ||
| errors.push(`access_error: ${fullPath}`); | ||
| } | ||
| } | ||
| } | ||
| try { | ||
| walk(dir); | ||
| } | ||
| catch (e) { | ||
| errors.push(`scan_error: ${e instanceof Error ? e.message : 'unknown'}`); | ||
| } | ||
| return { files, errors }; | ||
| } | ||
| function analyzeFile(filePath) { | ||
| const errors = []; | ||
| let content = ''; | ||
| try { | ||
| content = fs.readFileSync(filePath, 'utf-8'); | ||
| } | ||
| catch (e) { | ||
| errors.push(`file_read_error: ${e instanceof Error ? e.message : 'unknown'}`); | ||
| return { file: filePath, fileName: path.basename(filePath, '.md'), metadata: null, fragmentMeta: null, headings: [], sections: [], links: [], wikilinks: [], tables: [], | ||
| stats: { totalHeadings: 0, totalLinks: 0, internalLinks: 0, externalLinks: 0, totalWikilinks: 0, wordCount: 0, charCount: 0, lineCount: 0, codeBlocks: 0, tables: 0, tokens: 0, errors } }; | ||
| } | ||
| const { metadata, content: markdownContent } = extractFrontmatter(content); | ||
| const fragmentMeta = extractFragmentMeta(metadata); | ||
| const headings = extractHeadings(markdownContent); | ||
| const links = extractLinks(markdownContent); | ||
| const wikilinks = extractWikilinks(markdownContent); | ||
| const tables = extractTables(markdownContent); | ||
| const counts = countStats(markdownContent); | ||
| if (counts.tokens === 0) | ||
| errors.push('token_count_fallback: tiktoken unavailable'); | ||
| const bodyLines = markdownContent.split('\n'); | ||
| const sections = headings.map((h, i) => { | ||
| const startIdx = h.line - 1; | ||
| const endIdx = i + 1 < headings.length ? headings[i + 1].line - 1 : bodyLines.length; | ||
| const sectionText = bodyLines.slice(startIdx, endIdx).join('\n'); | ||
| let tokens = 0; | ||
| try { | ||
| tokens = (0, js_tiktoken_1.encodingForModel)('gpt-4').encode(sectionText).length; | ||
| } | ||
| catch { | ||
| tokens = Math.ceil(sectionText.length / 4); | ||
| } | ||
| return { line: h.line, tokens }; | ||
| }); | ||
| return { file: filePath, fileName: path.basename(filePath, '.md'), metadata, fragmentMeta, headings, sections, links, wikilinks, tables, | ||
| stats: { totalHeadings: headings.length, totalLinks: links.length, internalLinks: links.filter(l => l.isInternal).length, | ||
| externalLinks: links.filter(l => !l.isInternal).length, totalWikilinks: wikilinks.length, wordCount: counts.wordCount, | ||
| charCount: counts.charCount, lineCount: counts.lineCount, codeBlocks: counts.codeBlocks, tables: tables.length, | ||
| tokens: counts.tokens, errors: errors.length > 0 ? errors : undefined } }; | ||
| } | ||
| function addEdge(graph, edges, source, target, type) { | ||
| if (!graph[source]) | ||
| graph[source] = { inbound: [], outbound: [] }; | ||
| if (!graph[target]) | ||
| graph[target] = { inbound: [], outbound: [] }; | ||
| if (!graph[source].outbound.includes(target)) | ||
| graph[source].outbound.push(target); | ||
| if (!graph[target].inbound.includes(source)) | ||
| graph[target].inbound.push(source); | ||
| edges.push({ source, target, type }); | ||
| } | ||
| function buildGraph(results) { | ||
| const graph = {}, edges = []; | ||
| results.forEach(doc => { | ||
| const source = doc.fileName; | ||
| if (!graph[source]) | ||
| graph[source] = { inbound: [], outbound: [] }; | ||
| doc.links.forEach(link => { | ||
| if (link.isInternal && link.fileName) | ||
| addEdge(graph, edges, source, link.fileName, 'link'); | ||
| }); | ||
| doc.wikilinks.forEach(w => { | ||
| const slug = w.target.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''); | ||
| if (slug !== source && results.some(r => r.fileName === slug)) | ||
| addEdge(graph, edges, source, slug, 'wikilink'); | ||
| }); | ||
| if (doc.fragmentMeta) { | ||
| doc.fragmentMeta.depends_on.forEach(dep => { | ||
| const depName = dep.replace(/\.md$/, ''); | ||
| if (depName !== source && results.some(r => r.fileName === depName)) | ||
| addEdge(graph, edges, source, depName, 'depends_on'); | ||
| }); | ||
| } | ||
| }); | ||
| return { nodes: graph, edges }; | ||
| } | ||
| function findOrphans(graph, excludeOrphansWithDeps) { | ||
| return Object.keys(graph.nodes).filter(node => { | ||
| if (excludeOrphansWithDeps?.has(node)) | ||
| return false; | ||
| return graph.nodes[node].inbound.length === 0 && graph.nodes[node].outbound.length === 0; | ||
| }); | ||
| } | ||
| function findBacklinks(results, targetFileName) { | ||
| const backlinks = []; | ||
| results.forEach(doc => { | ||
| doc.links.forEach(link => { | ||
| if (link.isInternal && link.fileName === targetFileName && !backlinks.includes(doc.fileName)) { | ||
| backlinks.push(doc.fileName); | ||
| } | ||
| }); | ||
| doc.wikilinks.forEach(w => { | ||
| const slug = w.target.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''); | ||
| if (slug === targetFileName && !backlinks.includes(doc.fileName)) | ||
| backlinks.push(doc.fileName); | ||
| }); | ||
| }); | ||
| return backlinks; | ||
| } | ||
| function searchContent(results, keyword) { | ||
| const kw = keyword.toLowerCase(); | ||
| return results.filter(doc => { | ||
| const content = fs.readFileSync(doc.file, 'utf-8').toLowerCase(); | ||
| return content.includes(kw); | ||
| }); | ||
| } | ||
| function filterByMetadata(results, key, value) { | ||
| return results.filter(doc => doc.metadata && String(doc.metadata[key] || '') === value); | ||
| } | ||
| function rankByRelevance(results, keyword) { | ||
| const kw = keyword.toLowerCase(); | ||
| return [...results].sort((a, b) => { | ||
| const countA = (fs.readFileSync(a.file, 'utf-8').toLowerCase().match(new RegExp(kw, 'g')) || []).length; | ||
| const countB = (fs.readFileSync(b.file, 'utf-8').toLowerCase().match(new RegExp(kw, 'g')) || []).length; | ||
| return countB - countA; | ||
| }); | ||
| } | ||
| function extractKeyPoints(doc) { | ||
| return { fileName: doc.fileName, title: doc.headings[0]?.text || doc.fileName, level: doc.headings[0]?.level || 1, | ||
| summary: { totalHeadings: doc.stats.totalHeadings, totalLinks: doc.stats.totalLinks, totalWikilinks: doc.stats.totalWikilinks, totalTokens: doc.stats.tokens, wordCount: doc.stats.wordCount }, | ||
| keyHeadings: doc.headings.slice(0, 10).map((h, i) => ({ | ||
| level: h.level, text: h.text, line: h.line, | ||
| tokens: doc.sections?.[i]?.tokens ?? 0 | ||
| })), | ||
| importantLinks: doc.links.filter(l => !l.isInternal).slice(0, 3).map(l => ({ text: l.text, url: l.url })), | ||
| internalReferences: doc.links.filter(l => l.isInternal && l.fileName).slice(0, 5).map(l => l.fileName), | ||
| metadata: doc.metadata, readingTime: Math.ceil(doc.stats.wordCount / 200) + ' min' }; | ||
| } | ||
| const SESSION_FILE = '/tmp/md-analyzer-session.json'; | ||
| function loadSession() { | ||
| try { | ||
| return JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8')); | ||
| } | ||
| catch { | ||
| return { sessionId: `session-${Date.now()}`, calls: 0, totalTokens: 0, filesProcessed: 0, startTime: new Date().toISOString() }; | ||
| } | ||
| } | ||
| function saveSession(session) { fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2)); } | ||
| function updateSessionStats(results, session) { | ||
| const tokensThisCall = results.reduce((sum, r) => sum + r.stats.tokens, 0); | ||
| return { ...session, calls: session.calls + 1, totalTokens: session.totalTokens + tokensThisCall, filesProcessed: session.filesProcessed + results.length }; | ||
| } | ||
| function getTokenBudgetReport(session, budget) { | ||
| const remaining = budget - session.totalTokens; | ||
| const percentUsed = Math.round((session.totalTokens / budget) * 100); | ||
| return { sessionId: session.sessionId, totalCalls: session.calls, totalTokens: session.totalTokens, budget, remaining, | ||
| percentUsed: percentUsed + '%', status: percentUsed >= 100 ? 'EXCEEDED' : percentUsed >= 80 ? 'WARNING' : 'OK' }; | ||
| } | ||
| const LOG_DIR = path.join(__dirname, 'log'); | ||
| function writeRunLog(log) { | ||
| try { | ||
| if (!fs.existsSync(LOG_DIR)) | ||
| fs.mkdirSync(LOG_DIR, { recursive: true }); | ||
| const logFile = path.join(LOG_DIR, `${log.sessionId}.json`); | ||
| const existing = fs.existsSync(logFile) ? JSON.parse(fs.readFileSync(logFile, 'utf-8')) : []; | ||
| existing.push(log); | ||
| fs.writeFileSync(logFile, JSON.stringify(existing, null, 2)); | ||
| } | ||
| catch { } | ||
| } | ||
| function getPositionalArg(start) { | ||
| for (let i = start; i < process.argv.length; i++) { | ||
| if (!process.argv[i].startsWith('-')) | ||
| return process.argv[i]; | ||
| } | ||
| return ''; | ||
| } | ||
| function getFlagArg(flag) { | ||
| const idx = process.argv.indexOf(flag); | ||
| return idx > 0 && idx + 1 < process.argv.length ? process.argv[idx + 1] : null; | ||
| } | ||
| function getFragmentHealth(results) { | ||
| const total = results.length; | ||
| const withFrontmatter = results.filter(r => r.fragmentMeta).length; | ||
| const withDeps = results.filter(r => r.fragmentMeta && r.fragmentMeta.depends_on.length > 0).length; | ||
| const withWikilinks = results.filter(r => r.stats.totalWikilinks > 0).length; | ||
| const withStatus = results.filter(r => r.fragmentMeta && r.fragmentMeta.status).length; | ||
| const withDescription = results.filter(r => r.fragmentMeta && r.fragmentMeta.description).length; | ||
| const withSource = results.filter(r => r.fragmentMeta && r.fragmentMeta.source).length; | ||
| const noTitle = results.filter(r => r.fragmentMeta && !r.fragmentMeta.title).length; | ||
| const issues = []; | ||
| for (const r of results) { | ||
| const fileIssues = []; | ||
| if (!r.fragmentMeta) | ||
| fileIssues.push('no_frontmatter'); | ||
| else { | ||
| if (!r.fragmentMeta.title) | ||
| fileIssues.push('empty_title'); | ||
| if (!r.fragmentMeta.source) | ||
| fileIssues.push('no_source'); | ||
| if (r.fragmentMeta.depends_on.length === 0 && r.stats.totalWikilinks > 0) | ||
| fileIssues.push('wikilinks_no_depends_on'); | ||
| } | ||
| if (fileIssues.length > 0) | ||
| issues.push({ file: r.fileName, issues: fileIssues }); | ||
| } | ||
| return { total, withFrontmatter, withDeps, withWikilinks, withStatus, withDescription, withSource, noTitle, filesWithIssues: issues.length, issues }; | ||
| } | ||
| function main() { | ||
| const startTime = Date.now(); | ||
| const configPath = path.join(__dirname, 'hooks.toml'); | ||
| const config = getTomlConfig(configPath); | ||
| if (process.argv.includes('--help') || process.argv.includes('-h')) { | ||
| console.log(`md-analyzer - Markdown document analyzer for AI agents | ||
| Usage: md-analyzer <directory> [options] | ||
| Options: | ||
| --json Output as JSON | ||
| --search <kw> Search keyword in content | ||
| --filter <k=v> Filter by metadata field | ||
| --rank Rank results by relevance | ||
| --graph Document relationship graph | ||
| --deps Dependency graph (DAG order + levels) | ||
| --orphans Find unreferenced docs | ||
| --backlinks <doc> Find docs linking to <doc> | ||
| --keypoints Quick overview (single-shot) | ||
| --lint-fragments Fragment health check | ||
| --session Token budget report | ||
| --budget <n> Set token budget limit | ||
| --max-results <n> Limit output | ||
| --help, -h Show this help message | ||
| Examples: | ||
| md-analyzer /path/to/docs --keypoints --json | ||
| md-analyzer . --search "task" --rank --json | ||
| md-analyzer . --session --budget 50000 --json | ||
| md-analyzer . --orphans --json | ||
| md-analyzer . --lint-fragments --json | ||
| md-analyzer . --deps --json`); | ||
| process.exit(0); | ||
| } | ||
| const cliDir = getPositionalArg(2); | ||
| const targetDir = cliDir || process.env['MD_ANALYZER_DEFAULT_DIR'] || config['default_directory'] || process.cwd(); | ||
| const jsonOnly = process.argv.includes('--json'); | ||
| const graphMode = process.argv.includes('--graph'); | ||
| const orphansMode = process.argv.includes('--orphans'); | ||
| const rankMode = process.argv.includes('--rank'); | ||
| const sessionMode = process.argv.includes('--session'); | ||
| const keypointsMode = process.argv.includes('--keypoints'); | ||
| const depsMode = process.argv.includes('--deps'); | ||
| const lintFragmentsMode = process.argv.includes('--lint-fragments'); | ||
| const budget = parseInt(getFlagArg('--budget') || '', 10) || 100000; | ||
| const maxResults = parseInt(getFlagArg('--max-results') || '', 10) || 0; | ||
| const backlinksTarget = getFlagArg('--backlinks'); | ||
| const searchKeyword = getFlagArg('--search'); | ||
| const filterRaw = getFlagArg('--filter'); | ||
| const session = loadSession(); | ||
| if (!jsonOnly) | ||
| console.log(`Scanning: ${targetDir}\n`); | ||
| const { files: mdFiles, errors: scanErrors } = scanMarkdownFiles(targetDir); | ||
| if (!jsonOnly) { | ||
| console.log(`Found ${mdFiles.length} .md files\n`); | ||
| if (scanErrors.length > 0) | ||
| console.log(`Warnings: ${scanErrors.length} directories skipped\n`); | ||
| } | ||
| let results = mdFiles.map(file => analyzeFile(file)); | ||
| if (scanErrors.length > 0 && results.length > 0) { | ||
| if (!results[0].stats.errors) | ||
| results[0].stats.errors = []; | ||
| results[0].stats.errors.push(...scanErrors); | ||
| } | ||
| if (filterRaw && filterRaw.includes('=')) { | ||
| const [key, value] = filterRaw.split('='); | ||
| results = filterByMetadata(results, key, value); | ||
| if (!jsonOnly) | ||
| console.log(`Filtered by ${key}=${value}: ${results.length} results\n`); | ||
| } | ||
| if (searchKeyword) { | ||
| results = searchContent(results, searchKeyword); | ||
| if (!jsonOnly) | ||
| console.log(`Search "${searchKeyword}": ${results.length} results\n`); | ||
| } | ||
| if (rankMode && searchKeyword) { | ||
| results = rankByRelevance(results, searchKeyword); | ||
| if (!jsonOnly) | ||
| console.log(`Ranked by relevance to "${searchKeyword}"\n`); | ||
| } | ||
| let limitedResults = results; | ||
| if (maxResults > 0 && results.length > maxResults) { | ||
| if (!jsonOnly) | ||
| console.log(`Warning: Limiting output to ${maxResults} of ${results.length} results\n`); | ||
| limitedResults = results.slice(0, maxResults); | ||
| } | ||
| const updatedSession = updateSessionStats(results, session); | ||
| saveSession(updatedSession); | ||
| const tokensThisCall = results.reduce((sum, r) => sum + r.stats.tokens, 0); | ||
| if (sessionMode) | ||
| console.log(JSON.stringify(getTokenBudgetReport(updatedSession, budget), null, 2)); | ||
| else if (keypointsMode) | ||
| console.log(JSON.stringify(limitedResults.map(doc => extractKeyPoints(doc)), null, 2)); | ||
| else if (lintFragmentsMode) | ||
| console.log(JSON.stringify(getFragmentHealth(limitedResults), null, 2)); | ||
| else if (depsMode) { | ||
| const graph = buildGraph(limitedResults); | ||
| console.log(JSON.stringify({ nodes: Object.keys(graph.nodes), edges: graph.edges, tokensThisCall }, null, 2)); | ||
| } | ||
| else if (orphansMode) { | ||
| const orphans = findOrphans(buildGraph(limitedResults)); | ||
| console.log(JSON.stringify({ orphans, count: orphans.length, tokensThisCall }, null, 2)); | ||
| } | ||
| else if (backlinksTarget) { | ||
| const backlinks = findBacklinks(limitedResults, backlinksTarget); | ||
| console.log(JSON.stringify({ target: backlinksTarget, backlinks, count: backlinks.length, tokensThisCall }, null, 2)); | ||
| } | ||
| else if (graphMode) | ||
| console.log(JSON.stringify(buildGraph(limitedResults), null, 2)); | ||
| else { | ||
| if (!jsonOnly) { | ||
| console.log(`\nTokens this call: ${tokensThisCall}`); | ||
| console.log(`Total session tokens: ${updatedSession.totalTokens}\n`); | ||
| } | ||
| ; | ||
| console.log(JSON.stringify(limitedResults, null, 2)); | ||
| } | ||
| const flags = process.argv.slice(2).filter(a => a.startsWith('--')).map(a => a.replace(/=.*/, '')); | ||
| const mode = depsMode ? 'deps' : lintFragmentsMode ? 'lint-fragments' : sessionMode ? 'session' : keypointsMode ? 'keypoints' : orphansMode ? 'orphans' : backlinksTarget ? 'backlinks' : graphMode ? 'graph' : searchKeyword ? 'search' : 'default'; | ||
| writeRunLog({ | ||
| timestamp: new Date().toISOString(), | ||
| sessionId: updatedSession.sessionId, | ||
| directory: targetDir, | ||
| flags, | ||
| filesFound: mdFiles.length, | ||
| filesProcessed: results.length, | ||
| tokensThisCall, | ||
| totalSessionTokens: updatedSession.totalTokens, | ||
| errors: scanErrors, | ||
| durationMs: Date.now() - startTime, | ||
| mode | ||
| }); | ||
| } | ||
| main(); | ||
| //# sourceMappingURL=md-analyzer.js.map |
| #!/usr/bin/env node | ||
| import * as fs from 'fs' | ||
| import * as path from 'path' | ||
| import { encodingForModel } from 'js-tiktoken' | ||
| import * as yaml from 'js-yaml' | ||
| const SKIP_DIRS = new Set([ | ||
| 'node_modules', '.git', '.svn', '.hg', | ||
| 'services', 'data', 'appendonlydir', | ||
| 'dist', 'build', '__pycache__', '.next', 'coverage' | ||
| ]) | ||
| interface Link { text: string; url: string; isInternal: boolean; fileName: string | null } | ||
| interface Wikilink { target: string; display: string | null } | ||
| interface Heading { level: number; text: string; line: number } | ||
| interface Table { headers: string[]; rows: string[][] } | ||
| interface FragmentMeta { | ||
| title: string | ||
| description: string | null | ||
| tags: string[] | ||
| depends_on: string[] | ||
| status: string | null | ||
| source: string | null | ||
| order: number | null | ||
| date_iso: string | null | ||
| } | ||
| interface Stats { | ||
| totalHeadings: number; totalLinks: number; internalLinks: number; externalLinks: number | ||
| totalWikilinks: number; wordCount: number; charCount: number; lineCount: number | ||
| codeBlocks: number; tables: number; tokens: number; errors?: string[] | ||
| } | ||
| interface SectionInfo { line: number; tokens: number } | ||
| interface AnalysisResult { | ||
| file: string; fileName: string | ||
| metadata: Record<string, string> | null | ||
| fragmentMeta: FragmentMeta | null | ||
| headings: Heading[]; sections: SectionInfo[] | ||
| links: Link[]; wikilinks: Wikilink[]; tables: Table[] | ||
| stats: Stats | ||
| } | ||
| interface GraphNode { inbound: string[]; outbound: string[] } | ||
| interface Graph { nodes: Record<string, GraphNode>; edges: { source: string; target: string; type: string }[] } | ||
| interface SessionStats { sessionId: string; calls: number; totalTokens: number; filesProcessed: number; startTime: string } | ||
| function getTomlConfig(tomlPath: string): Record<string, any> { | ||
| try { | ||
| const content = fs.readFileSync(tomlPath, 'utf-8') | ||
| const config: Record<string, any> = {} | ||
| let inConfigSection = false | ||
| content.split('\n').forEach(line => { | ||
| const trimmed = line.trim() | ||
| if (trimmed === '[tool.md-analyzer.config]') { inConfigSection = true; return } | ||
| if (inConfigSection) { | ||
| if (trimmed.startsWith('[')) { inConfigSection = false; return } | ||
| if (trimmed.includes('=')) { | ||
| const [key, ...valueParts] = trimmed.split('=') | ||
| const value = valueParts.join('=').trim().replace(/^["']|["']$/g, '') | ||
| config[key.trim()] = value | ||
| } | ||
| } | ||
| }) | ||
| return config | ||
| } catch { return {} } | ||
| } | ||
| function extractFrontmatter(content: string): { metadata: Record<string, any> | null; content: string } { | ||
| const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n/ | ||
| const match = content.match(frontmatterRegex) | ||
| if (!match) return { metadata: null, content } | ||
| try { | ||
| const parsed = yaml.load(match[1]) | ||
| const metadata = parsed && typeof parsed === 'object' ? parsed as Record<string, any> : null | ||
| return { metadata, content: content.substring(match[0].length) } | ||
| } catch { | ||
| return { metadata: null, content } | ||
| } | ||
| } | ||
| function extractHeadings(content: string): Heading[] { | ||
| const headings: Heading[] = [] | ||
| const headingRegex = /^(#{1,6})\s+(.+)$/gm | ||
| let match | ||
| while ((match = headingRegex.exec(content)) !== null) { | ||
| const line = content.substring(0, match.index).split('\n').length | ||
| headings.push({ level: match[1].length, text: match[2].trim(), line }) | ||
| } | ||
| return headings | ||
| } | ||
| function extractLinks(content: string): Link[] { | ||
| const links: Link[] = [] | ||
| const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g | ||
| let match | ||
| while ((match = linkRegex.exec(content)) !== null) { | ||
| const url = match[2].trim() | ||
| const isInternal = url.startsWith('#') || (!url.startsWith('http') && !url.startsWith('//')) | ||
| let fileName: string | null = null | ||
| if (isInternal && !url.startsWith('#')) { | ||
| const baseName = path.basename(url, '.md') | ||
| if (baseName && baseName !== url) fileName = baseName | ||
| } | ||
| links.push({ text: match[1].trim(), url, isInternal, fileName }) | ||
| } | ||
| return links | ||
| } | ||
| function extractWikilinks(content: string): Wikilink[] { | ||
| const wikilinks: Wikilink[] = [] | ||
| const wikiRegex = /\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g | ||
| let match | ||
| while ((match = wikiRegex.exec(content)) !== null) { | ||
| wikilinks.push({ target: match[1].trim(), display: match[2]?.trim() || null }) | ||
| } | ||
| return wikilinks | ||
| } | ||
| function extractFragmentMeta(metadata: Record<string, any> | null): FragmentMeta | null { | ||
| if (!metadata) return null | ||
| const dependsRaw = metadata.depends_on | ||
| const depends: string[] = Array.isArray(dependsRaw) ? dependsRaw.map(String) : (typeof dependsRaw === 'string' ? [dependsRaw] : []) | ||
| const tags: string[] = Array.isArray(metadata.tags) ? metadata.tags.map(String) : (typeof metadata.tags === 'string' ? [metadata.tags] : []) | ||
| return { | ||
| title: String(metadata.title || ''), | ||
| description: metadata.description ? String(metadata.description) : null, | ||
| tags, | ||
| depends_on: depends, | ||
| status: metadata.status ? String(metadata.status) : null, | ||
| source: metadata.source ? String(metadata.source) : null, | ||
| order: metadata.order != null ? Number(metadata.order) : null, | ||
| date_iso: metadata.date_iso ? String(metadata.date_iso) : null, | ||
| } | ||
| } | ||
| function extractTables(content: string): Table[] { | ||
| const tables: Table[] = [] | ||
| const tableRegex = /\|(.+)\|\n\|[-:\s|]+\|\n((?:\|.+\|\n?)+)/g | ||
| let match | ||
| while ((match = tableRegex.exec(content)) !== null) { | ||
| const headers = match[1].split('|').map(h => h.trim()).filter(h => h) | ||
| const rows: string[][] = [] | ||
| match[2].trim().split('\n').forEach(row => { | ||
| const cells = row.split('|').map(c => c.trim()).filter(c => c) | ||
| if (cells.length > 0) rows.push(cells) | ||
| }) | ||
| tables.push({ headers, rows }) | ||
| } | ||
| return tables | ||
| } | ||
| function countStats(content: string): { wordCount: number; charCount: number; lineCount: number; codeBlocks: number; tokens: number } { | ||
| const wordCount = content.split(/\s+/).filter(w => w.length > 0).length | ||
| const charCount = content.length | ||
| const lineCount = content.split('\n').length | ||
| const codeBlocks = (content.match(/```[\s\S]*?```/g) || []).length | ||
| let tokens = 0 | ||
| try { tokens = encodingForModel('gpt-4').encode(content).length } catch { tokens = Math.ceil(charCount / 4) } | ||
| return { wordCount, charCount, lineCount, codeBlocks, tokens } | ||
| } | ||
| function scanMarkdownFiles(dir: string): { files: string[]; errors: string[] } { | ||
| const files: string[] = [], errors: string[] = [] | ||
| function walk(dir: string): void { | ||
| let entries | ||
| try { entries = fs.readdirSync(dir, { withFileTypes: true }) } | ||
| catch (e) { errors.push(`permission_denied: ${dir}`); return } | ||
| for (const entry of entries) { | ||
| if (entry.name.startsWith('.') || SKIP_DIRS.has(entry.name)) continue | ||
| const fullPath = path.join(dir, entry.name) | ||
| try { | ||
| if (entry.isDirectory()) walk(fullPath) | ||
| else if (entry.isFile() && entry.name.endsWith('.md')) files.push(fullPath) | ||
| } catch (e) { errors.push(`access_error: ${fullPath}`) } | ||
| } | ||
| } | ||
| try { walk(dir) } catch (e) { errors.push(`scan_error: ${e instanceof Error ? e.message : 'unknown'}`) } | ||
| return { files, errors } | ||
| } | ||
| function analyzeFile(filePath: string): AnalysisResult { | ||
| const errors: string[] = [] | ||
| let content = '' | ||
| try { content = fs.readFileSync(filePath, 'utf-8') } | ||
| catch (e) { | ||
| errors.push(`file_read_error: ${e instanceof Error ? e.message : 'unknown'}`) | ||
| return { file: filePath, fileName: path.basename(filePath, '.md'), metadata: null, fragmentMeta: null, headings: [], sections: [], links: [], wikilinks: [], tables: [], | ||
| stats: { totalHeadings: 0, totalLinks: 0, internalLinks: 0, externalLinks: 0, totalWikilinks: 0, wordCount: 0, charCount: 0, lineCount: 0, codeBlocks: 0, tables: 0, tokens: 0, errors } } | ||
| } | ||
| const { metadata, content: markdownContent } = extractFrontmatter(content) | ||
| const fragmentMeta = extractFragmentMeta(metadata) | ||
| const headings = extractHeadings(markdownContent) | ||
| const links = extractLinks(markdownContent) | ||
| const wikilinks = extractWikilinks(markdownContent) | ||
| const tables = extractTables(markdownContent) | ||
| const counts = countStats(markdownContent) | ||
| if (counts.tokens === 0) errors.push('token_count_fallback: tiktoken unavailable') | ||
| const bodyLines = markdownContent.split('\n') | ||
| const sections = headings.map((h, i) => { | ||
| const startIdx = h.line - 1 | ||
| const endIdx = i + 1 < headings.length ? headings[i + 1].line - 1 : bodyLines.length | ||
| const sectionText = bodyLines.slice(startIdx, endIdx).join('\n') | ||
| let tokens = 0 | ||
| try { tokens = encodingForModel('gpt-4').encode(sectionText).length } | ||
| catch { tokens = Math.ceil(sectionText.length / 4) } | ||
| return { line: h.line, tokens } | ||
| }) | ||
| return { file: filePath, fileName: path.basename(filePath, '.md'), metadata, fragmentMeta, headings, sections, links, wikilinks, tables, | ||
| stats: { totalHeadings: headings.length, totalLinks: links.length, internalLinks: links.filter(l => l.isInternal).length, | ||
| externalLinks: links.filter(l => !l.isInternal).length, totalWikilinks: wikilinks.length, wordCount: counts.wordCount, | ||
| charCount: counts.charCount, lineCount: counts.lineCount, codeBlocks: counts.codeBlocks, tables: tables.length, | ||
| tokens: counts.tokens, errors: errors.length > 0 ? errors : undefined } } | ||
| } | ||
| function addEdge(graph: Record<string, GraphNode>, edges: { source: string; target: string; type: string }[], source: string, target: string, type: string): void { | ||
| if (!graph[source]) graph[source] = { inbound: [], outbound: [] } | ||
| if (!graph[target]) graph[target] = { inbound: [], outbound: [] } | ||
| if (!graph[source].outbound.includes(target)) graph[source].outbound.push(target) | ||
| if (!graph[target].inbound.includes(source)) graph[target].inbound.push(source) | ||
| edges.push({ source, target, type }) | ||
| } | ||
| function buildGraph(results: AnalysisResult[]): Graph { | ||
| const graph: Record<string, GraphNode> = {}, edges: { source: string; target: string; type: string }[] = [] | ||
| results.forEach(doc => { | ||
| const source = doc.fileName | ||
| if (!graph[source]) graph[source] = { inbound: [], outbound: [] } | ||
| doc.links.forEach(link => { | ||
| if (link.isInternal && link.fileName) addEdge(graph, edges, source, link.fileName, 'link') | ||
| }) | ||
| doc.wikilinks.forEach(w => { | ||
| const slug = w.target.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') | ||
| if (slug !== source && results.some(r => r.fileName === slug)) addEdge(graph, edges, source, slug, 'wikilink') | ||
| }) | ||
| if (doc.fragmentMeta) { | ||
| doc.fragmentMeta.depends_on.forEach(dep => { | ||
| const depName = dep.replace(/\.md$/, '') | ||
| if (depName !== source && results.some(r => r.fileName === depName)) addEdge(graph, edges, source, depName, 'depends_on') | ||
| }) | ||
| } | ||
| }) | ||
| return { nodes: graph, edges } | ||
| } | ||
| function findOrphans(graph: Graph, excludeOrphansWithDeps?: Set<string>): string[] { | ||
| return Object.keys(graph.nodes).filter(node => { | ||
| if (excludeOrphansWithDeps?.has(node)) return false | ||
| return graph.nodes[node].inbound.length === 0 && graph.nodes[node].outbound.length === 0 | ||
| }) | ||
| } | ||
| function findBacklinks(results: AnalysisResult[], targetFileName: string): string[] { | ||
| const backlinks: string[] = [] | ||
| results.forEach(doc => { | ||
| doc.links.forEach(link => { | ||
| if (link.isInternal && link.fileName === targetFileName && !backlinks.includes(doc.fileName)) { | ||
| backlinks.push(doc.fileName) | ||
| } | ||
| }) | ||
| doc.wikilinks.forEach(w => { | ||
| const slug = w.target.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') | ||
| if (slug === targetFileName && !backlinks.includes(doc.fileName)) backlinks.push(doc.fileName) | ||
| }) | ||
| }) | ||
| return backlinks | ||
| } | ||
| function searchContent(results: AnalysisResult[], keyword: string): AnalysisResult[] { | ||
| const kw = keyword.toLowerCase() | ||
| return results.filter(doc => { | ||
| const content = fs.readFileSync(doc.file, 'utf-8').toLowerCase() | ||
| return content.includes(kw) | ||
| }) | ||
| } | ||
| function filterByMetadata(results: AnalysisResult[], key: string, value: string): AnalysisResult[] { | ||
| return results.filter(doc => doc.metadata && String(doc.metadata[key] || '') === value) | ||
| } | ||
| function rankByRelevance(results: AnalysisResult[], keyword: string): AnalysisResult[] { | ||
| const kw = keyword.toLowerCase() | ||
| return [...results].sort((a, b) => { | ||
| const countA = (fs.readFileSync(a.file, 'utf-8').toLowerCase().match(new RegExp(kw, 'g')) || []).length | ||
| const countB = (fs.readFileSync(b.file, 'utf-8').toLowerCase().match(new RegExp(kw, 'g')) || []).length | ||
| return countB - countA | ||
| }) | ||
| } | ||
| function extractKeyPoints(doc: AnalysisResult): object { | ||
| return { fileName: doc.fileName, title: doc.headings[0]?.text || doc.fileName, level: doc.headings[0]?.level || 1, | ||
| summary: { totalHeadings: doc.stats.totalHeadings, totalLinks: doc.stats.totalLinks, totalWikilinks: doc.stats.totalWikilinks, totalTokens: doc.stats.tokens, wordCount: doc.stats.wordCount }, | ||
| keyHeadings: doc.headings.slice(0, 10).map((h, i) => ({ | ||
| level: h.level, text: h.text, line: h.line, | ||
| tokens: doc.sections?.[i]?.tokens ?? 0 | ||
| })), | ||
| importantLinks: doc.links.filter(l => !l.isInternal).slice(0, 3).map(l => ({ text: l.text, url: l.url })), | ||
| internalReferences: doc.links.filter(l => l.isInternal && l.fileName).slice(0, 5).map(l => l.fileName), | ||
| metadata: doc.metadata, readingTime: Math.ceil(doc.stats.wordCount / 200) + ' min' } | ||
| } | ||
| const SESSION_FILE = '/tmp/md-analyzer-session.json' | ||
| function loadSession(): SessionStats { | ||
| try { return JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8')) } | ||
| catch { return { sessionId: `session-${Date.now()}`, calls: 0, totalTokens: 0, filesProcessed: 0, startTime: new Date().toISOString() } } | ||
| } | ||
| function saveSession(session: SessionStats): void { fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2)) } | ||
| function updateSessionStats(results: AnalysisResult[], session: SessionStats): SessionStats { | ||
| const tokensThisCall = results.reduce((sum, r) => sum + r.stats.tokens, 0) | ||
| return { ...session, calls: session.calls + 1, totalTokens: session.totalTokens + tokensThisCall, filesProcessed: session.filesProcessed + results.length } | ||
| } | ||
| function getTokenBudgetReport(session: SessionStats, budget: number): object { | ||
| const remaining = budget - session.totalTokens | ||
| const percentUsed = Math.round((session.totalTokens / budget) * 100) | ||
| return { sessionId: session.sessionId, totalCalls: session.calls, totalTokens: session.totalTokens, budget, remaining, | ||
| percentUsed: percentUsed + '%', status: percentUsed >= 100 ? 'EXCEEDED' : percentUsed >= 80 ? 'WARNING' : 'OK' } | ||
| } | ||
| const LOG_DIR = path.join(__dirname, 'log') | ||
| interface RunLog { | ||
| timestamp: string | ||
| sessionId: string | ||
| directory: string | ||
| flags: string[] | ||
| filesFound: number | ||
| filesProcessed: number | ||
| tokensThisCall: number | ||
| totalSessionTokens: number | ||
| errors: string[] | ||
| durationMs: number | ||
| mode: string | ||
| } | ||
| function writeRunLog(log: RunLog): void { | ||
| try { | ||
| if (!fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true }) | ||
| const logFile = path.join(LOG_DIR, `${log.sessionId}.json`) | ||
| const existing: RunLog[] = fs.existsSync(logFile) ? JSON.parse(fs.readFileSync(logFile, 'utf-8')) : [] | ||
| existing.push(log) | ||
| fs.writeFileSync(logFile, JSON.stringify(existing, null, 2)) | ||
| } catch {} | ||
| } | ||
| function getPositionalArg(start: number): string { | ||
| for (let i = start; i < process.argv.length; i++) { | ||
| if (!process.argv[i].startsWith('-')) return process.argv[i] | ||
| } | ||
| return '' | ||
| } | ||
| function getFlagArg(flag: string): string | null { | ||
| const idx = process.argv.indexOf(flag) | ||
| return idx > 0 && idx + 1 < process.argv.length ? process.argv[idx + 1] : null | ||
| } | ||
| function getFragmentHealth(results: AnalysisResult[]): object { | ||
| const total = results.length | ||
| const withFrontmatter = results.filter(r => r.fragmentMeta).length | ||
| const withDeps = results.filter(r => r.fragmentMeta && r.fragmentMeta.depends_on.length > 0).length | ||
| const withWikilinks = results.filter(r => r.stats.totalWikilinks > 0).length | ||
| const withStatus = results.filter(r => r.fragmentMeta && r.fragmentMeta.status).length | ||
| const withDescription = results.filter(r => r.fragmentMeta && r.fragmentMeta.description).length | ||
| const withSource = results.filter(r => r.fragmentMeta && r.fragmentMeta.source).length | ||
| const noTitle = results.filter(r => r.fragmentMeta && !r.fragmentMeta.title).length | ||
| const issues: { file: string; issues: string[] }[] = [] | ||
| for (const r of results) { | ||
| const fileIssues: string[] = [] | ||
| if (!r.fragmentMeta) fileIssues.push('no_frontmatter') | ||
| else { | ||
| if (!r.fragmentMeta.title) fileIssues.push('empty_title') | ||
| if (!r.fragmentMeta.source) fileIssues.push('no_source') | ||
| if (r.fragmentMeta.depends_on.length === 0 && r.stats.totalWikilinks > 0) fileIssues.push('wikilinks_no_depends_on') | ||
| } | ||
| if (fileIssues.length > 0) issues.push({ file: r.fileName, issues: fileIssues }) | ||
| } | ||
| return { total, withFrontmatter, withDeps, withWikilinks, withStatus, withDescription, withSource, noTitle, filesWithIssues: issues.length, issues } | ||
| } | ||
| function main(): void { | ||
| const startTime = Date.now() | ||
| const configPath = path.join(__dirname, 'hooks.toml') | ||
| const config = getTomlConfig(configPath) | ||
| if (process.argv.includes('--help') || process.argv.includes('-h')) { | ||
| console.log(`md-analyzer - Markdown document analyzer for AI agents | ||
| Usage: md-analyzer <directory> [options] | ||
| Options: | ||
| --json Output as JSON | ||
| --search <kw> Search keyword in content | ||
| --filter <k=v> Filter by metadata field | ||
| --rank Rank results by relevance | ||
| --graph Document relationship graph | ||
| --deps Dependency graph (DAG order + levels) | ||
| --orphans Find unreferenced docs | ||
| --backlinks <doc> Find docs linking to <doc> | ||
| --keypoints Quick overview (single-shot) | ||
| --lint-fragments Fragment health check | ||
| --session Token budget report | ||
| --budget <n> Set token budget limit | ||
| --max-results <n> Limit output | ||
| --help, -h Show this help message | ||
| Examples: | ||
| md-analyzer /path/to/docs --keypoints --json | ||
| md-analyzer . --search "task" --rank --json | ||
| md-analyzer . --session --budget 50000 --json | ||
| md-analyzer . --orphans --json | ||
| md-analyzer . --lint-fragments --json | ||
| md-analyzer . --deps --json`) | ||
| process.exit(0) | ||
| } | ||
| const cliDir = getPositionalArg(2) | ||
| const targetDir = cliDir || process.env['MD_ANALYZER_DEFAULT_DIR'] || config['default_directory'] || process.cwd() | ||
| const jsonOnly = process.argv.includes('--json') | ||
| const graphMode = process.argv.includes('--graph') | ||
| const orphansMode = process.argv.includes('--orphans') | ||
| const rankMode = process.argv.includes('--rank') | ||
| const sessionMode = process.argv.includes('--session') | ||
| const keypointsMode = process.argv.includes('--keypoints') | ||
| const depsMode = process.argv.includes('--deps') | ||
| const lintFragmentsMode = process.argv.includes('--lint-fragments') | ||
| const budget = parseInt(getFlagArg('--budget') || '', 10) || 100000 | ||
| const maxResults = parseInt(getFlagArg('--max-results') || '', 10) || 0 | ||
| const backlinksTarget = getFlagArg('--backlinks') | ||
| const searchKeyword = getFlagArg('--search') | ||
| const filterRaw = getFlagArg('--filter') | ||
| const session = loadSession() | ||
| if (!jsonOnly) console.log(`Scanning: ${targetDir}\n`) | ||
| const { files: mdFiles, errors: scanErrors } = scanMarkdownFiles(targetDir) | ||
| if (!jsonOnly) { console.log(`Found ${mdFiles.length} .md files\n`); if (scanErrors.length > 0) console.log(`Warnings: ${scanErrors.length} directories skipped\n`) } | ||
| let results = mdFiles.map(file => analyzeFile(file)) | ||
| if (scanErrors.length > 0 && results.length > 0) { if (!results[0].stats.errors) results[0].stats.errors = []; results[0].stats.errors.push(...scanErrors) } | ||
| if (filterRaw && filterRaw.includes('=')) { | ||
| const [key, value] = filterRaw.split('=') | ||
| results = filterByMetadata(results, key, value) | ||
| if (!jsonOnly) console.log(`Filtered by ${key}=${value}: ${results.length} results\n`) | ||
| } | ||
| if (searchKeyword) { | ||
| results = searchContent(results, searchKeyword) | ||
| if (!jsonOnly) console.log(`Search "${searchKeyword}": ${results.length} results\n`) | ||
| } | ||
| if (rankMode && searchKeyword) { | ||
| results = rankByRelevance(results, searchKeyword) | ||
| if (!jsonOnly) console.log(`Ranked by relevance to "${searchKeyword}"\n`) | ||
| } | ||
| let limitedResults = results | ||
| if (maxResults > 0 && results.length > maxResults) { | ||
| if (!jsonOnly) console.log(`Warning: Limiting output to ${maxResults} of ${results.length} results\n`) | ||
| limitedResults = results.slice(0, maxResults) | ||
| } | ||
| const updatedSession = updateSessionStats(results, session) | ||
| saveSession(updatedSession) | ||
| const tokensThisCall = results.reduce((sum, r) => sum + r.stats.tokens, 0) | ||
| if (sessionMode) console.log(JSON.stringify(getTokenBudgetReport(updatedSession, budget), null, 2)) | ||
| else if (keypointsMode) console.log(JSON.stringify(limitedResults.map(doc => extractKeyPoints(doc)), null, 2)) | ||
| else if (lintFragmentsMode) console.log(JSON.stringify(getFragmentHealth(limitedResults), null, 2)) | ||
| else if (depsMode) { const graph = buildGraph(limitedResults); console.log(JSON.stringify({ nodes: Object.keys(graph.nodes), edges: graph.edges, tokensThisCall }, null, 2)) } | ||
| else if (orphansMode) { const orphans = findOrphans(buildGraph(limitedResults)); console.log(JSON.stringify({ orphans, count: orphans.length, tokensThisCall }, null, 2)) } | ||
| else if (backlinksTarget) { const backlinks = findBacklinks(limitedResults, backlinksTarget); console.log(JSON.stringify({ target: backlinksTarget, backlinks, count: backlinks.length, tokensThisCall }, null, 2)) } | ||
| else if (graphMode) console.log(JSON.stringify(buildGraph(limitedResults), null, 2)) | ||
| else { if (!jsonOnly) { console.log(`\nTokens this call: ${tokensThisCall}`); console.log(`Total session tokens: ${updatedSession.totalTokens}\n`) }; console.log(JSON.stringify(limitedResults, null, 2)) } | ||
| const flags = process.argv.slice(2).filter(a => a.startsWith('--')).map(a => a.replace(/=.*/, '')) | ||
| const mode = depsMode ? 'deps' : lintFragmentsMode ? 'lint-fragments' : sessionMode ? 'session' : keypointsMode ? 'keypoints' : orphansMode ? 'orphans' : backlinksTarget ? 'backlinks' : graphMode ? 'graph' : searchKeyword ? 'search' : 'default' | ||
| writeRunLog({ | ||
| timestamp: new Date().toISOString(), | ||
| sessionId: updatedSession.sessionId, | ||
| directory: targetDir, | ||
| flags, | ||
| filesFound: mdFiles.length, | ||
| filesProcessed: results.length, | ||
| tokensThisCall, | ||
| totalSessionTokens: updatedSession.totalTokens, | ||
| errors: scanErrors, | ||
| durationMs: Date.now() - startTime, | ||
| mode | ||
| }) | ||
| } | ||
| main() |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
170678
184.57%68
750%2348
139.59%5
66.67%14
250%1
Infinity%+ Added
+ Added
+ Added
+ Added