docrev 0.8.1 → 0.8.5

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.
Files changed (306) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/PLAN-tables-and-postprocess.md +850 -0
  3. package/README.md +33 -0
  4. package/bin/rev.js +12 -131
  5. package/bin/rev.ts +145 -0
  6. package/dist/bin/rev.d.ts +9 -0
  7. package/dist/bin/rev.d.ts.map +1 -0
  8. package/dist/bin/rev.js +118 -0
  9. package/dist/bin/rev.js.map +1 -0
  10. package/dist/lib/annotations.d.ts +91 -0
  11. package/dist/lib/annotations.d.ts.map +1 -0
  12. package/dist/lib/annotations.js +554 -0
  13. package/dist/lib/annotations.js.map +1 -0
  14. package/dist/lib/build.d.ts +171 -0
  15. package/dist/lib/build.d.ts.map +1 -0
  16. package/dist/lib/build.js +755 -0
  17. package/dist/lib/build.js.map +1 -0
  18. package/dist/lib/citations.d.ts +34 -0
  19. package/dist/lib/citations.d.ts.map +1 -0
  20. package/dist/lib/citations.js +140 -0
  21. package/dist/lib/citations.js.map +1 -0
  22. package/dist/lib/commands/build.d.ts +13 -0
  23. package/dist/lib/commands/build.d.ts.map +1 -0
  24. package/dist/lib/commands/build.js +678 -0
  25. package/dist/lib/commands/build.js.map +1 -0
  26. package/dist/lib/commands/citations.d.ts +11 -0
  27. package/dist/lib/commands/citations.d.ts.map +1 -0
  28. package/dist/lib/commands/citations.js +428 -0
  29. package/dist/lib/commands/citations.js.map +1 -0
  30. package/dist/lib/commands/comments.d.ts +11 -0
  31. package/dist/lib/commands/comments.d.ts.map +1 -0
  32. package/dist/lib/commands/comments.js +883 -0
  33. package/dist/lib/commands/comments.js.map +1 -0
  34. package/dist/lib/commands/context.d.ts +35 -0
  35. package/dist/lib/commands/context.d.ts.map +1 -0
  36. package/dist/lib/commands/context.js +59 -0
  37. package/dist/lib/commands/context.js.map +1 -0
  38. package/dist/lib/commands/core.d.ts +11 -0
  39. package/dist/lib/commands/core.d.ts.map +1 -0
  40. package/dist/lib/commands/core.js +246 -0
  41. package/dist/lib/commands/core.js.map +1 -0
  42. package/dist/lib/commands/doi.d.ts +11 -0
  43. package/dist/lib/commands/doi.d.ts.map +1 -0
  44. package/dist/lib/commands/doi.js +373 -0
  45. package/dist/lib/commands/doi.js.map +1 -0
  46. package/dist/lib/commands/history.d.ts +11 -0
  47. package/dist/lib/commands/history.d.ts.map +1 -0
  48. package/dist/lib/commands/history.js +245 -0
  49. package/dist/lib/commands/history.js.map +1 -0
  50. package/dist/lib/commands/index.d.ts +28 -0
  51. package/dist/lib/commands/index.d.ts.map +1 -0
  52. package/dist/lib/commands/index.js +35 -0
  53. package/dist/lib/commands/index.js.map +1 -0
  54. package/dist/lib/commands/init.d.ts +11 -0
  55. package/dist/lib/commands/init.d.ts.map +1 -0
  56. package/dist/lib/commands/init.js +209 -0
  57. package/dist/lib/commands/init.js.map +1 -0
  58. package/dist/lib/commands/response.d.ts +11 -0
  59. package/dist/lib/commands/response.d.ts.map +1 -0
  60. package/dist/lib/commands/response.js +317 -0
  61. package/dist/lib/commands/response.js.map +1 -0
  62. package/dist/lib/commands/sections.d.ts +11 -0
  63. package/dist/lib/commands/sections.d.ts.map +1 -0
  64. package/dist/lib/commands/sections.js +1071 -0
  65. package/dist/lib/commands/sections.js.map +1 -0
  66. package/dist/lib/commands/utilities.d.ts +19 -0
  67. package/dist/lib/commands/utilities.d.ts.map +1 -0
  68. package/dist/lib/commands/utilities.js +2009 -0
  69. package/dist/lib/commands/utilities.js.map +1 -0
  70. package/dist/lib/comment-realign.d.ts +50 -0
  71. package/dist/lib/comment-realign.d.ts.map +1 -0
  72. package/dist/lib/comment-realign.js +372 -0
  73. package/dist/lib/comment-realign.js.map +1 -0
  74. package/dist/lib/config.d.ts +41 -0
  75. package/dist/lib/config.d.ts.map +1 -0
  76. package/dist/lib/config.js +76 -0
  77. package/dist/lib/config.js.map +1 -0
  78. package/dist/lib/crossref.d.ts +108 -0
  79. package/dist/lib/crossref.d.ts.map +1 -0
  80. package/dist/lib/crossref.js +597 -0
  81. package/dist/lib/crossref.js.map +1 -0
  82. package/dist/lib/dependencies.d.ts +30 -0
  83. package/dist/lib/dependencies.d.ts.map +1 -0
  84. package/dist/lib/dependencies.js +95 -0
  85. package/dist/lib/dependencies.js.map +1 -0
  86. package/dist/lib/doi-cache.d.ts +29 -0
  87. package/dist/lib/doi-cache.d.ts.map +1 -0
  88. package/dist/lib/doi-cache.js +104 -0
  89. package/dist/lib/doi-cache.js.map +1 -0
  90. package/dist/lib/doi.d.ts +65 -0
  91. package/dist/lib/doi.d.ts.map +1 -0
  92. package/dist/lib/doi.js +710 -0
  93. package/dist/lib/doi.js.map +1 -0
  94. package/dist/lib/equations.d.ts +61 -0
  95. package/dist/lib/equations.d.ts.map +1 -0
  96. package/dist/lib/equations.js +445 -0
  97. package/dist/lib/equations.js.map +1 -0
  98. package/dist/lib/errors.d.ts +60 -0
  99. package/dist/lib/errors.d.ts.map +1 -0
  100. package/dist/lib/errors.js +303 -0
  101. package/dist/lib/errors.js.map +1 -0
  102. package/dist/lib/format.d.ts +104 -0
  103. package/dist/lib/format.d.ts.map +1 -0
  104. package/dist/lib/format.js +416 -0
  105. package/dist/lib/format.js.map +1 -0
  106. package/dist/lib/git.d.ts +88 -0
  107. package/dist/lib/git.d.ts.map +1 -0
  108. package/dist/lib/git.js +304 -0
  109. package/dist/lib/git.js.map +1 -0
  110. package/dist/lib/grammar.d.ts +62 -0
  111. package/dist/lib/grammar.d.ts.map +1 -0
  112. package/dist/lib/grammar.js +244 -0
  113. package/dist/lib/grammar.js.map +1 -0
  114. package/dist/lib/image-registry.d.ts +68 -0
  115. package/dist/lib/image-registry.d.ts.map +1 -0
  116. package/dist/lib/image-registry.js +112 -0
  117. package/dist/lib/image-registry.js.map +1 -0
  118. package/dist/lib/import.d.ts +184 -0
  119. package/dist/lib/import.d.ts.map +1 -0
  120. package/dist/lib/import.js +1581 -0
  121. package/dist/lib/import.js.map +1 -0
  122. package/dist/lib/journals.d.ts +55 -0
  123. package/dist/lib/journals.d.ts.map +1 -0
  124. package/dist/lib/journals.js +417 -0
  125. package/dist/lib/journals.js.map +1 -0
  126. package/dist/lib/merge.d.ts +138 -0
  127. package/dist/lib/merge.d.ts.map +1 -0
  128. package/dist/lib/merge.js +603 -0
  129. package/dist/lib/merge.js.map +1 -0
  130. package/dist/lib/orcid.d.ts +36 -0
  131. package/dist/lib/orcid.d.ts.map +1 -0
  132. package/dist/lib/orcid.js +117 -0
  133. package/dist/lib/orcid.js.map +1 -0
  134. package/dist/lib/pdf-comments.d.ts +95 -0
  135. package/dist/lib/pdf-comments.d.ts.map +1 -0
  136. package/dist/lib/pdf-comments.js +192 -0
  137. package/dist/lib/pdf-comments.js.map +1 -0
  138. package/dist/lib/pdf-import.d.ts +118 -0
  139. package/dist/lib/pdf-import.d.ts.map +1 -0
  140. package/dist/lib/pdf-import.js +397 -0
  141. package/dist/lib/pdf-import.js.map +1 -0
  142. package/dist/lib/plugins.d.ts +76 -0
  143. package/dist/lib/plugins.d.ts.map +1 -0
  144. package/dist/lib/plugins.js +235 -0
  145. package/dist/lib/plugins.js.map +1 -0
  146. package/dist/lib/postprocess.d.ts +42 -0
  147. package/dist/lib/postprocess.d.ts.map +1 -0
  148. package/dist/lib/postprocess.js +138 -0
  149. package/dist/lib/postprocess.js.map +1 -0
  150. package/dist/lib/pptx-template.d.ts +59 -0
  151. package/dist/lib/pptx-template.d.ts.map +1 -0
  152. package/dist/lib/pptx-template.js +613 -0
  153. package/dist/lib/pptx-template.js.map +1 -0
  154. package/dist/lib/pptx-themes.d.ts +80 -0
  155. package/dist/lib/pptx-themes.d.ts.map +1 -0
  156. package/dist/lib/pptx-themes.js +818 -0
  157. package/dist/lib/pptx-themes.js.map +1 -0
  158. package/dist/lib/protect-restore.d.ts +137 -0
  159. package/dist/lib/protect-restore.d.ts.map +1 -0
  160. package/dist/lib/protect-restore.js +394 -0
  161. package/dist/lib/protect-restore.js.map +1 -0
  162. package/dist/lib/rate-limiter.d.ts +27 -0
  163. package/dist/lib/rate-limiter.d.ts.map +1 -0
  164. package/dist/lib/rate-limiter.js +79 -0
  165. package/dist/lib/rate-limiter.js.map +1 -0
  166. package/dist/lib/response.d.ts +41 -0
  167. package/dist/lib/response.d.ts.map +1 -0
  168. package/dist/lib/response.js +150 -0
  169. package/dist/lib/response.js.map +1 -0
  170. package/dist/lib/review.d.ts +35 -0
  171. package/dist/lib/review.d.ts.map +1 -0
  172. package/dist/lib/review.js +263 -0
  173. package/dist/lib/review.js.map +1 -0
  174. package/dist/lib/schema.d.ts +66 -0
  175. package/dist/lib/schema.d.ts.map +1 -0
  176. package/dist/lib/schema.js +339 -0
  177. package/dist/lib/schema.js.map +1 -0
  178. package/dist/lib/scientific-words.d.ts +6 -0
  179. package/dist/lib/scientific-words.d.ts.map +1 -0
  180. package/dist/lib/scientific-words.js +66 -0
  181. package/dist/lib/scientific-words.js.map +1 -0
  182. package/dist/lib/sections.d.ts +40 -0
  183. package/dist/lib/sections.d.ts.map +1 -0
  184. package/dist/lib/sections.js +288 -0
  185. package/dist/lib/sections.js.map +1 -0
  186. package/dist/lib/slides.d.ts +86 -0
  187. package/dist/lib/slides.d.ts.map +1 -0
  188. package/dist/lib/slides.js +676 -0
  189. package/dist/lib/slides.js.map +1 -0
  190. package/dist/lib/spelling.d.ts +76 -0
  191. package/dist/lib/spelling.d.ts.map +1 -0
  192. package/dist/lib/spelling.js +272 -0
  193. package/dist/lib/spelling.js.map +1 -0
  194. package/dist/lib/templates.d.ts +30 -0
  195. package/dist/lib/templates.d.ts.map +1 -0
  196. package/dist/lib/templates.js +504 -0
  197. package/dist/lib/templates.js.map +1 -0
  198. package/dist/lib/themes.d.ts +85 -0
  199. package/dist/lib/themes.d.ts.map +1 -0
  200. package/dist/lib/themes.js +652 -0
  201. package/dist/lib/themes.js.map +1 -0
  202. package/dist/lib/trackchanges.d.ts +51 -0
  203. package/dist/lib/trackchanges.d.ts.map +1 -0
  204. package/dist/lib/trackchanges.js +202 -0
  205. package/dist/lib/trackchanges.js.map +1 -0
  206. package/dist/lib/tui.d.ts +76 -0
  207. package/dist/lib/tui.d.ts.map +1 -0
  208. package/dist/lib/tui.js +377 -0
  209. package/dist/lib/tui.js.map +1 -0
  210. package/dist/lib/types.d.ts +447 -0
  211. package/dist/lib/types.d.ts.map +1 -0
  212. package/dist/lib/types.js +6 -0
  213. package/dist/lib/types.js.map +1 -0
  214. package/dist/lib/undo.d.ts +57 -0
  215. package/dist/lib/undo.d.ts.map +1 -0
  216. package/dist/lib/undo.js +185 -0
  217. package/dist/lib/undo.js.map +1 -0
  218. package/dist/lib/utils.d.ts +16 -0
  219. package/dist/lib/utils.d.ts.map +1 -0
  220. package/dist/lib/utils.js +40 -0
  221. package/dist/lib/utils.js.map +1 -0
  222. package/dist/lib/variables.d.ts +42 -0
  223. package/dist/lib/variables.d.ts.map +1 -0
  224. package/dist/lib/variables.js +141 -0
  225. package/dist/lib/variables.js.map +1 -0
  226. package/dist/lib/word.d.ts +80 -0
  227. package/dist/lib/word.d.ts.map +1 -0
  228. package/dist/lib/word.js +360 -0
  229. package/dist/lib/word.js.map +1 -0
  230. package/dist/lib/wordcomments.d.ts +51 -0
  231. package/dist/lib/wordcomments.d.ts.map +1 -0
  232. package/dist/lib/wordcomments.js +587 -0
  233. package/dist/lib/wordcomments.js.map +1 -0
  234. package/eslint.config.js +27 -0
  235. package/lib/annotations.ts +622 -0
  236. package/lib/apply-buildup-colors.py +88 -0
  237. package/lib/build.ts +1013 -0
  238. package/lib/{citations.js → citations.ts} +38 -27
  239. package/lib/commands/{build.js → build.ts} +80 -27
  240. package/lib/commands/{citations.js → citations.ts} +36 -18
  241. package/lib/commands/{comments.js → comments.ts} +187 -54
  242. package/lib/commands/{context.js → context.ts} +18 -8
  243. package/lib/commands/{core.js → core.ts} +34 -20
  244. package/lib/commands/{doi.js → doi.ts} +32 -16
  245. package/lib/commands/{history.js → history.ts} +25 -12
  246. package/lib/commands/{index.js → index.ts} +9 -5
  247. package/lib/commands/{init.js → init.ts} +20 -8
  248. package/lib/commands/{response.js → response.ts} +47 -20
  249. package/lib/commands/{sections.js → sections.ts} +273 -68
  250. package/lib/commands/{utilities.js → utilities.ts} +338 -158
  251. package/lib/{comment-realign.js → comment-realign.ts} +117 -45
  252. package/lib/config.ts +84 -0
  253. package/lib/{crossref.js → crossref.ts} +213 -138
  254. package/lib/dependencies.ts +106 -0
  255. package/lib/doi-cache.ts +115 -0
  256. package/lib/{doi.js → doi.ts} +115 -281
  257. package/lib/{equations.js → equations.ts} +60 -64
  258. package/lib/{errors.js → errors.ts} +56 -48
  259. package/lib/{format.js → format.ts} +137 -63
  260. package/lib/{git.js → git.ts} +66 -63
  261. package/lib/{grammar.js → grammar.ts} +45 -32
  262. package/lib/image-registry.ts +180 -0
  263. package/lib/import.ts +2060 -0
  264. package/lib/journals.ts +505 -0
  265. package/lib/{merge.js → merge.ts} +185 -135
  266. package/lib/{orcid.js → orcid.ts} +17 -22
  267. package/lib/{pdf-comments.js → pdf-comments.ts} +76 -18
  268. package/lib/{pdf-import.js → pdf-import.ts} +148 -70
  269. package/lib/{plugins.js → plugins.ts} +82 -39
  270. package/lib/postprocess.ts +188 -0
  271. package/lib/pptx-color-filter.lua +37 -0
  272. package/lib/pptx-template.ts +625 -0
  273. package/lib/pptx-themes/academic.pptx +0 -0
  274. package/lib/pptx-themes/corporate.pptx +0 -0
  275. package/lib/pptx-themes/dark.pptx +0 -0
  276. package/lib/pptx-themes/default.pptx +0 -0
  277. package/lib/pptx-themes/minimal.pptx +0 -0
  278. package/lib/pptx-themes/plant.pptx +0 -0
  279. package/lib/pptx-themes.ts +896 -0
  280. package/lib/protect-restore.ts +516 -0
  281. package/lib/rate-limiter.ts +94 -0
  282. package/lib/{response.js → response.ts} +36 -21
  283. package/lib/{review.js → review.ts} +53 -43
  284. package/lib/{schema.js → schema.ts} +70 -25
  285. package/lib/{sections.js → sections.ts} +71 -76
  286. package/lib/slides.ts +793 -0
  287. package/lib/{spelling.js → spelling.ts} +43 -59
  288. package/lib/{templates.js → templates.ts} +20 -17
  289. package/lib/themes.ts +742 -0
  290. package/lib/{trackchanges.js → trackchanges.ts} +52 -23
  291. package/lib/types.ts +509 -0
  292. package/lib/{undo.js → undo.ts} +75 -52
  293. package/lib/utils.ts +41 -0
  294. package/lib/{variables.js → variables.ts} +60 -54
  295. package/lib/word.ts +428 -0
  296. package/lib/{wordcomments.js → wordcomments.ts} +94 -40
  297. package/package.json +15 -5
  298. package/skill/REFERENCE.md +67 -0
  299. package/tsconfig.json +26 -0
  300. package/lib/annotations.js +0 -414
  301. package/lib/build.js +0 -639
  302. package/lib/config.js +0 -79
  303. package/lib/import.js +0 -1145
  304. package/lib/journals.js +0 -629
  305. package/lib/word.js +0 -225
  306. /package/lib/{scientific-words.js → scientific-words.ts} +0 -0
