rdstudio 0.2.0__py3-none-any.whl

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 (251) hide show
  1. rdstudio/__init__.py +3 -0
  2. rdstudio/_node/rdstudio.mjs +59351 -0
  3. rdstudio/brief.py +46 -0
  4. rdstudio/build.py +218 -0
  5. rdstudio/classify.py +131 -0
  6. rdstudio/cli.py +369 -0
  7. rdstudio/config.py +115 -0
  8. rdstudio/gitlog.py +92 -0
  9. rdstudio/launcher.py +63 -0
  10. rdstudio/learner.py +187 -0
  11. rdstudio/mcp_server.py +313 -0
  12. rdstudio/okf.py +775 -0
  13. rdstudio/procedures.py +233 -0
  14. rdstudio/references.py +228 -0
  15. rdstudio/reports.py +75 -0
  16. rdstudio/scaffold.py +227 -0
  17. rdstudio/scopes.py +162 -0
  18. rdstudio/search.py +149 -0
  19. rdstudio/serve.py +214 -0
  20. rdstudio/store.py +161 -0
  21. rdstudio/templates/agents/critic.md +20 -0
  22. rdstudio/templates/agents/librarian.md +21 -0
  23. rdstudio/templates/agents/searcher.md +21 -0
  24. rdstudio/templates/agents_section.md +31 -0
  25. rdstudio/templates/claude_section.md +3 -0
  26. rdstudio/templates/knowledge/overview.md +20 -0
  27. rdstudio/templates/knowledge/tasks/bootstrap-knowledge-base.md +45 -0
  28. rdstudio/templates/skills/decision/SKILL.md +47 -0
  29. rdstudio/templates/skills/handoff/SKILL.md +18 -0
  30. rdstudio/templates/skills/ingest-ref/SKILL.md +32 -0
  31. rdstudio/templates/skills/lint-okf/SKILL.md +15 -0
  32. rdstudio/templates/skills/promote/SKILL.md +25 -0
  33. rdstudio/templates/skills/question/SKILL.md +33 -0
  34. rdstudio/templates/skills/record-okf/SKILL.md +61 -0
  35. rdstudio/templates/skills/report/SKILL.md +46 -0
  36. rdstudio/templates/skills/report/template.html +74 -0
  37. rdstudio/templates/skills/search-okf/SKILL.md +36 -0
  38. rdstudio/templates/skills/task/SKILL.md +51 -0
  39. rdstudio/web/app.js +140 -0
  40. rdstudio/web/fonts.css +48 -0
  41. rdstudio/web/icon-192.png +0 -0
  42. rdstudio/web/icon-512.png +0 -0
  43. rdstudio/web/index.html +109 -0
  44. rdstudio/web/js/data.js +125 -0
  45. rdstudio/web/js/diagrams.js +116 -0
  46. rdstudio/web/js/graph.js +396 -0
  47. rdstudio/web/js/knowledge.js +212 -0
  48. rdstudio/web/js/layout-worker.js +15 -0
  49. rdstudio/web/js/layout.js +226 -0
  50. rdstudio/web/js/learn.js +83 -0
  51. rdstudio/web/js/map.js +1293 -0
  52. rdstudio/web/js/markdown.js +173 -0
  53. rdstudio/web/js/pages.js +219 -0
  54. rdstudio/web/js/procedures.js +262 -0
  55. rdstudio/web/js/settings.js +95 -0
  56. rdstudio/web/js/util.js +99 -0
  57. rdstudio/web/report.css +35 -0
  58. rdstudio/web/report.js +104 -0
  59. rdstudio/web/style.css +566 -0
  60. rdstudio/web/sw.js +166 -0
  61. rdstudio/web/themes/cyber.css +139 -0
  62. rdstudio/web/themes/map.css +133 -0
  63. rdstudio/web/themes/notebook.css +134 -0
  64. rdstudio/web/themes/space.css +135 -0
  65. rdstudio/web/themes/studio.css +110 -0
  66. rdstudio/web/vendor/d3.min.js +2 -0
  67. rdstudio/web/vendor/fonts/atkinson-hyperlegible-next-latin-ext-wght-italic.woff2 +0 -0
  68. rdstudio/web/vendor/fonts/atkinson-hyperlegible-next-latin-ext-wght-normal.woff2 +0 -0
  69. rdstudio/web/vendor/fonts/atkinson-hyperlegible-next-latin-wght-italic.woff2 +0 -0
  70. rdstudio/web/vendor/fonts/atkinson-hyperlegible-next-latin-wght-normal.woff2 +0 -0
  71. rdstudio/web/vendor/fonts/caveat-latin-wght-normal.woff2 +0 -0
  72. rdstudio/web/vendor/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  73. rdstudio/web/vendor/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
  74. rdstudio/web/vendor/fonts/ibm-plex-sans-latin-wght-italic.woff2 +0 -0
  75. rdstudio/web/vendor/fonts/ibm-plex-sans-latin-wght-normal.woff2 +0 -0
  76. rdstudio/web/vendor/fonts/inter-latin-opsz-italic.woff2 +0 -0
  77. rdstudio/web/vendor/fonts/inter-latin-opsz-normal.woff2 +0 -0
  78. rdstudio/web/vendor/fonts/jetbrains-mono-latin-wght-italic.woff2 +0 -0
  79. rdstudio/web/vendor/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
  80. rdstudio/web/vendor/fonts/literata-latin-ext-opsz-italic.woff2 +0 -0
  81. rdstudio/web/vendor/fonts/literata-latin-ext-opsz-normal.woff2 +0 -0
  82. rdstudio/web/vendor/fonts/literata-latin-opsz-italic.woff2 +0 -0
  83. rdstudio/web/vendor/fonts/literata-latin-opsz-normal.woff2 +0 -0
  84. rdstudio/web/vendor/fonts/source-sans-3-latin-wght-italic.woff2 +0 -0
  85. rdstudio/web/vendor/fonts/source-sans-3-latin-wght-normal.woff2 +0 -0
  86. rdstudio/web/vendor/fonts/source-serif-4-latin-opsz-italic.woff2 +0 -0
  87. rdstudio/web/vendor/fonts/source-serif-4-latin-opsz-normal.woff2 +0 -0
  88. rdstudio/web/vendor/hljs/github-dark.min.css +10 -0
  89. rdstudio/web/vendor/hljs/github.min.css +10 -0
  90. rdstudio/web/vendor/hljs/highlight.min.js +1262 -0
  91. rdstudio/web/vendor/hljs/julia.min.js +18 -0
  92. rdstudio/web/vendor/hljs/latex.min.js +33 -0
  93. rdstudio/web/vendor/hljs/matlab.min.js +15 -0
  94. rdstudio/web/vendor/katex/auto-render.min.js +1 -0
  95. rdstudio/web/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  96. rdstudio/web/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  97. rdstudio/web/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  98. rdstudio/web/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  99. rdstudio/web/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  100. rdstudio/web/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  101. rdstudio/web/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  102. rdstudio/web/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  103. rdstudio/web/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  104. rdstudio/web/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  105. rdstudio/web/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  106. rdstudio/web/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  107. rdstudio/web/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  108. rdstudio/web/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  109. rdstudio/web/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  110. rdstudio/web/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  111. rdstudio/web/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  112. rdstudio/web/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  113. rdstudio/web/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  114. rdstudio/web/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  115. rdstudio/web/vendor/katex/katex.min.css +1 -0
  116. rdstudio/web/vendor/katex/katex.min.js +1 -0
  117. rdstudio/web/vendor/licenses/atkinson-hyperlegible-next-OFL.txt +93 -0
  118. rdstudio/web/vendor/licenses/caveat-OFL.txt +93 -0
  119. rdstudio/web/vendor/licenses/d3-7.9.0.txt +13 -0
  120. rdstudio/web/vendor/licenses/highlightjs-cdn-assets-11.12.0.txt +29 -0
  121. rdstudio/web/vendor/licenses/ibm-plex-mono-OFL.txt +93 -0
  122. rdstudio/web/vendor/licenses/ibm-plex-sans-OFL.txt +93 -0
  123. rdstudio/web/vendor/licenses/inter-OFL.txt +93 -0
  124. rdstudio/web/vendor/licenses/jetbrains-mono-OFL.txt +93 -0
  125. rdstudio/web/vendor/licenses/katex-0.18.7.txt +21 -0
  126. rdstudio/web/vendor/licenses/literata-OFL.txt +93 -0
  127. rdstudio/web/vendor/licenses/markdown-it-15.0.2.txt +22 -0
  128. rdstudio/web/vendor/licenses/markdown-it-footnote-4.0.0.txt +22 -0
  129. rdstudio/web/vendor/licenses/markdown-it-texmath-1.0.0.txt +21 -0
  130. rdstudio/web/vendor/licenses/mermaid-12.0.0.txt +21 -0
  131. rdstudio/web/vendor/licenses/source-sans-3-OFL.txt +93 -0
  132. rdstudio/web/vendor/licenses/source-serif-4-OFL.txt +93 -0
  133. rdstudio/web/vendor/licenses/vega-6.4.0.txt +27 -0
  134. rdstudio/web/vendor/licenses/vega-embed-7.2.0.txt +27 -0
  135. rdstudio/web/vendor/licenses/vega-lite-6.4.3.txt +27 -0
  136. rdstudio/web/vendor/markdown-it-footnote.min.js +2 -0
  137. rdstudio/web/vendor/markdown-it.min.js +14 -0
  138. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-DGLNOSUI.mjs +1 -0
  139. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-HF4J73XS.mjs +1 -0
  140. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-IU6H5EVY.mjs +36 -0
  141. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-FSX6DT2V.mjs +140 -0
  142. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-YNRVYILN.mjs +38 -0
  143. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-22SYA5HA.mjs +1 -0
  144. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-2FTYKNOQ.mjs +1 -0
  145. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-36VM3LGA.mjs +1 -0
  146. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SEI7E7V.mjs +1 -0
  147. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-3T2MNPEU.mjs +1 -0
  148. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-44AU3KAP.mjs +174 -0
  149. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-4MZOTL6B.mjs +1 -0
  150. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-52IMQVNL.mjs +1 -0
  151. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-5NYYYZGL.mjs +1 -0
  152. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-BOZ75642.mjs +15 -0
  153. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-CVBX6EWK.mjs +1 -0
  154. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-D36F7FYG.mjs +14 -0
  155. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-DEGLBX6O.mjs +213 -0
  156. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-F6PIGZHG.mjs +1 -0
  157. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-FE53TMRB.mjs +133 -0
  158. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-FNR2RGIF.mjs +1 -0
  159. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDOATIN3.mjs +32 -0
  160. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7BX327W.mjs +2 -0
  161. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-HI3EPXIC.mjs +2 -0
  162. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-JCBDXJ5W.mjs +1 -0
  163. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-JGXDHUYS.mjs +1 -0
  164. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-L2B47AAZ.mjs +1 -0
  165. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-LIEGGFIX.mjs +1 -0
  166. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-LLWYPJEZ.mjs +1 -0
  167. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-MFYRHTYG.mjs +1 -0
  168. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-MIE3LDYP.mjs +1 -0
  169. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-MLQVGYRW.mjs +1 -0
  170. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-MNT3HHAC.mjs +1 -0
  171. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-NRRTYJAG.mjs +1 -0
  172. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-OCMJQKYE.mjs +10 -0
  173. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-PKJCTQEK.mjs +339 -0
  174. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-QADHU5CF.mjs +2 -0
  175. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-QVIEFLGF.mjs +1 -0
  176. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-QX7LGXZU.mjs +1 -0
  177. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-R2KH53MI.mjs +1 -0
  178. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-RXM7Z7NK.mjs +2 -0
  179. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-S4TE5DX6.mjs +1 -0
  180. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-SDIBIOXT.mjs +1 -0
  181. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-TDFULVG6.mjs +1 -0
  182. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-TECWHJ6D.mjs +62 -0
  183. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-TINYC7RT.mjs +70 -0
  184. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-V3KJR4XU.mjs +1 -0
  185. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTMXW4ES.mjs +1 -0
  186. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-Y5NT6LW5.mjs +88 -0
  187. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-Y5O5OPKC.mjs +3 -0
  188. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-YZG4BXY6.mjs +1 -0
  189. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZZ52EIBP.mjs +1 -0
  190. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-VV4INRHB.mjs +217 -0
  191. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-MSIMRU2B.mjs +1 -0
  192. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-KMPDV6GS.mjs +1 -0
  193. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-KTGLXFXK.mjs +62 -0
  194. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/dagre-QJJCSHY4.mjs +4 -0
  195. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/diagram-2ZIM67RP.mjs +3 -0
  196. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/diagram-FYHH5O6R.mjs +24 -0
  197. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/diagram-LYLDACMS.mjs +200 -0
  198. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/diagram-TYT3HRBC.mjs +24 -0
  199. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/diagram-XVIN6YVD.mjs +30 -0
  200. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/diagram-ZO2NLLYC.mjs +41 -0
  201. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-TU3FGH35.mjs +1 -0
  202. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/elk-GHAXNSLK.mjs +27 -0
  203. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-XID3VV23.mjs +99 -0
  204. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-P5QJSWSK.mjs +1 -0
  205. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-WPHQAKTU.mjs +1 -0
  206. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-QACGSKG6.mjs +292 -0
  207. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-V5NEQQYP.mjs +1 -0
  208. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-EB76O4HC.mjs +106 -0
  209. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-IGBHH2L7.mjs +1 -0
  210. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-343ZKIDN.mjs +2 -0
  211. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-UEEU5D75.mjs +70 -0
  212. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-4QTZOVLF.mjs +139 -0
  213. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-VEJL4RG2.mjs +89 -0
  214. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/katex-QVRI4OIV.mjs +257 -0
  215. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-C5K5ZT7T.mjs +96 -0
  216. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-PJTKDECJ.mjs +1 -0
  217. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-FHIILVJB.mjs +1 -0
  218. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-U3JFQXZN.mjs +1 -0
  219. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-S76BU6V7.mjs +39 -0
  220. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-HZBLWUZE.mjs +7 -0
  221. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-7T2UW32D.mjs +1 -0
  222. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-DZY7ARDL.mjs +1 -0
  223. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-LWS535ZK.mjs +1 -0
  224. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-EA3S4ZFM.mjs +1 -0
  225. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-DUKZONPX.mjs +1 -0
  226. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-R6RP3MQ2.mjs +1 -0
  227. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SGBABQV6.mjs +84 -0
  228. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-IM6M5BOW.mjs +40 -0
  229. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-FGJOIFJ2.mjs +169 -0
  230. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-ERV5D3W5.mjs +1 -0
  231. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-6KDGCZ5J.mjs +291 -0
  232. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-OJBUSBEF.mjs +1 -0
  233. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-LNXYTHDW.mjs +8 -0
  234. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-7G6YQRTY.mjs +120 -0
  235. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-AABPVERA.mjs +1 -0
  236. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-F2JSY3V3.mjs +1 -0
  237. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/usecaseDiagram-6Y6TMLBO.mjs +383 -0
  238. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-6RODYPCK.mjs +34 -0
  239. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-EIT5NWVN.mjs +1 -0
  240. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-RFZKLAPA.mjs +78 -0
  241. rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-TWWI2QJZ.mjs +7 -0
  242. rdstudio/web/vendor/mermaid/mermaid.esm.min.mjs +14 -0
  243. rdstudio/web/vendor/texmath.js +368 -0
  244. rdstudio/web/vendor/vega/vega-embed.min.js +7 -0
  245. rdstudio/web/vendor/vega/vega-lite.min.js +2 -0
  246. rdstudio/web/vendor/vega/vega.min.js +1 -0
  247. rdstudio-0.2.0.dist-info/METADATA +201 -0
  248. rdstudio-0.2.0.dist-info/RECORD +251 -0
  249. rdstudio-0.2.0.dist-info/WHEEL +4 -0
  250. rdstudio-0.2.0.dist-info/entry_points.txt +3 -0
  251. rdstudio-0.2.0.dist-info/licenses/LICENSE +21 -0
