@billwgong/contextlint 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js ADDED
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ import { lint } from './index.js';
3
+ import { agents } from './types.js';
4
+ import { render } from './reporter.js';
5
+ import { renderHtml } from './html.js';
6
+ import { terminalText } from './safe-io.js';
7
+ import { pathToFileURL } from 'node:url';
8
+ import { saveHtmlReport } from './report-storage.js';
9
+ import { resolveLanguage, words } from './i18n.js';
10
+ const rawArgs = process.argv.slice(2);
11
+ let requestedLanguage;
12
+ for (let i = 0; i < rawArgs.length && rawArgs[i] !== '--'; i++) {
13
+ if (rawArgs[i] === '--lang')
14
+ requestedLanguage = rawArgs[++i];
15
+ }
16
+ const language = resolveLanguage(requestedLanguage);
17
+ const w = words(language);
18
+ const help = `${w.help}
19
+
20
+ --agent claude|codex|cursor|copilot ${w.helpAgent}
21
+ --json ${w.helpJson}
22
+ --html [output.html] ${w.helpHtml}
23
+ --lang en|zh ${w.helpLang}
24
+ --strict ${w.helpStrict}
25
+ --exclude <relative-path> ${w.helpExclude}
26
+ --help ${w.helpHelp}
27
+ --version ${w.helpVersion}
28
+
29
+ ${w.exitCodes}`;
30
+ async function main() {
31
+ const argv = process.argv.slice(2);
32
+ let root = '.', positional = false, json = false, strict = false;
33
+ let agent;
34
+ let html;
35
+ const exclude = [];
36
+ for (let i = 0; i < argv.length; i++) {
37
+ const arg = argv[i];
38
+ if (arg === '--') {
39
+ const rest = argv.slice(i + 1);
40
+ if (rest.length !== 1 || positional)
41
+ throw new Error(w.oneDirectory);
42
+ root = rest[0];
43
+ positional = true;
44
+ break;
45
+ }
46
+ if (arg === '--help' || arg === '-h') {
47
+ console.log(help);
48
+ return;
49
+ }
50
+ if (arg === '--version') {
51
+ console.log('0.1.0');
52
+ return;
53
+ }
54
+ if (arg === '--json') {
55
+ json = true;
56
+ continue;
57
+ }
58
+ if (arg === '--strict') {
59
+ strict = true;
60
+ continue;
61
+ }
62
+ if (arg === '--html') {
63
+ const next = argv[i + 1];
64
+ html = next && !next.startsWith('-') && (positional || /\.html$/i.test(next)) ? argv[++i] : true;
65
+ continue;
66
+ }
67
+ if (arg === '--agent' || arg === '--exclude' || arg === '--lang') {
68
+ const value = argv[++i];
69
+ if (!value || value.startsWith('-'))
70
+ throw new Error(`${arg}: ${w.requiresValue}`);
71
+ if (arg === '--agent') {
72
+ if (!agents.includes(value))
73
+ throw new Error(`${w.unknownAgent}: ${value}`);
74
+ agent = value;
75
+ }
76
+ else if (arg === '--lang') {
77
+ if (value !== 'en' && value !== 'zh')
78
+ throw new Error(w.unknownLanguage);
79
+ }
80
+ else
81
+ exclude.push(value);
82
+ continue;
83
+ }
84
+ if (arg.startsWith('-'))
85
+ throw new Error(`${w.unknownOption}: ${arg}`);
86
+ if (positional)
87
+ throw new Error(w.oneDirectory);
88
+ root = arg;
89
+ positional = true;
90
+ }
91
+ if (json && html)
92
+ throw new Error(w.incompatible);
93
+ if (typeof html === 'string' && !html.toLowerCase().endsWith('.html'))
94
+ throw new Error(w.htmlExtension);
95
+ const report = await lint(root, { agent, exclude });
96
+ if (html) {
97
+ const saved = await saveHtmlReport(report.root, renderHtml(report, language), typeof html === 'string' ? html : undefined);
98
+ console.log(terminalText(`${w.saved}: ${saved.outputPath}`));
99
+ if (saved.outputPath !== saved.archivePath)
100
+ console.log(terminalText(`${w.history}: ${saved.archivePath}`));
101
+ console.log(`${w.open}: ${pathToFileURL(saved.archivePath).href}`);
102
+ }
103
+ else
104
+ console.log(json ? JSON.stringify(report, null, 2) : render(report, language));
105
+ if (strict && report.findings.some(f => f.severity === 'warning'))
106
+ process.exitCode = 1;
107
+ else if (strict && report.status !== 'complete')
108
+ process.exitCode = 2;
109
+ }
110
+ main().catch((error) => {
111
+ console.error(terminalText(`ContextLint: ${w.error} — ${error instanceof Error ? error.message : String(error)}`));
112
+ process.exitCode = 2;
113
+ });
114
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/html.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { Report } from './types.js';
2
+ import { type Language } from './i18n.js';
3
+ export declare function renderHtml(report: Report, language?: Language): string;
package/dist/html.js ADDED
@@ -0,0 +1,41 @@
1
+ import path from 'node:path';
2
+ import { createHash } from 'node:crypto';
3
+ import { terminalText } from './safe-io.js';
4
+ import { describeFinding, en, zh, skipReason, words } from './i18n.js';
5
+ const escape = (value) => terminalText(String(value)).replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c]);
6
+ const style = `:root{color-scheme:light;--ink:#142b38;--muted:#657681;--line:#dfe6e7;--paper:#f4f7f7;--teal:#067c77}*{box-sizing:border-box}body{margin:0;font:15px/1.65 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC",sans-serif;color:var(--ink);background:var(--paper)}button,input{font:inherit}button{cursor:pointer}aside{position:fixed;inset:0 auto 0 0;width:230px;background:#112a36;color:#ecf4f5;padding:32px 24px;display:flex;flex-direction:column}.brand{font-size:23px;font-weight:750;letter-spacing:-1px}.brand b{color:#68d9cb}.tagline{font-size:12px;color:#9ab0ba;margin:8px 0 42px}.nav{display:block;padding:12px 16px;color:#b9ccd3;text-decoration:none;border-radius:9px;margin:7px 0}.nav.active{background:#244450;color:white}main{margin-left:230px;max-width:1450px;padding:42px 48px 64px}.top{display:flex;align-items:center;justify-content:space-between;color:var(--muted);font-size:13px}.hero{margin:30px 0 24px}.eyebrow{font-size:12px;letter-spacing:2px;color:var(--teal);font-weight:650}h1{font-size:36px;letter-spacing:-1.3px;line-height:1.3;margin:8px 0 12px}h1 em{font-style:normal;color:var(--teal)}.subtitle{color:var(--muted);max-width:750px}.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin:26px 0}.stat{padding:20px 22px;background:white;border:1px solid var(--line);border-radius:12px}.stat-label{font-size:13px;color:var(--muted)}.stat strong{font-size:32px;font-weight:700;display:block;margin:5px 0}.stat small{color:var(--muted);font-size:12px}.stat.red strong{color:#b74a42}.stat.amber strong{color:#ad781e}.layout{display:grid;grid-template-columns:minmax(0,1fr) 285px;gap:24px;align-items:start}.toolbar{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;margin:3px 0 18px}.filters{display:flex;gap:5px}.filters button{background:transparent;border:1px solid transparent;border-radius:7px;padding:7px 12px;color:var(--muted);font-size:13px}.filters button.selected{background:var(--ink);color:white}.search{min-width:160px;width:200px;padding:7px 11px;border:1px solid var(--line);border-radius:7px;background:white}.issue{background:white;border:1px solid var(--line);border-radius:13px;padding:24px;margin-bottom:18px}.issue-head{display:flex;gap:14px;align-items:center}.issue-icon{width:42px;height:42px;display:grid;place-items:center;border-radius:10px;font-size:25px;flex-shrink:0}.conflict{color:#b64e46;background:#fff0ed}.duplicate{color:#ac7d21;background:#fff6dd}.reference{color:#4c73a8;background:#edf2fa}.issue-meta{font-size:11px;color:var(--teal);text-transform:uppercase;letter-spacing:.6px}.issue-meta span{color:var(--muted);letter-spacing:0}h3{margin:2px 0;font-size:18px}.number{margin-left:auto;color:#a9b7bf;font-size:13px}.why{font-size:14px;color:var(--muted);margin:17px 0}.evidence{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.snippet{border:1px solid var(--line);border-radius:8px;overflow:hidden;background:#f7f9fa}.snippet:only-child{grid-column:1/-1}.file{display:flex;justify-content:space-between;gap:8px;padding:9px 12px;border-bottom:1px solid var(--line);font-size:11px;color:var(--muted);flex-wrap:wrap}.file span:first-child{color:var(--ink);font-weight:600}pre{margin:0;padding:14px 12px;white-space:pre-wrap;overflow-wrap:anywhere;font:12px/1.8 ui-monospace,SFMono-Regular,Consolas,monospace;color:#274451}.fix{background:#edf7f3;border-left:3px solid #58a68c;padding:12px 15px;margin-top:16px;border-radius:0 7px 7px 0}.fix span{font-size:11px;font-weight:700;color:#2a7660}.fix p{margin:3px 0 0;font-size:13px;color:#315f54}details{font-size:12px;color:var(--muted);margin-top:15px}summary{cursor:pointer}details p{overflow-wrap:anywhere}code{font:11px ui-monospace,monospace}.side{border:1px solid var(--line);border-radius:12px;background:white;padding:22px;margin-bottom:18px}.side h2{font-size:15px;margin:0 0 18px}.source{padding:13px 0;border-bottom:1px solid var(--line)}.source:last-child{border:0}.source strong{font:12px ui-monospace,monospace;display:block;overflow-wrap:anywhere}.source small{font-size:11px;color:var(--muted)}.bar{height:4px;border-radius:3px;background:#e6eeee;margin:9px 0 5px}.bar span{display:block;height:100%;background:#51a69b;border-radius:3px}.note{color:var(--muted);font-size:12px}.note strong{color:var(--ink)}.empty{padding:40px;border:1px dashed var(--line);border-radius:12px;text-align:center;color:var(--muted);background:white}footer{font-size:11px;color:var(--muted);border-top:1px solid var(--line);padding-top:20px;margin-top:24px}.skipped{font-size:12px;padding-left:16px;color:var(--muted)}[hidden]{display:none!important}@media(max-width:1100px){.layout{grid-template-columns:1fr}.right{display:grid;grid-template-columns:1fr 1fr;gap:16px}main{padding:30px}.evidence{grid-template-columns:1fr}}@media(max-width:760px){aside{position:static;width:auto;padding:16px 22px;display:block}.tagline,.nav{display:none}main{margin:0;padding:24px 18px}.stats{grid-template-columns:1fr 1fr;gap:10px}.right{display:block}h1{font-size:28px}.top{flex-wrap:wrap;gap:10px}.issue{padding:18px}.search{width:100%}}@media print{aside,.toolbar{display:none}main{margin:0;padding:0}.layout{display:block}.issue{break-inside:avoid}details{display:none}}
7
+ select{font:inherit;border:1px solid var(--line);border-radius:7px;background:white;padding:5px 8px;color:var(--ink)}.language-label{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}.top>div{flex-wrap:wrap}html[lang="en"] .brand{letter-spacing:-1px}`;
8
+ export function renderHtml(report, language = 'en') {
9
+ const scannedAt = new Date().toISOString();
10
+ const s = report.summary, count = report.findings.length, w = words(language);
11
+ const pair = (english, chinese) => `<span data-en="${escape(english)}" data-zh="${escape(chinese)}">${escape(language === 'zh' ? chinese : english)}</span>`;
12
+ const t = (key) => pair(en[key], zh[key]);
13
+ const cards = report.findings.map((f, i) => {
14
+ const d = describeFinding(f.id, 'en'), z = describeFinding(f.id, 'zh');
15
+ return `<article class="issue" data-kind="${d.category}" data-search="${escape(f.locations.map(l => l.path + ' ' + l.text).join(' '))}">
16
+ <div class="issue-head"><span class="issue-icon ${d.category}">${d.category === 'conflict' ? '↔' : d.category === 'duplicate' ? '≋' : '↗'}</span><div><div class="issue-meta">${t(f.confidence === 'high' ? 'high' : 'medium')}</div><h3>${pair(d.title, z.title)}</h3></div><span class="number">${String(i + 1).padStart(2, '0')}</span></div>
17
+ <p class="why">${pair(d.why, z.why)}</p>
18
+ <div class="evidence">${f.locations.map((l, index) => {
19
+ const range = String(l.line) + (l.endLine === l.line ? '' : '–' + l.endLine);
20
+ return `<div class="snippet"><div class="file"><span>${escape(l.path)}</span><span>${pair('Line ' + range + (f.locations.length > 1 ? ' · Source ' + (index + 1) : ''), '第 ' + range + ' 行' + (f.locations.length > 1 ? ' · 来源 ' + (index + 1) : ''))}</span></div><pre>${escape(l.text)}</pre></div>`;
21
+ }).join('')}</div>
22
+ <div class="fix"><span>${t('fix')}</span><p>${pair(d.fix, z.fix)}</p></div>
23
+ <details><summary>${t('evidence')}</summary><p>${pair(d.reason, z.reason)}</p><code>${f.id}</code></details>
24
+ </article>`;
25
+ }).join('');
26
+ const html = `<!doctype html><html lang="${language === 'zh' ? 'zh-CN' : 'en'}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${w.title}</title><style>${style}</style></head><body>
27
+ <aside><div class="brand">context<b>lint</b></div><div class="tagline">${t('tagline')}</div><a class="nav active" href="#overview">◉ &nbsp; ${t('overview')}</a><a class="nav" href="#issues">≋ &nbsp; ${t('issues')}</a><a class="nav" href="#sources">▤ &nbsp; ${t('sources')}</a></aside>
28
+ <main id="overview"><div class="top"><span>${t('project')} / ${escape(report.projectName ?? path.basename(report.root))}</span><div style="display:flex;gap:12px;align-items:center"><label for="language" class="language-label">${t('language')}</label><select id="language" aria-label="Language / 语言"><option value="en" ${language === 'en' ? 'selected' : ''}>English</option><option value="zh" ${language === 'zh' ? 'selected' : ''}>简体中文</option></select></div></div>
29
+ <section class="hero"><div class="eyebrow">${t('review')}</div><h1>${count ? `<em>${count}</em> ${t('headline')}` : t(s.files ? 'zeroHeadline' : 'noSourcesHeadline')}</h1><div class="subtitle">${t(count ? 'subtitle' : s.files ? 'zeroSubtitle' : 'noSourcesHint')}</div><p class="note">${t('scannedAt')}: <time datetime="${scannedAt}">${scannedAt}</time></p>${report.status === 'limited' ? `<p class="fix" role="status">${t('limitedNotice')}</p>` : ''}</section>
30
+ <div class="stats"><div class="stat red"><span class="stat-label">${t('conflicts')}</span><strong>${s.conflicts}</strong><small>${t('conflictNote')}</small></div><div class="stat amber"><span class="stat-label">${t('duplicates')}</span><strong>${s.duplicates}</strong><small>${t('duplicateNote')}</small></div><div class="stat"><span class="stat-label">${t('references')}</span><strong>${s.brokenReferences}</strong><small>${t('referenceNote')}</small></div><div class="stat"><span class="stat-label">${t('files')}</span><strong>${s.files}</strong><small>~${s.estimatedTokens.toLocaleString('en-US')} ${t('tokenNote')}</small></div></div>
31
+ <div class="layout"><section id="issues"><div class="toolbar"><div class="filters"><button class="selected" data-filter="all">${t('all')} ${count}</button><button data-filter="conflict">${t('conflict')}</button><button data-filter="duplicate">${t('duplicate')}</button><button data-filter="reference">${t('reference')}</button></div><input class="search" type="search" aria-label="${w.search}" placeholder="${w.search}" data-en-placeholder="${escape(en.search)}" data-zh-placeholder="${escape(zh.search)}"></div>${cards}<div id="empty" class="empty" ${count ? 'hidden' : ''}>${t('empty')}<br><small>${t('emptyNote')}</small></div></section>
32
+ <div class="right"><section id="sources" class="side"><h2>${t('sourceTitle')}</h2>${report.sources.map(source => `<div class="source"><strong>${escape(source.path)}</strong><div class="bar"><span style="width:${s.estimatedTokens ? Math.max(2, Math.round(source.estimatedTokens / s.estimatedTokens * 100)) : 0}%"></span></div><small>~${source.estimatedTokens} tokens<br>${source.ruleCount} ${t('rules')} · ${t(source.activation === 'directory' ? 'directory' : 'conditional')}</small></div>`).join('') || `<p class="note">${t('noFiles')}</p>`}</section><section class="side"><h2>${t('how')}</h2><p class="note"><strong>${t('step1')}</strong><br>${t('step1Text')}</p><p class="note"><strong>${t('step2')}</strong><br>${t('step2Text')}</p><p class="note"><strong>${t('step3')}</strong><br>${t('step3Text')}</p>${report.skipped.length ? `<details><summary>${report.skipped.length} ${t('limits')}</summary><ul class="skipped">${report.skipped.map(item => `<li>${escape(item.path)}: ${pair(skipReason(item.reason, 'en'), skipReason(item.reason, 'zh'))}</li>`).join('')}</ul></details>` : ''}</section></div></div>
33
+ <footer>${t('footer')}</footer></main>
34
+ <script>
35
+ (()=>{function formatTime(lang){const time=document.querySelector('time');time.textContent=new Date(time.dateTime).toLocaleString(lang==='zh'?'zh-CN':'en')}formatTime(document.documentElement.lang.startsWith('zh')?'zh':'en');let filter='all';const cards=[...document.querySelectorAll('.issue')],search=document.querySelector('.search'),buttons=[...document.querySelectorAll('[data-filter]')],language=document.getElementById('language');function update(){let visible=0;const query=search.value.toLocaleLowerCase();cards.forEach(card=>{const show=(filter==='all'||card.dataset.kind===filter)&&card.dataset.search.toLocaleLowerCase().includes(query);card.hidden=!show;if(show)visible++});document.getElementById('empty').hidden=visible>0}buttons.forEach(button=>button.addEventListener('click',()=>{filter=button.dataset.filter;buttons.forEach(b=>b.classList.toggle('selected',b===button));update()}));search.addEventListener('input',update);search.addEventListener('search',update);language.addEventListener('change',()=>{const lang=language.value;document.documentElement.lang=lang==='zh'?'zh-CN':'en';document.title=lang==='zh'?'ContextLint · 指令检查报告':'ContextLint · Instruction review';document.querySelectorAll('[data-en][data-zh]').forEach(node=>{node.textContent=node.dataset[lang]});search.placeholder=search.dataset[lang+'Placeholder'];search.setAttribute('aria-label',search.placeholder);formatTime(lang)})})();
36
+ </script></body></html>`;
37
+ const script = html.match(/<script>([\s\S]*?)<\/script>/)[1];
38
+ const hash = createHash('sha256').update(script).digest('base64');
39
+ return html.replace('<meta name="viewport"', `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'sha256-${hash}'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'"><meta name="viewport"`);
40
+ }
41
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"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"}
package/dist/i18n.d.ts ADDED
@@ -0,0 +1,188 @@
1
+ import type { Finding } from './types.js';
2
+ export type Language = 'en' | 'zh';
3
+ export declare function resolveLanguage(explicit?: string, env?: NodeJS.ProcessEnv): Language;
4
+ export declare const en: {
5
+ noSourcesHeadline: string;
6
+ noSourcesHint: string;
7
+ limitedNotice: string;
8
+ scannedAt: string;
9
+ title: string;
10
+ tagline: string;
11
+ overview: string;
12
+ issues: string;
13
+ sources: string;
14
+ project: string;
15
+ allAgents: string;
16
+ review: string;
17
+ headline: string;
18
+ zeroHeadline: string;
19
+ subtitle: string;
20
+ zeroSubtitle: string;
21
+ conflicts: string;
22
+ duplicates: string;
23
+ references: string;
24
+ files: string;
25
+ conflictNote: string;
26
+ duplicateNote: string;
27
+ referenceNote: string;
28
+ tokenNote: string;
29
+ all: string;
30
+ conflict: string;
31
+ duplicate: string;
32
+ reference: string;
33
+ search: string;
34
+ high: string;
35
+ medium: string;
36
+ line: string;
37
+ location: string;
38
+ fix: string;
39
+ evidence: string;
40
+ empty: string;
41
+ emptyNote: string;
42
+ sourceTitle: string;
43
+ rules: string;
44
+ directory: string;
45
+ conditional: string;
46
+ noFiles: string;
47
+ how: string;
48
+ step1: string;
49
+ step1Text: string;
50
+ step2: string;
51
+ step2Text: string;
52
+ step3: string;
53
+ step3Text: string;
54
+ limits: string;
55
+ footer: string;
56
+ warning: string;
57
+ info: string;
58
+ found: string;
59
+ tokens: string;
60
+ ruleCount: string;
61
+ skipped: string;
62
+ reason: string;
63
+ terminalFooter: string;
64
+ saved: string;
65
+ history: string;
66
+ open: string;
67
+ language: string;
68
+ error: string;
69
+ help: string;
70
+ helpAgent: string;
71
+ helpJson: string;
72
+ helpHtml: string;
73
+ helpLang: string;
74
+ helpStrict: string;
75
+ helpExclude: string;
76
+ helpHelp: string;
77
+ helpVersion: string;
78
+ exitCodes: string;
79
+ requiresValue: string;
80
+ unknownAgent: string;
81
+ unknownOption: string;
82
+ oneDirectory: string;
83
+ incompatible: string;
84
+ htmlExtension: string;
85
+ unknownLanguage: string;
86
+ symLink: string;
87
+ excluded: string;
88
+ large: string;
89
+ unresolved: string;
90
+ };
91
+ export declare const zh: Record<keyof typeof en, string>;
92
+ export declare const words: (language: Language) => {
93
+ noSourcesHeadline: string;
94
+ noSourcesHint: string;
95
+ limitedNotice: string;
96
+ scannedAt: string;
97
+ title: string;
98
+ tagline: string;
99
+ overview: string;
100
+ issues: string;
101
+ sources: string;
102
+ project: string;
103
+ allAgents: string;
104
+ review: string;
105
+ headline: string;
106
+ zeroHeadline: string;
107
+ subtitle: string;
108
+ zeroSubtitle: string;
109
+ conflicts: string;
110
+ duplicates: string;
111
+ references: string;
112
+ files: string;
113
+ conflictNote: string;
114
+ duplicateNote: string;
115
+ referenceNote: string;
116
+ tokenNote: string;
117
+ all: string;
118
+ conflict: string;
119
+ duplicate: string;
120
+ reference: string;
121
+ search: string;
122
+ high: string;
123
+ medium: string;
124
+ line: string;
125
+ location: string;
126
+ fix: string;
127
+ evidence: string;
128
+ empty: string;
129
+ emptyNote: string;
130
+ sourceTitle: string;
131
+ rules: string;
132
+ directory: string;
133
+ conditional: string;
134
+ noFiles: string;
135
+ how: string;
136
+ step1: string;
137
+ step1Text: string;
138
+ step2: string;
139
+ step2Text: string;
140
+ step3: string;
141
+ step3Text: string;
142
+ limits: string;
143
+ footer: string;
144
+ warning: string;
145
+ info: string;
146
+ found: string;
147
+ tokens: string;
148
+ ruleCount: string;
149
+ skipped: string;
150
+ reason: string;
151
+ terminalFooter: string;
152
+ saved: string;
153
+ history: string;
154
+ open: string;
155
+ language: string;
156
+ error: string;
157
+ help: string;
158
+ helpAgent: string;
159
+ helpJson: string;
160
+ helpHtml: string;
161
+ helpLang: string;
162
+ helpStrict: string;
163
+ helpExclude: string;
164
+ helpHelp: string;
165
+ helpVersion: string;
166
+ exitCodes: string;
167
+ requiresValue: string;
168
+ unknownAgent: string;
169
+ unknownOption: string;
170
+ oneDirectory: string;
171
+ incompatible: string;
172
+ htmlExtension: string;
173
+ unknownLanguage: string;
174
+ symLink: string;
175
+ excluded: string;
176
+ large: string;
177
+ unresolved: string;
178
+ };
179
+ type Description = {
180
+ title: string;
181
+ why: string;
182
+ fix: string;
183
+ reason: string;
184
+ category: string;
185
+ };
186
+ export declare const describeFinding: (id: Finding["id"], language: Language) => Description;
187
+ export declare function skipReason(reason: string, language: Language): string;
188
+ export {};
package/dist/i18n.js ADDED
@@ -0,0 +1,93 @@
1
+ export function resolveLanguage(explicit, env = process.env) {
2
+ if (explicit === 'en' || explicit === 'zh')
3
+ return explicit;
4
+ return /^zh(?:[_-]|$)/i.test(env.LC_ALL || env.LC_MESSAGES || env.LANG || '') ? 'zh' : 'en';
5
+ }
6
+ export const en = {
7
+ noSourcesHeadline: 'No instruction files checked', noSourcesHint: 'Check the scan directory, agent filter and exclusions. Add AGENTS.md or CLAUDE.md to start.',
8
+ limitedNotice: 'Partial analysis: some sources or checks were skipped. Review the limitations below.', scannedAt: 'Scanned',
9
+ title: 'ContextLint · Instruction review', tagline: 'Clear instructions. Visible evidence.',
10
+ overview: 'Overview', issues: 'Findings & suggestions', sources: 'Instruction sources',
11
+ project: 'Project', allAgents: 'All agents', review: 'Instruction review', headline: 'findings to review', zeroHeadline: 'No supported issues found',
12
+ subtitle: 'Read the evidence before editing. Every finding includes source locations, detection details, and a suggested next step.',
13
+ zeroSubtitle: 'No supported checks matched. Complex conditions and instruction meaning still need human review.',
14
+ conflicts: 'Potential conflicts', duplicates: 'Duplicate rules', references: 'Broken references', files: 'Instruction files',
15
+ conflictNote: 'Counted as pairs · Check conditions', duplicateNote: 'Counted as groups', referenceNote: 'Local link target not found',
16
+ tokenNote: 'estimated tokens · Not session usage', all: 'All', conflict: 'Conflicts', duplicate: 'Duplicates', reference: 'References',
17
+ search: 'Search files or instruction text…', high: 'High confidence', medium: 'Needs review', line: 'Line', location: 'Source',
18
+ fix: 'Suggested next step', evidence: 'Detection details', empty: 'No findings match this filter.', emptyNote: 'No findings does not mean no issues.',
19
+ sourceTitle: 'Files checked', rules: 'candidate rules', directory: 'Directory scope', conditional: 'Activation unresolved',
20
+ noFiles: 'No supported instruction files found.', how: 'Reading this report',
21
+ step1: '01 Read both instructions', step1Text: 'Check whether they describe the same scenario.',
22
+ step2: '02 Look for missing conditions', step2Text: 'A difference may be an intentional exception.',
23
+ step3: '03 Edit, then scan again', step3Text: 'This report never edits your files.', limits: 'scan or analysis limitations',
24
+ footer: 'ContextLint · Static checks cannot prove what an agent loaded or followed. Detection is limited; token counts are estimates.',
25
+ warning: 'warning', info: 'info', found: 'Found', tokens: 'estimated tokens', ruleCount: 'rules', skipped: 'Skipped / limited analysis', reason: 'Reason',
26
+ terminalFooter: 'Static file analysis only. Token counts are estimates; review findings before editing.',
27
+ saved: 'Visual report saved', history: 'History copy', open: 'Open saved report', language: 'Report language', error: 'Could not complete the scan',
28
+ help: 'Usage: contextlint [directory] [options]', helpAgent: 'Check sources for one agent', helpJson: 'Output versioned JSON',
29
+ helpHtml: 'Save a persistent visual report; optional additional output path', helpLang: 'Choose output language (default: locale, then English)',
30
+ helpStrict: 'Exit 1 for warnings; 2 for incomplete analysis or no sources', helpExclude: 'Exclude a literal path (repeatable)', helpHelp: 'Show help', helpVersion: 'Show version',
31
+ exitCodes: 'Exit codes: 0 completed; 1 strict warnings; 2 execution/usage error or strict scan without complete coverage.',
32
+ requiresValue: 'requires a value', unknownAgent: 'Unknown agent', unknownOption: 'Unknown option', oneDirectory: 'Only one directory can be scanned',
33
+ incompatible: '--json and --html cannot be combined', htmlExtension: '--html output must end with .html', unknownLanguage: 'Language must be en or zh',
34
+ symLink: 'Symbolic links are not followed', excluded: 'Explicitly excluded', large: 'Instruction file exceeds the 1 MiB limit',
35
+ unresolved: 'Activation conditions are unresolved; cross-file comparisons disabled'
36
+ };
37
+ export const zh = {
38
+ noSourcesHeadline: '没有检查到指令文件', noSourcesHint: '检查扫描目录、助手筛选和排除项。可添加 AGENTS.md 或 CLAUDE.md 后重试。',
39
+ limitedNotice: '本次分析不完整:部分来源或检查被跳过,请查看下方限制。', scannedAt: '扫描时间',
40
+ title: 'ContextLint · 指令检查报告', tagline: '指令更清楚,依据看得见', overview: '检查概览', issues: '问题与建议', sources: '指令来源',
41
+ project: '项目', allAgents: '全部助手', review: '指令检查', headline: '项发现值得检查', zeroHeadline: '本次未发现已支持的问题',
42
+ subtitle: '先看原文,再决定怎么修改。每一项都保留了来源位置、检测依据与修改建议。',
43
+ zeroSubtitle: '当前规则没有匹配到问题。复杂条件和语义仍需人工检查。',
44
+ conflicts: '潜在冲突', duplicates: '重复规则', references: '失效引用', files: '指令文件',
45
+ conflictNote: '按冲突对计数 · 需确认条件', duplicateNote: '按重复组计数', referenceNote: '未找到本地链接目标',
46
+ tokenNote: '估算 tokens · 非会话用量', all: '全部', conflict: '冲突', duplicate: '重复', reference: '引用',
47
+ search: '搜索文件或原文…', high: '高置信度', medium: '需要人工确认', line: '行', location: '来源',
48
+ fix: '建议修改', evidence: '查看检测依据', empty: '当前筛选下没有发现。', emptyNote: '未发现不等于已验证无问题。',
49
+ sourceTitle: '检查了哪些文件', rules: '条候选规则', directory: '目录范围', conditional: '激活条件未解析',
50
+ noFiles: '没有找到支持的指令文件。', how: '如何阅读这份报告',
51
+ step1: '01 看两段原文', step1Text: '确认是否用于同一场景。', step2: '02 检查缺失的条件', step2Text: '有些差异可能是有意的例外。',
52
+ step3: '03 修改后重新扫描', step3Text: '报告不会自动修改你的文件。', limits: '项扫描或分析限制',
53
+ footer: 'ContextLint · 静态检查无法证明助手实际加载或遵守了哪些规则。检测能力有限,token 数为估算。',
54
+ warning: '警告', info: '提示', found: '发现', tokens: '估算 tokens', ruleCount: '条规则', skipped: '跳过项 / 分析限制', reason: '依据',
55
+ terminalFooter: '仅进行静态文件分析。Token 数为估算,修改前请审查诊断结果。',
56
+ saved: '可视化报告已保存', history: '历史副本', open: '打开已保存报告', language: '报告语言', error: '未能完成扫描',
57
+ help: '用法:contextlint [目录] [选项]', helpAgent: '只检查指定助手的来源', helpJson: '输出版本化 JSON',
58
+ helpHtml: '持续保存可视化报告,可指定额外输出路径', helpLang: '选择输出语言(默认按系统语言,否则使用英语)',
59
+ helpStrict: '警告返回 1;分析不完整或无文件返回 2', helpExclude: '排除指定相对路径(可重复使用)', helpHelp: '显示帮助', helpVersion: '显示版本',
60
+ exitCodes: '退出码:0 完成;1 严格检查发现警告;2 执行/参数错误或严格检查无完整结果。',
61
+ requiresValue: '需要提供参数值', unknownAgent: '未知助手', unknownOption: '未知选项', oneDirectory: '一次只能扫描一个目录',
62
+ incompatible: '--json 和 --html 不能同时使用', htmlExtension: '--html 输出文件必须以 .html 结尾', unknownLanguage: '语言必须为 en 或 zh',
63
+ symLink: '不跟随符号链接', excluded: '已按参数排除', large: '指令文件超过 1 MiB 上限', unresolved: '激活条件未解析,已禁用跨文件比较'
64
+ };
65
+ export const words = (language) => language === 'zh' ? zh : en;
66
+ const english = {
67
+ PACKAGE_MANAGER_CONFLICT: { title: 'Competing package-manager choices', why: 'Different package managers are selected within the same static scope. Installing or updating dependencies may become ambiguous.', fix: 'Check the project lockfile and choose the intended package manager. If the choices belong to different subprojects, make those conditions explicit.', reason: 'Explicit incompatible package-manager choices share an agent and static scope. Choices within one file may cross headings; cross-file choices require matching headings. Runtime loading and precedence are not inferred.', category: 'conflict' },
68
+ POLARITY_CONFLICT: { title: 'The same action is required and forbidden', why: 'These instructions give opposite directions for the same action. Check whether a condition or exception is missing.', fix: 'Confirm the intended behavior and keep the matching rule. If both rules are valid in different situations, write those conditions explicitly.', reason: 'Matching action text uses opposite supported directives in the same agent, heading context, and static scope.', category: 'conflict' },
69
+ DUPLICATE_RULE: { title: 'A rule appears more than once', why: 'The instructions match after ignoring list markers, whitespace differences, and trailing sentence punctuation.', fix: 'Check whether both copies are needed. Within the same scope, one clear instruction may be easier to maintain.', reason: 'Normalized rule text matches in the same static scope and heading context for a shared agent.', category: 'duplicate' },
70
+ BROKEN_REFERENCE: { title: 'A local reference is missing', why: 'A Markdown link points to a file that could not be found relative to the instruction file.', fix: 'Update the relative path, restore the file, or remove the outdated reference.', reason: 'A relative Markdown link was resolved from the instruction file directory within the scan root, but the target was not found.', category: 'reference' }
71
+ };
72
+ const chinese = {
73
+ PACKAGE_MANAGER_CONFLICT: { title: '包管理器选择不一致', why: '同一适用范围内出现了不同的包管理器选择,执行安装或更新依赖时可能产生歧义。', fix: '结合 lockfile 确定项目使用的包管理器,再统一这些指令。若用于不同子项目,请明确写出适用条件。', reason: '明确的不兼容包管理器选择具有相同助手和静态范围。同一文件内允许跨标题比较,跨文件仍要求标题相同。未推断运行时加载或优先级。', category: 'conflict' },
74
+ POLARITY_CONFLICT: { title: '同一个动作被同时要求和禁止', why: '这些原文对相同动作给出了相反要求,需要确认是否遗漏了条件或例外。', fix: '确认你期望的行为,保留对应规则;如果两者适用于不同场景,请分别补充条件。', reason: '相同助手、标题上下文和静态范围内,匹配的动作文本使用了相反的明确要求。', category: 'conflict' },
75
+ DUPLICATE_RULE: { title: '同一条规则重复出现', why: '忽略列表符号、空白和句末标点后,这些规则一致。', fix: '检查是否需要多处保留。对于同范围的重复条目,可以保留一份清晰的表述。', reason: '相同静态范围和标题上下文中,共享助手的规则文本在归一化后相同。', category: 'duplicate' },
76
+ BROKEN_REFERENCE: { title: '引用的本地文件不存在', why: '按指令文件所在目录解析 Markdown 链接,未找到对应文件。', fix: '将链接更新为正确的相对路径,恢复文件,或删除已经过时的引用。', reason: '在扫描根目录内,按指令文件所在目录解析 Markdown 相对链接,未找到目标。', category: 'reference' }
77
+ };
78
+ export const describeFinding = (id, language) => (language === 'zh' ? chinese : english)[id];
79
+ export function skipReason(reason, language) {
80
+ const w = words(language);
81
+ if (reason.startsWith('Activation conditions'))
82
+ return w.unresolved;
83
+ if (reason.startsWith('Instruction file exceeds'))
84
+ return w.large;
85
+ if (language === 'zh') {
86
+ if (reason.includes('limit'))
87
+ return '达到扫描或分析上限;结果不完整(' + reason + ')';
88
+ if (reason.includes('could not be read'))
89
+ return '无法读取文件或目录;结果不完整(' + reason + ')';
90
+ }
91
+ return ({ 'Symbolic links are not followed': w.symLink, 'Explicitly excluded': w.excluded, 'Instruction file exceeds 1 MiB limit': w.large })[reason] ?? reason;
92
+ }
93
+ //# sourceMappingURL=i18n.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.js","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Report, ScanOptions } from './types.js';
2
+ export type { Report, ScanOptions, Finding, Source, Agent } from './types.js';
3
+ export declare function lint(root?: string, options?: ScanOptions): Promise<Report>;
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ import path from 'node:path';
2
+ import { readLocalFile } from './safe-io.js';
3
+ import { scan, describe } from './scanner.js';
4
+ import { analyzeReferences, analyzeRules } from './analyzers.js';
5
+ async function projectName(root) {
6
+ try {
7
+ const manifest = path.join(root, 'package.json');
8
+ const data = JSON.parse((await readLocalFile(root, manifest, 1024 * 1024)).text);
9
+ if (typeof data?.name === 'string' && data.name.trim())
10
+ return data.name.trim();
11
+ }
12
+ catch { /* Missing or invalid metadata falls back to the scanned directory. */ }
13
+ return path.basename(root) || root;
14
+ }
15
+ export async function lint(root = '.', options = {}) {
16
+ const scanned = await scan(root, options);
17
+ const findings = [...analyzeRules(scanned.rules, options.agent, scanned.skipped), ...await analyzeReferences(scanned.root, scanned.rules, options.agent, scanned.skipped)]
18
+ .sort((a, b) => a.locations[0].path.localeCompare(b.locations[0].path, 'en') || a.locations[0].line - b.locations[0].line || a.id.localeCompare(b.id));
19
+ return {
20
+ schemaVersion: '1.0', root: scanned.root, projectName: await projectName(scanned.root),
21
+ status: scanned.skipped.some(item => item.reason !== 'Explicitly excluded' && (item.reason !== 'Symbolic links are not followed' || Boolean(describe(item.path)))) ? 'limited' : scanned.sources.length ? 'complete' : 'no-sources',
22
+ options: { agent: options.agent ?? null, exclude: options.exclude ?? [] },
23
+ tokenEstimation: 'Heuristic: ASCII UTF-16 units / 4 + non-ASCII code points. Not a model tokenizer or session cost.',
24
+ sources: scanned.sources, skipped: scanned.skipped, findings,
25
+ summary: { files: scanned.sources.length, rules: scanned.rules.length,
26
+ estimatedTokens: scanned.sources.reduce((sum, s) => sum + s.estimatedTokens, 0),
27
+ conflicts: findings.filter(f => f.id.endsWith('CONFLICT')).length,
28
+ duplicates: findings.filter(f => f.id === 'DUPLICATE_RULE').length,
29
+ brokenReferences: findings.filter(f => f.id === 'BROKEN_REFERENCE').length }
30
+ };
31
+ }
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Rule, Source } from './types.js';
2
+ export declare function estimateTokens(text: string): number;
3
+ export declare function normalize(text: string): string;
4
+ export declare function parse(content: string, source: Source): Rule[];