@@ -7,10 +7,78 @@ import * as fs from 'fs';
7
7
  import AdmZip from 'adm-zip';
8
8
  import { parseStringPromise } from 'xml2js';
9
9
 
10
+ interface CommentData {
11
+ author: string;
12
+ text: string;
13
+ }
14
+
15
+ interface CommentWithPosition {
16
+ id: string;
17
+ position: number;
18
+ author: string;
19
+ text: string;
20
+ }
21
+
22
+ interface Paragraph {
23
+ text: string;
24
+ comments: CommentWithPosition[];
25
+ }
26
+
27
+ interface MdParagraph {
28
+ text: string;
29
+ start: number;
30
+ end: number;
31
+ }
32
+
33
+ interface ParagraphMatch {
34
+ index: number;
35
+ score: number;
36
+ paragraph: MdParagraph;
37
+ }
38
+
39
+ interface WordContext {
40
+ before: string[];
41
+ after: string[];
42
+ }
43
+
44
+ interface CommentInsertion {
45
+ position: number;
46
+ text: string;
47
+ commentText: string;
48
+ hasReplies: boolean;
49
+ debug: string;
50
+ }
51
+
52
+ interface RealignOptions {
53
+ dryRun?: boolean;
54
+ author?: string;
55
+ replyAuthor?: string;
56
+ }
57
+
58
+ interface RealignResult {
59
+ success: boolean;
60
+ dryRun?: boolean;
61
+ insertions: number;
62
+ matched?: number;
63
+ unmatched?: number;
64
+ }
65
+
66
+ interface RealignMarkdownOptions {
67
+ author?: string;
68
+ replyAuthor?: string;
69
+ }
70
+
71
+ interface RealignMarkdownResult {
72
+ success: boolean;
73
+ markdown: string;
74
+ insertions: number;
75
+ error?: string;
76
+ }
77
+
10
78
  /**
11
79
  * Extract paragraphs with their full text and comment positions from DOCX
12
80
  */