rdstudio/brief.py ADDED
@@ -0,0 +1,46 @@
1
+ """A short orientation for the start of an agent session (``rdstudio brief``).
2
+
3
+ Printed by a Claude Code SessionStart hook and returned by the MCP ``brief``
4
+ tool, so it must stay small: it tells the
5
+ agent what exists and where to look, not the content itself.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from . import gitlog
11
+ from .config import Config
12
+ from .okf import Bundle
13
+
14
+
15
+ def brief(cfg: Config, *, commits: int = 3) -> str:
16
+ b = Bundle.load(cfg.knowledge_dir)
17
+ if not b.concepts:
18
+ return f"rdstudio: no knowledge base found in {cfg.knowledge}/ (run `rdstudio init`)."
19
+ lines = [f"Project knowledge base ({cfg.title}): {len(b.concepts)} concepts in {cfg.knowledge}/, OKF format."]
20
+ root = b.directories[""]
21
+ dirs = []
22
+ for d in sorted(root.children):
23
+ count = sum(1 for cid in b.concepts if cid.startswith(d + "/"))
24
+ dirs.append(f"{d} ({count})")
25
+ if dirs:
26
+ lines.append("Directories: " + ", ".join(dirs))
27
+ active = [c for c in b.concepts.values() if c.type.lower() == "task" and "active" in c.tags]
28
+ if active:
29
+ lines.append("Active tasks: " + "; ".join(f"{c.title} [{c.id}]" for c in active[:5]))
30
+ stale = sum(c.verification_stale for c in b.concepts.values())
31
+ questions = sum(c.type.lower() == "question" and "answered" not in c.tags for c in b.concepts.values())
32
+ unverified = sum(c.trust == "unverified" for c in b.concepts.values())
33
+ lines.append(f"Awaiting the developer: {stale} changed since review, {questions} open questions, {unverified} unverified.")
34
+ history = gitlog.history(cfg.root, cfg.category_globs(), limit=commits, exclude=(cfg.output.strip("/") + "/",))
35
+ recent = [c["subject"] for c in history.get("commits", []) if not c.get("pending")][:commits]
36
+ if recent:
37
+ lines.append("Recent commits: " + " | ".join(recent))
38
+ from .scopes import global_config
39
+
40
+ g = global_config(cfg)
41
+ if g is not None:
42
+ count = len(Bundle.load(g.knowledge_dir).concepts)
43
+ lines.append(f"A global knowledge base (cross-project; {count} concepts) is searchable with scope=\"global\".")
44
+ lines.append("Search it (rdstudio MCP tools, the search-okf skill, or the librarian subagent) before re-deriving; "
45
+ "record decisions, questions and findings as you go.")
46
+ return "\n".join(lines)
rdstudio/build.py ADDED
@@ -0,0 +1,218 @@
1
+ """Build the static dashboard: web assets plus JSON data under ``.rdstudio/site``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import shutil
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from . import gitlog, learner, reports
12
+ from .config import Config
13
+ from .okf import Bundle, FrontmatterError, headings, iso, jsonable, now, split_frontmatter
14
+
15
+ WEB_DIR = Path(__file__).parent / "web"
16
+
17
+
18
+ def _sync_tree(src: Path, dst: Path, *, skip: frozenset[str] = frozenset()) -> None:
19
+ """Copy ``src`` into ``dst``, skipping files whose size and mtime already match
20
+ and the relative paths in ``skip``."""
21
+ for path in src.rglob("*"):
22
+ rel = path.relative_to(src)
23
+ if rel.as_posix() in skip:
24
+ continue
25
+ target = dst / rel
26
+ if path.is_dir():
27
+ target.mkdir(parents=True, exist_ok=True)
28
+ continue
29
+ st = path.stat()
30
+ if target.exists():
31
+ tt = target.stat()
32
+ if tt.st_size == st.st_size and int(tt.st_mtime) == int(st.st_mtime):
33
+ continue
34
+ target.parent.mkdir(parents=True, exist_ok=True)
35
+ shutil.copy2(path, target)
36
+
37
+
38
+ def service_worker() -> str:
39
+ """The service worker, stamped with a fingerprint of the app's files, so a
40
+ new rdstudio (or an edited app file) replaces what browsers have cached."""
41
+ digest = hashlib.sha256()
42
+ for path in sorted(WEB_DIR.rglob("*")):
43
+ if path.is_file():
44
+ st = path.stat()
45
+ digest.update(f"{path.relative_to(WEB_DIR).as_posix()}:{st.st_size}:{st.st_mtime_ns}\n".encode())
46
+ return (WEB_DIR / "sw.js").read_text(encoding="utf-8").replace("__SHELL__", digest.hexdigest()[:16])
47
+
48
+
49
+ def _write_if_changed(path: Path, content: str) -> None:
50
+ if path.exists() and path.read_text(encoding="utf-8") == content:
51
+ return
52
+ path.parent.mkdir(parents=True, exist_ok=True)
53
+ path.write_text(content, encoding="utf-8")
54
+
55
+
56
+ def _dump(value: Any) -> str:
57
+ return json.dumps(value, ensure_ascii=False, separators=(",", ":"), default=str)
58
+
59
+
60
+ def concept_record(bundle: Bundle, cid: str) -> dict[str, Any]:
61
+ c = bundle.concepts[cid]
62
+ rec = c.summary()
63
+ place = bundle.prerequisite_order()[cid]
64
+ rec.update({
65
+ "hash": learner.content_hash(c.body), # the version learner-record events refer to
66
+ "order": place["order"], # reading order from requires links
67
+ "depth": place["depth"], # longest chain of prerequisites below this note
68
+ "requires": bundle.requires_graph()[cid],
69
+ "meta": jsonable(c.meta),
70
+ "directory": c.directory,
71
+ "links": [{"target": l.target, "kind": l.kind, "broken": l.broken, "rel": l.rel} for l in c.links],
72
+ "backlinks": bundle.backlinks(cid),
73
+ "headings": [{"level": h.level, "text": h.text, "slug": h.slug} for h in headings(c.body)],
74
+ "generated_at": iso(c.generated_at) if c.generated_at else None,
75
+ "mtime": int(c.mtime),
76
+ })
77
+ return rec
78
+
79
+
80
+ def tree_record(bundle: Bundle) -> dict[str, Any]:
81
+ out = {}
82
+ for did, d in bundle.directories.items():
83
+ overview = bundle.overview_for(did)
84
+ out[did] = {
85
+ "id": did,
86
+ "name": d.name,
87
+ "concepts": sorted(d.concepts, key=lambda c: bundle.concepts[c].title.lower()),
88
+ "children": sorted(d.children),
89
+ "overview": overview.id if overview else None,
90
+ "index": bundle.render_index(did),
91
+ }
92
+ return out
93
+
94
+
95
+ def _skill_files(root: Path, *, user: bool = True) -> dict[str, list[dict[str, Any]]]:
96
+ """Skills (``.claude/skills/<name>/SKILL.md``) and agents (``.claude/agents/*.md``),
97
+ from the project and, unless ``user`` is false, from ``~/.claude``."""
98
+ out: dict[str, list[dict[str, Any]]] = {"skills": [], "agents": []}
99
+ specs = [("skills", "project", root, sorted((root / ".claude" / "skills").glob("*/SKILL.md"))),
100
+ ("agents", "project", root, sorted((root / ".claude" / "agents").glob("*.md")))]
101
+ if user:
102
+ home = Path.home()
103
+ specs += [("skills", "user", home, sorted((home / ".claude" / "skills").glob("*/SKILL.md"))),
104
+ ("agents", "user", home, sorted((home / ".claude" / "agents").glob("*.md")))]
105
+ for kind, scope, base, paths in specs:
106
+ for path in paths:
107
+ try:
108
+ meta, body = split_frontmatter(path.read_text(encoding="utf-8"))
109
+ except FrontmatterError:
110
+ meta, body = {}, path.read_text(encoding="utf-8")
111
+ meta = meta or {}
112
+ name = meta.get("name") or (path.parent.name if kind == "skills" else path.stem)
113
+ out[kind].append({
114
+ "name": str(name),
115
+ "description": str(meta.get("description") or ""),
116
+ "path": ("~/" if scope == "user" else "") + path.relative_to(base).as_posix(),
117
+ "scope": scope,
118
+ "meta": jsonable(meta),
119
+ "body": body,
120
+ })
121
+ return out
122
+
123
+
124
+ def build(cfg: Config, *, write_indexes: bool | None = None, export: bool = False,
125
+ site: Path | None = None) -> Path:
126
+ """Build the dashboard into ``site`` (default ``.rdstudio/site``).
127
+
128
+ ``export`` produces a snapshot for static hosting: no user-level skills, no
129
+ uncommitted changes, and the page does not poll for updates."""
130
+ site = site or cfg.site_dir
131
+ data = site / "data"
132
+ site.mkdir(parents=True, exist_ok=True)
133
+ _sync_tree(WEB_DIR, site, skip=frozenset({"sw.js"}))
134
+ _write_if_changed(site / "sw.js", service_worker())
135
+
136
+ auto_index = cfg.raw.get("index", {}).get("auto", True) if write_indexes is None else write_indexes
137
+ bundle = Bundle.load(cfg.knowledge_dir)
138
+ if auto_index and cfg.knowledge_dir.is_dir():
139
+ if bundle.write_indexes():
140
+ bundle = Bundle.load(cfg.knowledge_dir)
141
+
142
+ concepts = [concept_record(bundle, cid) for cid in sorted(bundle.concepts)]
143
+ tree = tree_record(bundle)
144
+ changes = gitlog.history(cfg.root, cfg.category_globs(), exclude=(cfg.output.strip("/") + "/",))
145
+ if export:
146
+ changes["commits"] = [c for c in changes["commits"] if not c.get("pending")]
147
+ report_items = reports.scan(cfg.reports_dir, cfg.knowledge, cfg.reports)
148
+ skills = _skill_files(cfg.root, user=not export)
149
+ issues = [{"path": i.path, "level": i.level, "code": i.code, "message": i.message} for i in bundle.lint()]
150
+
151
+ payload = {
152
+ "concepts.json": _dump(concepts),
153
+ "tree.json": _dump(tree),
154
+ "changes.json": _dump(changes),
155
+ "reports.json": _dump(report_items),
156
+ "skills.json": _dump(skills),
157
+ }
158
+ site_info = {
159
+ "title": cfg.title,
160
+ "knowledge": cfg.knowledge,
161
+ "reports": cfg.reports,
162
+ "human": cfg.human,
163
+ "okf_version": bundle.root_meta.get("okf_version"),
164
+ "static": export,
165
+ "map": cfg.raw.get("map", {}), # project defaults for the Map tab ([map] in rdstudio.toml)
166
+ "issues": issues,
167
+ "counts": {"concepts": len(concepts), "reports": len(report_items),
168
+ "skills": len(skills["skills"]), "agents": len(skills["agents"])},
169
+ }
170
+ payload["site.json"] = _dump(site_info)
171
+
172
+ bodies: dict[str, str] = {f"k/{cid}.md": c.body for cid, c in bundle.concepts.items()}
173
+ digest = hashlib.sha256()
174
+ for name in sorted(payload):
175
+ digest.update(payload[name].encode())
176
+ for name in sorted(bodies):
177
+ digest.update(name.encode() + bodies[name].encode())
178
+ version = digest.hexdigest()[:16]
179
+
180
+ for rel, text in bodies.items():
181
+ _write_if_changed(data / rel, text)
182
+ keep = {data / rel for rel in bodies}
183
+ # Non-markdown bundle files (images, data) are served beside the bodies.
184
+ if cfg.knowledge_dir.is_dir():
185
+ for path in cfg.knowledge_dir.rglob("*"):
186
+ rel = path.relative_to(cfg.knowledge_dir)
187
+ if path.is_file() and path.suffix != ".md" and not any(p.startswith(".") for p in rel.parts):
188
+ target = data / "k" / rel
189
+ target.parent.mkdir(parents=True, exist_ok=True)
190
+ if not target.exists() or target.stat().st_mtime < path.stat().st_mtime:
191
+ shutil.copy2(path, target)
192
+ if (data / "k").is_dir():
193
+ for stale in (data / "k").rglob("*.md"):
194
+ if stale not in keep:
195
+ stale.unlink()
196
+
197
+ # Reports are served beside the app so relative media keeps working.
198
+ if cfg.reports_dir.is_dir():
199
+ _sync_tree(cfg.reports_dir, site / "reports")
200
+
201
+ for name, text in payload.items():
202
+ _write_if_changed(data / name, text)
203
+ # Installable as an app (add to home screen), opening full screen.
204
+ _write_if_changed(site / "manifest.webmanifest", _dump({
205
+ "name": cfg.title, "short_name": cfg.title[:24], "start_url": "./", "scope": "./",
206
+ "display": "fullscreen", "display_override": ["fullscreen", "standalone"],
207
+ "background_color": "#141a20", "theme_color": "#141a20",
208
+ "icons": [{"src": f"icon-{n}.png", "sizes": f"{n}x{n}", "type": "image/png", "purpose": "any maskable"}
209
+ for n in (192, 512)],
210
+ }))
211
+ vfile = data / "version.json"
212
+ try:
213
+ old = json.loads(vfile.read_text(encoding="utf-8")).get("version")
214
+ except (OSError, ValueError):
215
+ old = None
216
+ if old != version:
217
+ vfile.write_text(_dump({"version": version, "built": iso(now())}), encoding="utf-8")
218
+ return site
rdstudio/classify.py ADDED
@@ -0,0 +1,131 @@
1
+ """Small structured decisions behind one interface.
2
+
3
+ Two places in rdstudio need a quick classification rather than generated text:
4
+
5
+ - whether an edit to a concept is significant (it bumps ``generated`` and flags
6
+ human-reviewed concepts for re-review), when the agent leaves it to rdstudio;
7
+ - which step of a procedure an agent is on, when its description does not match
8
+ a step exactly.
9
+
10
+ The default backend is deterministic and offline. A ``command`` backend hands
11
+ the question to any external program, which is how a structured-output model
12
+ (for example TypeSafe's Jev) or a local model can be plugged in without
13
+ rdstudio depending on a vendor SDK::
14
+
15
+ [classifier]
16
+ backend = "command"
17
+ command = ["my-classifier"] # reads JSON on stdin, writes JSON on stdout
18
+ min_confidence = 0.7 # below this, fall back to the rules
19
+
20
+ The program receives ``{"question", "options", "state"}`` and must answer
21
+ ``{"choice": <one of options>, "confidence": <0..1>}``.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import difflib
27
+ import json
28
+ import re
29
+ import subprocess
30
+ from dataclasses import dataclass
31
+ from typing import Any, Protocol
32
+
33
+ from .search import tokenize
34
+
35
+
36
+ @dataclass
37
+ class Decision:
38
+ choice: str | None
39
+ confidence: float
40
+ backend: str
41
+
42
+
43
+ class Classifier(Protocol):
44
+ def choose(self, question: str, options: list[str], state: dict[str, Any]) -> Decision: ...
45
+
46
+
47
+ # --------------------------------------------------------------------------- #
48
+ # Rules
49
+ # --------------------------------------------------------------------------- #
50
+
51
+ _WS = re.compile(r"\s+")
52
+ _PUNCT = re.compile(r"[^\w\s]")
53
+
54
+
55
+ def _norm(text: str) -> str:
56
+ return _WS.sub(" ", _PUNCT.sub("", text.lower())).strip()
57
+
58
+
59
+ def edit_significance(before: str, after: str) -> Decision:
60
+ """``minor`` for formatting, punctuation, case or very small wording changes;
61
+ ``significant`` otherwise."""
62
+ if _norm(before) == _norm(after):
63
+ return Decision("minor", 0.95, "rules")
64
+ a, b = tokenize(before), tokenize(after)
65
+ matcher = difflib.SequenceMatcher(a=a, b=b, autojunk=False)
66
+ changed = sum(max(i2 - i1, j2 - j1) for tag, i1, i2, j1, j2 in matcher.get_opcodes() if tag != "equal")
67
+ if changed <= 3 and changed / max(len(a), 1) < 0.05:
68
+ return Decision("minor", 0.7, "rules")
69
+ return Decision("significant", 0.8 if changed > 10 else 0.6, "rules")
70
+
71
+
72
+ def match_step(description: str, steps: dict[str, str]) -> Decision:
73
+ """The step whose label shares the most words with ``description``."""
74
+ def same(a: str, b: str) -> bool: # tolerate inflection: "renamed" ~ "rename"
75
+ return a == b or (min(len(a), len(b)) >= 4 and (a.startswith(b) or b.startswith(a)))
76
+
77
+ words = set(tokenize(description))
78
+ best, best_score = None, 0.0
79
+ for sid, label in steps.items():
80
+ label_words = set(tokenize(label)) | set(tokenize(sid.replace("-", " ").replace("_", " ")))
81
+ if not label_words or not words:
82
+ continue
83
+ shared = sum(1 for w in words if any(same(w, l) for l in label_words))
84
+ score = shared / (len(words) + len(label_words) - shared)
85
+ if score > best_score:
86
+ best, best_score = sid, score
87
+ return Decision(best if best_score >= 0.34 else None, round(best_score, 2), "rules")
88
+
89
+
90
+ class RulesClassifier:
91
+ def choose(self, question: str, options: list[str], state: dict[str, Any]) -> Decision:
92
+ if question == "edit_significance":
93
+ return edit_significance(state.get("before", ""), state.get("after", ""))
94
+ if question == "procedure_step":
95
+ return match_step(state.get("description", ""), state.get("steps", {}))
96
+ return Decision(None, 0.0, "rules")
97
+
98
+
99
+ # --------------------------------------------------------------------------- #
100
+ # External command
101
+ # --------------------------------------------------------------------------- #
102
+
103
+
104
+ class CommandClassifier:
105
+ def __init__(self, command: list[str], min_confidence: float = 0.7, timeout: float = 10.0):
106
+ self.command = command
107
+ self.min_confidence = min_confidence
108
+ self.timeout = timeout
109
+ self.fallback = RulesClassifier()
110
+
111
+ def choose(self, question: str, options: list[str], state: dict[str, Any]) -> Decision:
112
+ payload = json.dumps({"question": question, "options": options, "state": state})
113
+ try:
114
+ proc = subprocess.run(self.command, input=payload, capture_output=True, text=True,
115
+ timeout=self.timeout, check=True)
116
+ answer = json.loads(proc.stdout)
117
+ choice, confidence = answer.get("choice"), float(answer.get("confidence", 0))
118
+ except (OSError, subprocess.SubprocessError, ValueError, AttributeError):
119
+ return self.fallback.choose(question, options, state)
120
+ if choice not in options or confidence < self.min_confidence:
121
+ return self.fallback.choose(question, options, state)
122
+ return Decision(choice, confidence, "command")
123
+
124
+
125
+ def from_config(raw: dict[str, Any]) -> Classifier:
126
+ conf = raw.get("classifier", {})
127
+ if conf.get("backend") == "command" and conf.get("command"):
128
+ command = conf["command"]
129
+ return CommandClassifier(command if isinstance(command, list) else str(command).split(),
130
+ float(conf.get("min_confidence", 0.7)))
131
+ return RulesClassifier()