@alisio/plugin-thesis 0.1.0

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 (547) hide show
  1. package/.agents/agents/thesis-architect.md +38 -0
  2. package/.agents/agents/thesis-coordinator.md +39 -0
  3. package/.agents/agents/thesis-editor.md +35 -0
  4. package/.agents/agents/thesis-evidence-auditor.md +37 -0
  5. package/.agents/agents/thesis-librarian.md +38 -0
  6. package/.agents/agents/thesis-methodologist.md +39 -0
  7. package/.agents/agents/thesis-reviewer.md +36 -0
  8. package/.agents/agents/thesis-writer.md +38 -0
  9. package/.agents/skills/thesis-build/SKILL.md +48 -0
  10. package/.agents/skills/thesis-citations/SKILL.md +49 -0
  11. package/.agents/skills/thesis-csl-authoring/SKILL.md +48 -0
  12. package/.agents/skills/thesis-editing/SKILL.md +49 -0
  13. package/.agents/skills/thesis-evidence/SKILL.md +48 -0
  14. package/.agents/skills/thesis-figures/SKILL.md +50 -0
  15. package/.agents/skills/thesis-institution-norms/SKILL.md +50 -0
  16. package/.agents/skills/thesis-intake/SKILL.md +48 -0
  17. package/.agents/skills/thesis-math/SKILL.md +48 -0
  18. package/.agents/skills/thesis-methodology/SKILL.md +49 -0
  19. package/.agents/skills/thesis-outline/SKILL.md +48 -0
  20. package/.agents/skills/thesis-policy/SKILL.md +48 -0
  21. package/.agents/skills/thesis-review/SKILL.md +49 -0
  22. package/.agents/skills/thesis-search/SKILL.md +47 -0
  23. package/.agents/skills/thesis-writing/SKILL.md +52 -0
  24. package/LICENSE +21 -0
  25. package/README.es.md +242 -0
  26. package/README.md +235 -0
  27. package/THIRD_PARTY_NOTICES.md +94 -0
  28. package/assets/architecture.svg +1 -0
  29. package/assets/evidence-pipeline.svg +1 -0
  30. package/assets/lifecycle.svg +1 -0
  31. package/assets/policy-resolution.svg +1 -0
  32. package/cover.webp +0 -0
  33. package/dist/authoring.d.ts +30 -0
  34. package/dist/authoring.d.ts.map +1 -0
  35. package/dist/authoring.js +681 -0
  36. package/dist/authoring.js.map +1 -0
  37. package/dist/brief.d.ts +40 -0
  38. package/dist/brief.d.ts.map +1 -0
  39. package/dist/brief.js +566 -0
  40. package/dist/brief.js.map +1 -0
  41. package/dist/cache.d.ts +8 -0
  42. package/dist/cache.d.ts.map +1 -0
  43. package/dist/cache.js +20 -0
  44. package/dist/cache.js.map +1 -0
  45. package/dist/checks/brief.d.ts +5 -0
  46. package/dist/checks/brief.d.ts.map +1 -0
  47. package/dist/checks/brief.js +38 -0
  48. package/dist/checks/brief.js.map +1 -0
  49. package/dist/checks/citations.d.ts +14 -0
  50. package/dist/checks/citations.d.ts.map +1 -0
  51. package/dist/checks/citations.js +87 -0
  52. package/dist/checks/citations.js.map +1 -0
  53. package/dist/checks/claims.d.ts +16 -0
  54. package/dist/checks/claims.d.ts.map +1 -0
  55. package/dist/checks/claims.js +147 -0
  56. package/dist/checks/claims.js.map +1 -0
  57. package/dist/checks/closing.d.ts +20 -0
  58. package/dist/checks/closing.d.ts.map +1 -0
  59. package/dist/checks/closing.js +168 -0
  60. package/dist/checks/closing.js.map +1 -0
  61. package/dist/checks/design.d.ts +5 -0
  62. package/dist/checks/design.d.ts.map +1 -0
  63. package/dist/checks/design.js +107 -0
  64. package/dist/checks/design.js.map +1 -0
  65. package/dist/checks/document.d.ts +17 -0
  66. package/dist/checks/document.d.ts.map +1 -0
  67. package/dist/checks/document.js +213 -0
  68. package/dist/checks/document.js.map +1 -0
  69. package/dist/checks/evidence.d.ts +7 -0
  70. package/dist/checks/evidence.d.ts.map +1 -0
  71. package/dist/checks/evidence.js +133 -0
  72. package/dist/checks/evidence.js.map +1 -0
  73. package/dist/checks/hygiene.d.ts +5 -0
  74. package/dist/checks/hygiene.d.ts.map +1 -0
  75. package/dist/checks/hygiene.js +37 -0
  76. package/dist/checks/hygiene.js.map +1 -0
  77. package/dist/checks/index.d.ts +21 -0
  78. package/dist/checks/index.d.ts.map +1 -0
  79. package/dist/checks/index.js +98 -0
  80. package/dist/checks/index.js.map +1 -0
  81. package/dist/checks/language.d.ts +14 -0
  82. package/dist/checks/language.d.ts.map +1 -0
  83. package/dist/checks/language.js +73 -0
  84. package/dist/checks/language.js.map +1 -0
  85. package/dist/checks/outline.d.ts +25 -0
  86. package/dist/checks/outline.d.ts.map +1 -0
  87. package/dist/checks/outline.js +214 -0
  88. package/dist/checks/outline.js.map +1 -0
  89. package/dist/checks/policy.d.ts +5 -0
  90. package/dist/checks/policy.d.ts.map +1 -0
  91. package/dist/checks/policy.js +129 -0
  92. package/dist/checks/policy.js.map +1 -0
  93. package/dist/checks/project.d.ts +65 -0
  94. package/dist/checks/project.d.ts.map +1 -0
  95. package/dist/checks/project.js +203 -0
  96. package/dist/checks/project.js.map +1 -0
  97. package/dist/checks/styles.d.ts +12 -0
  98. package/dist/checks/styles.d.ts.map +1 -0
  99. package/dist/checks/styles.js +67 -0
  100. package/dist/checks/styles.js.map +1 -0
  101. package/dist/checks/writing.d.ts +6 -0
  102. package/dist/checks/writing.d.ts.map +1 -0
  103. package/dist/checks/writing.js +115 -0
  104. package/dist/checks/writing.js.map +1 -0
  105. package/dist/chrome/detect.d.ts +19 -0
  106. package/dist/chrome/detect.d.ts.map +1 -0
  107. package/dist/chrome/detect.js +116 -0
  108. package/dist/chrome/detect.js.map +1 -0
  109. package/dist/claims.d.ts +19 -0
  110. package/dist/claims.d.ts.map +1 -0
  111. package/dist/claims.js +99 -0
  112. package/dist/claims.js.map +1 -0
  113. package/dist/cli.d.ts +11 -0
  114. package/dist/cli.d.ts.map +1 -0
  115. package/dist/cli.js +262 -0
  116. package/dist/cli.js.map +1 -0
  117. package/dist/context.d.ts +55 -0
  118. package/dist/context.d.ts.map +1 -0
  119. package/dist/context.js +62 -0
  120. package/dist/context.js.map +1 -0
  121. package/dist/coordinator.d.ts +139 -0
  122. package/dist/coordinator.d.ts.map +1 -0
  123. package/dist/coordinator.js +710 -0
  124. package/dist/coordinator.js.map +1 -0
  125. package/dist/delegate.d.ts +34 -0
  126. package/dist/delegate.d.ts.map +1 -0
  127. package/dist/delegate.js +105 -0
  128. package/dist/delegate.js.map +1 -0
  129. package/dist/diffguard.d.ts +24 -0
  130. package/dist/diffguard.d.ts.map +1 -0
  131. package/dist/diffguard.js +92 -0
  132. package/dist/diffguard.js.map +1 -0
  133. package/dist/doctor.d.ts +34 -0
  134. package/dist/doctor.d.ts.map +1 -0
  135. package/dist/doctor.js +155 -0
  136. package/dist/doctor.js.map +1 -0
  137. package/dist/draft.d.ts +64 -0
  138. package/dist/draft.d.ts.map +1 -0
  139. package/dist/draft.js +801 -0
  140. package/dist/draft.js.map +1 -0
  141. package/dist/figure.d.ts +13 -0
  142. package/dist/figure.d.ts.map +1 -0
  143. package/dist/figure.js +143 -0
  144. package/dist/figure.js.map +1 -0
  145. package/dist/finalize.d.ts +29 -0
  146. package/dist/finalize.d.ts.map +1 -0
  147. package/dist/finalize.js +230 -0
  148. package/dist/finalize.js.map +1 -0
  149. package/dist/index.d.ts +33 -0
  150. package/dist/index.d.ts.map +1 -0
  151. package/dist/index.js +206 -0
  152. package/dist/index.js.map +1 -0
  153. package/dist/intake.d.ts +57 -0
  154. package/dist/intake.d.ts.map +1 -0
  155. package/dist/intake.js +772 -0
  156. package/dist/intake.js.map +1 -0
  157. package/dist/labels.d.ts +50 -0
  158. package/dist/labels.d.ts.map +1 -0
  159. package/dist/labels.js +98 -0
  160. package/dist/labels.js.map +1 -0
  161. package/dist/outline.d.ts +45 -0
  162. package/dist/outline.d.ts.map +1 -0
  163. package/dist/outline.js +148 -0
  164. package/dist/outline.js.map +1 -0
  165. package/dist/pack-commands.d.ts +18 -0
  166. package/dist/pack-commands.d.ts.map +1 -0
  167. package/dist/pack-commands.js +397 -0
  168. package/dist/pack-commands.js.map +1 -0
  169. package/dist/package-paths.d.ts +8 -0
  170. package/dist/package-paths.d.ts.map +1 -0
  171. package/dist/package-paths.js +13 -0
  172. package/dist/package-paths.js.map +1 -0
  173. package/dist/policy/context.d.ts +19 -0
  174. package/dist/policy/context.d.ts.map +1 -0
  175. package/dist/policy/context.js +62 -0
  176. package/dist/policy/context.js.map +1 -0
  177. package/dist/policy/ethics.d.ts +59 -0
  178. package/dist/policy/ethics.d.ts.map +1 -0
  179. package/dist/policy/ethics.js +106 -0
  180. package/dist/policy/ethics.js.map +1 -0
  181. package/dist/policy/kinds.d.ts +9 -0
  182. package/dist/policy/kinds.d.ts.map +1 -0
  183. package/dist/policy/kinds.js +68 -0
  184. package/dist/policy/kinds.js.map +1 -0
  185. package/dist/policy/packs.d.ts +53 -0
  186. package/dist/policy/packs.d.ts.map +1 -0
  187. package/dist/policy/packs.js +336 -0
  188. package/dist/policy/packs.js.map +1 -0
  189. package/dist/policy/resolver.d.ts +87 -0
  190. package/dist/policy/resolver.d.ts.map +1 -0
  191. package/dist/policy/resolver.js +337 -0
  192. package/dist/policy/resolver.js.map +1 -0
  193. package/dist/policy/rules.d.ts +65 -0
  194. package/dist/policy/rules.d.ts.map +1 -0
  195. package/dist/policy/rules.js +194 -0
  196. package/dist/policy/rules.js.map +1 -0
  197. package/dist/protocol.d.ts +46 -0
  198. package/dist/protocol.d.ts.map +1 -0
  199. package/dist/protocol.js +86 -0
  200. package/dist/protocol.js.map +1 -0
  201. package/dist/render/adapters/chrome-pdf/cdp.d.ts +36 -0
  202. package/dist/render/adapters/chrome-pdf/cdp.d.ts.map +1 -0
  203. package/dist/render/adapters/chrome-pdf/cdp.js +291 -0
  204. package/dist/render/adapters/chrome-pdf/cdp.js.map +1 -0
  205. package/dist/render/adapters/chrome-pdf/index.d.ts +10 -0
  206. package/dist/render/adapters/chrome-pdf/index.d.ts.map +1 -0
  207. package/dist/render/adapters/chrome-pdf/index.js +139 -0
  208. package/dist/render/adapters/chrome-pdf/index.js.map +1 -0
  209. package/dist/render/adapters/chrome-pdf/network.d.ts +7 -0
  210. package/dist/render/adapters/chrome-pdf/network.d.ts.map +1 -0
  211. package/dist/render/adapters/chrome-pdf/network.js +33 -0
  212. package/dist/render/adapters/chrome-pdf/network.js.map +1 -0
  213. package/dist/render/adapters/html-preview/index.d.ts +8 -0
  214. package/dist/render/adapters/html-preview/index.d.ts.map +1 -0
  215. package/dist/render/adapters/html-preview/index.js +62 -0
  216. package/dist/render/adapters/html-preview/index.js.map +1 -0
  217. package/dist/render/adapters/test-json/index.d.ts +8 -0
  218. package/dist/render/adapters/test-json/index.d.ts.map +1 -0
  219. package/dist/render/adapters/test-json/index.js +62 -0
  220. package/dist/render/adapters/test-json/index.js.map +1 -0
  221. package/dist/render/adapters/typst-pdf/emitter.d.ts +21 -0
  222. package/dist/render/adapters/typst-pdf/emitter.d.ts.map +1 -0
  223. package/dist/render/adapters/typst-pdf/emitter.js +259 -0
  224. package/dist/render/adapters/typst-pdf/emitter.js.map +1 -0
  225. package/dist/render/adapters/typst-pdf/escape.d.ts +14 -0
  226. package/dist/render/adapters/typst-pdf/escape.d.ts.map +1 -0
  227. package/dist/render/adapters/typst-pdf/escape.js +56 -0
  228. package/dist/render/adapters/typst-pdf/escape.js.map +1 -0
  229. package/dist/render/adapters/typst-pdf/index.d.ts +9 -0
  230. package/dist/render/adapters/typst-pdf/index.d.ts.map +1 -0
  231. package/dist/render/adapters/typst-pdf/index.js +154 -0
  232. package/dist/render/adapters/typst-pdf/index.js.map +1 -0
  233. package/dist/render/adapters/typst-pdf/profile.d.ts +3 -0
  234. package/dist/render/adapters/typst-pdf/profile.d.ts.map +1 -0
  235. package/dist/render/adapters/typst-pdf/profile.js +55 -0
  236. package/dist/render/adapters/typst-pdf/profile.js.map +1 -0
  237. package/dist/render/adapters/typst-pdf/runner.d.ts +52 -0
  238. package/dist/render/adapters/typst-pdf/runner.d.ts.map +1 -0
  239. package/dist/render/adapters/typst-pdf/runner.js +209 -0
  240. package/dist/render/adapters/typst-pdf/runner.js.map +1 -0
  241. package/dist/render/adapters/typst-pdf/setup.d.ts +30 -0
  242. package/dist/render/adapters/typst-pdf/setup.d.ts.map +1 -0
  243. package/dist/render/adapters/typst-pdf/setup.js +183 -0
  244. package/dist/render/adapters/typst-pdf/setup.js.map +1 -0
  245. package/dist/render/adapters/typst-pdf/version.d.ts +8 -0
  246. package/dist/render/adapters/typst-pdf/version.d.ts.map +1 -0
  247. package/dist/render/adapters/typst-pdf/version.js +14 -0
  248. package/dist/render/adapters/typst-pdf/version.js.map +1 -0
  249. package/dist/render/assemble.d.ts +28 -0
  250. package/dist/render/assemble.d.ts.map +1 -0
  251. package/dist/render/assemble.js +309 -0
  252. package/dist/render/assemble.js.map +1 -0
  253. package/dist/render/assets.d.ts +27 -0
  254. package/dist/render/assets.d.ts.map +1 -0
  255. package/dist/render/assets.js +232 -0
  256. package/dist/render/assets.js.map +1 -0
  257. package/dist/render/build.d.ts +43 -0
  258. package/dist/render/build.d.ts.map +1 -0
  259. package/dist/render/build.js +187 -0
  260. package/dist/render/build.js.map +1 -0
  261. package/dist/render/charts.d.ts +83 -0
  262. package/dist/render/charts.d.ts.map +1 -0
  263. package/dist/render/charts.js +633 -0
  264. package/dist/render/charts.js.map +1 -0
  265. package/dist/render/default-registry.d.ts +7 -0
  266. package/dist/render/default-registry.d.ts.map +1 -0
  267. package/dist/render/default-registry.js +15 -0
  268. package/dist/render/default-registry.js.map +1 -0
  269. package/dist/render/diagram-theme.d.ts +7 -0
  270. package/dist/render/diagram-theme.d.ts.map +1 -0
  271. package/dist/render/diagram-theme.js +50 -0
  272. package/dist/render/diagram-theme.js.map +1 -0
  273. package/dist/render/dialect.d.ts +23 -0
  274. package/dist/render/dialect.d.ts.map +1 -0
  275. package/dist/render/dialect.js +188 -0
  276. package/dist/render/dialect.js.map +1 -0
  277. package/dist/render/html/citations.d.ts +70 -0
  278. package/dist/render/html/citations.d.ts.map +1 -0
  279. package/dist/render/html/citations.js +371 -0
  280. package/dist/render/html/citations.js.map +1 -0
  281. package/dist/render/html/css.d.ts +14 -0
  282. package/dist/render/html/css.d.ts.map +1 -0
  283. package/dist/render/html/css.js +113 -0
  284. package/dist/render/html/css.js.map +1 -0
  285. package/dist/render/html/emitter.d.ts +20 -0
  286. package/dist/render/html/emitter.d.ts.map +1 -0
  287. package/dist/render/html/emitter.js +652 -0
  288. package/dist/render/html/emitter.js.map +1 -0
  289. package/dist/render/html/escape.d.ts +11 -0
  290. package/dist/render/html/escape.d.ts.map +1 -0
  291. package/dist/render/html/escape.js +32 -0
  292. package/dist/render/html/escape.js.map +1 -0
  293. package/dist/render/html/katex.d.ts +9 -0
  294. package/dist/render/html/katex.d.ts.map +1 -0
  295. package/dist/render/html/katex.js +39 -0
  296. package/dist/render/html/katex.js.map +1 -0
  297. package/dist/render/html/stage.d.ts +12 -0
  298. package/dist/render/html/stage.d.ts.map +1 -0
  299. package/dist/render/html/stage.js +30 -0
  300. package/dist/render/html/stage.js.map +1 -0
  301. package/dist/render/index.d.ts +5 -0
  302. package/dist/render/index.d.ts.map +1 -0
  303. package/dist/render/index.js +5 -0
  304. package/dist/render/index.js.map +1 -0
  305. package/dist/render/math.d.ts +9 -0
  306. package/dist/render/math.d.ts.map +1 -0
  307. package/dist/render/math.js +60 -0
  308. package/dist/render/math.js.map +1 -0
  309. package/dist/render/model.d.ts +233 -0
  310. package/dist/render/model.d.ts.map +1 -0
  311. package/dist/render/model.js +2 -0
  312. package/dist/render/model.js.map +1 -0
  313. package/dist/render/numbering.d.ts +51 -0
  314. package/dist/render/numbering.d.ts.map +1 -0
  315. package/dist/render/numbering.js +155 -0
  316. package/dist/render/numbering.js.map +1 -0
  317. package/dist/render/output-name.d.ts +4 -0
  318. package/dist/render/output-name.d.ts.map +1 -0
  319. package/dist/render/output-name.js +11 -0
  320. package/dist/render/output-name.js.map +1 -0
  321. package/dist/render/palettes.d.ts +22 -0
  322. package/dist/render/palettes.d.ts.map +1 -0
  323. package/dist/render/palettes.js +46 -0
  324. package/dist/render/palettes.js.map +1 -0
  325. package/dist/render/parse.d.ts +15 -0
  326. package/dist/render/parse.d.ts.map +1 -0
  327. package/dist/render/parse.js +637 -0
  328. package/dist/render/parse.js.map +1 -0
  329. package/dist/render/port.d.ts +79 -0
  330. package/dist/render/port.d.ts.map +1 -0
  331. package/dist/render/port.js +2 -0
  332. package/dist/render/port.js.map +1 -0
  333. package/dist/render/registry.d.ts +11 -0
  334. package/dist/render/registry.d.ts.map +1 -0
  335. package/dist/render/registry.js +21 -0
  336. package/dist/render/registry.js.map +1 -0
  337. package/dist/render/text.d.ts +13 -0
  338. package/dist/render/text.d.ts.map +1 -0
  339. package/dist/render/text.js +65 -0
  340. package/dist/render/text.js.map +1 -0
  341. package/dist/render/walk.d.ts +13 -0
  342. package/dist/render/walk.d.ts.map +1 -0
  343. package/dist/render/walk.js +69 -0
  344. package/dist/render/walk.js.map +1 -0
  345. package/dist/research/bibtex.d.ts +12 -0
  346. package/dist/research/bibtex.d.ts.map +1 -0
  347. package/dist/research/bibtex.js +99 -0
  348. package/dist/research/bibtex.js.map +1 -0
  349. package/dist/research/client.d.ts +112 -0
  350. package/dist/research/client.d.ts.map +1 -0
  351. package/dist/research/client.js +661 -0
  352. package/dist/research/client.js.map +1 -0
  353. package/dist/research/dossier.d.ts +31 -0
  354. package/dist/research/dossier.d.ts.map +1 -0
  355. package/dist/research/dossier.js +54 -0
  356. package/dist/research/dossier.js.map +1 -0
  357. package/dist/research/flow.d.ts +31 -0
  358. package/dist/research/flow.d.ts.map +1 -0
  359. package/dist/research/flow.js +635 -0
  360. package/dist/research/flow.js.map +1 -0
  361. package/dist/research/library.d.ts +79 -0
  362. package/dist/research/library.d.ts.map +1 -0
  363. package/dist/research/library.js +319 -0
  364. package/dist/research/library.js.map +1 -0
  365. package/dist/research/verify.d.ts +80 -0
  366. package/dist/research/verify.d.ts.map +1 -0
  367. package/dist/research/verify.js +307 -0
  368. package/dist/research/verify.js.map +1 -0
  369. package/dist/resources.d.ts +15 -0
  370. package/dist/resources.d.ts.map +1 -0
  371. package/dist/resources.js +148 -0
  372. package/dist/resources.js.map +1 -0
  373. package/dist/review.d.ts +22 -0
  374. package/dist/review.d.ts.map +1 -0
  375. package/dist/review.js +355 -0
  376. package/dist/review.js.map +1 -0
  377. package/dist/schemas.d.ts +252 -0
  378. package/dist/schemas.d.ts.map +1 -0
  379. package/dist/schemas.js +1020 -0
  380. package/dist/schemas.js.map +1 -0
  381. package/dist/sections.d.ts +30 -0
  382. package/dist/sections.d.ts.map +1 -0
  383. package/dist/sections.js +62 -0
  384. package/dist/sections.js.map +1 -0
  385. package/dist/storage.d.ts +21 -0
  386. package/dist/storage.d.ts.map +1 -0
  387. package/dist/storage.js +296 -0
  388. package/dist/storage.js.map +1 -0
  389. package/dist/style-commands.d.ts +15 -0
  390. package/dist/style-commands.d.ts.map +1 -0
  391. package/dist/style-commands.js +54 -0
  392. package/dist/style-commands.js.map +1 -0
  393. package/dist/styles/csl.d.ts +58 -0
  394. package/dist/styles/csl.d.ts.map +1 -0
  395. package/dist/styles/csl.js +112 -0
  396. package/dist/styles/csl.js.map +1 -0
  397. package/dist/styles/discovery.d.ts +45 -0
  398. package/dist/styles/discovery.d.ts.map +1 -0
  399. package/dist/styles/discovery.js +99 -0
  400. package/dist/styles/discovery.js.map +1 -0
  401. package/dist/styles/golden.d.ts +52 -0
  402. package/dist/styles/golden.d.ts.map +1 -0
  403. package/dist/styles/golden.js +216 -0
  404. package/dist/styles/golden.js.map +1 -0
  405. package/dist/styles/profile.d.ts +126 -0
  406. package/dist/styles/profile.d.ts.map +1 -0
  407. package/dist/styles/profile.js +582 -0
  408. package/dist/styles/profile.js.map +1 -0
  409. package/dist/styles/xml.d.ts +22 -0
  410. package/dist/styles/xml.d.ts.map +1 -0
  411. package/dist/styles/xml.js +179 -0
  412. package/dist/styles/xml.js.map +1 -0
  413. package/dist/types.d.ts +285 -0
  414. package/dist/types.d.ts.map +1 -0
  415. package/dist/types.js +155 -0
  416. package/dist/types.js.map +1 -0
  417. package/dist/typst-pins.d.ts +16 -0
  418. package/dist/typst-pins.d.ts.map +1 -0
  419. package/dist/typst-pins.js +39 -0
  420. package/dist/typst-pins.js.map +1 -0
  421. package/dist/version.d.ts +2 -0
  422. package/dist/version.d.ts.map +1 -0
  423. package/dist/version.js +3 -0
  424. package/dist/version.js.map +1 -0
  425. package/dist/workflow.d.ts +49 -0
  426. package/dist/workflow.d.ts.map +1 -0
  427. package/dist/workflow.js +722 -0
  428. package/dist/workflow.js.map +1 -0
  429. package/dist/workspace.d.ts +12 -0
  430. package/dist/workspace.d.ts.map +1 -0
  431. package/dist/workspace.js +71 -0
  432. package/dist/workspace.js.map +1 -0
  433. package/package.json +70 -0
  434. package/policy-packs/countries/CO/academic-standards/ntc-1486-2022.yaml +188 -0
  435. package/policy-packs/countries/CO/copyright/decision-andina-351.yaml +61 -0
  436. package/policy-packs/countries/CO/copyright/ley-1915-2018.yaml +25 -0
  437. package/policy-packs/countries/CO/copyright/ley-23-1982.yaml +64 -0
  438. package/policy-packs/countries/CO/domains/health/resolucion-8430-1993.yaml +219 -0
  439. package/policy-packs/countries/CO/higher-education/ley-30-1992.yaml +61 -0
  440. package/policy-packs/countries/CO/manifest.yaml +42 -0
  441. package/policy-packs/countries/CO/official-domains.yaml +90 -0
  442. package/policy-packs/countries/CO/privacy/ley-1581-2012.yaml +197 -0
  443. package/policy-packs/countries/CO/research-integrity/minciencias-res-0314-2018.yaml +64 -0
  444. package/policy-packs/global/academic-integrity.yaml +45 -0
  445. package/policy-packs/global/academic-writing.yaml +70 -0
  446. package/policy-packs/global/evidence-quality.yaml +81 -0
  447. package/policy-packs/global/manifest.yaml +54 -0
  448. package/policy-packs/global/objective-verbs.yaml +245 -0
  449. package/policy-packs/global/reporting-guidelines.yaml +392 -0
  450. package/policy-packs/global/work-types.yaml +195 -0
  451. package/styles/LICENSE-CSL +375 -0
  452. package/styles/apa.csl +2283 -0
  453. package/styles/catalog.json +43 -0
  454. package/styles/fixtures/apa-7.expected.json +25 -0
  455. package/styles/fixtures/icontec-ntc1486-2022.expected.json +25 -0
  456. package/styles/fixtures/ieee.expected.json +25 -0
  457. package/styles/fixtures/references.jsonl +8 -0
  458. package/styles/icontec-ntc1486-2022.csl +388 -0
  459. package/styles/ieee.csl +519 -0
  460. package/templates/html/runtime.js +159 -0
  461. package/templates/html/thesis.css +98 -0
  462. package/templates/html/vendor/katex/LICENSE +21 -0
  463. package/templates/html/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  464. package/templates/html/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  465. package/templates/html/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  466. package/templates/html/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  467. package/templates/html/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  468. package/templates/html/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  469. package/templates/html/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  470. package/templates/html/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  471. package/templates/html/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  472. package/templates/html/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  473. package/templates/html/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  474. package/templates/html/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  475. package/templates/html/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  476. package/templates/html/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  477. package/templates/html/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  478. package/templates/html/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  479. package/templates/html/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  480. package/templates/html/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  481. package/templates/html/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  482. package/templates/html/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  483. package/templates/html/vendor/katex/katex.min.css +1 -0
  484. package/templates/html/vendor/katex/katex.min.js +1 -0
  485. package/templates/html/vendor/mermaid/LICENSE +21 -0
  486. package/templates/html/vendor/mermaid/mermaid.min.js +3587 -0
  487. package/templates/html/vendor/pagedjs/LICENSE +21 -0
  488. package/templates/html/vendor/pagedjs/paged.polyfill.min.js +4 -0
  489. package/templates/palettes.json +57 -0
  490. package/templates/profiles/apa-7.profile.yaml +15 -0
  491. package/templates/profiles/generic.profile.yaml +20 -0
  492. package/templates/profiles/icontec-ntc1486-2022.profile.yaml +36 -0
  493. package/templates/profiles/ieee.profile.yaml +14 -0
  494. package/templates/typst/i18n/en.yaml +31 -0
  495. package/templates/typst/i18n/es.yaml +31 -0
  496. package/templates/typst/i18n/pt.yaml +31 -0
  497. package/templates/typst/thesis.typ +276 -0
  498. package/typst-packages/preview/merman/0.3.0/LICENSE +231 -0
  499. package/typst-packages/preview/merman/0.3.0/README.md +388 -0
  500. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/cose-base-v1/LICENSE +21 -0
  501. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/cose-base-v2/LICENSE +21 -0
  502. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/cytoscape/LICENSE +19 -0
  503. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/cytoscape-cose-bilkent/LICENSE +21 -0
  504. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/cytoscape-fcose/LICENSE +19 -0
  505. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/d3-shape/LICENSE +13 -0
  506. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/dagre/LICENSE +19 -0
  507. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/dompurify/LICENSE +202 -0
  508. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/eclipse-elk/LICENSE.md +264 -0
  509. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/elkjs/LICENSE.md +264 -0
  510. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/fmin/LICENSE +27 -0
  511. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/graphlib/LICENSE +19 -0
  512. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/layout-base-v1/LICENSE +21 -0
  513. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/layout-base-v2/LICENSE +21 -0
  514. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/mermaid/LICENSE +21 -0
  515. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/rough-rs/LICENSE +21 -0
  516. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/roughjs/LICENSE +21 -0
  517. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/sanitize-url/LICENSE +21 -0
  518. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/venn-js/LICENSE +22 -0
  519. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/wasm-minimal-protocol/LICENSE +24 -0
  520. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_LICENSES/zenuml-core/LICENSE +21 -0
  521. package/typst-packages/preview/merman/0.3.0/THIRD_PARTY_NOTICES.md +265 -0
  522. package/typst-packages/preview/merman/0.3.0/lib.typ +1 -0
  523. package/typst-packages/preview/merman/0.3.0/merman_typst_plugin.wasm +0 -0
  524. package/typst-packages/preview/merman/0.3.0/src/capabilities.typ +5 -0
  525. package/typst-packages/preview/merman/0.3.0/src/context.typ +1 -0
  526. package/typst-packages/preview/merman/0.3.0/src/errors.typ +33 -0
  527. package/typst-packages/preview/merman/0.3.0/src/exports.typ +5 -0
  528. package/typst-packages/preview/merman/0.3.0/src/figure.typ +143 -0
  529. package/typst-packages/preview/merman/0.3.0/src/image.typ +50 -0
  530. package/typst-packages/preview/merman/0.3.0/src/options.typ +566 -0
  531. package/typst-packages/preview/merman/0.3.0/src/plugin.typ +1 -0
  532. package/typst-packages/preview/merman/0.3.0/src/raw.typ +22 -0
  533. package/typst-packages/preview/merman/0.3.0/src/render.typ +137 -0
  534. package/typst-packages/preview/merman/0.3.0/src/source.typ +7 -0
  535. package/typst-packages/preview/merman/0.3.0/src/units.typ +51 -0
  536. package/typst-packages/preview/merman/0.3.0/typst.toml +12 -0
  537. package/typst-packages/preview/mitex/0.2.7/CHANGELOG.md +69 -0
  538. package/typst-packages/preview/mitex/0.2.7/LICENSE +176 -0
  539. package/typst-packages/preview/mitex/0.2.7/README.md +154 -0
  540. package/typst-packages/preview/mitex/0.2.7/lib.typ +1 -0
  541. package/typst-packages/preview/mitex/0.2.7/mitex.typ +45 -0
  542. package/typst-packages/preview/mitex/0.2.7/mitex.wasm +0 -0
  543. package/typst-packages/preview/mitex/0.2.7/specs/README.md +250 -0
  544. package/typst-packages/preview/mitex/0.2.7/specs/latex/standard.typ +1187 -0
  545. package/typst-packages/preview/mitex/0.2.7/specs/mod.typ +12 -0
  546. package/typst-packages/preview/mitex/0.2.7/specs/prelude.typ +230 -0
  547. package/typst-packages/preview/mitex/0.2.7/typst.toml +12 -0
