@softure-ai/blog 0.0.0-stage → 0.1.6

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 (419) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +21 -0
  3. package/README.md +604 -2
  4. package/dist/cli/bin.d.ts +3 -0
  5. package/dist/cli/bin.d.ts.map +1 -0
  6. package/dist/cli/bin.js +5 -0
  7. package/dist/cli/bin.js.map +1 -0
  8. package/dist/cli/command.d.ts +9 -0
  9. package/dist/cli/command.d.ts.map +1 -0
  10. package/dist/cli/command.js +40 -0
  11. package/dist/cli/command.js.map +1 -0
  12. package/dist/cli/index.d.ts +4 -0
  13. package/dist/cli/index.d.ts.map +1 -0
  14. package/dist/cli/index.js +5 -0
  15. package/dist/cli/index.js.map +1 -0
  16. package/dist/cli/run.d.ts +62 -0
  17. package/dist/cli/run.d.ts.map +1 -0
  18. package/dist/cli/run.js +555 -0
  19. package/dist/cli/run.js.map +1 -0
  20. package/dist/cli/skill.d.ts +47 -0
  21. package/dist/cli/skill.d.ts.map +1 -0
  22. package/dist/cli/skill.js +221 -0
  23. package/dist/cli/skill.js.map +1 -0
  24. package/dist/content/article-file.d.ts +27 -0
  25. package/dist/content/article-file.d.ts.map +1 -0
  26. package/dist/content/article-file.js +179 -0
  27. package/dist/content/article-file.js.map +1 -0
  28. package/dist/contract.d.ts +77 -0
  29. package/dist/contract.d.ts.map +1 -0
  30. package/dist/contract.js +5 -0
  31. package/dist/contract.js.map +1 -0
  32. package/dist/db/articles.d.ts +30 -0
  33. package/dist/db/articles.d.ts.map +1 -0
  34. package/dist/db/articles.js +138 -0
  35. package/dist/db/articles.js.map +1 -0
  36. package/dist/db/publish-run.d.ts +50 -0
  37. package/dist/db/publish-run.d.ts.map +1 -0
  38. package/dist/db/publish-run.js +168 -0
  39. package/dist/db/publish-run.js.map +1 -0
  40. package/dist/db/schema.d.ts +403 -0
  41. package/dist/db/schema.d.ts.map +1 -0
  42. package/dist/db/schema.js +33 -0
  43. package/dist/db/schema.js.map +1 -0
  44. package/dist/discovery/dates.d.ts +9 -0
  45. package/dist/discovery/dates.d.ts.map +1 -0
  46. package/dist/discovery/dates.js +19 -0
  47. package/dist/discovery/dates.js.map +1 -0
  48. package/dist/discovery/index.d.ts +8 -0
  49. package/dist/discovery/index.d.ts.map +1 -0
  50. package/dist/discovery/index.js +11 -0
  51. package/dist/discovery/index.js.map +1 -0
  52. package/dist/discovery/indexnow.d.ts +11 -0
  53. package/dist/discovery/indexnow.d.ts.map +1 -0
  54. package/dist/discovery/indexnow.js +23 -0
  55. package/dist/discovery/indexnow.js.map +1 -0
  56. package/dist/discovery/refresh.d.ts +49 -0
  57. package/dist/discovery/refresh.d.ts.map +1 -0
  58. package/dist/discovery/refresh.js +55 -0
  59. package/dist/discovery/refresh.js.map +1 -0
  60. package/dist/discovery/related.d.ts +17 -0
  61. package/dist/discovery/related.d.ts.map +1 -0
  62. package/dist/discovery/related.js +28 -0
  63. package/dist/discovery/related.js.map +1 -0
  64. package/dist/discovery/rss.d.ts +30 -0
  65. package/dist/discovery/rss.d.ts.map +1 -0
  66. package/dist/discovery/rss.js +48 -0
  67. package/dist/discovery/rss.js.map +1 -0
  68. package/dist/discovery/sitemap.d.ts +25 -0
  69. package/dist/discovery/sitemap.d.ts.map +1 -0
  70. package/dist/discovery/sitemap.js +31 -0
  71. package/dist/discovery/sitemap.js.map +1 -0
  72. package/dist/discovery/submit.d.ts +44 -0
  73. package/dist/discovery/submit.d.ts.map +1 -0
  74. package/dist/discovery/submit.js +46 -0
  75. package/dist/discovery/submit.js.map +1 -0
  76. package/dist/index.d.ts +371 -0
  77. package/dist/index.d.ts.map +1 -0
  78. package/dist/index.js +69 -0
  79. package/dist/index.js.map +1 -0
  80. package/dist/messages/en.d.ts +83 -0
  81. package/dist/messages/en.d.ts.map +1 -0
  82. package/dist/messages/en.js +83 -0
  83. package/dist/messages/en.js.map +1 -0
  84. package/dist/messages/index.d.ts +158 -0
  85. package/dist/messages/index.d.ts.map +1 -0
  86. package/dist/messages/index.js +5 -0
  87. package/dist/messages/index.js.map +1 -0
  88. package/dist/messages/pl.d.ts +78 -0
  89. package/dist/messages/pl.d.ts.map +1 -0
  90. package/dist/messages/pl.js +78 -0
  91. package/dist/messages/pl.js.map +1 -0
  92. package/dist/next/context.d.ts +6 -0
  93. package/dist/next/context.d.ts.map +1 -0
  94. package/dist/next/context.js +27 -0
  95. package/dist/next/context.js.map +1 -0
  96. package/dist/next/data.d.ts +11 -0
  97. package/dist/next/data.d.ts.map +1 -0
  98. package/dist/next/data.js +40 -0
  99. package/dist/next/data.js.map +1 -0
  100. package/dist/next/discovery.d.ts +6 -0
  101. package/dist/next/discovery.d.ts.map +1 -0
  102. package/dist/next/discovery.js +49 -0
  103. package/dist/next/discovery.js.map +1 -0
  104. package/dist/next/index.d.ts +9 -0
  105. package/dist/next/index.d.ts.map +1 -0
  106. package/dist/next/index.js +10 -0
  107. package/dist/next/index.js.map +1 -0
  108. package/dist/next/og-fonts.d.ts +6 -0
  109. package/dist/next/og-fonts.d.ts.map +1 -0
  110. package/dist/next/og-fonts.js +8 -0
  111. package/dist/next/og-fonts.js.map +1 -0
  112. package/dist/next/og-image.d.ts +36 -0
  113. package/dist/next/og-image.d.ts.map +1 -0
  114. package/dist/next/og-image.js +77 -0
  115. package/dist/next/og-image.js.map +1 -0
  116. package/dist/next/pages.d.ts +45 -0
  117. package/dist/next/pages.d.ts.map +1 -0
  118. package/dist/next/pages.js +189 -0
  119. package/dist/next/pages.js.map +1 -0
  120. package/dist/next/refresh.d.ts +9 -0
  121. package/dist/next/refresh.d.ts.map +1 -0
  122. package/dist/next/refresh.js +81 -0
  123. package/dist/next/refresh.js.map +1 -0
  124. package/dist/options.d.ts +320 -0
  125. package/dist/options.d.ts.map +1 -0
  126. package/dist/options.js +184 -0
  127. package/dist/options.js.map +1 -0
  128. package/dist/pages/body.d.ts +18 -0
  129. package/dist/pages/body.d.ts.map +1 -0
  130. package/dist/pages/body.js +21 -0
  131. package/dist/pages/body.js.map +1 -0
  132. package/dist/pages/dates.d.ts +21 -0
  133. package/dist/pages/dates.d.ts.map +1 -0
  134. package/dist/pages/dates.js +24 -0
  135. package/dist/pages/dates.js.map +1 -0
  136. package/dist/pages/index.d.ts +7 -0
  137. package/dist/pages/index.d.ts.map +1 -0
  138. package/dist/pages/index.js +9 -0
  139. package/dist/pages/index.js.map +1 -0
  140. package/dist/pages/json-ld.d.ts +33 -0
  141. package/dist/pages/json-ld.d.ts.map +1 -0
  142. package/dist/pages/json-ld.js +95 -0
  143. package/dist/pages/json-ld.js.map +1 -0
  144. package/dist/pages/listing.d.ts +46 -0
  145. package/dist/pages/listing.d.ts.map +1 -0
  146. package/dist/pages/listing.js +57 -0
  147. package/dist/pages/listing.js.map +1 -0
  148. package/dist/pages/paths.d.ts +37 -0
  149. package/dist/pages/paths.d.ts.map +1 -0
  150. package/dist/pages/paths.js +61 -0
  151. package/dist/pages/paths.js.map +1 -0
  152. package/dist/pages/redirects.d.ts +57 -0
  153. package/dist/pages/redirects.d.ts.map +1 -0
  154. package/dist/pages/redirects.js +78 -0
  155. package/dist/pages/redirects.js.map +1 -0
  156. package/dist/proxy/index.d.ts +15 -0
  157. package/dist/proxy/index.d.ts.map +1 -0
  158. package/dist/proxy/index.js +60 -0
  159. package/dist/proxy/index.js.map +1 -0
  160. package/dist/quality/blocks.d.ts +21 -0
  161. package/dist/quality/blocks.d.ts.map +1 -0
  162. package/dist/quality/blocks.js +113 -0
  163. package/dist/quality/blocks.js.map +1 -0
  164. package/dist/quality/catalog.d.ts +10 -0
  165. package/dist/quality/catalog.d.ts.map +1 -0
  166. package/dist/quality/catalog.js +71 -0
  167. package/dist/quality/catalog.js.map +1 -0
  168. package/dist/quality/check-article.d.ts +34 -0
  169. package/dist/quality/check-article.d.ts.map +1 -0
  170. package/dist/quality/check-article.js +74 -0
  171. package/dist/quality/check-article.js.map +1 -0
  172. package/dist/quality/check-files.d.ts +23 -0
  173. package/dist/quality/check-files.d.ts.map +1 -0
  174. package/dist/quality/check-files.js +44 -0
  175. package/dist/quality/check-files.js.map +1 -0
  176. package/dist/quality/external-links.d.ts +19 -0
  177. package/dist/quality/external-links.d.ts.map +1 -0
  178. package/dist/quality/external-links.js +30 -0
  179. package/dist/quality/external-links.js.map +1 -0
  180. package/dist/quality/finding.d.ts +17 -0
  181. package/dist/quality/finding.d.ts.map +1 -0
  182. package/dist/quality/finding.js +15 -0
  183. package/dist/quality/finding.js.map +1 -0
  184. package/dist/quality/gate.d.ts +5 -0
  185. package/dist/quality/gate.d.ts.map +1 -0
  186. package/dist/quality/gate.js +10 -0
  187. package/dist/quality/gate.js.map +1 -0
  188. package/dist/quality/index.d.ts +15 -0
  189. package/dist/quality/index.d.ts.map +1 -0
  190. package/dist/quality/index.js +16 -0
  191. package/dist/quality/index.js.map +1 -0
  192. package/dist/quality/link-targets.d.ts +44 -0
  193. package/dist/quality/link-targets.d.ts.map +1 -0
  194. package/dist/quality/link-targets.js +91 -0
  195. package/dist/quality/link-targets.js.map +1 -0
  196. package/dist/quality/options.d.ts +113 -0
  197. package/dist/quality/options.d.ts.map +1 -0
  198. package/dist/quality/options.js +93 -0
  199. package/dist/quality/options.js.map +1 -0
  200. package/dist/quality/plugin.d.ts +34 -0
  201. package/dist/quality/plugin.d.ts.map +1 -0
  202. package/dist/quality/plugin.js +7 -0
  203. package/dist/quality/plugin.js.map +1 -0
  204. package/dist/quality/rules/blocks.d.ts +5 -0
  205. package/dist/quality/rules/blocks.d.ts.map +1 -0
  206. package/dist/quality/rules/blocks.js +35 -0
  207. package/dist/quality/rules/blocks.js.map +1 -0
  208. package/dist/quality/rules/images.d.ts +4 -0
  209. package/dist/quality/rules/images.d.ts.map +1 -0
  210. package/dist/quality/rules/images.js +33 -0
  211. package/dist/quality/rules/images.js.map +1 -0
  212. package/dist/quality/rules/input.d.ts +11 -0
  213. package/dist/quality/rules/input.d.ts.map +1 -0
  214. package/dist/quality/rules/input.js +2 -0
  215. package/dist/quality/rules/input.js.map +1 -0
  216. package/dist/quality/rules/links.d.ts +17 -0
  217. package/dist/quality/rules/links.d.ts.map +1 -0
  218. package/dist/quality/rules/links.js +46 -0
  219. package/dist/quality/rules/links.js.map +1 -0
  220. package/dist/quality/rules/structure.d.ts +13 -0
  221. package/dist/quality/rules/structure.d.ts.map +1 -0
  222. package/dist/quality/rules/structure.js +139 -0
  223. package/dist/quality/rules/structure.js.map +1 -0
  224. package/dist/quality/rules/style.d.ts +11 -0
  225. package/dist/quality/rules/style.d.ts.map +1 -0
  226. package/dist/quality/rules/style.js +129 -0
  227. package/dist/quality/rules/style.js.map +1 -0
  228. package/dist/quality/rules/ymyl.d.ts +5 -0
  229. package/dist/quality/rules/ymyl.d.ts.map +1 -0
  230. package/dist/quality/rules/ymyl.js +51 -0
  231. package/dist/quality/rules/ymyl.js.map +1 -0
  232. package/dist/quality/rulesets/en/ruleset.d.ts +3 -0
  233. package/dist/quality/rulesets/en/ruleset.d.ts.map +1 -0
  234. package/dist/quality/rulesets/en/ruleset.js +106 -0
  235. package/dist/quality/rulesets/en/ruleset.js.map +1 -0
  236. package/dist/quality/rulesets/index.d.ts +7 -0
  237. package/dist/quality/rulesets/index.d.ts.map +1 -0
  238. package/dist/quality/rulesets/index.js +7 -0
  239. package/dist/quality/rulesets/index.js.map +1 -0
  240. package/dist/quality/rulesets/pl/ruleset.d.ts +3 -0
  241. package/dist/quality/rulesets/pl/ruleset.d.ts.map +1 -0
  242. package/dist/quality/rulesets/pl/ruleset.js +114 -0
  243. package/dist/quality/rulesets/pl/ruleset.js.map +1 -0
  244. package/dist/quality/rulesets/types.d.ts +28 -0
  245. package/dist/quality/rulesets/types.d.ts.map +1 -0
  246. package/dist/quality/rulesets/types.js +2 -0
  247. package/dist/quality/rulesets/types.js.map +1 -0
  248. package/dist/quality/settings.d.ts +26 -0
  249. package/dist/quality/settings.d.ts.map +1 -0
  250. package/dist/quality/settings.js +32 -0
  251. package/dist/quality/settings.js.map +1 -0
  252. package/dist/quality/text.d.ts +43 -0
  253. package/dist/quality/text.d.ts.map +1 -0
  254. package/dist/quality/text.js +85 -0
  255. package/dist/quality/text.js.map +1 -0
  256. package/dist/render/glossary.d.ts +27 -0
  257. package/dist/render/glossary.d.ts.map +1 -0
  258. package/dist/render/glossary.js +71 -0
  259. package/dist/render/glossary.js.map +1 -0
  260. package/dist/render/images.d.ts +42 -0
  261. package/dist/render/images.d.ts.map +1 -0
  262. package/dist/render/images.js +84 -0
  263. package/dist/render/images.js.map +1 -0
  264. package/dist/render/index.d.ts +6 -0
  265. package/dist/render/index.d.ts.map +1 -0
  266. package/dist/render/index.js +8 -0
  267. package/dist/render/index.js.map +1 -0
  268. package/dist/render/reading-time.d.ts +8 -0
  269. package/dist/render/reading-time.d.ts.map +1 -0
  270. package/dist/render/reading-time.js +17 -0
  271. package/dist/render/reading-time.js.map +1 -0
  272. package/dist/render/render-article.d.ts +97 -0
  273. package/dist/render/render-article.d.ts.map +1 -0
  274. package/dist/render/render-article.js +373 -0
  275. package/dist/render/render-article.js.map +1 -0
  276. package/dist/render/slugify-heading.d.ts +2 -0
  277. package/dist/render/slugify-heading.d.ts.map +1 -0
  278. package/dist/render/slugify-heading.js +21 -0
  279. package/dist/render/slugify-heading.js.map +1 -0
  280. package/dist/server/health.d.ts +3 -0
  281. package/dist/server/health.d.ts.map +1 -0
  282. package/dist/server/health.js +11 -0
  283. package/dist/server/health.js.map +1 -0
  284. package/dist/server/index.d.ts +10 -0
  285. package/dist/server/index.d.ts.map +1 -0
  286. package/dist/server/index.js +13 -0
  287. package/dist/server/index.js.map +1 -0
  288. package/dist/server/og-fonts.d.ts +24 -0
  289. package/dist/server/og-fonts.d.ts.map +1 -0
  290. package/dist/server/og-fonts.js +85 -0
  291. package/dist/server/og-fonts.js.map +1 -0
  292. package/dist/server/options.d.ts +17 -0
  293. package/dist/server/options.d.ts.map +1 -0
  294. package/dist/server/options.js +56 -0
  295. package/dist/server/options.js.map +1 -0
  296. package/dist/sitemap.d.ts +11 -0
  297. package/dist/sitemap.d.ts.map +1 -0
  298. package/dist/sitemap.js +35 -0
  299. package/dist/sitemap.js.map +1 -0
  300. package/dist/ui/blog-article.d.ts +49 -0
  301. package/dist/ui/blog-article.d.ts.map +1 -0
  302. package/dist/ui/blog-article.js +45 -0
  303. package/dist/ui/blog-article.js.map +1 -0
  304. package/dist/ui/blog-glossary.d.ts +27 -0
  305. package/dist/ui/blog-glossary.d.ts.map +1 -0
  306. package/dist/ui/blog-glossary.js +20 -0
  307. package/dist/ui/blog-glossary.js.map +1 -0
  308. package/dist/ui/blog-layout.d.ts +31 -0
  309. package/dist/ui/blog-layout.d.ts.map +1 -0
  310. package/dist/ui/blog-layout.js +25 -0
  311. package/dist/ui/blog-layout.js.map +1 -0
  312. package/dist/ui/blog-listing.d.ts +15 -0
  313. package/dist/ui/blog-listing.d.ts.map +1 -0
  314. package/dist/ui/blog-listing.js +29 -0
  315. package/dist/ui/blog-listing.js.map +1 -0
  316. package/dist/ui/blog-method.d.ts +5 -0
  317. package/dist/ui/blog-method.d.ts.map +1 -0
  318. package/dist/ui/blog-method.js +21 -0
  319. package/dist/ui/blog-method.js.map +1 -0
  320. package/dist/ui/index.d.ts +7 -0
  321. package/dist/ui/index.d.ts.map +1 -0
  322. package/dist/ui/index.js +8 -0
  323. package/dist/ui/index.js.map +1 -0
  324. package/dist/ui/page-context.d.ts +15 -0
  325. package/dist/ui/page-context.d.ts.map +1 -0
  326. package/dist/ui/page-context.js +2 -0
  327. package/dist/ui/page-context.js.map +1 -0
  328. package/migrations/0001_create_articles.sql +67 -0
  329. package/migrations/README.md +8 -0
  330. package/module.json +28 -0
  331. package/package.json +102 -4
  332. package/skill/SKILL.md +72 -0
  333. package/skill/references/reviewer.md +39 -0
  334. package/skill/references/rules.md +122 -0
  335. package/skill/references/structure.md +69 -0
  336. package/skill/references/template.md +86 -0
  337. package/src/cli/bin.ts +5 -0
  338. package/src/cli/command.ts +49 -0
  339. package/src/cli/index.ts +20 -0
  340. package/src/cli/run.ts +591 -0
  341. package/src/cli/skill.ts +242 -0
  342. package/src/content/article-file.ts +184 -0
  343. package/src/contract.ts +88 -0
  344. package/src/db/articles.ts +160 -0
  345. package/src/db/publish-run.ts +224 -0
  346. package/src/db/schema.ts +36 -0
  347. package/src/discovery/dates.ts +27 -0
  348. package/src/discovery/index.ts +18 -0
  349. package/src/discovery/indexnow.ts +25 -0
  350. package/src/discovery/refresh.ts +79 -0
  351. package/src/discovery/related.ts +35 -0
  352. package/src/discovery/rss.ts +77 -0
  353. package/src/discovery/sitemap.ts +59 -0
  354. package/src/discovery/submit.ts +69 -0
  355. package/src/index.ts +99 -0
  356. package/src/messages/en.ts +82 -0
  357. package/src/messages/index.ts +7 -0
  358. package/src/messages/pl.ts +77 -0
  359. package/src/next/context.ts +30 -0
  360. package/src/next/data.ts +55 -0
  361. package/src/next/discovery.ts +49 -0
  362. package/src/next/index.ts +25 -0
  363. package/src/next/next-modules.d.ts +15 -0
  364. package/src/next/og-fonts.ts +14 -0
  365. package/src/next/og-image.tsx +108 -0
  366. package/src/next/pages.tsx +251 -0
  367. package/src/next/refresh.ts +85 -0
  368. package/src/options.ts +224 -0
  369. package/src/pages/body.ts +38 -0
  370. package/src/pages/dates.ts +39 -0
  371. package/src/pages/index.ts +37 -0
  372. package/src/pages/json-ld.ts +120 -0
  373. package/src/pages/listing.ts +95 -0
  374. package/src/pages/paths.ts +84 -0
  375. package/src/pages/redirects.ts +120 -0
  376. package/src/proxy/index.ts +66 -0
  377. package/src/quality/blocks.ts +130 -0
  378. package/src/quality/catalog.ts +86 -0
  379. package/src/quality/check-article.ts +112 -0
  380. package/src/quality/check-files.ts +63 -0
  381. package/src/quality/external-links.ts +37 -0
  382. package/src/quality/finding.ts +29 -0
  383. package/src/quality/gate.ts +15 -0
  384. package/src/quality/index.ts +28 -0
  385. package/src/quality/link-targets.ts +107 -0
  386. package/src/quality/options.ts +105 -0
  387. package/src/quality/plugin.ts +43 -0
  388. package/src/quality/rules/blocks.ts +43 -0
  389. package/src/quality/rules/images.ts +34 -0
  390. package/src/quality/rules/input.ts +12 -0
  391. package/src/quality/rules/links.ts +56 -0
  392. package/src/quality/rules/structure.ts +137 -0
  393. package/src/quality/rules/style.ts +137 -0
  394. package/src/quality/rules/ymyl.ts +57 -0
  395. package/src/quality/rulesets/en/ruleset.ts +117 -0
  396. package/src/quality/rulesets/index.ts +9 -0
  397. package/src/quality/rulesets/pl/ruleset.ts +126 -0
  398. package/src/quality/rulesets/types.ts +31 -0
  399. package/src/quality/settings.ts +60 -0
  400. package/src/quality/text.ts +113 -0
  401. package/src/render/glossary.ts +103 -0
  402. package/src/render/images.ts +110 -0
  403. package/src/render/index.ts +29 -0
  404. package/src/render/reading-time.ts +18 -0
  405. package/src/render/render-article.ts +487 -0
  406. package/src/render/slugify-heading.ts +22 -0
  407. package/src/server/health.ts +12 -0
  408. package/src/server/index.ts +28 -0
  409. package/src/server/og-fonts.ts +102 -0
  410. package/src/server/options.ts +62 -0
  411. package/src/sitemap.ts +36 -0
  412. package/src/ui/blog-article.tsx +186 -0
  413. package/src/ui/blog-glossary.tsx +104 -0
  414. package/src/ui/blog-layout.tsx +89 -0
  415. package/src/ui/blog-listing.tsx +95 -0
  416. package/src/ui/blog-method.tsx +34 -0
  417. package/src/ui/index.ts +8 -0
  418. package/src/ui/page-context.ts +17 -0
  419. package/styles.css +498 -0
