docrev 0.9.13 → 0.9.15
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/.claude/settings.local.json +9 -9
- package/.gitattributes +1 -1
- package/CHANGELOG.md +149 -149
- package/PLAN-tables-and-postprocess.md +850 -850
- package/README.md +411 -391
- package/bin/rev.js +11 -11
- package/bin/rev.ts +145 -145
- package/completions/rev.bash +127 -127
- package/completions/rev.ps1 +210 -210
- package/completions/rev.zsh +207 -207
- package/dev_notes/stress2/build_adversarial.ts +186 -186
- package/dev_notes/stress2/drift_matcher.ts +62 -62
- package/dev_notes/stress2/probe_anchors.ts +35 -35
- package/dev_notes/stress2/project/discussion.before.md +3 -3
- package/dev_notes/stress2/project/discussion.md +3 -3
- package/dev_notes/stress2/project/methods.before.md +20 -20
- package/dev_notes/stress2/project/methods.md +20 -20
- package/dev_notes/stress2/project/rev.yaml +5 -5
- package/dev_notes/stress2/project/sections.yaml +4 -4
- package/dev_notes/stress2/sections.yaml +5 -5
- package/dev_notes/stress2/trace_placement.ts +50 -50
- package/dev_notes/stresstest_boundaries.ts +27 -27
- package/dev_notes/stresstest_drift_apply.ts +43 -43
- package/dev_notes/stresstest_drift_compare.ts +43 -43
- package/dev_notes/stresstest_drift_v2.ts +54 -54
- package/dev_notes/stresstest_inspect.ts +54 -54
- package/dev_notes/stresstest_pstyle.ts +55 -55
- package/dev_notes/stresstest_section_debug.ts +23 -23
- package/dev_notes/stresstest_split.ts +70 -70
- package/dev_notes/stresstest_trace.ts +19 -19
- package/dev_notes/stresstest_verify_no_overwrite.ts +40 -40
- package/dist/lib/build.d.ts +38 -1
- package/dist/lib/build.d.ts.map +1 -1
- package/dist/lib/build.js +68 -30
- package/dist/lib/build.js.map +1 -1
- package/dist/lib/commands/build.d.ts.map +1 -1
- package/dist/lib/commands/build.js +38 -5
- package/dist/lib/commands/build.js.map +1 -1
- package/dist/lib/commands/utilities.js +164 -164
- package/dist/lib/commands/word-tools.js +8 -8
- package/dist/lib/grammar.js +3 -3
- package/dist/lib/pdf-comments.js +44 -44
- package/dist/lib/plugins.js +57 -57
- package/dist/lib/pptx-themes.js +115 -115
- package/dist/lib/spelling.js +2 -2
- package/dist/lib/templates.js +387 -387
- package/dist/lib/themes.js +51 -51
- package/eslint.config.js +27 -27
- package/lib/anchor-match.ts +276 -276
- package/lib/annotations.ts +644 -644
- package/lib/build.ts +1300 -1251
- package/lib/citations.ts +160 -160
- package/lib/commands/build.ts +833 -801
- package/lib/commands/citations.ts +515 -515
- package/lib/commands/comments.ts +1050 -1050
- package/lib/commands/context.ts +174 -174
- package/lib/commands/core.ts +309 -309
- package/lib/commands/doi.ts +435 -435
- package/lib/commands/file-ops.ts +372 -372
- package/lib/commands/history.ts +320 -320
- package/lib/commands/index.ts +87 -87
- package/lib/commands/init.ts +259 -259
- package/lib/commands/merge-resolve.ts +378 -378
- package/lib/commands/preview.ts +178 -178
- package/lib/commands/project-info.ts +244 -244
- package/lib/commands/quality.ts +517 -517
- package/lib/commands/response.ts +454 -454
- package/lib/commands/section-boundaries.ts +82 -82
- package/lib/commands/sections.ts +451 -451
- package/lib/commands/sync.ts +706 -706
- package/lib/commands/text-ops.ts +449 -449
- package/lib/commands/utilities.ts +448 -448
- package/lib/commands/verify-anchors.ts +272 -272
- package/lib/commands/word-tools.ts +340 -340
- package/lib/comment-realign.ts +517 -517
- package/lib/config.ts +84 -84
- package/lib/crossref.ts +781 -781
- package/lib/csl.ts +191 -191
- package/lib/dependencies.ts +98 -98
- package/lib/diff-engine.ts +465 -465
- package/lib/doi-cache.ts +115 -115
- package/lib/doi.ts +897 -897
- package/lib/equations.ts +506 -506
- package/lib/errors.ts +346 -346
- package/lib/format.ts +541 -541
- package/lib/git.ts +326 -326
- package/lib/grammar.ts +303 -303
- package/lib/image-registry.ts +180 -180
- package/lib/import.ts +911 -911
- package/lib/journals.ts +543 -543
- package/lib/merge.ts +633 -633
- package/lib/orcid.ts +144 -144
- package/lib/pdf-comments.ts +263 -263
- package/lib/pdf-import.ts +524 -524
- package/lib/plugins.ts +362 -362
- package/lib/postprocess.ts +188 -188
- package/lib/pptx-color-filter.lua +37 -37
- package/lib/pptx-template.ts +469 -469
- package/lib/pptx-themes.ts +483 -483
- package/lib/protect-restore.ts +520 -520
- package/lib/rate-limiter.ts +94 -94
- package/lib/response.ts +197 -197
- package/lib/restore-references.ts +240 -240
- package/lib/review.ts +327 -327
- package/lib/schema.ts +417 -417
- package/lib/scientific-words.ts +73 -73
- package/lib/sections.ts +335 -335
- package/lib/slides.ts +756 -756
- package/lib/spelling.ts +334 -334
- package/lib/templates.ts +526 -526
- package/lib/themes.ts +742 -742
- package/lib/trackchanges.ts +247 -247
- package/lib/tui.ts +450 -450
- package/lib/types.ts +550 -550
- package/lib/undo.ts +250 -250
- package/lib/utils.ts +69 -69
- package/lib/variables.ts +179 -179
- package/lib/word-extraction.ts +806 -806
- package/lib/word.ts +643 -643
- package/lib/wordcomments.ts +817 -817
- package/package.json +137 -137
- package/scripts/postbuild.js +28 -28
- package/skill/REFERENCE.md +473 -431
- package/skill/SKILL.md +274 -258
- package/tsconfig.json +26 -26
- package/types/index.d.ts +525 -525
package/lib/citations.ts
CHANGED
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Citation validation utilities
|
|
3
|
-
* Check that all [@cite] references exist in .bib file
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import * as path from 'path';
|
|
8
|
-
import type { Citation, CitationValidation, CitationStats } from './types.js';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Extract all citation keys from markdown text
|
|
12
|
-
* Handles: [@Key], [@Key1; @Key2], @Key (inline)
|
|
13
|
-
* @param text - Markdown text to parse
|
|
14
|
-
* @param file - Optional filename for context
|
|
15
|
-
* @returns Array of citation objects
|
|
16
|
-
*/
|
|
17
|
-
export function extractCitations(text: string, file: string = ''): Citation[] {
|
|
18
|
-
const citations: Citation[] = [];
|
|
19
|
-
const lines = text.split('\n');
|
|
20
|
-
|
|
21
|
-
// Pattern for bracketed citations: [@Key] or [@Key1; @Key2]
|
|
22
|
-
const bracketPattern = /\[@([^\]]+)\]/g;
|
|
23
|
-
// Pattern for inline citations: @Key (word boundary)
|
|
24
|
-
const inlinePattern = /(?<!\[)@([A-Za-z][A-Za-z0-9_-]*\d{4}[a-z]?)(?![;\]])/g;
|
|
25
|
-
|
|
26
|
-
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
27
|
-
const line = lines[lineNum];
|
|
28
|
-
if (!line) continue;
|
|
29
|
-
|
|
30
|
-
// Skip code blocks and comments
|
|
31
|
-
if (line.trim().startsWith('```') || line.trim().startsWith('<!--')) continue;
|
|
32
|
-
|
|
33
|
-
// Bracketed citations
|
|
34
|
-
let match: RegExpExecArray | null;
|
|
35
|
-
while ((match = bracketPattern.exec(line)) !== null) {
|
|
36
|
-
// Split by ; for multiple citations
|
|
37
|
-
const citationGroup = match[1];
|
|
38
|
-
if (!citationGroup) continue;
|
|
39
|
-
const keys = citationGroup.split(';').map(k => k.trim().replace(/^@/, ''));
|
|
40
|
-
for (const key of keys) {
|
|
41
|
-
if (key) {
|
|
42
|
-
citations.push({ key, line: lineNum + 1, file });
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Inline citations (reset lastIndex)
|
|
48
|
-
inlinePattern.lastIndex = 0;
|
|
49
|
-
while ((match = inlinePattern.exec(line)) !== null) {
|
|
50
|
-
const citationKey = match[1];
|
|
51
|
-
if (citationKey) {
|
|
52
|
-
citations.push({ key: citationKey, line: lineNum + 1, file });
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return citations;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Parse .bib file and extract all entry keys
|
|
62
|
-
* @param bibPath - Path to bibliography file
|
|
63
|
-
* @returns Set of citation keys found in the bib file
|
|
64
|
-
*/
|
|
65
|
-
export function parseBibFile(bibPath: string): Set<string> {
|
|
66
|
-
const keys = new Set<string>();
|
|
67
|
-
|
|
68
|
-
if (!fs.existsSync(bibPath)) {
|
|
69
|
-
return keys;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const content = fs.readFileSync(bibPath, 'utf-8');
|
|
73
|
-
|
|
74
|
-
// Pattern for bib entries: @type{key,
|
|
75
|
-
const entryPattern = /@\w+\s*\{\s*([^,\s]+)\s*,/g;
|
|
76
|
-
|
|
77
|
-
let match: RegExpExecArray | null;
|
|
78
|
-
while ((match = entryPattern.exec(content)) !== null) {
|
|
79
|
-
const key = match[1];
|
|
80
|
-
if (key) {
|
|
81
|
-
keys.add(key);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return keys;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Validate citations against bib file
|
|
90
|
-
* @param mdFiles - Markdown files to check
|
|
91
|
-
* @param bibPath - Path to .bib file
|
|
92
|
-
* @returns Validation result with valid, missing, unused, and duplicate citations
|
|
93
|
-
*/
|
|
94
|
-
export function validateCitations(mdFiles: string[], bibPath: string): CitationValidation {
|
|
95
|
-
// Collect all citations from markdown
|
|
96
|
-
const allCitations: Citation[] = [];
|
|
97
|
-
for (const file of mdFiles) {
|
|
98
|
-
if (!fs.existsSync(file)) continue;
|
|
99
|
-
const text = fs.readFileSync(file, 'utf-8');
|
|
100
|
-
const citations = extractCitations(text, path.basename(file));
|
|
101
|
-
allCitations.push(...citations);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Get bib keys
|
|
105
|
-
const bibKeys = parseBibFile(bibPath);
|
|
106
|
-
|
|
107
|
-
// Categorize
|
|
108
|
-
const valid: Citation[] = [];
|
|
109
|
-
const missing: Citation[] = [];
|
|
110
|
-
const citedKeys = new Set<string>();
|
|
111
|
-
const keyOccurrences = new Map<string, Citation[]>();
|
|
112
|
-
|
|
113
|
-
for (const citation of allCitations) {
|
|
114
|
-
citedKeys.add(citation.key);
|
|
115
|
-
|
|
116
|
-
// Track occurrences for duplicates
|
|
117
|
-
if (!keyOccurrences.has(citation.key)) {
|
|
118
|
-
keyOccurrences.set(citation.key, []);
|
|
119
|
-
}
|
|
120
|
-
keyOccurrences.get(citation.key)!.push(citation);
|
|
121
|
-
|
|
122
|
-
if (bibKeys.has(citation.key)) {
|
|
123
|
-
valid.push(citation);
|
|
124
|
-
} else {
|
|
125
|
-
missing.push(citation);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Find unused bib entries
|
|
130
|
-
const unused = [...bibKeys].filter(key => !citedKeys.has(key));
|
|
131
|
-
|
|
132
|
-
// Find duplicate citations (same key cited multiple times - not an error, just info)
|
|
133
|
-
const duplicates = [...keyOccurrences.entries()]
|
|
134
|
-
.filter(([key, occurrences]) => occurrences.length > 1)
|
|
135
|
-
.map(([key, occurrences]) => ({ key, count: occurrences.length, locations: occurrences }));
|
|
136
|
-
|
|
137
|
-
return { valid, missing, unused, duplicates };
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Get citation statistics
|
|
142
|
-
* @param mdFiles - Markdown files to analyze
|
|
143
|
-
* @param bibPath - Path to bibliography file
|
|
144
|
-
* @returns Statistics object
|
|
145
|
-
*/
|
|
146
|
-
export function getCitationStats(mdFiles: string[], bibPath: string): CitationStats {
|
|
147
|
-
const result = validateCitations(mdFiles, bibPath);
|
|
148
|
-
const bibKeys = parseBibFile(bibPath);
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
totalCitations: result.valid.length + result.missing.length,
|
|
152
|
-
uniqueCited: new Set([...result.valid, ...result.missing].map(c => c.key)).size,
|
|
153
|
-
valid: result.valid.length,
|
|
154
|
-
missing: result.missing.length,
|
|
155
|
-
missingKeys: [...new Set(result.missing.map(c => c.key))],
|
|
156
|
-
bibEntries: bibKeys.size,
|
|
157
|
-
unused: result.unused.length,
|
|
158
|
-
unusedKeys: result.unused,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Citation validation utilities
|
|
3
|
+
* Check that all [@cite] references exist in .bib file
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import type { Citation, CitationValidation, CitationStats } from './types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Extract all citation keys from markdown text
|
|
12
|
+
* Handles: [@Key], [@Key1; @Key2], @Key (inline)
|
|
13
|
+
* @param text - Markdown text to parse
|
|
14
|
+
* @param file - Optional filename for context
|
|
15
|
+
* @returns Array of citation objects
|
|
16
|
+
*/
|
|
17
|
+
export function extractCitations(text: string, file: string = ''): Citation[] {
|
|
18
|
+
const citations: Citation[] = [];
|
|
19
|
+
const lines = text.split('\n');
|
|
20
|
+
|
|
21
|
+
// Pattern for bracketed citations: [@Key] or [@Key1; @Key2]
|
|
22
|
+
const bracketPattern = /\[@([^\]]+)\]/g;
|
|
23
|
+
// Pattern for inline citations: @Key (word boundary)
|
|
24
|
+
const inlinePattern = /(?<!\[)@([A-Za-z][A-Za-z0-9_-]*\d{4}[a-z]?)(?![;\]])/g;
|
|
25
|
+
|
|
26
|
+
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
27
|
+
const line = lines[lineNum];
|
|
28
|
+
if (!line) continue;
|
|
29
|
+
|
|
30
|
+
// Skip code blocks and comments
|
|
31
|
+
if (line.trim().startsWith('```') || line.trim().startsWith('<!--')) continue;
|
|
32
|
+
|
|
33
|
+
// Bracketed citations
|
|
34
|
+
let match: RegExpExecArray | null;
|
|
35
|
+
while ((match = bracketPattern.exec(line)) !== null) {
|
|
36
|
+
// Split by ; for multiple citations
|
|
37
|
+
const citationGroup = match[1];
|
|
38
|
+
if (!citationGroup) continue;
|
|
39
|
+
const keys = citationGroup.split(';').map(k => k.trim().replace(/^@/, ''));
|
|
40
|
+
for (const key of keys) {
|
|
41
|
+
if (key) {
|
|
42
|
+
citations.push({ key, line: lineNum + 1, file });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Inline citations (reset lastIndex)
|
|
48
|
+
inlinePattern.lastIndex = 0;
|
|
49
|
+
while ((match = inlinePattern.exec(line)) !== null) {
|
|
50
|
+
const citationKey = match[1];
|
|
51
|
+
if (citationKey) {
|
|
52
|
+
citations.push({ key: citationKey, line: lineNum + 1, file });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return citations;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Parse .bib file and extract all entry keys
|
|
62
|
+
* @param bibPath - Path to bibliography file
|
|
63
|
+
* @returns Set of citation keys found in the bib file
|
|
64
|
+
*/
|
|
65
|
+
export function parseBibFile(bibPath: string): Set<string> {
|
|
66
|
+
const keys = new Set<string>();
|
|
67
|
+
|
|
68
|
+
if (!fs.existsSync(bibPath)) {
|
|
69
|
+
return keys;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const content = fs.readFileSync(bibPath, 'utf-8');
|
|
73
|
+
|
|
74
|
+
// Pattern for bib entries: @type{key,
|
|
75
|
+
const entryPattern = /@\w+\s*\{\s*([^,\s]+)\s*,/g;
|
|
76
|
+
|
|
77
|
+
let match: RegExpExecArray | null;
|
|
78
|
+
while ((match = entryPattern.exec(content)) !== null) {
|
|
79
|
+
const key = match[1];
|
|
80
|
+
if (key) {
|
|
81
|
+
keys.add(key);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return keys;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Validate citations against bib file
|
|
90
|
+
* @param mdFiles - Markdown files to check
|
|
91
|
+
* @param bibPath - Path to .bib file
|
|
92
|
+
* @returns Validation result with valid, missing, unused, and duplicate citations
|
|
93
|
+
*/
|
|
94
|
+
export function validateCitations(mdFiles: string[], bibPath: string): CitationValidation {
|
|
95
|
+
// Collect all citations from markdown
|
|
96
|
+
const allCitations: Citation[] = [];
|
|
97
|
+
for (const file of mdFiles) {
|
|
98
|
+
if (!fs.existsSync(file)) continue;
|
|
99
|
+
const text = fs.readFileSync(file, 'utf-8');
|
|
100
|
+
const citations = extractCitations(text, path.basename(file));
|
|
101
|
+
allCitations.push(...citations);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Get bib keys
|
|
105
|
+
const bibKeys = parseBibFile(bibPath);
|
|
106
|
+
|
|
107
|
+
// Categorize
|
|
108
|
+
const valid: Citation[] = [];
|
|
109
|
+
const missing: Citation[] = [];
|
|
110
|
+
const citedKeys = new Set<string>();
|
|
111
|
+
const keyOccurrences = new Map<string, Citation[]>();
|
|
112
|
+
|
|
113
|
+
for (const citation of allCitations) {
|
|
114
|
+
citedKeys.add(citation.key);
|
|
115
|
+
|
|
116
|
+
// Track occurrences for duplicates
|
|
117
|
+
if (!keyOccurrences.has(citation.key)) {
|
|
118
|
+
keyOccurrences.set(citation.key, []);
|
|
119
|
+
}
|
|
120
|
+
keyOccurrences.get(citation.key)!.push(citation);
|
|
121
|
+
|
|
122
|
+
if (bibKeys.has(citation.key)) {
|
|
123
|
+
valid.push(citation);
|
|
124
|
+
} else {
|
|
125
|
+
missing.push(citation);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Find unused bib entries
|
|
130
|
+
const unused = [...bibKeys].filter(key => !citedKeys.has(key));
|
|
131
|
+
|
|
132
|
+
// Find duplicate citations (same key cited multiple times - not an error, just info)
|
|
133
|
+
const duplicates = [...keyOccurrences.entries()]
|
|
134
|
+
.filter(([key, occurrences]) => occurrences.length > 1)
|
|
135
|
+
.map(([key, occurrences]) => ({ key, count: occurrences.length, locations: occurrences }));
|
|
136
|
+
|
|
137
|
+
return { valid, missing, unused, duplicates };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get citation statistics
|
|
142
|
+
* @param mdFiles - Markdown files to analyze
|
|
143
|
+
* @param bibPath - Path to bibliography file
|
|
144
|
+
* @returns Statistics object
|
|
145
|
+
*/
|
|
146
|
+
export function getCitationStats(mdFiles: string[], bibPath: string): CitationStats {
|
|
147
|
+
const result = validateCitations(mdFiles, bibPath);
|
|
148
|
+
const bibKeys = parseBibFile(bibPath);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
totalCitations: result.valid.length + result.missing.length,
|
|
152
|
+
uniqueCited: new Set([...result.valid, ...result.missing].map(c => c.key)).size,
|
|
153
|
+
valid: result.valid.length,
|
|
154
|
+
missing: result.missing.length,
|
|
155
|
+
missingKeys: [...new Set(result.missing.map(c => c.key))],
|
|
156
|
+
bibEntries: bibKeys.size,
|
|
157
|
+
unused: result.unused.length,
|
|
158
|
+
unusedKeys: result.unused,
|
|
159
|
+
};
|
|
160
|
+
}
|