13
- export async function extractParagraphsWithComments(docxPath) {
81
+ export async function extractParagraphsWithComments(docxPath: string): Promise<Paragraph[]> {
14
82
  const zip = new AdmZip(docxPath);
15
83
  const doc = zip.readAsText('word/document.xml');
16
84
  const commentsXml = zip.readAsText('word/comments.xml');
@@ -21,13 +89,13 @@ export async function extractParagraphsWithComments(docxPath) {
21
89
  if (!commentNodes) return [];
22
90
 
23
91
  const nodes = Array.isArray(commentNodes) ? commentNodes : [commentNodes];
24
- const commentData = {};
92
+ const commentData: Record<string, CommentData> = {};
25
93
 
26
94
  for (const c of nodes) {
27
- const id = c.$['w:id'];
28
- const author = c.$['w:author'] || 'Unknown';
95
+ const id = c.$?.['w:id'] ?? '';
96
+ const author = c.$?.['w:author'] ?? 'Unknown';
29
97
  let text = '';
30
- const extractT = (n) => {
98
+ const extractT = (n: any): void => {
31
99
  if (!n) return;
32
100
  if (n['w:t']) {
33
101
  const t = n['w:t'];
@@ -45,7 +113,7 @@ export async function extractParagraphsWithComments(docxPath) {
45
113
  }
46
114
 
47
115
  // Extract paragraphs with comments
48
- const paragraphs = [];
116
+ const paragraphs: Paragraph[] = [];
49
117
  const paraPattern = /<w:p\b[^>]*>([\s\S]*?)<\/w:p>/g;
50
118
  let match;
51
119
 
@@ -55,7 +123,7 @@ export async function extractParagraphsWithComments(docxPath) {
55
123
 
56
124
  // Build paragraph text and track comment positions
57
125
  let text = '';
58
- const comments = [];
126
+ const comments: CommentWithPosition[] = [];
59
127
 
60
128
  const tokenPattern = /<w:t[^>]*>([^<]*)<\/w:t>|<w:commentRangeStart[^>]*w:id="(\d+)"[^>]*\/?>/g;
61
129
  let tokenMatch;
@@ -88,14 +156,14 @@ export async function extractParagraphsWithComments(docxPath) {
88
156
  /**
89
157
  * Find best matching paragraph in markdown for a reference paragraph
90
158
  */
91
- function findMatchingParagraph(refText, mdParagraphs) {
159
+ function findMatchingParagraph(refText: string, mdParagraphs: MdParagraph[]): ParagraphMatch | null {
92
160
  // Normalize for comparison
93
- const normalize = (s) => s.toLowerCase().replace(/\s+/g, ' ').trim();
161
+ const normalize = (s: string): string => s.toLowerCase().replace(/\s+/g, ' ').trim();
94
162
  const refNorm = normalize(refText);
95
163
 
96
164
  if (refNorm.length < 20) return null;
97
165
 
98
- let bestMatch = null;
166
+ let bestMatch: ParagraphMatch | null = null;
99
167
  let bestScore = 0;
100
168
 
101
169
  for (let i = 0; i < mdParagraphs.length; i++) {
@@ -122,20 +190,23 @@ function findMatchingParagraph(refText, mdParagraphs) {
122
190
  /**
123
191
  * Extract paragraphs from markdown (split by blank lines)
124
192
  */
125
- function parseMdParagraphs(markdown) {
126
- const paragraphs = [];
193
+ function parseMdParagraphs(markdown: string): MdParagraph[] {
194
+ const paragraphs: MdParagraph[] = [];
127
195
  const parts = markdown.split(/\n\n+/);
128
196
 
129
197
  let pos = 0;
130
198
  for (const part of parts) {
131
199
  const trimmed = part.trim();
132
200
  if (trimmed) {
133
- paragraphs.push({
134
- text: trimmed,
135
- start: markdown.indexOf(part, pos),
136
- end: markdown.indexOf(part, pos) + part.length,
137
- });
138
- pos = markdown.indexOf(part, pos) + part.length;
201
+ const partStart = markdown.indexOf(part, pos);
202
+ if (partStart !== -1) {
203
+ paragraphs.push({
204
+ text: trimmed,
205
+ start: partStart,
206
+ end: partStart + part.length,
207
+ });
208
+ pos = partStart + part.length;
209
+ }
139
210
  }
140
211
  }
141
212
 
@@ -145,7 +216,7 @@ function parseMdParagraphs(markdown) {
145
216
  /**
146
217
  * Strip existing comments from a specific author
147
218
  */
148
- function stripAuthorComments(text, author) {
219
+ function stripAuthorComments(text: string, author: string): string {
149
220
  const pattern = new RegExp(`\\s*\\{>>${author}:[^<]*<<\\}`, 'g');
150
221
  return text.replace(pattern, '');
151
222
  }
@@ -153,7 +224,7 @@ function stripAuthorComments(text, author) {
153
224
  /**
154
225
  * Normalize text for matching (remove citations, extra whitespace)
155
226
  */
156
- function normalizeForMatching(text) {
227
+ function normalizeForMatching(text: string): string {
157
228
  return text
158
229
  // Remove Word citation placeholders
159
230
  .replace(/\(\s*\$+\s*\)/g, '')
@@ -174,7 +245,7 @@ function normalizeForMatching(text) {
174
245
  /**
175
246
  * Find the word at or near a position in text
176
247
  */
177
- function getWordAtPosition(text, pos) {
248
+ function getWordAtPosition(text: string, pos: number): WordContext {
178
249
  const before = text.slice(Math.max(0, pos - 30), pos);
179
250
  const after = text.slice(pos, pos + 30);
180
251
 
@@ -192,7 +263,7 @@ function getWordAtPosition(text, pos) {
192
263
  * Find position in markdown paragraph matching reference position
193
264
  * Uses the anchor word (word immediately before the comment) for precise matching
194
265
  */
195
- function findMdPosition(refText, refPos, mdText) {
266
+ function findMdPosition(refText: string, refPos: number, mdText: string): number {
196
267
  // Get the word(s) immediately before the comment position in reference
197
268
  const refWords = getWordAtPosition(refText, refPos);
198
269
  const normalizedMd = normalizeForMatching(mdText);
@@ -218,7 +289,7 @@ function findMdPosition(refText, refPos, mdText) {
218
289
 
219
290
  if (matches.length === 1) {
220
291
  // Unique match - use this position
221
- const matchEnd = matches[0].index + matches[0][0].length;
292
+ const matchEnd = matches[0].index! + matches[0][0].length;
222
293
  // Map back to original markdown position
223
294
  const ratio = matchEnd / Math.max(normalizedMd.length, 1);
224
295
  return Math.round(ratio * mdText.length);
@@ -228,7 +299,7 @@ function findMdPosition(refText, refPos, mdText) {
228
299
  if (afterWords.length > 0) {
229
300
  const afterPattern = afterWords[0].toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
230
301
  for (const match of matches) {
231
- const matchEnd = match.index + match[0].length;
302
+ const matchEnd = match.index! + match[0].length;
232
303
  const afterContext = normalizedMd.slice(matchEnd, matchEnd + 50);
233
304
  if (afterContext.includes(afterPattern)) {
234
305
  const ratio = matchEnd / Math.max(normalizedMd.length, 1);
@@ -237,7 +308,7 @@ function findMdPosition(refText, refPos, mdText) {
237
308
  }
238
309
  }
239
310
  // Fall back to first match
240
- const matchEnd = matches[0].index + matches[0][0].length;
311
+ const matchEnd = matches[0].index! + matches[0][0].length;
241
312
  const ratio = matchEnd / Math.max(normalizedMd.length, 1);
242
313
  return Math.round(ratio * mdText.length);
243
314
  }
@@ -252,8 +323,8 @@ function findMdPosition(refText, refPos, mdText) {
252
323
  * Extract reply comments that follow a parent comment
253
324
  * Returns map of parent comment text -> array of reply texts
254
325
  */
255
- function extractReplies(markdown, parentAuthor, replyAuthor) {
256
- const replies = new Map();
326
+ function extractReplies(markdown: string, parentAuthor: string, replyAuthor: string): Map<string, string[]> {
327
+ const replies = new Map<string, string[]>();
257
328
  const pattern = new RegExp(
258
329
  `\\{>>${parentAuthor}:\\s*([^<]+)<<\\}((?:\\s*\\{>>${replyAuthor}:[^<]+<<\\})*)`,
259
330
  'g'
@@ -266,7 +337,7 @@ function extractReplies(markdown, parentAuthor, replyAuthor) {
266
337
 
267
338
  if (replyBlock) {
268
339
  const replyPattern = new RegExp(`\\{>>${replyAuthor}:\\s*([^<]+)<<\\}`, 'g');
269
- const replyTexts = [];
340
+ const replyTexts: string[] = [];
270
341
  let replyMatch;
271
342
  while ((replyMatch = replyPattern.exec(replyBlock)) !== null) {
272
343
  replyTexts.push(replyMatch[1].trim());
@@ -282,11 +353,12 @@ function extractReplies(markdown, parentAuthor, replyAuthor) {
282
353
 
283
354
  /**
284
355
  * Realign comments from reference DOCX to markdown
285
- * @param {string} docxPath - Reference DOCX with correctly positioned comments
286
- * @param {string} markdownPath - Markdown to realign
287
- * @param {object} options - {dryRun: boolean, author: string, replyAuthor: string}
288
356
  */
289
- export async function realignComments(docxPath, markdownPath, options = {}) {
357
+ export async function realignComments(
358
+ docxPath: string,
359
+ markdownPath: string,
360
+ options: RealignOptions = {}
361
+ ): Promise<RealignResult> {
290
362
  const { dryRun = false, author = 'Guy Colling', replyAuthor = 'Gilles Colling' } = options;
291
363
 
292
364
  // Read original markdown to extract replies before stripping
@@ -313,7 +385,7 @@ export async function realignComments(docxPath, markdownPath, options = {}) {
313
385
  const mdParagraphs = parseMdParagraphs(markdown);
314
386
 
315
387
  // Track insertions (position, text) - will insert from end to start
316
- const insertions = [];
388
+ const insertions: CommentInsertion[] = [];
317
389
  let matched = 0;
318
390
  let unmatched = 0;
319
391
 
@@ -334,8 +406,8 @@ export async function realignComments(docxPath, markdownPath, options = {}) {
334
406
 
335
407
  for (const comment of authorComments) {
336
408
  // Find corresponding position in markdown paragraph
337
- const mdPos = findMdPosition(refPara.text, comment.position, mdPara.text);
338
- const absolutePos = mdPara.start + mdPos;
409
+ const mdPos = findMdPosition(refPara.text, comment.position, mdPara?.text ?? '');
410
+ const absolutePos = (mdPara?.start ?? 0) + mdPos;
339
411
 
340
412
  // Build comment mark with any replies
341
413
  let commentMark = ` {>>${comment.author}: ${comment.text}<<}`;
@@ -354,7 +426,7 @@ export async function realignComments(docxPath, markdownPath, options = {}) {
354
426
  text: commentMark,
355
427
  commentText: comment.text.slice(0, 30),
356
428
  hasReplies: !!replyTexts,
357
- debug: `"${mdPara.text.slice(Math.max(0, mdPos - 20), mdPos)}|HERE|${mdPara.text.slice(mdPos, mdPos + 20)}"`,
429
+ debug: `"${(mdPara?.text ?? '').slice(Math.max(0, mdPos - 20), mdPos)}|HERE|${(mdPara?.text ?? '').slice(mdPos, mdPos + 20)}"`,
358
430
  });
359
431
  }
360
432
  }
@@ -386,12 +458,12 @@ export async function realignComments(docxPath, markdownPath, options = {}) {
386
458
 
387
459
  /**
388
460
  * Realign comments in markdown string (in-memory, doesn't write to file)
389
- * @param {string} docxPath - Reference DOCX with correctly positioned comments
390
- * @param {string} markdown - Markdown content to realign
391
- * @param {object} options - {author: string, replyAuthor: string}
392
- * @returns {Promise<{success: boolean, markdown: string, insertions: number}>}
393
461
  */
394
- export async function realignMarkdown(docxPath, markdown, options = {}) {
462
+ export async function realignMarkdown(
463
+ docxPath: string,
464
+ markdown: string,
465
+ options: RealignMarkdownOptions = {}
466
+ ): Promise<RealignMarkdownResult> {
395
467
  const { author = 'Guy Colling', replyAuthor = 'Gilles Colling' } = options;
396
468
 
397
469
  try {
@@ -411,7 +483,7 @@ export async function realignMarkdown(docxPath, markdown, options = {}) {
411
483
  const mdParagraphs = parseMdParagraphs(result);
412
484
 
413
485
  // Track insertions
414
- const insertions = [];
486
+ const insertions: Array<{ position: number; text: string }> = [];
415
487
 
416
488
  for (const refPara of refWithComments) {
417
489
  const match = findMatchingParagraph(refPara.text, mdParagraphs);
@@ -421,8 +493,8 @@ export async function realignMarkdown(docxPath, markdown, options = {}) {
421
493
  const authorComments = refPara.comments.filter((c) => c.author === author);
422
494
 
423
495
  for (const comment of authorComments) {
424
- const mdPos = findMdPosition(refPara.text, comment.position, mdPara.text);
425
- const absolutePos = mdPara.start + mdPos;
496
+ const mdPos = findMdPosition(refPara.text, comment.position, mdPara?.text ?? '');
497
+ const absolutePos = (mdPara?.start ?? 0) + mdPos;
426
498
 
427
499
  let commentMark = ` {>>${comment.author}: ${comment.text}<<}`;
428
500
 
@@ -447,7 +519,7 @@ export async function realignMarkdown(docxPath, markdown, options = {}) {
447
519
  }
448
520
 
449
521
  return { success: true, markdown: result, insertions: insertions.length };
450
- } catch (err) {
522
+ } catch (err: any) {
451
523
  return { success: false, markdown, insertions: 0, error: err.message };
452
524
  }
453
525
  }
package/lib/config.ts ADDED
@@ -0,0 +1,84 @@
1
+ /**
2
+ * User configuration management
3
+ * Stores user preferences in ~/.revrc
4
+ */
5
+
6
+ import type { UserConfig } from './types.js';
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import * as os from 'os';
10
+
11
+ const CONFIG_PATH = path.join(os.homedir(), '.revrc');
12
+
13
+ /**
14
+ * Load user config
15
+ * @returns User configuration object
16
+ */
17
+ export function loadUserConfig(): UserConfig {
18
+ try {
19
+ if (fs.existsSync(CONFIG_PATH)) {
20
+ const content = fs.readFileSync(CONFIG_PATH, 'utf-8');
21
+ return JSON.parse(content) as UserConfig;
22
+ }
23
+ } catch (e) {
24
+ if (process.env.DEBUG) {
25
+ const error = e as Error;
26
+ console.warn('config: Failed to parse ~/.revrc:', error.message);
27
+ }
28
+ }
29
+ return {};
30
+ }
31
+
32
+ /**
33
+ * Save user config
34
+ * @param config - User configuration to save
35
+ */
36
+ export function saveUserConfig(config: UserConfig): void {
37
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
38
+ }
39
+
40
+ /**
41
+ * Get user name
42
+ * @returns User name or null if not set
43
+ */
44
+ export function getUserName(): string | null {
45
+ const config = loadUserConfig();
46
+ return config.userName || null;
47
+ }
48
+
49
+ /**
50
+ * Set user name
51
+ * @param name - User name to set
52
+ */
53
+ export function setUserName(name: string): void {
54
+ const config = loadUserConfig();
55
+ config.userName = name;
56
+ saveUserConfig(config);
57
+ }
58
+
59
+ /**
60
+ * Get config file path
61
+ * @returns Absolute path to config file
62
+ */
63
+ export function getConfigPath(): string {
64
+ return CONFIG_PATH;
65
+ }
66
+
67
+ /**
68
+ * Get default sections for new projects
69
+ * @returns Array of section names or null if not set
70
+ */
71
+ export function getDefaultSections(): string[] | null {
72
+ const config = loadUserConfig();
73
+ return config.defaultSections || null;
74
+ }
75
+
76
+ /**
77
+ * Set default sections for new projects
78
+ * @param sections - Array of section names (without .md extension)
79
+ */
80
+ export function setDefaultSections(sections: string[]): void {
81
+ const config = loadUserConfig();
82
+ config.defaultSections = sections;
83
+ saveUserConfig(config);
84
+ }