@@ -0,0 +1,69 @@
1
+ # Structure for readers and for AI citations
2
+
3
+ AI assistants quote a fragment, not a whole text, and most quotes come from the first third of a
4
+ text. Numbers and quotes with a source raise the chance of being cited. Hence the order:
5
+
6
+ 1. **Lead (one or two paragraphs, up to {{leadWordsArticle}} words):** the answer to the title's
7
+ question and one concrete number with its footnote (rule `lead-number`). No warm-up about "these
8
+ times" and no announcement of what follows.
9
+ 2. **Context in one or two sentences:** where the number comes from.
10
+ 3. **`##` sections as questions** people ask assistants. The first sentence under a question is a
11
+ self-contained answer that can be cut out and quoted (rule `section-answer`), up to
12
+ {{answerWords}} words.
13
+ 4. **A worked example** for a concrete person or case: inputs, result, a small table (two to six
14
+ rows) and one sentence that reads one number out of it. This is what generic summaries lack.
15
+ 5. **When it does not work:** the condition under which the answer turns around. It separates a
16
+ guide from an advert and is what comparisons quote.
17
+ 6. **A checklist or steps** only when the reader really does something; do not turn prose into a
18
+ list of "**Label:** text" items.
19
+
20
+ Length: an article of {{articleWordsMin}} to {{articleWordsMax}} words, a glossary term of
21
+ {{termWordsMin}} to {{termWordsMax}} words with a lead of up to {{leadWordsTerm}}.
22
+
23
+ ## Footnotes
24
+
25
+ - `[^id]` right after the sentence with the number; definitions at the end of the file.
26
+ {{#ymyl}}
27
+ - A source: `[^limit]: <source name>: https://…`, and the same address stands in `sources` in the
28
+ frontmatter (rule `footnote-not-in-sources`).
29
+ {{#ownCalculationMark}}
30
+ - Your own calculation: `[^calc]: {{ownCalculationMark}}: <inputs>, as of YYYY-MM-DD.` Write the inputs
31
+ so that anyone can repeat the calculation and get the same result.
32
+ {{/ownCalculationMark}}
33
+ - Every amount, percentage and number from 1000 up carries a footnote (rule `number-source`).
34
+ Years, ages, small counts and legal references need none.
35
+ {{/ymyl}}
36
+ {{^ymyl}}
37
+ - Footnote numbers a reader might doubt, with the source's address.
38
+ {{/ymyl}}
39
+
40
+ ## Metadata
41
+
42
+ - `title` up to {{titleChars}} characters, with the question or the number; `description`
43
+ {{descriptionCharsMin}} to {{descriptionCharsMax}} characters, with the answer.
44
+ - `summary`: the "in short" box above the body, a few sentences with numbers an assistant can quote.
45
+ - `current_as_of`: the day the facts were checked against their sources. Change it only after
46
+ checking every number, never for a typo fix.
47
+ - `faq`: two to five `question`/`answer` pairs, each answer one sentence with a number.
48
+ - `cluster`: the topic, for "read next" lists. `pillar: true` only for the main text of a cluster
49
+ (one per cluster); the others link to it.
50
+ {{#fields}}
51
+ - The app's own keys: {{fieldKeys}}. Their meaning is the app's; follow the examples in
52
+ `{{contentDir}}`.
53
+ {{/fields}}
54
+
55
+ ## Links
56
+
57
+ - Internal, at least {{internalLinksArticle}}: one or two texts of the same cluster
58
+ (`{{articlesPath}}/<slug>`), a glossary term at the first use of a concept
59
+ (`{{termsPath}}/<slug>`; the renderer links the first mention of a term's forms by itself, so link
60
+ by hand only where no form matches), and the app's pages.
61
+ - External: primary sources only (laws, official statistics, the issuer's own documents). No
62
+ competitors' blogs and no comparison sites.
63
+
64
+ ## Disclaimers
65
+
66
+ A disclaimer such as "this is not investment advice" belongs to the page template, not the text.
67
+ {{#ymyl}}
68
+ The text guards the other side: no promise of a gain and no order to buy (rule `profit-promise`).
69
+ {{/ymyl}}
@@ -0,0 +1,86 @@
1
+ # The file `{{contentDir}}/<slug>.md`
2
+
3
+ One file per text; the file name is the slug plus `.md`. The frontmatter keys are fixed and an
4
+ unknown key is an error (rule `file`). Copy a text of `{{contentDir}}` that passes the gate when
5
+ there is one; otherwise start from this:
6
+
7
+ ```markdown
8
+ ---
9
+ id: <stable-kebab-key> # given once, never changed
10
+ slug: <kebab-file-name> # the address under {{articlesPath}}/; equals the file name
11
+ kind: article # or: term (a glossary definition)
12
+ cluster: <topic> # optional: the topic for "read next" lists
13
+ pillar: false # true only for the main text of the cluster
14
+ title: <the question or the number, up to {{titleChars}} characters>
15
+ description: <the answer with its number, {{descriptionCharsMin}} to {{descriptionCharsMax}} characters>
16
+ summary: <a few sentences with the answer and its numbers>
17
+ status: draft # draft | published | withdrawn
18
+ current_as_of: YYYY-MM-DD # the day the facts were checked
19
+ sources:
20
+ - name: <name of the act, dataset or document>
21
+ url: https://<primary source>
22
+ faq:
23
+ - question: <the question as a person asks it>
24
+ answer: <one sentence with a number>
25
+ ---
26
+
27
+ <Lead: the answer and its number[^source]. A second sentence with the worked example's number.>
28
+
29
+ <Context in one or two sentences.>
30
+
31
+ ## <The question a person asks?>
32
+
33
+ <The answer in the first sentence, with a footnote on the number[^source].>
34
+
35
+ ## <What does it mean for a concrete case?>
36
+
37
+ <Inputs, result, a small table, and one sentence that reads a number out of it.>
38
+
39
+ ## <When does it not work?>
40
+
41
+ <The condition under which the answer turns around.>
42
+
43
+ [^source]: <Source name>: https://<the same address as in sources>
44
+ ```
45
+ {{#fields}}
46
+
47
+ The app adds its own keys: {{fieldKeys}}. Look up their use in a published text of
48
+ `{{contentDir}}`.
49
+ {{/fields}}
50
+ {{#reservedSlugs}}
51
+
52
+ Slugs taken by the blog's own pages, never usable for a text: {{reservedSlugList}}.
53
+ {{/reservedSlugs}}
54
+
55
+ ## A glossary term (`kind: term`)
56
+
57
+ The same folder and format, at `{{termsPath}}/<slug>`. The differences:
58
+
59
+ ```markdown
60
+ ---
61
+ id: <kebab-key>
62
+ slug: <kebab-key>
63
+ kind: term
64
+ title: <the name of the concept, as the glossary index lists it>
65
+ description: <the definition in one sentence, with a number>
66
+ forms: # required: the phrases that link to this term from articles
67
+ - <phrase as it stands in texts>
68
+ - <another inflected form>
69
+ status: draft
70
+ current_as_of: YYYY-MM-DD
71
+ sources:
72
+ - name: <source>
73
+ url: https://<primary source>
74
+ ---
75
+
76
+ <The definition in the first sentence, with a number and its footnote[^source]. {{termWordsMin}} to
77
+ {{termWordsMax}} words in all, a lead of up to {{leadWordsTerm}}.>
78
+ ```
79
+
80
+ - **Forms** are phrases, not single words: "bridge to retirement", not "bridge", or every sentence
81
+ about a bridge gets a link. Each inflected form separately; a capital at the start of a sentence
82
+ matches by itself.
83
+ - The renderer links the **first** mention of a form in the prose of an article (not in a heading,
84
+ link, code or footnote); a manual link is needed only where no form matches.
85
+ - **Slug change:** change `slug` and rename the file, keep `id`; the old address redirects.
86
+ - **Withdrawal:** `status: withdrawn`; never delete the file.
package/src/cli/bin.ts ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ // The `softure-blog` executable. All logic lives in command.ts and run.ts, which the tests call directly.
3
+ import { runBlogCommand } from "./command.js";
4
+
5
+ process.exitCode = await runBlogCommand({ argv: process.argv.slice(2), cwd: process.cwd() });
@@ -0,0 +1,49 @@
1
+ // The `softure-blog` bin. It loads the app's config through `@softure-ai/core/cli`, like
2
+ // `softure migrate` (@softure-ai/db): the file must be loadable by Node itself (`.js`/`.mjs`, or `.ts`
3
+ // where Node strips types). Anything else uses an app script with `runBlogCli` instead (see run.ts).
4
+ import { loadAppConfig, takeConfigOption } from "@softure-ai/core/cli";
5
+ import { BLOG_USAGE, EXIT_FAILED, EXIT_OK, EXIT_USAGE, parseBlogCommand, runBlogCli, type BlogCommand, type CliOutput } from "./run.js";
6
+
7
+ export interface RunBlogCommandOptions {
8
+ readonly argv: readonly string[];
9
+ readonly cwd: string;
10
+ readonly output?: CliOutput;
11
+ }
12
+
13
+ const APP_SCRIPT = { runner: "runBlogCli", packageName: "@softure-ai/blog" };
14
+
15
+ // The commands that never connect, so a CI job runs them with a config that has no database URL.
16
+ // Any other command (and a new one, until it is listed here) needs the database.
17
+ const COMMANDS_WITHOUT_DATABASE: ReadonlySet<BlogCommand["kind"]> = new Set(["check", "skill-install"]);
18
+
19
+ const consoleOutput: CliOutput = {
20
+ log: (line) => console.log(line),
21
+ error: (line) => console.error(line),
22
+ };
23
+
24
+ /** Checks the arguments, loads the config and runs `runBlogCli`. A usage error never loads the config. */
25
+ export async function runBlogCommand(options: RunBlogCommandOptions): Promise<number> {
26
+ const output = options.output ?? consoleOutput;
27
+ const taken = takeConfigOption(options.argv);
28
+ if (!taken.ok) return refuseUsage(output, taken.problem);
29
+ const command = parseBlogCommand(taken.argv);
30
+ if (typeof command === "string") return refuseUsage(output, command);
31
+ if (command.kind === "help") {
32
+ output.log(BLOG_USAGE);
33
+ return EXIT_OK;
34
+ }
35
+
36
+ const database = COMMANDS_WITHOUT_DATABASE.has(command.kind) ? "optional" : "required";
37
+ const loaded = await loadAppConfig({ cwd: options.cwd, configPath: taken.configPath, appScript: APP_SCRIPT, database });
38
+ if (!loaded.ok) {
39
+ output.error(`softure-blog: ${loaded.problem}`);
40
+ return EXIT_FAILED;
41
+ }
42
+ return runBlogCli({ config: loaded.config, argv: taken.argv, cwd: options.cwd, output });
43
+ }
44
+
45
+ function refuseUsage(output: CliOutput, problem: string): number {
46
+ output.error(`softure-blog: ${problem}`);
47
+ output.error(BLOG_USAGE);
48
+ return EXIT_USAGE;
49
+ }
@@ -0,0 +1,20 @@
1
+ // `@softure-ai/blog/cli`: the `softure-blog` command for app scripts and container bundles.
2
+ export { runBlogCommand, type RunBlogCommandOptions } from "./command.js";
3
+ export { BLOG_USAGE, EXIT_FAILED, EXIT_OK, EXIT_USAGE, parseBlogCommand, runBlogCli, type BlogCommand, type CliOutput, type RunBlogCliOptions } from "./run.js";
4
+ export {
5
+ APP_SECTIONS_FILE,
6
+ DEFAULT_SKILL_COMMAND,
7
+ DEFAULT_SKILL_DIR,
8
+ fillRulesTables,
9
+ findTemplateRuleIds,
10
+ getSkillValues,
11
+ listTemplateFiles,
12
+ readSkillTemplate,
13
+ renderAppSections,
14
+ renderBlogSkill,
15
+ renderSkillTemplate,
16
+ SKILL_MARKER,
17
+ type RenderBlogSkillOptions,
18
+ type SkillFile,
19
+ type SkillValues,
20
+ } from "./skill.js";