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
@@ -5,17 +5,7 @@
5
5
  import * as fs from 'fs';
6
6
  import * as path from 'path';
7
7
  import YAML from 'yaml';
8
-
9
- /**
10
- * @typedef {Object} SectionConfig
11
- * @property {string} header - Primary header text to match
12
- * @property {string[]} [aliases] - Alternative header texts
13
- * @property {number} [order] - Sort order for building
14
- */
15
-
16
- /**
17
- * @typedef {Object<string, SectionConfig|string>} SectionsConfig
18
- */
8
+ import type { SectionConfig, SectionsConfig, ExtractedSection } from './types.js';
19
9
 
20
10
  /**
21
11
  * Default section order (common academic paper structure)
@@ -40,10 +30,8 @@ const DEFAULT_ORDER = [
40
30
 
41
31
  /**
42
32
  * Extract header from a markdown file
43
- * @param {string} filePath
44
- * @returns {string|null}
45
33
  */
46
- export function extractHeader(filePath) {
34
+ export function extractHeader(filePath: string): string | null {
47
35
  if (!fs.existsSync(filePath)) return null;
48
36
 
49
37
  const content = fs.readFileSync(filePath, 'utf-8');
@@ -51,7 +39,7 @@ export function extractHeader(filePath) {
51
39
 
52
40
  for (const line of lines) {
53
41
  const match = line.match(/^#\s+(.+)$/);
54
- if (match) {
42
+ if (match && match[1]) {
55
43
  return match[1].trim();
56
44
  }
57
45
  }
@@ -61,18 +49,18 @@ export function extractHeader(filePath) {
61
49
 
62
50
  /**
63
51
  * Generate sections.yaml from existing .md files
64
- * @param {string} directory
65
- * @param {string[]} [excludePatterns]
66
- * @returns {object}
67
52
  */
68
- export function generateConfig(directory, excludePatterns = ['paper.md', 'README.md', 'CLAUDE.md']) {
53
+ export function generateConfig(
54
+ directory: string,
55
+ excludePatterns: string[] = ['paper.md', 'README.md', 'CLAUDE.md']
56
+ ): SectionsConfig {
69
57
  const files = fs.readdirSync(directory).filter((f) => {
70
58
  if (!f.endsWith('.md')) return false;
71
59
  if (excludePatterns.some((p) => f.toLowerCase().includes(p.toLowerCase()))) return false;
72
60
  return true;
73
61
  });
74
62
 
75
- const sections = {};
63
+ const sections: Record<string, SectionConfig> = {};
76
64
 
77
65
  for (const file of files) {
78
66
  const filePath = path.join(directory, file);
@@ -92,11 +80,11 @@ export function generateConfig(directory, excludePatterns = ['paper.md', 'README
92
80
 
93
81
  // Sort by order
94
82
  const sorted = Object.entries(sections)
95
- .sort((a, b) => a[1].order - b[1].order)
83
+ .sort((a, b) => (a[1].order ?? 999) - (b[1].order ?? 999))
96
84
  .reduce((acc, [k, v]) => {
97
85
  acc[k] = v;
98
86
  return acc;
99
- }, {});
87
+ }, {} as Record<string, SectionConfig>);
100
88
 
101
89
  return {
102
90
  version: 1,
@@ -107,10 +95,8 @@ export function generateConfig(directory, excludePatterns = ['paper.md', 'README
107
95
 
108
96
  /**
109
97
  * Convert string to title case
110
- * @param {string} str
111
- * @returns {string}
112
98
  */
113
- function titleCase(str) {
99
+ function titleCase(str: string): string {
114
100
  return str
115
101
  .split(/[-_\s]+/)
116
102
  .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
@@ -119,16 +105,17 @@ function titleCase(str) {
119
105
 
120
106
  /**
121
107
  * Load sections config from yaml file
122
- * @param {string} configPath
123
- * @returns {object}
124
108
  */
125
- export function loadConfig(configPath) {
109
+ export function loadConfig(configPath: string): SectionsConfig {
126
110
  const content = fs.readFileSync(configPath, 'utf-8');
127
111
  const config = YAML.parse(content);
128
112
 
129
113
  // Normalize: convert string values to full config objects
130
- const normalized = { ...config };
131
- normalized.sections = {};
114
+ const normalized: SectionsConfig = {
115
+ version: config.version || 1,
116
+ description: config.description,
117
+ sections: {},
118
+ };
132
119
 
133
120
  for (const [file, value] of Object.entries(config.sections || {})) {
134
121
  if (typeof value === 'string') {
@@ -137,10 +124,11 @@ export function loadConfig(configPath) {
137
124
  aliases: [],
138
125
  };
139
126
  } else {
127
+ const typedValue = value as SectionConfig;
140
128
  normalized.sections[file] = {
141
- header: value.header,
142
- aliases: value.aliases || [],
143
- order: value.order,
129
+ header: typedValue.header,
130
+ aliases: typedValue.aliases || [],
131
+ order: typedValue.order,
144
132
  };
145
133
  }
146
134
  }
@@ -150,22 +138,21 @@ export function loadConfig(configPath) {
150
138
 
151
139
  /**
152
140
  * Save sections config to yaml file
153
- * @param {string} configPath
154
- * @param {object} config
155
141
  */
156
- export function saveConfig(configPath, config) {
142
+ export function saveConfig(configPath: string, config: SectionsConfig): void {
157
143
  const yamlStr = YAML.stringify(config, { indent: 2, lineWidth: 100 });
158
144
  fs.writeFileSync(configPath, yamlStr, 'utf-8');
159
145
  }
160
146
 
161
147
  /**
162
148
  * Match a heading to a section file
163
- * @param {string} heading - Heading text from Word
164
- * @param {object} sections - Sections config
165
- * @returns {{file: string, config: SectionConfig}|null}
166
149
  */
167
- export function matchHeading(heading, sections) {
168
- const normalizedHeading = heading.toLowerCase().trim();
150
+ export function matchHeading(
151
+ heading: string,
152
+ sections: Record<string, SectionConfig>
153
+ ): { file: string; config: SectionConfig } | null {
154
+ // Strip markdown header prefix (# or ##, etc.) before matching
155
+ const normalizedHeading = heading.replace(/^#{1,6}\s+/, '').toLowerCase().trim();
169
156
 
170
157
  for (const [file, config] of Object.entries(sections)) {
171
158
  // Check primary header
@@ -196,37 +183,44 @@ export function matchHeading(heading, sections) {
196
183
 
197
184
  /**
198
185
  * Extract sections from Word document text
199
- * @param {string} text - Extracted text from Word
200
- * @param {object} sections - Sections config
201
- * @returns {Array<{file: string, header: string, content: string, matched: boolean}>}
202
186
  */
203
- export function extractSectionsFromText(text, sections) {
204
- const result = [];
205
-
206
- // Find all headings (lines that look like headers - typically short, at start of "paragraph")
207
- const paragraphs = text.split(/\n\n+/);
208
- let currentSection = null;
209
- let currentContent = [];
210
-
211
- for (const para of paragraphs) {
212
- const trimmed = para.trim();
213
-
214
- // Detect if this paragraph is a heading
215
- // Heuristics: short (< 100 chars), no periods, matches a known section
216
- const isLikelyHeading = trimmed.length < 100 && !trimmed.includes('.') && trimmed.length > 0;
217
-
218
- let matchedSection = null;
219
- if (isLikelyHeading) {
187
+ export function extractSectionsFromText(
188
+ text: string,
189
+ sections: Record<string, SectionConfig>
190
+ ): ExtractedSection[] {
191
+ const result: ExtractedSection[] = [];
192
+
193
+ // Process line by line to detect markdown headers
194
+ const lines = text.split('\n');
195
+ let currentSection: { file: string; header: string } | null = null;
196
+ let currentContent: string[] = [];
197
+
198
+ for (let i = 0; i < lines.length; i++) {
199
+ const line = lines[i];
200
+ if (!line) continue;
201
+ const trimmed = line.trim();
202
+
203
+ // Explicitly check for markdown headers (# Header)
204
+ const headerMatch = trimmed.match(/^(#{1,6})\s+(.+)$/);
205
+
206
+ let matchedSection: { file: string; config: SectionConfig } | null = null;
207
+ if (headerMatch) {
208
+ // This is a markdown header - try to match it to a section
209
+ matchedSection = matchHeading(trimmed, sections);
210
+ } else if (trimmed.length > 0 && trimmed.length < 100 && !trimmed.includes('.')) {
211
+ // Fallback: check if short text without periods matches a section (for plain text headings)
220
212
  matchedSection = matchHeading(trimmed, sections);
221
213
  }
222
214
 
223
215
  if (matchedSection) {
224
216
  // Save previous section
225
217
  if (currentSection) {
218
+ // Include header in content for proper diffing
219
+ const fullContent = currentSection.header + '\n\n' + currentContent.join('\n').trim();
226
220
  result.push({
227
221
  file: currentSection.file,
228
222
  header: currentSection.header,
229
- content: currentContent.join('\n\n'),
223
+ content: fullContent.trim(),
230
224
  matched: true,
231
225
  });
232
226
  }
@@ -237,16 +231,18 @@ export function extractSectionsFromText(text, sections) {
237
231
  };
238
232
  currentContent = [];
239
233
  } else {
240
- currentContent.push(para);
234
+ currentContent.push(line);
241
235
  }
242
236
  }
243
237
 
244
238
  // Save last section
245
239
  if (currentSection) {
240
+ // Include header in content for proper diffing
241
+ const fullContent = currentSection.header + '\n\n' + currentContent.join('\n').trim();
246
242
  result.push({
247
243
  file: currentSection.file,
248
244
  header: currentSection.header,
249
- content: currentContent.join('\n\n'),
245
+ content: fullContent.trim(),
250
246
  matched: true,
251
247
  });
252
248
  }
@@ -256,12 +252,12 @@ export function extractSectionsFromText(text, sections) {
256
252
 
257
253
  /**
258
254
  * Parse annotated paper.md and split back to section files
259
- * @param {string} paperContent - Content of annotated paper.md
260
- * @param {object} sections - Sections config
261
- * @returns {Map<string, string>} - Map of filename → content
262
255
  */
263
- export function splitAnnotatedPaper(paperContent, sections) {
264
- const result = new Map();
256
+ export function splitAnnotatedPaper(
257
+ paperContent: string,
258
+ sections: Record<string, SectionConfig>
259
+ ): Map<string, string> {
260
+ const result = new Map<string, string>();
265
261
 
266
262
  // Look for section markers: <!-- @section:filename.md -->
267
263
  const markerPattern = /<!--\s*@section:(\S+\.md)\s*-->/g;
@@ -271,8 +267,9 @@ export function splitAnnotatedPaper(paperContent, sections) {
271
267
  // Use markers
272
268
  for (let i = 0; i < markers.length; i++) {
273
269
  const marker = markers[i];
270
+ if (!marker || !marker[1]) continue;
274
271
  const file = marker[1];
275
- const start = marker.index + marker[0].length;
272
+ const start = (marker.index || 0) + marker[0].length;
276
273
  const end = markers[i + 1]?.index || paperContent.length;
277
274
 
278
275
  let content = paperContent.slice(start, end).trim();
@@ -285,13 +282,13 @@ export function splitAnnotatedPaper(paperContent, sections) {
285
282
  } else {
286
283
  // Fall back to header detection
287
284
  const lines = paperContent.split('\n');
288
- let currentFile = null;
289
- let currentContent = [];
285
+ let currentFile: string | null = null;
286
+ let currentContent: string[] = [];
290
287
 
291
288
  for (const line of lines) {
292
289
  const headerMatch = line.match(/^#\s+(.+)$/);
293
290
 
294
- if (headerMatch) {
291
+ if (headerMatch && headerMatch[1]) {
295
292
  // Save previous section
296
293
  if (currentFile) {
297
294
  result.set(currentFile, currentContent.join('\n').trim());
@@ -324,10 +321,8 @@ export function splitAnnotatedPaper(paperContent, sections) {
324
321
 
325
322
  /**
326
323
  * Get ordered list of section files from config
327
- * @param {object} config
328
- * @returns {string[]}
329
324
  */
330
- export function getOrderedSections(config) {
325
+ export function getOrderedSections(config: SectionsConfig): string[] {
331
326
  const entries = Object.entries(config.sections || {});
332
327
 
333
328
  return entries