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
@@ -0,0 +1,505 @@
1
+ /**
2
+ * Journal validation profiles
3
+ * Check manuscripts against journal-specific requirements
4
+ */
5
+
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import type { JournalProfile, JournalRequirements, ValidationResult } from './types.js';
9
+ import { loadCustomProfiles } from './plugins.js';
10
+ import { countWords } from './utils.js';
11
+
12
+ /**
13
+ * Journal requirement profiles
14
+ * Based on publicly available author guidelines
15
+ */
16
+ export const JOURNAL_PROFILES: Record<string, JournalProfile> = {
17
+ nature: {
18
+ name: 'Nature',
19
+ url: 'https://www.nature.com/nature/for-authors',
20
+ requirements: {
21
+ wordLimit: { main: 3000, abstract: 150 },
22
+ references: { max: 50, doiRequired: true },
23
+ figures: { max: 6 },
24
+ sections: ['Abstract', 'Introduction', 'Results', 'Discussion', 'Methods'],
25
+ },
26
+ },
27
+
28
+ science: {
29
+ name: 'Science',
30
+ url: 'https://www.science.org/content/page/instructions-preparing-initial-manuscript',
31
+ requirements: {
32
+ wordLimit: { main: 2500, abstract: 125 },
33
+ references: { max: 40, doiRequired: true },
34
+ figures: { max: 4 },
35
+ sections: ['Abstract', 'Introduction', 'Results', 'Discussion'],
36
+ },
37
+ },
38
+
39
+ 'plos-one': {
40
+ name: 'PLOS ONE',
41
+ url: 'https://journals.plos.org/plosone/s/submission-guidelines',
42
+ requirements: {
43
+ wordLimit: { abstract: 300 },
44
+ references: { doiRequired: false },
45
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
46
+ },
47
+ },
48
+
49
+ 'pnas': {
50
+ name: 'PNAS',
51
+ url: 'https://www.pnas.org/author-center/submitting-your-manuscript',
52
+ requirements: {
53
+ wordLimit: { main: 4500, abstract: 250 },
54
+ references: { max: 50, doiRequired: true },
55
+ figures: { max: 6 },
56
+ sections: ['Abstract', 'Introduction', 'Results', 'Discussion'],
57
+ },
58
+ },
59
+
60
+ 'ecology-letters': {
61
+ name: 'Ecology Letters',
62
+ url: 'https://onlinelibrary.wiley.com/page/journal/14610248/homepage/forauthors.html',
63
+ requirements: {
64
+ wordLimit: { main: 5000, abstract: 150 },
65
+ references: { max: 50, doiRequired: true },
66
+ figures: { max: 6 },
67
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
68
+ },
69
+ },
70
+
71
+ 'ecological-applications': {
72
+ name: 'Ecological Applications',
73
+ url: 'https://esajournals.onlinelibrary.wiley.com/hub/journal/19395582/author-guidelines',
74
+ requirements: {
75
+ wordLimit: { main: 7000, abstract: 350 },
76
+ references: { doiRequired: true },
77
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
78
+ },
79
+ },
80
+
81
+ 'molecular-ecology': {
82
+ name: 'Molecular Ecology',
83
+ url: 'https://onlinelibrary.wiley.com/page/journal/1365294x/homepage/forauthors.html',
84
+ requirements: {
85
+ wordLimit: { main: 8000, abstract: 250 },
86
+ references: { doiRequired: true },
87
+ figures: { max: 8 },
88
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
89
+ },
90
+ },
91
+
92
+ 'elife': {
93
+ name: 'eLife',
94
+ url: 'https://reviewer.elifesciences.org/author-guide/full',
95
+ requirements: {
96
+ wordLimit: { abstract: 150 },
97
+ references: { doiRequired: true },
98
+ sections: ['Abstract', 'Introduction', 'Results', 'Discussion', 'Methods'],
99
+ },
100
+ },
101
+
102
+ 'cell': {
103
+ name: 'Cell',
104
+ url: 'https://www.cell.com/cell/authors',
105
+ requirements: {
106
+ wordLimit: { main: 7000, abstract: 150 },
107
+ references: { max: 100, doiRequired: true },
108
+ figures: { max: 7 },
109
+ sections: ['Abstract', 'Introduction', 'Results', 'Discussion'],
110
+ },
111
+ },
112
+
113
+ 'current-biology': {
114
+ name: 'Current Biology',
115
+ url: 'https://www.cell.com/current-biology/authors',
116
+ requirements: {
117
+ wordLimit: { main: 5000, abstract: 150 },
118
+ references: { max: 60, doiRequired: true },
119
+ figures: { max: 4 },
120
+ sections: ['Summary', 'Results', 'Discussion'],
121
+ },
122
+ },
123
+
124
+ 'conservation-biology': {
125
+ name: 'Conservation Biology',
126
+ url: 'https://conbio.onlinelibrary.wiley.com/hub/journal/15231739/homepage/forauthors.html',
127
+ requirements: {
128
+ wordLimit: { main: 7000, abstract: 300 },
129
+ references: { doiRequired: true },
130
+ figures: { max: 6 },
131
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
132
+ },
133
+ },
134
+
135
+ 'biological-conservation': {
136
+ name: 'Biological Conservation',
137
+ url: 'https://www.elsevier.com/journals/biological-conservation/0006-3207/guide-for-authors',
138
+ requirements: {
139
+ wordLimit: { main: 8000, abstract: 400 },
140
+ references: { doiRequired: true },
141
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
142
+ },
143
+ },
144
+
145
+ 'journal-of-ecology': {
146
+ name: 'Journal of Ecology',
147
+ url: 'https://besjournals.onlinelibrary.wiley.com/hub/journal/13652745/author-guidelines',
148
+ requirements: {
149
+ wordLimit: { main: 7000, abstract: 350 },
150
+ references: { doiRequired: true },
151
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
152
+ },
153
+ },
154
+
155
+ 'functional-ecology': {
156
+ name: 'Functional Ecology',
157
+ url: 'https://besjournals.onlinelibrary.wiley.com/hub/journal/13652435/author-guidelines',
158
+ requirements: {
159
+ wordLimit: { main: 7000, abstract: 350 },
160
+ references: { doiRequired: true },
161
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
162
+ },
163
+ },
164
+
165
+ 'global-change-biology': {
166
+ name: 'Global Change Biology',
167
+ url: 'https://onlinelibrary.wiley.com/page/journal/13652486/homepage/forauthors.html',
168
+ requirements: {
169
+ wordLimit: { main: 7000, abstract: 300 },
170
+ references: { doiRequired: true },
171
+ figures: { max: 8 },
172
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
173
+ },
174
+ },
175
+
176
+ 'oikos': {
177
+ name: 'Oikos',
178
+ url: 'https://nsojournals.onlinelibrary.wiley.com/hub/journal/16000706/author-guidelines',
179
+ requirements: {
180
+ wordLimit: { main: 8000, abstract: 350 },
181
+ references: { doiRequired: true },
182
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
183
+ },
184
+ },
185
+
186
+ 'oecologia': {
187
+ name: 'Oecologia',
188
+ url: 'https://www.springer.com/journal/442/submission-guidelines',
189
+ requirements: {
190
+ wordLimit: { main: 8000, abstract: 250 },
191
+ references: { doiRequired: true },
192
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
193
+ },
194
+ },
195
+
196
+ 'biological-invasions': {
197
+ name: 'Biological Invasions',
198
+ url: 'https://www.springer.com/journal/10530/submission-guidelines',
199
+ requirements: {
200
+ wordLimit: { abstract: 250 },
201
+ references: { doiRequired: true },
202
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
203
+ },
204
+ },
205
+
206
+ 'diversity-distributions': {
207
+ name: 'Diversity and Distributions',
208
+ url: 'https://onlinelibrary.wiley.com/page/journal/14724642/homepage/forauthors.html',
209
+ requirements: {
210
+ wordLimit: { main: 6000, abstract: 300 },
211
+ references: { doiRequired: true },
212
+ figures: { max: 6 },
213
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
214
+ },
215
+ },
216
+
217
+ 'neobiota': {
218
+ name: 'NeoBiota',
219
+ url: 'https://neobiota.pensoft.net/about#Author_Guidelines',
220
+ requirements: {
221
+ wordLimit: { abstract: 350 },
222
+ references: { doiRequired: true },
223
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
224
+ },
225
+ },
226
+
227
+ 'peerj': {
228
+ name: 'PeerJ',
229
+ url: 'https://peerj.com/about/author-instructions/',
230
+ requirements: {
231
+ wordLimit: { abstract: 500 },
232
+ references: { doiRequired: false },
233
+ sections: ['Abstract', 'Introduction', 'Methods', 'Results', 'Discussion'],
234
+ },
235
+ },
236
+ };
237
+
238
+ /**
239
+ * Get all profiles (built-in + custom)
240
+ * Custom profiles override built-in ones with the same ID
241
+ */
242
+ function getAllProfiles(): Record<string, JournalProfile> {
243
+ const customProfiles = loadCustomProfiles() as Record<string, JournalProfile>;
244
+ return { ...JOURNAL_PROFILES, ...customProfiles };
245
+ }
246
+
247
+ interface ListJournalsOptions {
248
+ includeCustom?: boolean;
249
+ customOnly?: boolean;
250
+ }
251
+
252
+ interface JournalListItem {
253
+ id: string;
254
+ name: string;
255
+ url: string;
256
+ custom?: boolean;
257
+ }
258
+
259
+ /**
260
+ * List all available journal profiles
261
+ */
262
+ export function listJournals(options: ListJournalsOptions = {}): JournalListItem[] {
263
+ const { includeCustom = true, customOnly = false } = options;
264
+
265
+ const profiles = customOnly
266
+ ? (loadCustomProfiles() as Record<string, JournalProfile>)
267
+ : includeCustom
268
+ ? getAllProfiles()
269
+ : JOURNAL_PROFILES;
270
+
271
+ return Object.entries(profiles).map(([id, profile]) => ({
272
+ id,
273
+ name: profile.name,
274
+ url: profile.url,
275
+ custom: (profile as any).custom || false,
276
+ }));
277
+ }
278
+
279
+ /**
280
+ * Get a specific journal profile
281
+ */
282
+ export function getJournalProfile(journalId: string): JournalProfile | null {
283
+ const normalized = journalId.toLowerCase().replace(/\s+/g, '-');
284
+ const profiles = getAllProfiles();
285
+ return profiles[normalized] || null;
286
+ }
287
+
288
+
289
+ /**
290
+ * Extract abstract from markdown
291
+ */
292
+ function extractAbstract(text: string): string | null {
293
+ // Try to find abstract section
294
+ const patterns = [
295
+ /^#+\s*Abstract\s*\n([\s\S]*?)(?=^#+|\Z)/mi,
296
+ /^Abstract[:\s]*\n([\s\S]*?)(?=^#+|\n\n)/mi,
297
+ ];
298
+
299
+ for (const pattern of patterns) {
300
+ const match = text.match(pattern);
301
+ if (match && match[1]) {
302
+ return match[1].trim();
303
+ }
304
+ }
305
+
306
+ return null;
307
+ }
308
+
309
+ /**
310
+ * Extract title from markdown
311
+ */
312
+ function extractTitle(text: string): string | null {
313
+ // Try YAML frontmatter
314
+ const yamlMatch = text.match(/^---\n[\s\S]*?title:\s*["']?([^"'\n]+)["']?[\s\S]*?\n---/m);
315
+ if (yamlMatch && yamlMatch[1]) {
316
+ return yamlMatch[1].trim();
317
+ }
318
+
319
+ // Try first H1
320
+ const h1Match = text.match(/^#\s+(.+)$/m);
321
+ if (h1Match && h1Match[1]) {
322
+ return h1Match[1].trim();
323
+ }
324
+
325
+ return null;
326
+ }
327
+
328
+ /**
329
+ * Extract sections from markdown
330
+ */
331
+ function extractSections(text: string): string[] {
332
+ const sections: string[] = [];
333
+ const headerPattern = /^#+\s+(.+)$/gm;
334
+ let match: RegExpExecArray | null;
335
+
336
+ while ((match = headerPattern.exec(text)) !== null) {
337
+ if (match[1]) {
338
+ sections.push(match[1].trim());
339
+ }
340
+ }
341
+
342
+ return sections;
343
+ }
344
+
345
+ /**
346
+ * Count figures in markdown
347
+ */
348
+ function countFigures(text: string): number {
349
+ // Count images with figure captions
350
+ const figurePattern = /!\[.*?\]\(.*?\)(\{#fig:[^}]+\})?/g;
351
+ const matches = text.match(figurePattern) || [];
352
+ return matches.length;
353
+ }
354
+
355
+ /**
356
+ * Count tables in markdown
357
+ */
358
+ function countTables(text: string): number {
359
+ // Count tables (lines starting with |)
360
+ const tablePattern = /^\|[^|]+\|/gm;
361
+ const matches = text.match(tablePattern) || [];
362
+ // Divide by approximate rows per table
363
+ return Math.ceil(matches.length / 5);
364
+ }
365
+
366
+ /**
367
+ * Count references/citations in markdown
368
+ */
369
+ function countReferences(text: string): number {
370
+ // Count unique citation keys
371
+ const citationPattern = /@(\w+)/g;
372
+ const citations = new Set<string>();
373
+ let match: RegExpExecArray | null;
374
+
375
+ while ((match = citationPattern.exec(text)) !== null) {
376
+ // Exclude cross-refs like @fig:label
377
+ if (match[1] && !match[0].includes(':')) {
378
+ citations.add(match[1]);
379
+ }
380
+ }
381
+
382
+ return citations.size;
383
+ }
384
+
385
+ interface ManuscriptStats {
386
+ wordCount: number;
387
+ abstractWords: number;
388
+ titleChars: number;
389
+ figures: number;
390
+ tables: number;
391
+ references: number;
392
+ sections: number;
393
+ }
394
+
395
+ interface ManuscriptValidationResult {
396
+ valid: boolean;
397
+ errors: string[];
398
+ warnings: string[];
399
+ stats: ManuscriptStats | null;
400
+ journal?: string;
401
+ url?: string;
402
+ }
403
+
404
+ /**
405
+ * Validate manuscript against journal requirements
406
+ */
407
+ export function validateManuscript(text: string, journalId: string): ManuscriptValidationResult {
408
+ const profile = getJournalProfile(journalId);
409
+
410
+ if (!profile) {
411
+ return {
412
+ valid: false,
413
+ errors: [`Unknown journal: ${journalId}`],
414
+ warnings: [],
415
+ stats: null,
416
+ };
417
+ }
418
+
419
+ const req = profile.requirements;
420
+ const errors: string[] = [];
421
+ const warnings: string[] = [];
422
+
423
+ // Extract content
424
+ const abstract = extractAbstract(text);
425
+ const title = extractTitle(text);
426
+ const sections = extractSections(text);
427
+ const mainWordCount = countWords(text);
428
+ const figureCount = countFigures(text);
429
+ const tableCount = countTables(text);
430
+ const refCount = countReferences(text);
431
+
432
+ const stats: ManuscriptStats = {
433
+ wordCount: mainWordCount,
434
+ abstractWords: abstract ? countWords(abstract) : 0,
435
+ titleChars: title ? title.length : 0,
436
+ figures: figureCount,
437
+ tables: tableCount,
438
+ references: refCount,
439
+ sections: sections.length,
440
+ };
441
+
442
+ // Word limits
443
+ if (req.wordLimit) {
444
+ if (req.wordLimit.main && mainWordCount > req.wordLimit.main) {
445
+ errors.push(`Main text exceeds ${req.wordLimit.main} words (current: ${mainWordCount})`);
446
+ }
447
+ if (req.wordLimit.abstract && abstract) {
448
+ const absWords = countWords(abstract);
449
+ if (absWords > req.wordLimit.abstract) {
450
+ errors.push(`Abstract exceeds ${req.wordLimit.abstract} words (current: ${absWords})`);
451
+ }
452
+ }
453
+ }
454
+
455
+ // References
456
+ if (req.references) {
457
+ if (req.references.max && refCount > req.references.max) {
458
+ errors.push(`References exceed ${req.references.max} (current: ${refCount})`);
459
+ }
460
+ if (req.references.doiRequired) {
461
+ warnings.push('DOI required for all references - run "rev doi check" to verify');
462
+ }
463
+ }
464
+
465
+ // Figures/tables
466
+ if (req.figures) {
467
+ if (req.figures.max && figureCount > req.figures.max) {
468
+ errors.push(`Figures exceed ${req.figures.max} (current: ${figureCount})`);
469
+ }
470
+ }
471
+
472
+ // Required sections
473
+ if (req.sections) {
474
+ for (const reqSection of req.sections) {
475
+ const found = sections.some(s =>
476
+ s.toLowerCase().includes(reqSection.toLowerCase())
477
+ );
478
+ if (!found) {
479
+ warnings.push(`Missing required section: ${reqSection}`);
480
+ }
481
+ }
482
+ }
483
+
484
+ return {
485
+ valid: errors.length === 0,
486
+ errors,
487
+ warnings,
488
+ stats,
489
+ journal: profile.name,
490
+ url: profile.url,
491
+ };
492
+ }
493
+
494
+ /**
495
+ * Validate multiple files against journal requirements
496
+ */
497
+ export function validateProject(files: string[], journalId: string): ManuscriptValidationResult {
498
+ // Combine all file contents
499
+ const combined = files
500
+ .filter(f => fs.existsSync(f))
501
+ .map(f => fs.readFileSync(f, 'utf-8'))
502
+ .join('\n\n');
503
+
504
+ return validateManuscript(combined, journalId);
505
+ }