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,8 @@
4
4
  * Basic annotation operations for track changes workflow.
5
5
  */
6
6
 
7
+ import type { Command } from 'commander';
8
+ import type { Comment } from '../types.js';
7
9
  import {
8
10
  chalk,
9
11
  fs,
@@ -23,11 +25,15 @@ import {
23
25
  requireFile,
24
26
  } from './context.js';
25
27
 
28
+ interface StripOptions {
29
+ output?: string;
30
+ keepComments?: boolean;
31
+ }
32
+
26
33
  /**
27
34
  * Register core commands with the program
28
- * @param {import('commander').Command} program
29
35
  */
30
- export function register(program) {
36
+ export function register(program: Command): void {
31
37
  // ==========================================================================
32
38
  // REVIEW command - Interactive track change review
33
39
  // ==========================================================================
@@ -36,7 +42,7 @@ export function register(program) {
36
42
  .command('review')
37
43
  .description('Interactively review and accept/reject track changes')
38
44
  .argument('<file>', 'Markdown file to review')
39
- .action(async (file) => {
45
+ .action(async (file: string) => {
40
46
  requireFile(file, 'Markdown file');
41
47
 
42
48
  const text = fs.readFileSync(file, 'utf-8');
@@ -72,7 +78,7 @@ export function register(program) {
72
78
  .argument('<file>', 'Markdown file to strip')
73
79
  .option('-o, --output <file>', 'Output file (default: stdout)')
74
80
  .option('-c, --keep-comments', 'Keep comment annotations')
75
- .action((file, options) => {
81
+ .action((file: string, options: StripOptions) => {
76
82
  requireFile(file, 'Markdown file');
77
83
 
78
84
  const text = fs.readFileSync(file, 'utf-8');
@@ -95,7 +101,7 @@ export function register(program) {
95
101
  .alias('s')
96
102
  .description('Show project overview or file annotation statistics')
97
103
  .argument('[file]', 'Markdown file to analyze (default: project overview)')
98
- .action(async (file) => {
104
+ .action(async (file?: string) => {
99
105
  // If a specific file is given, show its annotations
100
106
  if (file) {
101
107
  if (!fs.existsSync(file)) {
@@ -114,7 +120,7 @@ export function register(program) {
114
120
  jsonOutput({
115
121
  file: path.basename(file),
116
122
  annotations: counts,
117
- comments: comments.map(c => ({
123
+ comments: comments.map((c) => ({
118
124
  author: c.author || null,
119
125
  content: c.content,
120
126
  line: c.line,
@@ -133,12 +139,12 @@ export function register(program) {
133
139
  console.log();
134
140
 
135
141
  // Build stats table
136
- const rows = [];
137
- if (counts.inserts > 0) rows.push([chalk.green('+'), 'Insertions', chalk.green(counts.inserts)]);
138
- if (counts.deletes > 0) rows.push([chalk.red('-'), 'Deletions', chalk.red(counts.deletes)]);
139
- if (counts.substitutes > 0) rows.push([chalk.yellow('~'), 'Substitutions', chalk.yellow(counts.substitutes)]);
140
- if (counts.comments > 0) rows.push([chalk.blue('#'), 'Comments', chalk.blue(counts.comments)]);
141
- rows.push([chalk.dim('Σ'), chalk.dim('Total'), chalk.dim(counts.total)]);
142
+ const rows: string[][] = [];
143
+ if (counts.inserts > 0) rows.push([chalk.green('+'), 'Insertions', chalk.green(counts.inserts.toString())]);
144
+ if (counts.deletes > 0) rows.push([chalk.red('-'), 'Deletions', chalk.red(counts.deletes.toString())]);
145
+ if (counts.substitutes > 0) rows.push([chalk.yellow('~'), 'Substitutions', chalk.yellow(counts.substitutes.toString())]);
146
+ if (counts.comments > 0) rows.push([chalk.blue('#'), 'Comments', chalk.blue(counts.comments.toString())]);
147
+ rows.push([chalk.dim('Σ'), chalk.dim('Total'), chalk.dim(counts.total.toString())]);
142
148
 
143
149
  console.log(fmt.table(['', 'Type', 'Count'], rows, { align: ['center', 'left', 'right'] }));
144
150
 
@@ -148,8 +154,8 @@ export function register(program) {
148
154
  console.log(fmt.header('Comments'));
149
155
  console.log();
150
156
 
151
- const commentRows = comments.map((c, i) => [
152
- chalk.dim(i + 1),
157
+ const commentRows = comments.map((c, i: number) => [
158
+ chalk.dim((i + 1).toString()),
153
159
  c.author ? chalk.blue(c.author) : chalk.dim('Anonymous'),
154
160
  c.content.length > 45 ? c.content.slice(0, 45) + '...' : c.content,
155
161
  chalk.dim(`L${c.line}`),
@@ -181,13 +187,21 @@ export function register(program) {
181
187
  let totalInserts = 0;
182
188
  let totalDeletes = 0;
183
189
  let totalSubstitutes = 0;
184
- const fileStats = [];
190
+ const fileStats: Array<{
191
+ file: string;
192
+ words: number;
193
+ inserts: number;
194
+ deletes: number;
195
+ substitutions: number;
196
+ comments: number;
197
+ pending: number;
198
+ }> = [];
185
199
 
186
200
  for (const f of mdFiles) {
187
201
  const text = fs.readFileSync(f, 'utf-8');
188
202
  const counts = countAnnotations(text);
189
203
  const comments = getComments(text);
190
- const pending = comments.filter(c => !c.resolved).length;
204
+ const pending = comments.filter((c) => c.resolved === false).length;
191
205
 
192
206
  // Simple word count (excluding annotations)
193
207
  const stripped = stripAnnotations(text);
@@ -219,7 +233,7 @@ export function register(program) {
219
233
  const latestDocx = docxFiles.length > 0
220
234
  ? docxFiles
221
235
  .map(f => ({ name: f, mtime: fs.statSync(f).mtime }))
222
- .sort((a, b) => b.mtime - a.mtime)[0]
236
+ .sort((a, b) => b.mtime.getTime() - a.mtime.getTime())[0]
223
237
  : null;
224
238
 
225
239
  jsonOutput({
@@ -246,7 +260,7 @@ export function register(program) {
246
260
  console.log(` ${chalk.bold(totalWords.toLocaleString())} words across ${mdFiles.length} files`);
247
261
 
248
262
  if (totalComments > 0) {
249
- console.log(` ${chalk.blue(totalComments)} comments (${chalk.yellow(pendingComments)} pending)`);
263
+ console.log(` ${chalk.blue(totalComments.toString())} comments (${chalk.yellow(pendingComments.toString())} pending)`);
250
264
  }
251
265
 
252
266
  const totalChanges = totalInserts + totalDeletes + totalSubstitutes;
@@ -265,7 +279,7 @@ export function register(program) {
265
279
  f.inserts > 0 ? chalk.green(`+${f.inserts}`) : chalk.dim('-'),
266
280
  f.deletes > 0 ? chalk.red(`-${f.deletes}`) : chalk.dim('-'),
267
281
  f.substitutions > 0 ? chalk.yellow(`~${f.substitutions}`) : chalk.dim('-'),
268
- f.pending > 0 ? chalk.yellow(f.pending) : (f.comments > 0 ? chalk.dim(f.comments) : chalk.dim('-')),
282
+ f.pending > 0 ? chalk.yellow(f.pending.toString()) : (f.comments > 0 ? chalk.dim(f.comments.toString()) : chalk.dim('-')),
269
283
  ]);
270
284
 
271
285
  if (rows.length > 0) {
@@ -282,7 +296,7 @@ export function register(program) {
282
296
  if (docxFiles.length > 0) {
283
297
  const sorted = docxFiles
284
298
  .map(f => ({ name: f, mtime: fs.statSync(f).mtime }))
285
- .sort((a, b) => b.mtime - a.mtime);
299
+ .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
286
300
  const latest = sorted[0];
287
301
  const age = Date.now() - latest.mtime.getTime();
288
302
  const ageStr = age < 3600000 ? `${Math.round(age / 60000)}m ago` :
@@ -10,12 +10,24 @@ import {
10
10
  path,
11
11
  fmt,
12
12
  } from './context.js';
13
+ import type { Command } from 'commander';
14
+
15
+ interface DoiOptions {
16
+ bib: string;
17
+ strict?: boolean;
18
+ resolve?: boolean;
19
+ confidence: string;
20
+ }
21
+
22
+ interface OrcidOptions {
23
+ yaml?: boolean;
24
+ badge?: boolean;
25
+ }
13
26
 
14
27
  /**
15
28
  * Register DOI commands with the program
16
- * @param {import('commander').Command} program
17
29
  */
18
- export function register(program) {
30
+ export function register(program: Command): void {
19
31
  // ==========================================================================
20
32
  // DOI command - Validate and fetch DOIs
21
33
  // ==========================================================================
@@ -29,7 +41,7 @@ export function register(program) {
29
41
  .option('--strict', 'Fail on missing DOIs for articles')
30
42
  .option('--no-resolve', 'Only check format, skip resolution check')
31
43
  .option('--confidence <level>', 'Minimum confidence: high, medium, low (default: medium)', 'medium')
32
- .action(async (action, input, options) => {
44
+ .action(async (action: string, input: string | undefined, options: DoiOptions) => {
33
45
  const { parseBibEntries, checkBibDois, fetchBibtex, addToBib, isValidDoiFormat, lookupDoi, lookupMissingDois } = await import('../doi.js');
34
46
 
35
47
  if (action === 'check') {
@@ -59,7 +71,7 @@ export function register(program) {
59
71
  const skipped = results.entries.filter(e => e.status === 'skipped');
60
72
 
61
73
  // Summary table
62
- const summaryRows = [
74
+ const summaryRows: string[][] = [
63
75
  [chalk.green('Valid'), chalk.green(valid.length.toString())],
64
76
  [invalid.length > 0 ? chalk.red('Invalid') : 'Invalid', invalid.length > 0 ? chalk.red(invalid.length.toString()) : '0'],
65
77
  [missing.length > 0 ? chalk.yellow('Missing (articles)') : 'Missing', missing.length > 0 ? chalk.yellow(missing.length.toString()) : '0'],
@@ -126,7 +138,8 @@ export function register(program) {
126
138
 
127
139
  } catch (err) {
128
140
  spin.stop();
129
- console.error(fmt.status('error', err.message));
141
+ const error = err as Error;
142
+ console.error(fmt.status('error', error.message));
130
143
  process.exit(1);
131
144
  }
132
145
 
@@ -147,11 +160,12 @@ export function register(program) {
147
160
  console.log();
148
161
  console.log(result.bibtex);
149
162
  } else {
150
- spin.error(result.error);
163
+ spin.error(result.error!);
151
164
  process.exit(1);
152
165
  }
153
166
  } catch (err) {
154
- spin.error(err.message);
167
+ const error = err as Error;
168
+ spin.error(error.message);
155
169
  process.exit(1);
156
170
  }
157
171
 
@@ -169,20 +183,21 @@ export function register(program) {
169
183
  const fetchResult = await fetchBibtex(input);
170
184
 
171
185
  if (!fetchResult.success) {
172
- spin.error(fetchResult.error);
186
+ spin.error(fetchResult.error!);
173
187
  process.exit(1);
174
188
  }
175
189
 
176
- const addResult = addToBib(bibPath, fetchResult.bibtex);
190
+ const addResult = addToBib(bibPath, fetchResult.bibtex!);
177
191
 
178
192
  if (addResult.success) {
179
193
  spin.success(`Added @${addResult.key} to ${bibPath}`);
180
194
  } else {
181
- spin.error(addResult.error);
195
+ spin.error(addResult.error!);
182
196
  process.exit(1);
183
197
  }
184
198
  } catch (err) {
185
- spin.error(err.message);
199
+ const error = err as Error;
200
+ spin.error(error.message);
186
201
  process.exit(1);
187
202
  }
188
203
 
@@ -210,7 +225,7 @@ export function register(program) {
210
225
  let found = 0;
211
226
  let notFound = 0;
212
227
  let lowConfidence = 0;
213
- const results = [];
228
+ const results: Array<{ entry: any; result: any; status: string }> = [];
214
229
 
215
230
  for (let i = 0; i < missing.length; i++) {
216
231
  const entry = missing[i];
@@ -268,7 +283,7 @@ export function register(program) {
268
283
 
269
284
  // Filter by confidence level
270
285
  const confLevel = options.confidence || 'medium';
271
- const confLevels = { high: 3, medium: 2, low: 1 };
286
+ const confLevels: Record<string, number> = { high: 3, medium: 2, low: 1 };
272
287
  const minConf = confLevels[confLevel] || 2;
273
288
 
274
289
  const filteredResults = results.filter(r => {
@@ -304,7 +319,7 @@ export function register(program) {
304
319
  console.log(chalk.cyan(` DOI: ${result.doi}`));
305
320
 
306
321
  if (result.metadata?.journal) {
307
- let yearDisplay;
322
+ let yearDisplay: string;
308
323
  if (yearExact) {
309
324
  yearDisplay = chalk.green(`(${foundYear})`);
310
325
  } else if (yearClose) {
@@ -368,7 +383,7 @@ export function register(program) {
368
383
  .argument('<orcid>', 'ORCID iD (e.g., 0000-0002-1825-0097)')
369
384
  .option('--yaml', 'Output as YAML for rev.yaml authors section')
370
385
  .option('--badge', 'Output markdown badge')
371
- .action(async (orcidInput, options) => {
386
+ .action(async (orcidInput: string, options: OrcidOptions) => {
372
387
  const { fetchOrcidProfile, fetchOrcidWorkCount, formatAuthorYaml, getOrcidBadge, cleanOrcid, isValidOrcid } = await import('../orcid.js');
373
388
 
374
389
  const orcid = cleanOrcid(orcidInput);
@@ -412,7 +427,8 @@ export function register(program) {
412
427
  console.log(chalk.dim(' Use --yaml to output for rev.yaml authors section'));
413
428
  console.log(chalk.dim(' Use --badge to get markdown badge'));
414
429
  } catch (err) {
415
- console.error(fmt.status('error', err.message));
430
+ const error = err as Error;
431
+ console.error(fmt.status('error', error.message));
416
432
  process.exit(1);
417
433
  }
418
434
  });
@@ -4,6 +4,7 @@
4
4
  * Commands for git-based revision tracking and author statistics.
5
5
  */
6
6
 
7
+ import type { Command } from 'commander';
7
8
  import {
8
9
  chalk,
9
10
  fs,
@@ -12,11 +13,23 @@ import {
12
13
  loadBuildConfig,
13
14
  } from './context.js';
14
15
 
16
+ interface DiffOptions {
17
+ files?: string;
18
+ stat?: boolean;
19
+ }
20
+
21
+ interface HistoryOptions {
22
+ limit: string;
23
+ }
24
+
25
+ interface ContributorsOptions {
26
+ blame?: boolean;
27
+ }
28
+
15
29
  /**
16
30
  * Register history commands with the program
17
- * @param {import('commander').Command} program
18
31
  */
19
- export function register(program) {
32
+ export function register(program: Command): void {
20
33
  // ==========================================================================
21
34
  // DIFF command - Compare sections against git history
22
35
  // ==========================================================================
@@ -27,7 +40,7 @@ export function register(program) {
27
40
  .argument('[ref]', 'Git reference to compare against (default: main/master)')
28
41
  .option('-f, --files <files>', 'Specific files to compare (comma-separated)')
29
42
  .option('--stat', 'Show only statistics, not full diff')
30
- .action(async (ref, options) => {
43
+ .action(async (ref: string | undefined, options: DiffOptions) => {
31
44
  const {
32
45
  isGitRepo,
33
46
  getDefaultBranch,
@@ -50,7 +63,7 @@ export function register(program) {
50
63
  console.log();
51
64
 
52
65
  // Get files to compare
53
- let filesToCompare;
66
+ let filesToCompare: string[];
54
67
  if (options.files) {
55
68
  filesToCompare = options.files.split(',').map(f => f.trim());
56
69
  } else {
@@ -73,7 +86,7 @@ export function register(program) {
73
86
  const { total, byFile } = getWordCountDiff(filesToCompare, compareRef);
74
87
 
75
88
  // Show results
76
- const rows = [];
89
+ const rows: string[][] = [];
77
90
  for (const file of filesToCompare) {
78
91
  const stats = byFile[file];
79
92
  if (stats && (stats.added > 0 || stats.removed > 0)) {
@@ -115,10 +128,10 @@ export function register(program) {
115
128
  console.log(chalk.dim(' ...'));
116
129
  break;
117
130
  }
118
- const preview = change.text.slice(0, 60).replace(/\n/g, ' ');
119
- if (change.type === 'add') {
131
+ const preview = change.value.slice(0, 60).replace(/\n/g, ' ');
132
+ if (change.added) {
120
133
  console.log(chalk.green(` + "${preview}..."`));
121
- } else {
134
+ } else if (change.removed) {
122
135
  console.log(chalk.red(` - "${preview}..."`));
123
136
  }
124
137
  shown++;
@@ -137,7 +150,7 @@ export function register(program) {
137
150
  .description('Show revision history for section files')
138
151
  .argument('[file]', 'Specific file (default: all sections)')
139
152
  .option('-n, --limit <count>', 'Number of commits to show', '10')
140
- .action(async (file, options) => {
153
+ .action(async (file: string | undefined, options: HistoryOptions) => {
141
154
  const {
142
155
  isGitRepo,
143
156
  getFileHistory,
@@ -209,7 +222,7 @@ export function register(program) {
209
222
  .description('Show author contributions across section files')
210
223
  .argument('[file]', 'Specific file (default: all sections)')
211
224
  .option('--blame', 'Show detailed line-by-line blame for a file')
212
- .action(async (file, options) => {
225
+ .action(async (file: string | undefined, options: ContributorsOptions) => {
213
226
  const { isGitRepo, getAuthorStats, getContributors, getFileBlame } = await import('../git.js');
214
227
 
215
228
  if (!isGitRepo()) {
@@ -262,9 +275,9 @@ export function register(program) {
262
275
  }
263
276
  } else {
264
277
  // Show contributors across all sections
265
- let config = {};
278
+ let config: { sections?: string[] } = {};
266
279
  try {
267
- config = loadBuildConfig() || {};
280
+ config = loadBuildConfig(process.cwd()) || {};
268
281
  } catch {
269
282
  // Not in a rev project
270
283
  }
@@ -5,6 +5,7 @@
5
5
  * Each module's register() function adds commands to the Commander program.
6
6
  */
7
7
 
8
+ import type { Command } from 'commander';
8
9
  import { register as registerCoreCommands } from './core.js';
9
10
  import { register as registerCommentCommands } from './comments.js';
10
11
  import { register as registerInitCommands } from './init.js';
@@ -37,20 +38,23 @@ export {
37
38
  jsonMode,
38
39
  } from './context.js';
39
40
 
41
+ interface PackageJson {
42
+ version?: string;
43
+ [key: string]: unknown;
44
+ }
45
+
40
46
  /**
41
47
  * Register all command modules with the program.
42
- * @param {import('commander').Command} program
43
- * @param {object} [pkg] - Package.json object for version info (optional)
44
48
  */
45
- export function registerAllCommands(program, pkg) {
49
+ export function registerAllCommands(program: Command, pkg?: PackageJson): void {
46
50
  registerCoreCommands(program);
47
51
  registerCommentCommands(program);
48
52
  registerInitCommands(program);
49
53
  registerSectionCommands(program);
50
- registerBuildCommands(program, pkg);
54
+ registerBuildCommands(program, pkg || {});
51
55
  registerResponseCommands(program);
52
56
  registerCitationCommands(program);
53
57
  registerDoiCommands(program);
54
58
  registerHistoryCommands(program);
55
- registerUtilityCommands(program, pkg);
59
+ registerUtilityCommands(program, pkg || {});
56
60
  }
@@ -4,6 +4,7 @@
4
4
  * Project initialization and configuration commands.
5
5
  */
6
6
 
7
+ import type { Command } from 'commander';
7
8
  import {
8
9
  chalk,
9
10
  fs,
@@ -22,11 +23,22 @@ import {
22
23
  setDefaultSections,
23
24
  } from './context.js';
24
25
 
26
+ interface InitOptions {
27
+ dir: string;
28
+ output: string;
29
+ force?: boolean;
30
+ }
31
+
32
+ interface NewOptions {
33
+ template: string;
34
+ sections?: string;
35
+ list?: boolean;
36
+ }
37
+
25
38
  /**
26
39
  * Register init commands with the program
27
- * @param {import('commander').Command} program
28
40
  */
29
- export function register(program) {
41
+ export function register(program: Command): void {
30
42
  // ==========================================================================
31
43
  // INIT command - Generate sections.yaml config
32
44
  // ==========================================================================
@@ -37,7 +49,7 @@ export function register(program) {
37
49
  .option('-d, --dir <directory>', 'Directory to scan', '.')
38
50
  .option('-o, --output <file>', 'Output config file', 'sections.yaml')
39
51
  .option('--force', 'Overwrite existing config')
40
- .action((options) => {
52
+ .action((options: InitOptions) => {
41
53
  const dir = path.resolve(options.dir);
42
54
 
43
55
  if (!fs.existsSync(dir)) {
@@ -70,7 +82,7 @@ export function register(program) {
70
82
  for (const [file, section] of Object.entries(config.sections)) {
71
83
  console.log(` ${chalk.bold(file)}`);
72
84
  console.log(chalk.dim(` header: "${section.header}"`));
73
- if (section.aliases?.length > 0) {
85
+ if (section.aliases && section.aliases.length > 0) {
74
86
  console.log(chalk.dim(` aliases: ${JSON.stringify(section.aliases)}`));
75
87
  }
76
88
  }
@@ -92,7 +104,7 @@ export function register(program) {
92
104
  .option('-t, --template <name>', 'Template: paper, minimal, thesis, review', 'paper')
93
105
  .option('-s, --sections <sections>', 'Comma-separated section names (e.g., intro,methods,results)')
94
106
  .option('--list', 'List available templates')
95
- .action(async (name, options) => {
107
+ .action(async (name: string | undefined, options: NewOptions) => {
96
108
  if (options.list) {
97
109
  console.log(chalk.cyan('Available templates:\n'));
98
110
  for (const t of listTemplates()) {
@@ -115,7 +127,7 @@ export function register(program) {
115
127
  }
116
128
 
117
129
  let template;
118
- let sections = null;
130
+ let sections: string[] | null = null;
119
131
 
120
132
  // Determine sections: CLI option > user config > prompt
121
133
  if (options.sections) {
@@ -138,7 +150,7 @@ export function register(program) {
138
150
  output: process.stdout,
139
151
  });
140
152
 
141
- const ask = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
153
+ const ask = (prompt: string): Promise<string> => new Promise((resolve) => rl.question(prompt, resolve));
142
154
 
143
155
  console.log(chalk.cyan('Enter your document sections (comma-separated):'));
144
156
  console.log(chalk.dim(' Example: introduction,methods,results,discussion'));
@@ -207,7 +219,7 @@ export function register(program) {
207
219
  .description('Set user preferences')
208
220
  .argument('<key>', 'Config key: user, sections')
209
221
  .argument('[value]', 'Value to set')
210
- .action((key, value) => {
222
+ .action((key: string, value?: string) => {
211
223
  if (key === 'user') {
212
224
  if (value) {
213
225
  setUserName(value);
@@ -4,6 +4,7 @@
4
4
  * Commands for generating reviewer responses and validating manuscripts.
5
5
  */
6
6
 
7
+ import type { Command } from 'commander';
7
8
  import {
8
9
  chalk,
9
10
  fs,
@@ -15,11 +16,35 @@ import {
15
16
  getUserName,
16
17
  } from './context.js';
17
18
 
19
+ interface ResponseOptions {
20
+ output?: string;
21
+ author?: string;
22
+ context?: boolean;
23
+ location?: boolean;
24
+ }
25
+
26
+ interface ValidateOptions {
27
+ journal?: string;
28
+ list?: boolean;
29
+ }
30
+
31
+ interface ProfilesOptions {
32
+ list?: boolean;
33
+ new?: string;
34
+ project?: boolean;
35
+ dirs?: boolean;
36
+ }
37
+
38
+ interface AnonymizeOptions {
39
+ output?: string;
40
+ authors?: string;
41
+ dryRun?: boolean;
42
+ }
43
+
18
44
  /**
19
45
  * Register response commands with the program
20
- * @param {import('commander').Command} program
21
46
  */
22
- export function register(program) {
47
+ export function register(program: Command): void {
23
48
  // ==========================================================================
24
49
  // RESPONSE command - Generate response letter for reviewers
25
50
  // ==========================================================================
@@ -32,7 +57,7 @@ export function register(program) {
32
57
  .option('-a, --author <name>', 'Author name for identifying replies')
33
58
  .option('--no-context', 'Omit context snippets')
34
59
  .option('--no-location', 'Omit file:line references')
35
- .action(async (files, options) => {
60
+ .action(async (files: string[] | undefined, options: ResponseOptions) => {
36
61
  let mdFiles = files;
37
62
  if (!mdFiles || mdFiles.length === 0) {
38
63
  const allFiles = fs.readdirSync('.').filter(f =>
@@ -74,7 +99,7 @@ export function register(program) {
74
99
  console.log(fmt.header('Response Letter Generated'));
75
100
  console.log();
76
101
 
77
- const rows = reviewers.map(r => [r, grouped.get(r).length.toString()]);
102
+ const rows = reviewers.map(r => [r, grouped.get(r)!.length.toString()]);
78
103
  console.log(fmt.table(['Reviewer', 'Comments'], rows));
79
104
  console.log();
80
105
  console.log(fmt.status('success', `Created ${outputPath}`));
@@ -90,7 +115,7 @@ export function register(program) {
90
115
  .argument('[files...]', 'Markdown files to validate (default: all section files)')
91
116
  .option('-j, --journal <name>', 'Journal profile (e.g., nature, plos-one, science)')
92
117
  .option('--list', 'List available journal profiles')
93
- .action(async (files, options) => {
118
+ .action(async (files: string[] | undefined, options: ValidateOptions) => {
94
119
  const { listJournals, validateProject, getJournalProfile } = await import('../journals.js');
95
120
 
96
121
  if (options.list) {
@@ -151,16 +176,18 @@ export function register(program) {
151
176
 
152
177
  const result = validateProject(mdFiles, options.journal);
153
178
 
154
- console.log(chalk.cyan('Manuscript Stats:'));
155
- console.log(fmt.table(['Metric', 'Value'], [
156
- ['Word count', result.stats.wordCount.toString()],
157
- ['Abstract', `${result.stats.abstractWords} words`],
158
- ['Title', `${result.stats.titleChars} chars`],
159
- ['Figures', result.stats.figures.toString()],
160
- ['Tables', result.stats.tables.toString()],
161
- ['References', result.stats.references.toString()],
162
- ]));
163
- console.log();
179
+ if (result.stats) {
180
+ console.log(chalk.cyan('Manuscript Stats:'));
181
+ console.log(fmt.table(['Metric', 'Value'], [
182
+ ['Word count', result.stats.wordCount.toString()],
183
+ ['Abstract', `${result.stats.abstractWords} words`],
184
+ ['Title', `${result.stats.titleChars} chars`],
185
+ ['Figures', result.stats.figures.toString()],
186
+ ['Tables', result.stats.tables.toString()],
187
+ ['References', result.stats.references.toString()],
188
+ ]));
189
+ console.log();
190
+ }
164
191
 
165
192
  if (result.errors.length > 0) {
166
193
  console.log(chalk.red('Errors:'));
@@ -197,7 +224,7 @@ export function register(program) {
197
224
  .option('--new <name>', 'Create a new profile template')
198
225
  .option('--project', 'Create profile in project directory (with --new)')
199
226
  .option('--dirs', 'Show profile directory locations')
200
- .action(async (options) => {
227
+ .action(async (options: ProfilesOptions) => {
201
228
  const {
202
229
  listCustomProfiles,
203
230
  saveProfileTemplate,
@@ -225,7 +252,7 @@ export function register(program) {
225
252
  console.log(fmt.status('success', `Created profile template: ${filePath}`));
226
253
  console.log(chalk.dim('Edit the file to customize journal requirements'));
227
254
  } catch (err) {
228
- console.error(fmt.status('error', err.message));
255
+ console.error(fmt.status('error', (err as Error).message));
229
256
  process.exit(1);
230
257
  }
231
258
  return;
@@ -272,7 +299,7 @@ export function register(program) {
272
299
  .option('-o, --output <file>', 'Output file (default: input-anonymous.md)')
273
300
  .option('--authors <names>', 'Author names to redact (comma-separated)')
274
301
  .option('--dry-run', 'Show what would be changed without writing')
275
- .action(async (input, options) => {
302
+ .action(async (input: string, options: AnonymizeOptions) => {
276
303
  const { default: YAML } = await import('yaml');
277
304
 
278
305
  const isDir = fs.existsSync(input) && fs.statSync(input).isDirectory();
@@ -287,7 +314,7 @@ export function register(program) {
287
314
  process.exit(1);
288
315
  }
289
316
 
290
- let authorNames = [];
317
+ let authorNames: string[] = [];
291
318
  if (options.authors) {
292
319
  authorNames = options.authors.split(',').map(n => n.trim());
293
320
  } else {
@@ -296,7 +323,7 @@ export function register(program) {
296
323
  try {
297
324
  const config = YAML.parse(fs.readFileSync(configPath, 'utf-8'));
298
325
  if (config.authors) {
299
- authorNames = config.authors.map(a => typeof a === 'string' ? a : a.name).filter(Boolean);
326
+ authorNames = config.authors.map((a: string | { name: string }) => typeof a === 'string' ? a : a.name).filter(Boolean);
300
327
  }
301
328
  } catch { /* ignore */ }
302
329
  }