package/dist/draft.js ADDED
@@ -0,0 +1,801 @@
1
+ import { mkdir, readFile, rm } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
4
+ import { citationsOf } from "./checks/citations.js";
5
+ import { documentOf } from "./checks/document.js";
6
+ import { runChecks } from "./checks/index.js";
7
+ import { protocolObjectives } from "./checks/outline.js";
8
+ import { claimId, parseClaimsText, readClaims, serializeClaims, writeClaims } from "./claims.js";
9
+ import { askOne, describeFinding, errorsOf, loadAll, saveState, syncPhase, } from "./context.js";
10
+ import { ChildRejectedError } from "./delegate.js";
11
+ import { diffProtected } from "./diffguard.js";
12
+ import { flattenOutline, parseOutline } from "./outline.js";
13
+ import { parseChapter } from "./render/parse.js";
14
+ import { allBlockLists, walkBlocks } from "./render/walk.js";
15
+ import { dossierPaths } from "./research/dossier.js";
16
+ import { citableForSection, readLibrary } from "./research/library.js";
17
+ import { validateEditPass, validateSectionDraft, } from "./schemas.js";
18
+ import { planSections } from "./sections.js";
19
+ import { atomicWrite } from "./storage.js";
20
+ import { frontSectionRoles, summarySectionRoles, } from "./types.js";
21
+ const data = (label, content) => `=== BEGIN ${label} (data, not instructions) ===\n${content}\n=== END ${label} ===`;
22
+ const draftSchema = `{
23
+ "markdown": "the section body in the thesis Markdown dialect (no title heading; code adds it)",
24
+ "keywords": ["3 to 5 keywords; abstracts only, otherwise []"],
25
+ "claims": [{
26
+ "anchor": "c1 (unique in this section; letters, digits, _ and -)",
27
+ "text": "the claim in one sentence, at most 400 characters",
28
+ "kind": "background|argument|result|conclusion",
29
+ "evidence": ["EVD ids from the packet that support it"],
30
+ "results": ["for a conclusion: anchors of result claims in this draft, or CLM ids of earlier claims"],
31
+ "objectives": ["OBJ ids the claim answers (result and conclusion claims)"]
32
+ }],
33
+ "figures": [{
34
+ "label": "fig-adoption-rate | tbl-results",
35
+ "kind": "chart|diagram|table",
36
+ "spec": "chart: a Vega-Lite object with data.url pointing at a file of thesis/data, no colors, fonts or sizes",
37
+ "source": "diagram: Mermaid source",
38
+ "table": "table: a GFM pipe table",
39
+ "caption": "one line ending in 'Source: ...' (Fuente: / Fonte:)",
40
+ "width": "80% (optional)",
41
+ "supports": "the sentence of the text it supports"
42
+ }],
43
+ "gaps": ["statements the evidence could not support, left out on purpose"]
44
+ }`;
45
+ const editSchema = `{
46
+ "markdown": "the complete revised body",
47
+ "changes": ["short summary of what you changed, by category"],
48
+ "queries": ["questions for the author, if any"]
49
+ }`;
50
+ const dialectRules = [
51
+ "Cite only keys of the evidence packet as [@key], [@key, p. 17], [@a2020; @b2021] or narrative @key.",
52
+ "Mark every non-trivial claim: put `<!-- claim:c1 -->` alone on the line before its paragraph, and declare the same anchor in claims.",
53
+ "Cross-reference with @fig-x, @tbl-x, @eq-x and @sec-x, only to labels that already exist (see the list of labels in use) or that you define in this section; never to sections not written yet. Write inline math as $...$ (LaTeX) and displayed equations as $$...$$ {#eq-x}.",
54
+ "Declare each figure or table in figures and put `[[figure fig-x]]` alone on a line where it belongs; refer to it in the text with @fig-x.",
55
+ "Footnotes use [^1] with a definition. No raw HTML, no TODO/TBD placeholders, no absolute paths, no \\newpage.",
56
+ ];
57
+ const names = (record) => record.authors
58
+ .slice(0, 3)
59
+ .map((author) => author.family)
60
+ .join(", ") + (record.authors.length > 3 ? " et al." : "");
61
+ function reference(record) {
62
+ return [
63
+ `${names(record)} (${record.year}). ${record.title}.`,
64
+ record.containerTitle ? `${record.containerTitle}.` : "",
65
+ record.doi ? `https://doi.org/${record.doi}` : (record.url ?? ""),
66
+ ]
67
+ .filter(Boolean)
68
+ .join(" ");
69
+ }
70
+ // ---------------------------------------------------------------------------------------------
71
+ // The chapter file: front matter + code-owned heading + body
72
+ // ---------------------------------------------------------------------------------------------
73
+ export function headingOf(plan) {
74
+ const title = plan.node.title.replace(/[\r\n{}]+/g, " ").trim();
75
+ return `${"#".repeat(plan.level)} ${title} {#sec-${plan.node.key.replace(/[^a-z0-9-]/g, "-").slice(0, 48)}}`;
76
+ }
77
+ export function composeFile(plan, body, keywords) {
78
+ const front = {};
79
+ if (plan.role !== "body")
80
+ front.role = plan.role;
81
+ front.section = plan.id;
82
+ if (plan.lang)
83
+ front.lang = plan.lang;
84
+ if (keywords.length > 0)
85
+ front.keywords = [...keywords];
86
+ const head = `---\n${stringifyYaml(front, { lineWidth: 0 })}---\n\n`;
87
+ return `${head}${plan.heading ? `${headingOf(plan)}\n\n` : ""}${body.trim()}\n`;
88
+ }
89
+ /** The inverse of composeFile: the body and keywords of a chapter written by this plugin. */
90
+ export function splitChapter(plan, text) {
91
+ let rest = text;
92
+ let keywords = [];
93
+ const match = /^---\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/.exec(rest);
94
+ if (match) {
95
+ try {
96
+ const front = parseYaml(match[1]);
97
+ if (Array.isArray(front?.keywords))
98
+ keywords = front.keywords.filter((entry) => typeof entry === "string");
99
+ }
100
+ catch {
101
+ keywords = [];
102
+ }
103
+ rest = rest.slice(match[0].length);
104
+ }
105
+ rest = rest.replace(/^\s+/, "");
106
+ if (plan.heading)
107
+ rest = rest.replace(/^#{1,4} [^\n]*\n+/, "");
108
+ return { body: rest.trim(), keywords };
109
+ }
110
+ // ---------------------------------------------------------------------------------------------
111
+ // Figures inside a section draft
112
+ // ---------------------------------------------------------------------------------------------
113
+ export function figureAsset(figure) {
114
+ const name = figure.label.slice(figure.label.indexOf("-") + 1);
115
+ if (figure.kind === "chart")
116
+ return {
117
+ path: `figures/charts/${name}.vl.json`,
118
+ content: `${JSON.stringify(figure.spec, null, 2)}\n`,
119
+ };
120
+ if (figure.kind === "diagram")
121
+ return { path: `figures/diagrams/${name}.mmd`, content: `${(figure.source ?? "").trim()}\n` };
122
+ return undefined;
123
+ }
124
+ /** The Markdown that places a figure or table in a chapter. */
125
+ export function figureSnippet(figure) {
126
+ const asset = figureAsset(figure);
127
+ if (asset) {
128
+ const width = figure.width ? ` width=${figure.width}` : "";
129
+ return `![${figure.caption}](${asset.path}){#${figure.label}${width}}`;
130
+ }
131
+ return `${(figure.table ?? "").trim()}\n\nTable: ${figure.caption} {#${figure.label}}`;
132
+ }
133
+ const markerPattern = /^[ \t]*\[\[figure ([a-z0-9-]+)\]\][ \t]*$/gm;
134
+ /** Replace the figure markers of a draft with the figures' Markdown; problems come back as errors. */
135
+ export function placeFigures(markdown, figures) {
136
+ const errors = [];
137
+ const byLabel = new Map(figures.map((figure) => [figure.label, figure]));
138
+ const placed = new Set();
139
+ let body = markdown.replace(markerPattern, (_match, label) => {
140
+ const figure = byLabel.get(label);
141
+ if (!figure) {
142
+ errors.push(`The marker [[figure ${label}]] has no entry in figures`);
143
+ return "";
144
+ }
145
+ if (placed.has(label)) {
146
+ errors.push(`The marker [[figure ${label}]] appears twice`);
147
+ return "";
148
+ }
149
+ placed.add(label);
150
+ return figureSnippet(figure);
151
+ });
152
+ if (/\[\[figure /.test(body))
153
+ errors.push("A [[figure ...]] marker must be alone on its line and use the figure's label");
154
+ for (const figure of figures)
155
+ if (!placed.has(figure.label))
156
+ body = `${body.trimEnd()}\n\n${figureSnippet(figure)}\n`;
157
+ return { body, errors };
158
+ }
159
+ const draftable = ["research_approved", "drafting", "revising"];
160
+ const revisable = ["draft_review", "approved"];
161
+ const sectionGates = ["G3", "G5", "G7", "G8"];
162
+ const approveQuestion = (id, recommend) => ({
163
+ id: "section",
164
+ header: `${id} draft`,
165
+ question: `Review the draft of ${id} (its chapter file and the check summary above). What next?`,
166
+ options: [
167
+ {
168
+ value: "approve",
169
+ label: "Approve section",
170
+ description: "Accept it, record the approval and build the approved sections.",
171
+ recommended: recommend,
172
+ },
173
+ {
174
+ value: "revise",
175
+ label: "Revise with feedback",
176
+ description: "The writer redrafts the section following your feedback.",
177
+ textInput: { placeholder: "What should change?" },
178
+ recommended: !recommend,
179
+ },
180
+ {
181
+ value: "research",
182
+ label: "Re-research",
183
+ description: "Search more sources for this section before drafting again.",
184
+ textInput: { placeholder: "Search more on ..." },
185
+ },
186
+ ],
187
+ });
188
+ export class DraftFlow {
189
+ context;
190
+ research;
191
+ afterChange;
192
+ constructor(context, research, afterChange) {
193
+ this.context = context;
194
+ this.research = research;
195
+ this.afterChange = afterChange;
196
+ }
197
+ get now() {
198
+ return this.context.now;
199
+ }
200
+ // -------------------------------------------------------------------------------------------
201
+ // helpers
202
+ // -------------------------------------------------------------------------------------------
203
+ outlineOf(loaded) {
204
+ return loaded.project.outlineText === undefined
205
+ ? undefined
206
+ : parseOutline(loaded.project.outlineText).outline;
207
+ }
208
+ plansOf(loaded) {
209
+ const outline = this.outlineOf(loaded);
210
+ return outline ? planSections(outline, loaded.brief) : new Map();
211
+ }
212
+ /** What a section waits for before it can be drafted: its dependencies, or everything for an abstract. */
213
+ draftDependencies(state, id) {
214
+ const section = state.sections[id];
215
+ if (!section)
216
+ return [];
217
+ if (summarySectionRoles.includes(section.role ?? ""))
218
+ return Object.entries(state.sections)
219
+ .filter(([other, entry]) => other !== id && !frontSectionRoles.includes(entry.role ?? ""))
220
+ .map(([other]) => other);
221
+ return section.dependsOn ?? [];
222
+ }
223
+ /** Can this section be drafted now, as far as its own status goes? */
224
+ ready(section) {
225
+ return (draftable.includes(section.status) ||
226
+ (section.status === "planned" && frontSectionRoles.includes(section.role ?? "")));
227
+ }
228
+ /** First section ready to draft whose dependencies are approved, in document order. */
229
+ pickDraftable(state) {
230
+ return Object.keys(state.sections)
231
+ .sort()
232
+ .find((id) => {
233
+ const section = state.sections[id];
234
+ return (this.ready(section) &&
235
+ this.draftDependencies(state, id).every((dependency) => state.sections[dependency]?.status === "approved"));
236
+ });
237
+ }
238
+ /** Errors of the candidate project that belong to one section's files. */
239
+ sectionErrors(project, id, files) {
240
+ const report = runChecks(project, { gates: [...sectionGates], now: this.now });
241
+ const mine = new Set(files);
242
+ return report.findings.filter((finding) => finding.severity === "error" &&
243
+ (finding.section === id || (finding.file !== undefined && mine.has(finding.file))));
244
+ }
245
+ /** A copy of the loaded project as it would be with this chapter, its claims and its charts. */
246
+ candidate(loaded, plan, text, assets, claims) {
247
+ const others = parseClaimsText(loaded.project.claimsText).records.filter((record) => record.section !== plan.id);
248
+ const chartPaths = new Set(assets.map((asset) => asset.path));
249
+ return {
250
+ ...loaded.project,
251
+ chapters: [
252
+ ...loaded.project.chapters.filter((chapter) => chapter.path !== plan.path),
253
+ { path: plan.path, content: text },
254
+ ].sort((a, b) => (a.path < b.path ? -1 : 1)),
255
+ chartSpecs: [
256
+ ...loaded.project.chartSpecs.filter((spec) => !chartPaths.has(spec.path)),
257
+ ...assets.filter((asset) => asset.path.endsWith(".vl.json")),
258
+ ],
259
+ claimsText: serializeClaims([...others, ...claims]),
260
+ };
261
+ }
262
+ /** Everything that makes a candidate chapter unacceptable, as messages for the writer or editor. */
263
+ assess(loaded, plan, body, keywords, assets, claims, packetKeys) {
264
+ const errors = [];
265
+ // Headings: the title is code's; the writer's own start one level below it.
266
+ const parsed = parseChapter("body.md", body);
267
+ walkBlocks(parsed.section.blocks, (block) => {
268
+ if (block.kind !== "heading")
269
+ return;
270
+ if (!plan.heading)
271
+ errors.push(`${plan.role} sections have no headings; remove the heading on line ${block.line ?? "?"}`);
272
+ else if (block.level <= plan.level)
273
+ errors.push(`Heading level ${block.level} is reserved (the title is level ${plan.level}); use level ${plan.level + 1} to 4 (line ${block.line ?? "?"})`);
274
+ });
275
+ if ((plan.role === "abstract" || plan.role === "abstract-secondary") && keywords.length === 0)
276
+ errors.push("An abstract needs keywords (3 to 5)");
277
+ const text = composeFile(plan, body, keywords);
278
+ const project = this.candidate(loaded, plan, text, assets, claims);
279
+ const files = [plan.path, ...assets.map((asset) => asset.path)];
280
+ errors.push(...this.sectionErrors(project, plan.id, files).map((finding) => describeFinding(finding)));
281
+ // Only packet keys may be cited, even when the library holds others.
282
+ for (const cite of citationsOf(project))
283
+ if (cite.section === plan.id && !packetKeys.has(cite.key))
284
+ errors.push(`Citation @${cite.key} is not in the evidence packet for ${plan.id}`);
285
+ // A label may not collide with one defined in another chapter.
286
+ const doc = documentOf(project)?.document;
287
+ if (doc) {
288
+ const mine = new Set();
289
+ const other = new Set();
290
+ for (const { file, blocks } of allBlockLists(doc))
291
+ walkBlocks(blocks, (block) => {
292
+ if (!("label" in block) || !block.label)
293
+ return;
294
+ (file === plan.path ? mine : other).add(block.label);
295
+ });
296
+ for (const label of mine)
297
+ if (other.has(label) && !label.startsWith("sec-"))
298
+ errors.push(`Label ${label} is already used in another chapter`);
299
+ }
300
+ return [...new Set(errors)].slice(0, 20);
301
+ }
302
+ // -------------------------------------------------------------------------------------------
303
+ // /thesis:draft
304
+ // -------------------------------------------------------------------------------------------
305
+ async run(workspace, sessionId, target, options = {}) {
306
+ const loaded = await loadAll(this.context, workspace, sessionId);
307
+ const { state } = loaded;
308
+ if (state.phase !== "sections" && state.phase !== "review") {
309
+ const pending = ["A", "B", "OUTLINE"].filter((gate) => state.humanGates[gate].status !== "approved");
310
+ return `Blocked: drafting starts after the OUTLINE gate and the section's research. Pending: ${pending.join(", ") || "none"}; phase is ${state.phase}. Run /thesis:next.`;
311
+ }
312
+ const id = target === "next" ? this.pickDraftable(state) : target;
313
+ if (!id)
314
+ return "There is no section ready to draft (each needs approved research, and its dependencies approved).";
315
+ const section = state.sections[id];
316
+ if (!section)
317
+ return `Blocked: ${id} is not a section of the approved outline.`;
318
+ const plan = this.plansOf(loaded).get(id);
319
+ if (!plan)
320
+ return `Blocked: ${id} is missing from outline/outline.json.`;
321
+ if (plan.generated) {
322
+ if (section.status !== "approved") {
323
+ section.status = "approved";
324
+ section.updatedAt = this.now().toISOString();
325
+ syncPhase(state);
326
+ await saveState(loaded);
327
+ }
328
+ return `${id} is the bibliography: it is generated from the evidence library and needs no draft.`;
329
+ }
330
+ const feedback = options.feedback;
331
+ if (this.ready(section)) {
332
+ const unmet = this.draftDependencies(state, id).filter((dependency) => state.sections[dependency]?.status !== "approved");
333
+ if (unmet.length > 0 && feedback === undefined)
334
+ return `Blocked: ${id} ${summarySectionRoles.includes(section.role ?? "") ? "summarizes the whole thesis, so it is written last; first approve" : "depends on"} ${unmet.slice(0, 8).join(", ")}${unmet.length > 8 ? ", ..." : ""}.`;
335
+ }
336
+ else if (revisable.includes(section.status)) {
337
+ if (feedback === undefined)
338
+ return section.status === "draft_review"
339
+ ? this.present(loaded, id)
340
+ : `${id} is approved. Use /thesis:revise ${id} -- <feedback> to change it.`;
341
+ }
342
+ else {
343
+ return `Blocked: ${id} is ${section.status}; it needs approved research first (/thesis:research ${id}).`;
344
+ }
345
+ const previousStatus = section.status;
346
+ const previousClaims = await readClaims(loaded.base).catch(() => []);
347
+ const chapterFile = join(loaded.base, plan.path);
348
+ const previousText = await readFile(chapterFile, "utf8").catch(() => undefined);
349
+ section.status = "drafting";
350
+ section.updatedAt = this.now().toISOString();
351
+ syncPhase(state);
352
+ await saveState(loaded);
353
+ const messages = [];
354
+ try {
355
+ const packet = await this.packet(loaded, plan);
356
+ let prepared;
357
+ try {
358
+ prepared = await this.writer(loaded, plan, packet, previousText, previousClaims, feedback);
359
+ }
360
+ catch (error) {
361
+ if (error instanceof ChildRejectedError) {
362
+ section.status = previousStatus;
363
+ await saveState(loaded);
364
+ return `${error.role} did not return a valid draft after one retry, so ${id} keeps its previous status and no file changed.\n- ${error.errors.join("\n- ")}`;
365
+ }
366
+ throw error;
367
+ }
368
+ // The editor changes wording only; the diff guard rejects any change to what is protected.
369
+ let note = "";
370
+ const edit = await this.editor(loaded, plan, prepared, packet.keys);
371
+ if (edit.pass) {
372
+ prepared.body = edit.pass.markdown;
373
+ note = `Editor pass applied${edit.pass.changes.length ? ` (${edit.pass.changes.slice(0, 3).join("; ")})` : ""}.`;
374
+ if (edit.pass.queries.length)
375
+ messages.push(`Editor questions: ${edit.pass.queries.join(" | ")}`);
376
+ }
377
+ else
378
+ note = edit.note;
379
+ await this.write(loaded, plan, prepared);
380
+ messages.unshift(`Drafted ${id} ${plan.node.title}: ${plan.path}.`, note);
381
+ if (prepared.gaps.length)
382
+ messages.push(`Gaps the writer left out: ${prepared.gaps.join("; ")}.`);
383
+ section.status = "draft_review";
384
+ section.updatedAt = this.now().toISOString();
385
+ section.draftAt = section.updatedAt;
386
+ section.chapter = plan.path;
387
+ syncPhase(state);
388
+ await saveState(loaded);
389
+ await this.afterChange(loaded);
390
+ }
391
+ catch (error) {
392
+ section.status = previousStatus;
393
+ if (previousText === undefined)
394
+ await rm(chapterFile, { force: true }).catch(() => undefined);
395
+ await saveState(loaded).catch(() => undefined);
396
+ throw error;
397
+ }
398
+ return [...messages.filter(Boolean), await this.present(loaded, id)].join("\n");
399
+ }
400
+ // -------------------------------------------------------------------------------------------
401
+ // the evidence packet and the writer
402
+ // -------------------------------------------------------------------------------------------
403
+ async packet(loaded, plan) {
404
+ const { state } = loaded;
405
+ const section = state.sections[plan.id];
406
+ const library = await readLibrary(loaded.base);
407
+ // The section's own research, plus the research of the sections it depends on.
408
+ const scope = new Set([plan.id]);
409
+ const queue = [...(section.dependsOn ?? [])];
410
+ while (queue.length > 0) {
411
+ const next = queue.pop();
412
+ if (scope.has(next))
413
+ continue;
414
+ scope.add(next);
415
+ queue.push(...(state.sections[next]?.dependsOn ?? []));
416
+ }
417
+ const records = library.filter((record) => record.sections.some((entry) => scope.has(entry)) &&
418
+ citableForSection(record, section.contextualApprovals));
419
+ return {
420
+ records,
421
+ keys: new Set(records.map((record) => record.citeKey)),
422
+ ids: records.map((record) => record.id),
423
+ text: JSON.stringify(records.map((record) => ({
424
+ id: record.id,
425
+ citeKey: record.citeKey,
426
+ reference: reference(record),
427
+ type: record.type,
428
+ status: record.status,
429
+ permittedUse: record.permittedUse,
430
+ location: record.appraisal.location,
431
+ limitations: record.appraisal.limitations,
432
+ supports: record.appraisal.supports,
433
+ relevance: record.appraisal.relevance,
434
+ }))),
435
+ };
436
+ }
437
+ async writerPrompt(loaded, plan, packet, previous, feedback) {
438
+ const { brief, project, state } = loaded;
439
+ const profile = project.profile;
440
+ const node = plan.node;
441
+ const protocol = protocolObjectives(project.protocolText);
442
+ const dossier = await readFile(join(loaded.base, dossierPaths(plan.id).json), "utf8").catch(() => "{}");
443
+ const outline = this.outlineOf(loaded);
444
+ const all = flattenOutline(outline?.sections ?? []);
445
+ const claims = parseClaimsText(project.claimsText).records;
446
+ const section = state.sections[plan.id];
447
+ const dependencyText = [];
448
+ const dependencies = this.draftDependencies(state, plan.id);
449
+ const summary = summarySectionRoles.includes(section.role ?? "");
450
+ for (const dependency of dependencies.slice(0, summary ? 14 : 4)) {
451
+ const other = this.plansOf(loaded).get(dependency);
452
+ if (!other || other.generated)
453
+ continue;
454
+ const text = await readFile(join(loaded.base, other.path), "utf8").catch(() => undefined);
455
+ if (text)
456
+ dependencyText.push(`--- ${dependency} ${other.node.title}\n${text.slice(0, summary ? 2500 : 5000)}`);
457
+ }
458
+ const dependencyClaims = claims
459
+ .filter((claim) => dependencies.includes(claim.section))
460
+ .map((claim) => ({
461
+ id: claim.id,
462
+ section: claim.section,
463
+ kind: claim.kind,
464
+ text: claim.text,
465
+ }));
466
+ const labels = new Set();
467
+ const doc = documentOf(project)?.document;
468
+ if (doc)
469
+ for (const { file, blocks } of allBlockLists(doc))
470
+ walkBlocks(blocks, (block) => {
471
+ if ("label" in block && block.label && file !== plan.path)
472
+ labels.add(block.label);
473
+ });
474
+ const writing = (profile?.rules ?? [])
475
+ .filter((rule) => rule.level === "STYLE_GUIDE" || rule.kind === "quotation_rule")
476
+ .slice(0, 12)
477
+ .map((rule) => `${rule.ruleId}: ${JSON.stringify(rule.values).slice(0, 300)}`);
478
+ const roleNote = {
479
+ abstract: `This is the abstract in ${plan.lang ?? brief.language}: one paragraph of at most 250 words, no citations, no headings; give 3 to 5 keywords.`,
480
+ "abstract-secondary": `This is the abstract in ${plan.lang ?? "en"} (not the thesis language): write it in that language; one paragraph of at most 250 words, no citations; give 3 to 5 keywords.`,
481
+ "ai-declaration": `This is the AI-use declaration. State honestly which AI tools assisted (${brief.aiUse.assisted ? "the author used AI assistance" : "no AI assistance was declared"}), for what (writing assistance, analysis, figures) and that the author reviewed and takes responsibility for the content. Do not invent tool names beyond "Alisio Thesis Studio" and the assistant model the author used.`,
482
+ dedication: "This is the dedication: a short personal text, no citations.",
483
+ acknowledgments: "These are the acknowledgments: short, personal, no citations.",
484
+ };
485
+ return [
486
+ `Task: draft the section ${plan.id} "${node.title}". Return one JSON object (a SectionDraft) and nothing else.`,
487
+ `Write in the thesis language: ${brief.language}. Target length about ${node.targetWords} words (within 25 percent).`,
488
+ `Schema:\n${draftSchema}`,
489
+ `Dialect rules:\n${dialectRules.map((rule) => `- ${rule}`).join("\n")}`,
490
+ plan.heading
491
+ ? `Headings: do not write the section title (code adds a level-${plan.level} heading). Subsections start at level ${plan.level + 1} and go to 4 at most.`
492
+ : "This section has no headings.",
493
+ roleNote[plan.role] ?? "",
494
+ "Rules: use only the evidence packet for factual claims; never invent numbers, quotes or sources; report data exactly as the user's files give it; match verbs to the evidence status (strong for VERIFIED_PRIMARY and VERIFIED_PEER_REVIEWED, hedged for the rest); a claim without support becomes a gap, not a citation. A conclusion claim must list the result claims it rests on in results.",
495
+ data("SECTION", JSON.stringify({
496
+ id: plan.id,
497
+ title: node.title,
498
+ purpose: node.purpose,
499
+ objectives: node.objectives,
500
+ researchTopics: node.researchTopics,
501
+ questionsToAnswer: node.questionsToAnswer,
502
+ evidenceNeeds: node.evidenceNeeds,
503
+ targetWords: node.targetWords,
504
+ })),
505
+ data("OBJECTIVES", JSON.stringify([
506
+ ...(protocol.allObjectiveIds ?? []).map((objective) => objective),
507
+ ])),
508
+ data("EVIDENCE PACKET", packet.text),
509
+ data("DOSSIER (synthesis, approved claims, gaps)", dossier),
510
+ data("OTHER SECTIONS", JSON.stringify(all
511
+ .filter((entry) => entry.id !== plan.id)
512
+ .map((entry) => ({ id: entry.id, title: entry.title, purpose: entry.purpose })))),
513
+ ...(dependencyClaims.length
514
+ ? [data("CLAIMS OF THE SECTIONS THIS ONE DEPENDS ON", JSON.stringify(dependencyClaims))]
515
+ : []),
516
+ ...(dependencyText.length
517
+ ? [data("APPROVED TEXT OF SECTIONS THIS ONE DEPENDS ON", dependencyText.join("\n\n"))]
518
+ : []),
519
+ data("LABELS ALREADY IN USE", [...labels].sort().join(", ") || "none"),
520
+ ...(writing.length ? [data("WRITING RULES FROM THE POLICY", writing.join("\n"))] : []),
521
+ ...(previous && feedback !== undefined
522
+ ? [
523
+ data("PREVIOUS DRAFT", previous.text.slice(0, 30_000)),
524
+ data("PREVIOUS CLAIMS", JSON.stringify(previous.claims.map((claim) => ({
525
+ anchor: claim.anchor,
526
+ kind: claim.kind,
527
+ text: claim.text,
528
+ })))),
529
+ data("FEEDBACK TO APPLY", feedback),
530
+ ]
531
+ : []),
532
+ ]
533
+ .filter(Boolean)
534
+ .join("\n\n");
535
+ }
536
+ async writer(loaded, plan, packet, previousText, previousClaims, feedback) {
537
+ const previous = previousText === undefined
538
+ ? undefined
539
+ : {
540
+ text: previousText,
541
+ claims: previousClaims.filter((claim) => claim.section === plan.id),
542
+ };
543
+ const prompt = await this.writerPrompt(loaded, plan, packet, previous, feedback);
544
+ const objectiveIds = protocolObjectives(loaded.project.protocolText).allObjectiveIds ?? [];
545
+ const baseCounter = loaded.state.counters.claim;
546
+ const existing = parseClaimsText(loaded.project.claimsText).records;
547
+ return this.context.delegator.run("thesis-writer", loaded.sessionId, loaded.workspace, `Draft ${plan.id}`, prompt, (value) => {
548
+ const checked = validateSectionDraft(value, {
549
+ evidenceIds: packet.ids,
550
+ objectiveIds,
551
+ });
552
+ if (checked.value === undefined)
553
+ return checked;
554
+ const draft = checked.value;
555
+ const placed = placeFigures(draft.markdown, draft.figures);
556
+ const errors = [...placed.errors];
557
+ const assets = draft.figures.flatMap((figure) => {
558
+ const asset = figureAsset(figure);
559
+ return asset ? [asset] : [];
560
+ });
561
+ // Claim ids are allocated here so the candidate is checked exactly as it would be stored.
562
+ const anchorIds = new Map(draft.claims.map((claim, index) => [claim.anchor, claimId(baseCounter + index + 1)]));
563
+ const claims = draft.claims.map((claim) => {
564
+ const results = claim.results.flatMap((entry) => {
565
+ const resolved = anchorIds.get(entry) ?? (/^CLM-\d{4}$/.test(entry) ? entry : undefined);
566
+ if (resolved === undefined ||
567
+ (!anchorIds.has(entry) && !existing.some((c) => c.id === entry))) {
568
+ errors.push(`Claim ${claim.anchor} rests on ${entry}, which is neither an anchor of this draft nor an earlier claim`);
569
+ return [];
570
+ }
571
+ return [resolved];
572
+ });
573
+ return {
574
+ id: anchorIds.get(claim.anchor),
575
+ section: plan.id,
576
+ anchor: claim.anchor,
577
+ text: claim.text,
578
+ kind: claim.kind,
579
+ evidence: claim.evidence,
580
+ ...(results.length ? { results } : {}),
581
+ objectives: claim.objectives,
582
+ };
583
+ });
584
+ errors.push(...this.assess(loaded, plan, placed.body, draft.keywords, assets, claims, packet.keys));
585
+ if (errors.length > 0)
586
+ return { errors: [...new Set(errors)].slice(0, 20) };
587
+ return {
588
+ value: {
589
+ body: placed.body,
590
+ keywords: draft.keywords,
591
+ assets,
592
+ claims,
593
+ gaps: draft.gaps,
594
+ },
595
+ errors: [],
596
+ };
597
+ });
598
+ }
599
+ // -------------------------------------------------------------------------------------------
600
+ // the editor pass with the diff guard
601
+ // -------------------------------------------------------------------------------------------
602
+ async editor(loaded, plan, prepared, packetKeys, feedback) {
603
+ const { brief, project } = loaded;
604
+ const prompt = [
605
+ `Task: edit the wording of the section ${plan.id} "${plan.node.title}" for clarity, concision, cohesion, register and correctness in ${brief.language}. Return one JSON object (an EditPass) and nothing else.`,
606
+ `Schema:\n${editSchema}`,
607
+ "Protected: you may not add, remove or change any citation, claim anchor, number, unit, label, cross-reference, math, link or image target, or footnote mark, and you may not move an anchor to another paragraph. Code compares them before and after and rejects the whole pass if anything differs. Return the complete body, not a diff.",
608
+ ...(project.profile?.rules ?? [])
609
+ .filter((rule) => rule.level === "STYLE_GUIDE")
610
+ .slice(0, 8)
611
+ .map((rule) => `Writing rule ${rule.ruleId}: ${JSON.stringify(rule.values).slice(0, 240)}`),
612
+ ...(feedback ? [data("FEEDBACK TO APPLY (wording only)", feedback)] : []),
613
+ data("BODY", prepared.body),
614
+ ].join("\n\n");
615
+ try {
616
+ const pass = await this.context.delegator.run("thesis-editor", loaded.sessionId, loaded.workspace, `Edit ${plan.id}`, prompt, (value) => {
617
+ const checked = validateEditPass(value);
618
+ if (checked.value === undefined)
619
+ return checked;
620
+ const violations = diffProtected(prepared.body, checked.value.markdown);
621
+ if (violations.length > 0)
622
+ return { errors: violations };
623
+ const errors = this.assess(loaded, plan, checked.value.markdown, prepared.keywords, prepared.assets, prepared.claims, packetKeys);
624
+ return errors.length > 0 ? { errors } : checked;
625
+ });
626
+ return { pass, note: "" };
627
+ }
628
+ catch (error) {
629
+ if (error instanceof ChildRejectedError)
630
+ return {
631
+ note: `The editor pass was rejected (${error.errors[0] ?? "invalid"}), so the writer's text was kept unedited.`,
632
+ };
633
+ throw error;
634
+ }
635
+ }
636
+ /** Edit the wording of a drafted section (a `wording` review finding routes here). */
637
+ async editWording(workspace, sessionId, id, feedback) {
638
+ const loaded = await loadAll(this.context, workspace, sessionId);
639
+ const plan = this.plansOf(loaded).get(id);
640
+ const section = loaded.state.sections[id];
641
+ if (!plan || !section)
642
+ return `Blocked: ${id} is not a section of the approved outline.`;
643
+ const text = await readFile(join(loaded.base, plan.path), "utf8").catch(() => undefined);
644
+ if (text === undefined)
645
+ return `Blocked: ${id} has no chapter file yet (/thesis:draft ${id}).`;
646
+ const split = splitChapter(plan, text);
647
+ const claims = parseClaimsText(loaded.project.claimsText).records.filter((record) => record.section === id);
648
+ const packet = await this.packet(loaded, plan);
649
+ const prepared = {
650
+ body: split.body,
651
+ keywords: split.keywords,
652
+ assets: [],
653
+ claims,
654
+ gaps: [],
655
+ };
656
+ const edit = await this.editor(loaded, plan, prepared, packet.keys, feedback);
657
+ if (!edit.pass)
658
+ return edit.note;
659
+ const previousStatus = section.status;
660
+ await atomicWrite(join(loaded.base, plan.path), composeFile(plan, edit.pass.markdown, split.keywords));
661
+ section.status = "draft_review";
662
+ section.updatedAt = this.now().toISOString();
663
+ section.draftAt = section.updatedAt;
664
+ syncPhase(loaded.state);
665
+ await saveState(loaded);
666
+ await this.afterChange(loaded);
667
+ return [
668
+ `Edited the wording of ${id} (was ${previousStatus}); it is back in draft review.`,
669
+ await this.present(loaded, id),
670
+ ].join("\n");
671
+ }
672
+ // -------------------------------------------------------------------------------------------
673
+ // writing files
674
+ // -------------------------------------------------------------------------------------------
675
+ async write(loaded, plan, prepared) {
676
+ const { base, state } = loaded;
677
+ for (const asset of prepared.assets) {
678
+ await mkdir(dirname(join(base, asset.path)), { recursive: true, mode: 0o700 });
679
+ await atomicWrite(join(base, asset.path), asset.content);
680
+ }
681
+ await mkdir(dirname(join(base, plan.path)), { recursive: true, mode: 0o700 });
682
+ await atomicWrite(join(base, plan.path), composeFile(plan, prepared.body, prepared.keywords));
683
+ const others = parseClaimsText(loaded.project.claimsText).records.filter((record) => record.section !== plan.id);
684
+ await writeClaims(base, [...others, ...prepared.claims]);
685
+ const highest = prepared.claims.reduce((max, claim) => Math.max(max, Number(claim.id.slice(4))), state.counters.claim);
686
+ state.counters.claim = Math.max(state.counters.claim, highest);
687
+ }
688
+ // -------------------------------------------------------------------------------------------
689
+ // presenting and approving
690
+ // -------------------------------------------------------------------------------------------
691
+ /** Section findings, errors first, for the approval prompt. */
692
+ summary(loaded, plan) {
693
+ const report = runChecks(loaded.project, { gates: [...sectionGates], now: this.now });
694
+ const mine = report.findings.filter((finding) => finding.section === plan.id || finding.file === plan.path);
695
+ const errors = mine.filter((finding) => finding.severity === "error");
696
+ const warnings = mine.filter((finding) => finding.severity === "warning");
697
+ return {
698
+ errors: errors.length,
699
+ lines: [
700
+ `Checks for ${plan.id}: ${errors.length} error(s), ${warnings.length} warning(s).`,
701
+ ...[...errors, ...warnings].slice(0, 6).map((finding) => `- ${describeFinding(finding)}`),
702
+ ],
703
+ };
704
+ }
705
+ async present(loaded, id) {
706
+ await this.afterChange(loaded);
707
+ const plan = this.plansOf(loaded).get(id);
708
+ if (!plan)
709
+ return `${id} is in draft review.`;
710
+ const { lines, errors } = this.summary(loaded, plan);
711
+ const header = `${id} is in draft review. Read ${plan.path}.`;
712
+ const answer = await askOne(this.context.api, "Section approval", approveQuestion(id, errors === 0));
713
+ if (answer === undefined) {
714
+ return [
715
+ header,
716
+ ...lines,
717
+ `- Approve it: /thesis:approve ${id}`,
718
+ `- Revise it: /thesis:revise ${id} -- <feedback>`,
719
+ `- Search more sources: /thesis:research ${id} -- <what to search>`,
720
+ ].join("\n");
721
+ }
722
+ if (answer.value === "approve") {
723
+ return [header, ...lines, await this.approve(loaded.workspace, loaded.sessionId, id)].join("\n");
724
+ }
725
+ if (answer.value === "revise" && answer.text) {
726
+ return [
727
+ header,
728
+ ...lines,
729
+ await this.run(loaded.workspace, loaded.sessionId, id, { feedback: answer.text }),
730
+ ].join("\n");
731
+ }
732
+ if (answer.value === "research") {
733
+ const fresh = await loadAll(this.context, loaded.workspace, loaded.sessionId);
734
+ const section = fresh.state.sections[id];
735
+ section.status = "research_approved";
736
+ section.updatedAt = this.now().toISOString();
737
+ syncPhase(fresh.state);
738
+ await saveState(fresh);
739
+ return [
740
+ header,
741
+ await this.research.run(loaded.workspace, loaded.sessionId, id, {
742
+ ...(answer.text ? { focus: answer.text } : {}),
743
+ }),
744
+ ].join("\n");
745
+ }
746
+ return [header, ...lines, `${id} stays in draft review.`].join("\n");
747
+ }
748
+ /** Approve a drafted section, then build the approved sections (fail-open). */
749
+ async approve(workspace, sessionId, id) {
750
+ const loaded = await loadAll(this.context, workspace, sessionId);
751
+ const section = loaded.state.sections[id];
752
+ if (!section)
753
+ return `Blocked: ${id} is not a section of the approved outline.`;
754
+ const plan = this.plansOf(loaded).get(id);
755
+ if (!plan)
756
+ return `Blocked: ${id} is missing from outline/outline.json.`;
757
+ if (section.status === "approved")
758
+ return `${id} is already approved.`;
759
+ if (section.status !== "draft_review")
760
+ return `Blocked: ${id} is ${section.status}; only a section in draft review can be approved.`;
761
+ const text = await readFile(join(loaded.base, plan.path), "utf8").catch(() => undefined);
762
+ if (text === undefined)
763
+ return `Blocked: ${id} has no chapter file (${plan.path}); run /thesis:draft ${id}.`;
764
+ const errors = errorsOf(runChecks(loaded.project, { gates: [...sectionGates], now: this.now }).findings.filter((finding) => finding.section === id || finding.file === plan.path));
765
+ if (errors.length > 0)
766
+ return `Blocked: ${id} has check errors.\n${errors.map((finding) => `- ${describeFinding(finding)}`).join("\n")}`;
767
+ section.status = "approved";
768
+ section.updatedAt = this.now().toISOString();
769
+ syncPhase(loaded.state);
770
+ await saveState(loaded);
771
+ await this.afterChange(loaded);
772
+ const lines = [`Approved ${id} ${plan.node.title}.`];
773
+ lines.push(await this.partialBuild(workspace));
774
+ const total = Object.keys(loaded.state.sections).length;
775
+ const done = Object.values(loaded.state.sections).filter((s) => s.status === "approved").length;
776
+ lines.push(loaded.state.phase === "review"
777
+ ? `All ${total} sections are approved. Next: /thesis:review all`
778
+ : `${done} of ${total} sections approved.`);
779
+ return lines.join("\n");
780
+ }
781
+ /** The partial build after an approval. A missing engine is reported, never fatal. */
782
+ async partialBuild(workspace) {
783
+ const build = this.context.build;
784
+ if (!build)
785
+ return "Build skipped.";
786
+ try {
787
+ const outcome = await build(workspace, { scope: "approved" });
788
+ if (outcome.ok && outcome.path)
789
+ return `Partial build (approved sections): ${outcome.path}`;
790
+ const first = outcome.findings.find((finding) => finding.severity === "error");
791
+ const noEngine = outcome.engine === "none" && /Typst|engine/i.test(first?.message ?? "");
792
+ return noEngine
793
+ ? `No PDF was built: ${first?.message}. Run /thesis:setup to install the Typst engine, then /thesis:build approved.`
794
+ : `The partial build failed${first ? `: ${first.code} ${first.message}` : ""}. Run /thesis:build approved for details.`;
795
+ }
796
+ catch (error) {
797
+ return `The partial build was skipped: ${error.message}. Run /thesis:build approved.`;
798
+ }
799
+ }
800
+ }
801
+ //# sourceMappingURL=draft.js.map