🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@ev3lynx/md-analyzer

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ev3lynx/md-analyzer - npm Package Compare versions

Comparing version
0.1.4
to
0.1.6
+2
dist/cli/index.d.ts
#!/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"}
#!/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'
}
}
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",

#!/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()