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
@@ -4,6 +4,7 @@
4
4
  * Commands for viewing, navigating, and managing reviewer comments and track changes.
5
5
  */
6
6
 
7
+ import { Command } from 'commander';
7
8
  import {
8
9
  chalk,
9
10
  fs,
@@ -16,23 +17,25 @@ import {
16
17
  setCommentStatus,
17
18
  getTrackChanges,
18
19
  applyDecision,
20
+ cleanupOrphanedMarkers,
19
21
  interactiveCommentReview,
20
- tuiCommentReview,
22
+ // tuiCommentReview, // TUI module not yet migrated
21
23
  getUserName,
22
24
  exitWithError,
23
25
  getAnnotationSuggestions,
24
26
  requireFile,
25
27
  } from './context.js';
28
+ import type { Comment, Annotation } from '../types.js';
26
29
 
27
30
  /**
28
31
  * Add a reply after a comment
29
- * @param {string} text - Full document text
30
- * @param {object} comment - Comment object with position and match
31
- * @param {string} author - Reply author name
32
- * @param {string} message - Reply message
33
- * @returns {string} Updated text
32
+ * @param text - Full document text
33
+ * @param comment - Comment object with position and match
34
+ * @param author - Reply author name
35
+ * @param message - Reply message
36
+ * @returns Updated text
34
37
  */
35
- function addReply(text, comment, author, message) {
38
+ function addReply(text: string, comment: Comment, author: string, message: string): string {
36
39
  const replyAnnotation = `{>>${author}: ${message}<<}`;
37
40
  const insertPos = comment.position + comment.match.length;
38
41
  return text.slice(0, insertPos) + ' ' + replyAnnotation + text.slice(insertPos);
@@ -41,7 +44,7 @@ function addReply(text, comment, author, message) {
41
44
  /**
42
45
  * Helper to find section file by name (deterministic priority)
43
46
  */
44
- function findSectionFile(section) {
47
+ function findSectionFile(section: string): string[] {
45
48
  const allMd = findFiles('.md');
46
49
  const sectionLower = section.toLowerCase();
47
50
 
@@ -68,7 +71,9 @@ function findSectionFile(section) {
68
71
  if (headerMatch && headerMatch[1].toLowerCase().trim() === sectionLower) {
69
72
  return [f];
70
73
  }
71
- } catch (e) {}
74
+ } catch {
75
+ // Skip unreadable files silently - not critical for section matching
76
+ }
72
77
  }
73
78
 
74
79
  // 4. Header contains (partial match)
@@ -80,7 +85,9 @@ function findSectionFile(section) {
80
85
  if (headerMatch && headerMatch[1].toLowerCase().includes(sectionLower)) {
81
86
  headerMatches.push(f);
82
87
  }
83
- } catch (e) {}
88
+ } catch {
89
+ // Skip unreadable files silently - not critical for section matching
90
+ }
84
91
  }
85
92
  if (headerMatches.length === 1) return headerMatches;
86
93
  if (headerMatches.length > 1) {
@@ -93,11 +100,60 @@ function findSectionFile(section) {
93
100
  return [section];
94
101
  }
95
102
 
103
+ interface CommentsOptions {
104
+ pending?: boolean;
105
+ resolved?: boolean;
106
+ author?: string;
107
+ export?: string;
108
+ interactive?: boolean;
109
+ tui?: boolean;
110
+ }
111
+
112
+ interface ResolveOptions {
113
+ number?: number;
114
+ all?: boolean;
115
+ unresolve?: boolean;
116
+ dryRun?: boolean;
117
+ }
118
+
119
+ interface NextOptions {
120
+ number?: number;
121
+ }
122
+
123
+ interface PrevOptions {
124
+ number?: number;
125
+ }
126
+
127
+ interface AcceptOptions {
128
+ number?: number;
129
+ all?: boolean;
130
+ dryRun?: boolean;
131
+ }
132
+
133
+ interface RejectOptions {
134
+ number?: number;
135
+ all?: boolean;
136
+ dryRun?: boolean;
137
+ }
138
+
139
+ interface ReplyOptions {
140
+ message?: string;
141
+ number?: number;
142
+ author?: string;
143
+ all?: boolean;
144
+ dryRun?: boolean;
145
+ }
146
+
147
+ interface ReplyDocOptions {
148
+ output?: string;
149
+ context?: boolean;
150
+ force?: boolean;
151
+ }
152
+
96
153
  /**
97
154
  * Register comment commands with the program
98
- * @param {import('commander').Command} program
99
155
  */
100
- export function register(program) {
156
+ export function register(program: Command): void {
101
157
  // ==========================================================================
102
158
  // COMMENTS command - List all comments
103
159
  // ==========================================================================
@@ -113,33 +169,38 @@ export function register(program) {
113
169
  .option('-e, --export <csvFile>', 'Export comments to CSV file')
114
170
  .option('-i, --interactive', 'Interactive review mode (reply, resolve, skip)')
115
171
  .option('-t, --tui', 'Visual TUI mode for comment review')
116
- .action(async (file, options) => {
172
+ .action(async (file: string, options: CommentsOptions) => {
117
173
  requireFile(file, 'Markdown file');
118
174
 
119
175
  const text = fs.readFileSync(file, 'utf-8');
120
176
 
121
177
  // TUI review mode
122
178
  if (options.tui) {
123
- let author = options.author || getUserName();
124
- if (!author) {
125
- exitWithError('No user name set for replies', getAnnotationSuggestions('no_author'));
126
- }
127
-
128
- const result = await tuiCommentReview(text, {
129
- author,
130
- addReply: (txt, comment, auth, msg) => {
131
- const replyAnnotation = `{>>${auth}: ${msg}<<}`;
132
- const insertPos = comment.position + comment.match.length;
133
- return txt.slice(0, insertPos) + ' ' + replyAnnotation + txt.slice(insertPos);
134
- },
135
- setStatus: setCommentStatus,
136
- });
179
+ console.error(chalk.yellow('TUI mode is temporarily unavailable (module migration in progress)'));
180
+ console.error(chalk.dim('Use --interactive mode instead: rev comments --interactive ' + file));
181
+ process.exit(1);
137
182
 
138
- if (result.resolved > 0 || result.replied > 0) {
139
- fs.writeFileSync(file, result.text, 'utf-8');
140
- console.log(fmt.status('success', `Changes saved to ${file}`));
141
- }
142
- return;
183
+ // TODO: Re-enable when tui.ts is migrated
184
+ // let author = options.author || getUserName();
185
+ // if (!author) {
186
+ // exitWithError('No user name set for replies', getAnnotationSuggestions('no_author'));
187
+ // }
188
+ //
189
+ // const result = await tuiCommentReview(text, {
190
+ // author,
191
+ // addReply: (txt: string, comment: Comment, auth: string, msg: string) => {
192
+ // const replyAnnotation = `{>>${auth}: ${msg}<<}`;
193
+ // const insertPos = comment.position + comment.match.length;
194
+ // return txt.slice(0, insertPos) + ' ' + replyAnnotation + txt.slice(insertPos);
195
+ // },
196
+ // setStatus: setCommentStatus,
197
+ // });
198
+ //
199
+ // if (result.resolved > 0 || result.replied > 0) {
200
+ // fs.writeFileSync(file, result.text, 'utf-8');
201
+ // console.log(fmt.status('success', `Changes saved to ${file}`));
202
+ // }
203
+ // return;
143
204
  }
144
205
 
145
206
  // Interactive review mode
@@ -151,7 +212,7 @@ export function register(program) {
151
212
 
152
213
  const result = await interactiveCommentReview(text, {
153
214
  author,
154
- addReply: (txt, comment, auth, msg) => {
215
+ addReply: (txt: string, comment: Comment, auth: string, msg: string) => {
155
216
  const replyAnnotation = `{>>${auth}: ${msg}<<}`;
156
217
  const insertPos = comment.position + comment.match.length;
157
218
  return txt.slice(0, insertPos) + ' ' + replyAnnotation + txt.slice(insertPos);
@@ -181,7 +242,7 @@ export function register(program) {
181
242
 
182
243
  // CSV export mode
183
244
  if (options.export) {
184
- const csvEscape = (str) => {
245
+ const csvEscape = (str?: string | number | null): string => {
185
246
  if (!str) return '';
186
247
  str = String(str);
187
248
  if (str.includes(',') || str.includes('"') || str.includes('\n')) {
@@ -257,7 +318,7 @@ export function register(program) {
257
318
  .option('-a, --all', 'Mark all comments as resolved')
258
319
  .option('-u, --unresolve', 'Mark as pending (unresolve)')
259
320
  .option('--dry-run', 'Preview without saving')
260
- .action((file, options) => {
321
+ .action((file: string, options: ResolveOptions) => {
261
322
  requireFile(file, 'Markdown file');
262
323
 
263
324
  let text = fs.readFileSync(file, 'utf-8');
@@ -339,7 +400,7 @@ export function register(program) {
339
400
  .description('Show next pending comment')
340
401
  .argument('[file]', 'Specific file (default: all markdown files)')
341
402
  .option('-n, --number <n>', 'Skip to nth pending comment', parseInt)
342
- .action((file, options) => {
403
+ .action((file: string | undefined, options: NextOptions) => {
343
404
  const files = file ? [file] : findFiles('.md');
344
405
 
345
406
  if (files.length === 0) {
@@ -348,7 +409,7 @@ export function register(program) {
348
409
  }
349
410
 
350
411
  // Collect all pending comments across files
351
- const allPending = [];
412
+ const allPending: Array<Annotation & { file: string; number: number }> = [];
352
413
  for (const f of files) {
353
414
  if (!fs.existsSync(f)) continue;
354
415
  const text = fs.readFileSync(f, 'utf-8');
@@ -404,7 +465,7 @@ export function register(program) {
404
465
  .description('Show previous pending comment')
405
466
  .argument('[file]', 'Specific file (default: all markdown files)')
406
467
  .option('-n, --number <n>', 'Skip to nth pending comment from end', parseInt)
407
- .action((file, options) => {
468
+ .action((file: string | undefined, options: PrevOptions) => {
408
469
  const files = file ? [file] : findFiles('.md');
409
470
 
410
471
  if (files.length === 0) {
@@ -413,7 +474,7 @@ export function register(program) {
413
474
  }
414
475
 
415
476
  // Collect all pending comments across files
416
- const allPending = [];
477
+ const allPending: Array<Annotation & { file: string; number: number }> = [];
417
478
  for (const f of files) {
418
479
  if (!fs.existsSync(f)) continue;
419
480
  const text = fs.readFileSync(f, 'utf-8');
@@ -471,7 +532,7 @@ export function register(program) {
471
532
  .command('first')
472
533
  .description('Show first comment')
473
534
  .argument('[section]', 'Specific file or section name (default: all markdown files)')
474
- .action((section) => {
535
+ .action((section: string | undefined) => {
475
536
  const files = section ? findSectionFile(section) : findFiles('.md');
476
537
 
477
538
  if (files.length === 0) {
@@ -517,7 +578,7 @@ export function register(program) {
517
578
  .command('last')
518
579
  .description('Show last comment')
519
580
  .argument('[section]', 'Specific file or section name (default: all markdown files)')
520
- .action((section) => {
581
+ .action((section: string | undefined) => {
521
582
  const files = section ? findSectionFile(section) : findFiles('.md').reverse();
522
583
 
523
584
  if (files.length === 0) {
@@ -566,7 +627,7 @@ export function register(program) {
566
627
  .description('List all pending comments as a checklist')
567
628
  .argument('[file]', 'Specific file (default: all markdown files)')
568
629
  .option('--by-author', 'Group by author')
569
- .action((file, options) => {
630
+ .action((file: string | undefined, options: { byAuthor?: boolean }) => {
570
631
  const files = file ? [file] : findFiles('.md');
571
632
 
572
633
  if (files.length === 0) {
@@ -575,7 +636,13 @@ export function register(program) {
575
636
  }
576
637
 
577
638
  // Collect all pending comments
578
- const todos = [];
639
+ const todos: Array<{
640
+ file: string;
641
+ number: number;
642
+ line: number;
643
+ author: string;
644
+ content: string;
645
+ }> = [];
579
646
  for (const f of files) {
580
647
  if (!fs.existsSync(f)) continue;
581
648
  const text = fs.readFileSync(f, 'utf-8');
@@ -604,7 +671,7 @@ export function register(program) {
604
671
 
605
672
  if (options.byAuthor) {
606
673
  // Group by author
607
- const byAuthor = {};
674
+ const byAuthor: Record<string, typeof todos> = {};
608
675
  for (const t of todos) {
609
676
  if (!byAuthor[t.author]) byAuthor[t.author] = [];
610
677
  byAuthor[t.author].push(t);
@@ -620,7 +687,7 @@ export function register(program) {
620
687
  }
621
688
  } else {
622
689
  // List by file
623
- let currentFile = null;
690
+ let currentFile: string | null = null;
624
691
  for (const t of todos) {
625
692
  if (t.file !== currentFile) {
626
693
  if (currentFile) console.log();
@@ -648,7 +715,7 @@ export function register(program) {
648
715
  .option('-n, --number <n>', 'Accept specific change by number', parseInt)
649
716
  .option('-a, --all', 'Accept all changes')
650
717
  .option('--dry-run', 'Preview without saving')
651
- .action((file, options) => {
718
+ .action((file: string, options: AcceptOptions) => {
652
719
  if (!fs.existsSync(file)) {
653
720
  console.error(chalk.red(`Error: File not found: ${file}`));
654
721
  process.exit(1);
@@ -668,6 +735,8 @@ export function register(program) {
668
735
  for (const change of sorted) {
669
736
  text = applyDecision(text, change, true);
670
737
  }
738
+ // Clean up any orphaned CriticMarkup markers that result from interleaved track changes
739
+ text = cleanupOrphanedMarkers(text);
671
740
  if (!options.dryRun) {
672
741
  fs.writeFileSync(file, text, 'utf-8');
673
742
  console.log(fmt.status('success', `Accepted ${changes.length} change(s)`));
@@ -700,13 +769,15 @@ export function register(program) {
700
769
 
701
770
  for (let i = 0; i < changes.length; i++) {
702
771
  const c = changes[i];
703
- let desc;
772
+ let desc: string;
704
773
  if (c.type === 'insert') {
705
774
  desc = chalk.green(`+++ "${c.content.slice(0, 40)}${c.content.length > 40 ? '...' : ''}"`);
706
775
  } else if (c.type === 'delete') {
707
776
  desc = chalk.red(`--- "${c.content.slice(0, 40)}${c.content.length > 40 ? '...' : ''}"`);
708
777
  } else if (c.type === 'substitute') {
709
778
  desc = chalk.yellow(`~~~ "${c.content.slice(0, 20)}" → "${(c.replacement || '').slice(0, 20)}"`);
779
+ } else {
780
+ desc = chalk.dim('???');
710
781
  }
711
782
  console.log(` #${i + 1} ${chalk.dim(`L${c.line}`)} ${desc}`);
712
783
  }
@@ -728,7 +799,7 @@ export function register(program) {
728
799
  .option('-n, --number <n>', 'Reject specific change by number', parseInt)
729
800
  .option('-a, --all', 'Reject all changes')
730
801
  .option('--dry-run', 'Preview without saving')
731
- .action((file, options) => {
802
+ .action((file: string, options: RejectOptions) => {
732
803
  if (!fs.existsSync(file)) {
733
804
  console.error(chalk.red(`Error: File not found: ${file}`));
734
805
  process.exit(1);
@@ -780,13 +851,15 @@ export function register(program) {
780
851
 
781
852
  for (let i = 0; i < changes.length; i++) {
782
853
  const c = changes[i];
783
- let desc;
854
+ let desc: string;
784
855
  if (c.type === 'insert') {
785
856
  desc = chalk.green(`+++ "${c.content.slice(0, 40)}${c.content.length > 40 ? '...' : ''}"`);
786
857
  } else if (c.type === 'delete') {
787
858
  desc = chalk.red(`--- "${c.content.slice(0, 40)}${c.content.length > 40 ? '...' : ''}"`);
788
859
  } else if (c.type === 'substitute') {
789
860
  desc = chalk.yellow(`~~~ "${c.content.slice(0, 20)}" → "${(c.replacement || '').slice(0, 20)}"`);
861
+ } else {
862
+ desc = chalk.dim('???');
790
863
  }
791
864
  console.log(` #${i + 1} ${chalk.dim(`L${c.line}`)} ${desc}`);
792
865
  }
@@ -809,7 +882,7 @@ export function register(program) {
809
882
  .option('-a, --author <name>', 'Override author name')
810
883
  .option('--all', 'Reply to all pending comments with the same message (requires -m)')
811
884
  .option('--dry-run', 'Preview without saving')
812
- .action(async (file, options) => {
885
+ .action(async (file: string, options: ReplyOptions) => {
813
886
  if (!fs.existsSync(file)) {
814
887
  console.error(chalk.red(`File not found: ${file}`));
815
888
  process.exit(1);
@@ -844,7 +917,7 @@ export function register(program) {
844
917
  // Process in reverse order to maintain positions
845
918
  const sortedComments = [...comments].sort((a, b) => b.position - a.position);
846
919
  for (const comment of sortedComments) {
847
- result = addReply(result, comment, author, options.message);
920
+ result = addReply(result, comment as Comment, author, options.message);
848
921
  count++;
849
922
  }
850
923
  if (options.dryRun) {
@@ -864,7 +937,7 @@ export function register(program) {
864
937
  console.error(chalk.red(`Invalid comment number. File has ${allComments.length} comments.`));
865
938
  process.exit(1);
866
939
  }
867
- const result = addReply(text, allComments[idx], author, options.message);
940
+ const result = addReply(text, allComments[idx] as Comment, author, options.message);
868
941
  if (options.dryRun) {
869
942
  console.log(fmt.status('info', `Would add reply to comment #${options.number}`));
870
943
  } else {
@@ -882,7 +955,7 @@ export function register(program) {
882
955
  output: process.stdout,
883
956
  });
884
957
 
885
- const askQuestion = (prompt) =>
958
+ const askQuestion = (prompt: string): Promise<string> =>
886
959
  new Promise((resolve) => rl.question(prompt, resolve));
887
960
 
888
961
  let result = text;
@@ -904,7 +977,7 @@ export function register(program) {
904
977
  }
905
978
 
906
979
  if (answer.trim()) {
907
- result = addReply(result, c, author, answer.trim());
980
+ result = addReply(result, c as Comment, author, answer.trim());
908
981
  repliesAdded++;
909
982
  console.log(chalk.green(' ✓ Reply added'));
910
983
  }
@@ -919,4 +992,64 @@ export function register(program) {
919
992
  console.log(chalk.dim('\nNo replies added.'));
920
993
  }
921
994
  });
995
+
996
+ // ==========================================================================
997
+ // REPLY-DOC command - Generate reply template document from comments
998
+ // ==========================================================================
999
+
1000
+ program
1001
+ .command('reply-doc')
1002
+ .alias('rd')
1003
+ .description('Generate a reply template document from comments')
1004
+ .argument('<file>', 'Markdown file with comments')
1005
+ .option('-o, --output <file>', 'Output file (default: <input>_reply.md)')
1006
+ .option('--no-context', 'Exclude commented text context')
1007
+ .option('--force', 'Overwrite existing output file')
1008
+ .action(async (file: string, options: ReplyDocOptions) => {
1009
+ requireFile(file, 'Markdown file');
1010
+
1011
+ const text = fs.readFileSync(file, 'utf-8');
1012
+ const comments = getComments(text);
1013
+
1014
+ if (comments.length === 0) {
1015
+ console.log(fmt.status('info', 'No comments found in file.'));
1016
+ return;
1017
+ }
1018
+
1019
+ // Determine output filename
1020
+ const baseName = path.basename(file, '.md');
1021
+ const dirName = path.dirname(file);
1022
+ const outputFile = options.output || path.join(dirName, `${baseName}_reply.md`);
1023
+
1024
+ // Check if output exists
1025
+ if (fs.existsSync(outputFile) && !options.force) {
1026
+ console.log(fmt.status('error', `Output file already exists: ${outputFile}`));
1027
+ console.log(chalk.dim(' Use --force to overwrite'));
1028
+ process.exit(1);
1029
+ }
1030
+
1031
+ // Generate reply template
1032
+ const sectionName = baseName.charAt(0).toUpperCase() + baseName.slice(1);
1033
+ let output = `# ${sectionName} - Response to Reviewer Comments\n\n`;
1034
+
1035
+ comments.forEach((c, i) => {
1036
+ const authorName = c.author || 'Anonymous';
1037
+
1038
+ output += `## Comment ${i + 1} (${authorName})\n`;
1039
+ output += `> "${c.content}"\n\n`;
1040
+
1041
+ if (options.context !== false && c.before) {
1042
+ const context = c.before.trim().slice(-80);
1043
+ if (context) {
1044
+ output += `*Context: "...${context}"*\n\n`;
1045
+ }
1046
+ }
1047
+
1048
+ output += `**Response:** \n\n`;
1049
+ });
1050
+
1051
+ fs.writeFileSync(outputFile, output, 'utf-8');
1052
+ console.log(fmt.status('success', `Generated reply template: ${outputFile}`));
1053
+ console.log(chalk.dim(` ${comments.length} comment(s) extracted`));
1054
+ });
922
1055
  }
@@ -13,11 +13,11 @@ import * as fmt from '../format.js';
13
13
  export let quietMode = false;
14
14
  export let jsonMode = false;
15
15
 
16
- export function setQuietMode(value) {
16
+ export function setQuietMode(value: boolean): void {
17
17
  quietMode = value;
18
18
  }
19
19
 
20
- export function setJsonMode(value) {
20
+ export function setJsonMode(value: boolean): void {
21
21
  jsonMode = value;
22
22
  if (value) {
23
23
  chalk.level = 0;
@@ -25,12 +25,12 @@ export function setJsonMode(value) {
25
25
  }
26
26
 
27
27
  // JSON output helper
28
- export function jsonOutput(data) {
28
+ export function jsonOutput(data: unknown): void {
29
29
  console.log(JSON.stringify(data, null, 2));
30
30
  }
31
31
 
32
32
  // Find files by extension
33
- export function findFiles(ext, cwd = process.cwd()) {
33
+ export function findFiles(ext: string, cwd: string = process.cwd()): string[] {
34
34
  try {
35
35
  return fs.readdirSync(cwd)
36
36
  .filter(f => f.endsWith(ext) && !f.startsWith('.'));
@@ -52,6 +52,7 @@ export {
52
52
  hasAnnotations,
53
53
  getTrackChanges,
54
54
  applyDecision,
55
+ cleanupOrphanedMarkers,
55
56
  } from '../annotations.js';
56
57
 
57
58
  export {
@@ -81,13 +82,16 @@ export {
81
82
  export {
82
83
  build,
83
84
  loadConfig as loadBuildConfig,
85
+ formatBuildResults,
86
+ } from '../build.js';
87
+
88
+ export {
84
89
  hasPandoc,
85
90
  hasPandocCrossref,
86
- formatBuildResults,
87
91
  hasLatex,
88
92
  checkDependencies,
89
93
  getInstallInstructions,
90
- } from '../build.js';
94
+ } from '../dependencies.js';
91
95
 
92
96
  export {
93
97
  getTemplate,
@@ -107,6 +111,8 @@ export {
107
111
 
108
112
  export { inlineDiffPreview } from '../format.js';
109
113
 
114
+ export { countWords } from '../utils.js';
115
+
110
116
  export {
111
117
  parseCommentsWithReplies,
112
118
  collectComments,
@@ -135,9 +141,12 @@ export {
135
141
  isValidDoiFormat,
136
142
  lookupDoi,
137
143
  lookupMissingDois,
144
+ } from '../doi.js';
145
+
146
+ export {
138
147
  clearDoiCache,
139
148
  getDoiCacheStats,
140
- } from '../doi.js';
149
+ } from '../doi-cache.js';
141
150
 
142
151
  export {
143
152
  formatError,
@@ -162,4 +171,5 @@ export {
162
171
  getPluginDirs,
163
172
  } from '../plugins.js';
164
173
 
165
- export { tuiCommentReview } from '../tui.js';
174
+ // TUI module not yet migrated to TypeScript
175
+ // export { tuiCommentReview } from '../tui.js';