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.
- rdstudio/__init__.py +3 -0
- rdstudio/_node/rdstudio.mjs +59351 -0
- rdstudio/brief.py +46 -0
- rdstudio/build.py +218 -0
- rdstudio/classify.py +131 -0
- rdstudio/cli.py +369 -0
- rdstudio/config.py +115 -0
- rdstudio/gitlog.py +92 -0
- rdstudio/launcher.py +63 -0
- rdstudio/learner.py +187 -0
- rdstudio/mcp_server.py +313 -0
- rdstudio/okf.py +775 -0
- rdstudio/procedures.py +233 -0
- rdstudio/references.py +228 -0
- rdstudio/reports.py +75 -0
- rdstudio/scaffold.py +227 -0
- rdstudio/scopes.py +162 -0
- rdstudio/search.py +149 -0
- rdstudio/serve.py +214 -0
- rdstudio/store.py +161 -0
- rdstudio/templates/agents/critic.md +20 -0
- rdstudio/templates/agents/librarian.md +21 -0
- rdstudio/templates/agents/searcher.md +21 -0
- rdstudio/templates/agents_section.md +31 -0
- rdstudio/templates/claude_section.md +3 -0
- rdstudio/templates/knowledge/overview.md +20 -0
- rdstudio/templates/knowledge/tasks/bootstrap-knowledge-base.md +45 -0
- rdstudio/templates/skills/decision/SKILL.md +47 -0
- rdstudio/templates/skills/handoff/SKILL.md +18 -0
- rdstudio/templates/skills/ingest-ref/SKILL.md +32 -0
- rdstudio/templates/skills/lint-okf/SKILL.md +15 -0
- rdstudio/templates/skills/promote/SKILL.md +25 -0
- rdstudio/templates/skills/question/SKILL.md +33 -0
- rdstudio/templates/skills/record-okf/SKILL.md +61 -0
- rdstudio/templates/skills/report/SKILL.md +46 -0
- rdstudio/templates/skills/report/template.html +74 -0
- rdstudio/templates/skills/search-okf/SKILL.md +36 -0
- rdstudio/templates/skills/task/SKILL.md +51 -0
- rdstudio/web/app.js +140 -0
- rdstudio/web/fonts.css +48 -0
- rdstudio/web/icon-192.png +0 -0
- rdstudio/web/icon-512.png +0 -0
- rdstudio/web/index.html +109 -0
- rdstudio/web/js/data.js +125 -0
- rdstudio/web/js/diagrams.js +116 -0
- rdstudio/web/js/graph.js +396 -0
- rdstudio/web/js/knowledge.js +212 -0
- rdstudio/web/js/layout-worker.js +15 -0
- rdstudio/web/js/layout.js +226 -0
- rdstudio/web/js/learn.js +83 -0
- rdstudio/web/js/map.js +1293 -0
- rdstudio/web/js/markdown.js +173 -0
- rdstudio/web/js/pages.js +219 -0
- rdstudio/web/js/procedures.js +262 -0
- rdstudio/web/js/settings.js +95 -0
- rdstudio/web/js/util.js +99 -0
- rdstudio/web/report.css +35 -0
- rdstudio/web/report.js +104 -0
- rdstudio/web/style.css +566 -0
- rdstudio/web/sw.js +166 -0
- rdstudio/web/themes/cyber.css +139 -0
- rdstudio/web/themes/map.css +133 -0
- rdstudio/web/themes/notebook.css +134 -0
- rdstudio/web/themes/space.css +135 -0
- rdstudio/web/themes/studio.css +110 -0
- rdstudio/web/vendor/d3.min.js +2 -0
- rdstudio/web/vendor/fonts/atkinson-hyperlegible-next-latin-ext-wght-italic.woff2 +0 -0
- rdstudio/web/vendor/fonts/atkinson-hyperlegible-next-latin-ext-wght-normal.woff2 +0 -0
- rdstudio/web/vendor/fonts/atkinson-hyperlegible-next-latin-wght-italic.woff2 +0 -0
- rdstudio/web/vendor/fonts/atkinson-hyperlegible-next-latin-wght-normal.woff2 +0 -0
- rdstudio/web/vendor/fonts/caveat-latin-wght-normal.woff2 +0 -0
- rdstudio/web/vendor/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- rdstudio/web/vendor/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
- rdstudio/web/vendor/fonts/ibm-plex-sans-latin-wght-italic.woff2 +0 -0
- rdstudio/web/vendor/fonts/ibm-plex-sans-latin-wght-normal.woff2 +0 -0
- rdstudio/web/vendor/fonts/inter-latin-opsz-italic.woff2 +0 -0
- rdstudio/web/vendor/fonts/inter-latin-opsz-normal.woff2 +0 -0
- rdstudio/web/vendor/fonts/jetbrains-mono-latin-wght-italic.woff2 +0 -0
- rdstudio/web/vendor/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
- rdstudio/web/vendor/fonts/literata-latin-ext-opsz-italic.woff2 +0 -0
- rdstudio/web/vendor/fonts/literata-latin-ext-opsz-normal.woff2 +0 -0
- rdstudio/web/vendor/fonts/literata-latin-opsz-italic.woff2 +0 -0
- rdstudio/web/vendor/fonts/literata-latin-opsz-normal.woff2 +0 -0
- rdstudio/web/vendor/fonts/source-sans-3-latin-wght-italic.woff2 +0 -0
- rdstudio/web/vendor/fonts/source-sans-3-latin-wght-normal.woff2 +0 -0
- rdstudio/web/vendor/fonts/source-serif-4-latin-opsz-italic.woff2 +0 -0
- rdstudio/web/vendor/fonts/source-serif-4-latin-opsz-normal.woff2 +0 -0
- rdstudio/web/vendor/hljs/github-dark.min.css +10 -0
- rdstudio/web/vendor/hljs/github.min.css +10 -0
- rdstudio/web/vendor/hljs/highlight.min.js +1262 -0
- rdstudio/web/vendor/hljs/julia.min.js +18 -0
- rdstudio/web/vendor/hljs/latex.min.js +33 -0
- rdstudio/web/vendor/hljs/matlab.min.js +15 -0
- rdstudio/web/vendor/katex/auto-render.min.js +1 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- rdstudio/web/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- rdstudio/web/vendor/katex/katex.min.css +1 -0
- rdstudio/web/vendor/katex/katex.min.js +1 -0
- rdstudio/web/vendor/licenses/atkinson-hyperlegible-next-OFL.txt +93 -0
- rdstudio/web/vendor/licenses/caveat-OFL.txt +93 -0
- rdstudio/web/vendor/licenses/d3-7.9.0.txt +13 -0
- rdstudio/web/vendor/licenses/highlightjs-cdn-assets-11.12.0.txt +29 -0
- rdstudio/web/vendor/licenses/ibm-plex-mono-OFL.txt +93 -0
- rdstudio/web/vendor/licenses/ibm-plex-sans-OFL.txt +93 -0
- rdstudio/web/vendor/licenses/inter-OFL.txt +93 -0
- rdstudio/web/vendor/licenses/jetbrains-mono-OFL.txt +93 -0
- rdstudio/web/vendor/licenses/katex-0.18.7.txt +21 -0
- rdstudio/web/vendor/licenses/literata-OFL.txt +93 -0
- rdstudio/web/vendor/licenses/markdown-it-15.0.2.txt +22 -0
- rdstudio/web/vendor/licenses/markdown-it-footnote-4.0.0.txt +22 -0
- rdstudio/web/vendor/licenses/markdown-it-texmath-1.0.0.txt +21 -0
- rdstudio/web/vendor/licenses/mermaid-12.0.0.txt +21 -0
- rdstudio/web/vendor/licenses/source-sans-3-OFL.txt +93 -0
- rdstudio/web/vendor/licenses/source-serif-4-OFL.txt +93 -0
- rdstudio/web/vendor/licenses/vega-6.4.0.txt +27 -0
- rdstudio/web/vendor/licenses/vega-embed-7.2.0.txt +27 -0
- rdstudio/web/vendor/licenses/vega-lite-6.4.3.txt +27 -0
- rdstudio/web/vendor/markdown-it-footnote.min.js +2 -0
- rdstudio/web/vendor/markdown-it.min.js +14 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-DGLNOSUI.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-HF4J73XS.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-IU6H5EVY.mjs +36 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-FSX6DT2V.mjs +140 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-YNRVYILN.mjs +38 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-22SYA5HA.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-2FTYKNOQ.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-36VM3LGA.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SEI7E7V.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-3T2MNPEU.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-44AU3KAP.mjs +174 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-4MZOTL6B.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-52IMQVNL.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-5NYYYZGL.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-BOZ75642.mjs +15 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-CVBX6EWK.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-D36F7FYG.mjs +14 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-DEGLBX6O.mjs +213 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-F6PIGZHG.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-FE53TMRB.mjs +133 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-FNR2RGIF.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDOATIN3.mjs +32 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7BX327W.mjs +2 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-HI3EPXIC.mjs +2 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-JCBDXJ5W.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-JGXDHUYS.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-L2B47AAZ.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-LIEGGFIX.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-LLWYPJEZ.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-MFYRHTYG.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-MIE3LDYP.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-MLQVGYRW.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-MNT3HHAC.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-NRRTYJAG.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-OCMJQKYE.mjs +10 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-PKJCTQEK.mjs +339 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-QADHU5CF.mjs +2 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-QVIEFLGF.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-QX7LGXZU.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-R2KH53MI.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-RXM7Z7NK.mjs +2 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-S4TE5DX6.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-SDIBIOXT.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-TDFULVG6.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-TECWHJ6D.mjs +62 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-TINYC7RT.mjs +70 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-V3KJR4XU.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTMXW4ES.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-Y5NT6LW5.mjs +88 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-Y5O5OPKC.mjs +3 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-YZG4BXY6.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZZ52EIBP.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-VV4INRHB.mjs +217 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-MSIMRU2B.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-KMPDV6GS.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-KTGLXFXK.mjs +62 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/dagre-QJJCSHY4.mjs +4 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/diagram-2ZIM67RP.mjs +3 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/diagram-FYHH5O6R.mjs +24 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/diagram-LYLDACMS.mjs +200 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/diagram-TYT3HRBC.mjs +24 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/diagram-XVIN6YVD.mjs +30 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/diagram-ZO2NLLYC.mjs +41 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-TU3FGH35.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/elk-GHAXNSLK.mjs +27 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-XID3VV23.mjs +99 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-P5QJSWSK.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-WPHQAKTU.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-QACGSKG6.mjs +292 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-V5NEQQYP.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-EB76O4HC.mjs +106 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-IGBHH2L7.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-343ZKIDN.mjs +2 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-UEEU5D75.mjs +70 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-4QTZOVLF.mjs +139 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-VEJL4RG2.mjs +89 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/katex-QVRI4OIV.mjs +257 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-C5K5ZT7T.mjs +96 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-PJTKDECJ.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-FHIILVJB.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-U3JFQXZN.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-S76BU6V7.mjs +39 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-HZBLWUZE.mjs +7 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-7T2UW32D.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-DZY7ARDL.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-LWS535ZK.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-EA3S4ZFM.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-DUKZONPX.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-R6RP3MQ2.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SGBABQV6.mjs +84 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-IM6M5BOW.mjs +40 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-FGJOIFJ2.mjs +169 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-ERV5D3W5.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-6KDGCZ5J.mjs +291 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-OJBUSBEF.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-LNXYTHDW.mjs +8 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-7G6YQRTY.mjs +120 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-AABPVERA.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-F2JSY3V3.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/usecaseDiagram-6Y6TMLBO.mjs +383 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-6RODYPCK.mjs +34 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-EIT5NWVN.mjs +1 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-RFZKLAPA.mjs +78 -0
- rdstudio/web/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-TWWI2QJZ.mjs +7 -0
- rdstudio/web/vendor/mermaid/mermaid.esm.min.mjs +14 -0
- rdstudio/web/vendor/texmath.js +368 -0
- rdstudio/web/vendor/vega/vega-embed.min.js +7 -0
- rdstudio/web/vendor/vega/vega-lite.min.js +2 -0
- rdstudio/web/vendor/vega/vega.min.js +1 -0
- rdstudio-0.2.0.dist-info/METADATA +201 -0
- rdstudio-0.2.0.dist-info/RECORD +251 -0
- rdstudio-0.2.0.dist-info/WHEEL +4 -0
- rdstudio-0.2.0.dist-info/entry_points.txt +3 -0
- rdstudio-0.2.0.dist-info/licenses/LICENSE +21 -0
rdstudio/serve.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""Serve the dashboard with the standard library, rebuilding when sources change."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import email.utils
|
|
6
|
+
import functools
|
|
7
|
+
import gzip
|
|
8
|
+
import io
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import secrets
|
|
12
|
+
import sys
|
|
13
|
+
import threading
|
|
14
|
+
import time
|
|
15
|
+
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
|
|
16
|
+
from urllib.parse import urlsplit
|
|
17
|
+
|
|
18
|
+
from . import learner
|
|
19
|
+
from .build import WEB_DIR, build
|
|
20
|
+
from .config import Config
|
|
21
|
+
|
|
22
|
+
POLL_SECONDS = 1.0
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
LOOPBACK = {"localhost", "127.0.0.1", "::1"}
|
|
26
|
+
# Text worth compressing: on a slow link (a VS Code tunnel, say) the dashboard's
|
|
27
|
+
# 1.3 MB of scripts and data shrink to about 0.5 MB.
|
|
28
|
+
COMPRESSIBLE = (".html", ".js", ".mjs", ".css", ".json", ".md", ".svg", ".txt", ".webmanifest", ".map")
|
|
29
|
+
_gzipped: dict[tuple[str, int, int], bytes] = {}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _Handler(SimpleHTTPRequestHandler):
|
|
33
|
+
"""Static files, plus ``/api/learner`` for the private learner record.
|
|
34
|
+
|
|
35
|
+
Writes are accepted only from the dashboard's own pages: the Origin must
|
|
36
|
+
match the Host, the body must be JSON, and the request must carry the token
|
|
37
|
+
handed out by GET (which other sites cannot read). When bound to localhost,
|
|
38
|
+
the API answers only to localhost names, Tailscale names (``*.ts.net``, as
|
|
39
|
+
``tailscale serve`` passes them through) and ``--allow-host`` names, against
|
|
40
|
+
DNS rebinding."""
|
|
41
|
+
|
|
42
|
+
# Keep connections open between requests: through a tunnel, each new
|
|
43
|
+
# connection is slow to set up and more likely to be dropped.
|
|
44
|
+
protocol_version = "HTTP/1.1"
|
|
45
|
+
|
|
46
|
+
cfg: Config
|
|
47
|
+
token: str
|
|
48
|
+
loopback: bool
|
|
49
|
+
allowed: frozenset[str] = frozenset()
|
|
50
|
+
|
|
51
|
+
def _host_ok(self) -> bool:
|
|
52
|
+
host = urlsplit("//" + (self.headers.get("Host") or "")).hostname or ""
|
|
53
|
+
return not self.loopback or host in LOOPBACK or host.endswith(".ts.net") or host in self.allowed
|
|
54
|
+
|
|
55
|
+
def _json(self, status: int, value: object) -> None:
|
|
56
|
+
body = json.dumps(value, ensure_ascii=False).encode()
|
|
57
|
+
self.send_response(status)
|
|
58
|
+
self.send_header("Content-Type", "application/json")
|
|
59
|
+
self.send_header("Content-Length", str(len(body)))
|
|
60
|
+
self.send_header("Cache-Control", "no-store")
|
|
61
|
+
if self.close_connection:
|
|
62
|
+
self.send_header("Connection", "close")
|
|
63
|
+
self.end_headers()
|
|
64
|
+
self.wfile.write(body)
|
|
65
|
+
|
|
66
|
+
def do_GET(self) -> None:
|
|
67
|
+
if urlsplit(self.path).path != "/api/learner":
|
|
68
|
+
return super().do_GET()
|
|
69
|
+
if not self._host_ok():
|
|
70
|
+
return self._json(403, {"error": "host not allowed"})
|
|
71
|
+
on = learner.enabled(self.cfg)
|
|
72
|
+
self._json(200, {
|
|
73
|
+
"enabled": on,
|
|
74
|
+
"token": self.token if on else None,
|
|
75
|
+
"dir": str(learner.record_dir(self.cfg)) if on else None,
|
|
76
|
+
"events": learner.events(self.cfg) if on else [],
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
def do_POST(self) -> None:
|
|
80
|
+
self.close_connection = True # a refused body is never read; do not reuse the connection
|
|
81
|
+
if urlsplit(self.path).path != "/api/learner":
|
|
82
|
+
return self._json(404, {"error": "not found"})
|
|
83
|
+
origin = urlsplit(self.headers.get("Origin") or "").netloc
|
|
84
|
+
if not self._host_ok() or origin != self.headers.get("Host"):
|
|
85
|
+
return self._json(403, {"error": "cross-origin request refused"})
|
|
86
|
+
if self.headers.get("X-Rdstudio-Token") != self.token:
|
|
87
|
+
return self._json(403, {"error": "bad token"})
|
|
88
|
+
if not (self.headers.get("Content-Type") or "").startswith("application/json"):
|
|
89
|
+
return self._json(415, {"error": "JSON only"})
|
|
90
|
+
if not learner.enabled(self.cfg):
|
|
91
|
+
return self._json(409, {"error": "the learner record is off ([learner] enabled in the user config)"})
|
|
92
|
+
length = int(self.headers.get("Content-Length") or 0)
|
|
93
|
+
if length > learner.MAX_EVENT_BYTES:
|
|
94
|
+
return self._json(413, {"error": "event too large"})
|
|
95
|
+
try:
|
|
96
|
+
event = learner.append(self.cfg, json.loads(self.rfile.read(length) or b"null"))
|
|
97
|
+
except (ValueError, learner.LearnerError) as exc:
|
|
98
|
+
return self._json(400, {"error": str(exc)})
|
|
99
|
+
self._json(200, event)
|
|
100
|
+
|
|
101
|
+
def _cache_control(self) -> str | None:
|
|
102
|
+
path = urlsplit(self.path).path
|
|
103
|
+
if path.startswith("/vendor/"):
|
|
104
|
+
return "public, max-age=604800" # changes only when rdstudio is upgraded
|
|
105
|
+
if path.startswith("/data/") or path.endswith((".html", ".js", ".css")) or path == "/":
|
|
106
|
+
return "no-cache" # revalidate (cheap: 304 when unchanged)
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
def end_headers(self) -> None:
|
|
110
|
+
if cc := self._cache_control():
|
|
111
|
+
self.send_header("Cache-Control", cc)
|
|
112
|
+
super().end_headers()
|
|
113
|
+
|
|
114
|
+
def send_head(self): # type: ignore[override]
|
|
115
|
+
"""Serve text gzipped when the browser accepts it; everything else as usual."""
|
|
116
|
+
path = self.translate_path(self.path)
|
|
117
|
+
if os.path.isdir(path) and urlsplit(self.path).path.endswith("/"):
|
|
118
|
+
path = os.path.join(path, "index.html")
|
|
119
|
+
if ("gzip" not in (self.headers.get("Accept-Encoding") or "") or not path.endswith(COMPRESSIBLE)
|
|
120
|
+
or not os.path.isfile(path)):
|
|
121
|
+
return super().send_head()
|
|
122
|
+
st = os.stat(path)
|
|
123
|
+
since = self.headers.get("If-Modified-Since")
|
|
124
|
+
if since:
|
|
125
|
+
try:
|
|
126
|
+
if int(st.st_mtime) <= email.utils.parsedate_to_datetime(since).timestamp():
|
|
127
|
+
self.send_response(304)
|
|
128
|
+
self.send_header("Vary", "Accept-Encoding")
|
|
129
|
+
self.end_headers()
|
|
130
|
+
return None
|
|
131
|
+
except (TypeError, ValueError, OverflowError):
|
|
132
|
+
pass
|
|
133
|
+
key = (path, st.st_mtime_ns, st.st_size)
|
|
134
|
+
body = _gzipped.get(key)
|
|
135
|
+
if body is None:
|
|
136
|
+
with open(path, "rb") as fh:
|
|
137
|
+
body = gzip.compress(fh.read(), compresslevel=6)
|
|
138
|
+
if len(_gzipped) > 512:
|
|
139
|
+
_gzipped.clear()
|
|
140
|
+
_gzipped[key] = body
|
|
141
|
+
self.send_response(200)
|
|
142
|
+
self.send_header("Content-Type", self.guess_type(path))
|
|
143
|
+
self.send_header("Content-Encoding", "gzip")
|
|
144
|
+
self.send_header("Content-Length", str(len(body)))
|
|
145
|
+
self.send_header("Last-Modified", self.date_time_string(int(st.st_mtime)))
|
|
146
|
+
self.send_header("Vary", "Accept-Encoding")
|
|
147
|
+
self.end_headers()
|
|
148
|
+
return io.BytesIO(body)
|
|
149
|
+
|
|
150
|
+
def log_message(self, format: str, *args: object) -> None: # noqa: A002 - stdlib signature
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _fingerprint(cfg: Config) -> tuple:
|
|
155
|
+
"""A cheap summary of everything the build reads."""
|
|
156
|
+
roots = [cfg.knowledge_dir, cfg.reports_dir, cfg.root / ".claude", WEB_DIR]
|
|
157
|
+
files = [cfg.root / "rdstudio.toml", cfg.root / ".git" / "HEAD", cfg.root / ".git" / "index"]
|
|
158
|
+
stamp: list[tuple[str, int, int]] = []
|
|
159
|
+
for root in roots:
|
|
160
|
+
if not root.is_dir():
|
|
161
|
+
continue
|
|
162
|
+
for dirpath, dirnames, filenames in os.walk(root):
|
|
163
|
+
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
|
|
164
|
+
for name in filenames:
|
|
165
|
+
p = os.path.join(dirpath, name)
|
|
166
|
+
try:
|
|
167
|
+
st = os.stat(p)
|
|
168
|
+
except OSError:
|
|
169
|
+
continue
|
|
170
|
+
stamp.append((p, st.st_mtime_ns, st.st_size))
|
|
171
|
+
for f in files:
|
|
172
|
+
if f.exists():
|
|
173
|
+
st = f.stat()
|
|
174
|
+
stamp.append((str(f), st.st_mtime_ns, st.st_size))
|
|
175
|
+
return tuple(sorted(stamp))
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _watch(cfg: Config, stop: threading.Event) -> None:
|
|
179
|
+
last = _fingerprint(cfg)
|
|
180
|
+
while not stop.wait(POLL_SECONDS):
|
|
181
|
+
current = _fingerprint(cfg)
|
|
182
|
+
if current == last:
|
|
183
|
+
continue
|
|
184
|
+
try:
|
|
185
|
+
build(cfg)
|
|
186
|
+
print(f"[{time.strftime('%H:%M:%S')}] rebuilt", flush=True)
|
|
187
|
+
except Exception as exc: # keep serving the last good build
|
|
188
|
+
print(f"[{time.strftime('%H:%M:%S')}] build failed: {exc}", file=sys.stderr, flush=True)
|
|
189
|
+
last = _fingerprint(cfg) # the build may regenerate index.md files
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def serve(cfg: Config, *, host: str = "127.0.0.1", port: int = 8000, watch: bool = True,
|
|
193
|
+
allow_hosts: tuple[str, ...] = ()) -> None:
|
|
194
|
+
site = build(cfg)
|
|
195
|
+
handler_cls = type("Handler", (_Handler,), {
|
|
196
|
+
"cfg": cfg, "token": secrets.token_urlsafe(24), "loopback": host in LOOPBACK,
|
|
197
|
+
"allowed": frozenset(h.lower() for h in allow_hosts)})
|
|
198
|
+
handler = functools.partial(handler_cls, directory=str(site))
|
|
199
|
+
server = ThreadingHTTPServer((host, port), handler)
|
|
200
|
+
stop = threading.Event()
|
|
201
|
+
if watch:
|
|
202
|
+
threading.Thread(target=_watch, args=(cfg, stop), daemon=True).start()
|
|
203
|
+
shown = "localhost" if host in ("127.0.0.1", "0.0.0.0") else host
|
|
204
|
+
print(f"Serving {cfg.title} at http://{shown}:{port}/ (Ctrl+C to stop)", flush=True)
|
|
205
|
+
if host == "0.0.0.0":
|
|
206
|
+
print("Listening on all interfaces (reachable over your tailnet/LAN).", flush=True)
|
|
207
|
+
try:
|
|
208
|
+
server.serve_forever()
|
|
209
|
+
except KeyboardInterrupt:
|
|
210
|
+
pass
|
|
211
|
+
finally:
|
|
212
|
+
stop.set()
|
|
213
|
+
server.server_close()
|
|
214
|
+
|
rdstudio/store.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Writing concepts with provenance: create, update (whole body or one section), verify."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import posixpath
|
|
6
|
+
import re
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from .okf import (
|
|
12
|
+
RESERVED,
|
|
13
|
+
FrontmatterError,
|
|
14
|
+
headings,
|
|
15
|
+
now,
|
|
16
|
+
render_concept,
|
|
17
|
+
split_frontmatter,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
_SAFE_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._/-]*$")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class StoreError(ValueError):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class WriteResult:
|
|
29
|
+
id: str
|
|
30
|
+
path: str
|
|
31
|
+
created: bool
|
|
32
|
+
significant: bool
|
|
33
|
+
decided_by: str = "caller"
|
|
34
|
+
|
|
35
|
+
def as_dict(self) -> dict[str, Any]:
|
|
36
|
+
return {"id": self.id, "path": self.path, "created": self.created, "significant": self.significant,
|
|
37
|
+
"decided_by": self.decided_by}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def concept_path(root: Path, cid: str) -> Path:
|
|
41
|
+
cid = cid.strip().lstrip("/")
|
|
42
|
+
if cid.endswith(".md"):
|
|
43
|
+
cid = cid[:-3]
|
|
44
|
+
norm = posixpath.normpath(cid)
|
|
45
|
+
if not _SAFE_ID.match(norm) or norm.startswith("..") or "/../" in f"/{norm}/":
|
|
46
|
+
raise StoreError(f"invalid concept id: {cid!r}")
|
|
47
|
+
if posixpath.basename(norm) + ".md" in RESERVED:
|
|
48
|
+
raise StoreError(f"{posixpath.basename(norm)}.md is reserved by OKF")
|
|
49
|
+
return root / f"{norm}.md"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _read(path: Path) -> tuple[dict[str, Any], str]:
|
|
53
|
+
meta, body = split_frontmatter(path.read_text(encoding="utf-8"))
|
|
54
|
+
return (meta or {}), body
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def replace_section(body: str, heading: str, content: str) -> str:
|
|
58
|
+
"""Replace the content under ``heading`` (keeping the heading line); append the
|
|
59
|
+
section at the end when it does not exist."""
|
|
60
|
+
wanted = heading.strip().lstrip("#").strip()
|
|
61
|
+
hs = headings(body)
|
|
62
|
+
lines = body.split("\n")
|
|
63
|
+
for idx, h in enumerate(hs):
|
|
64
|
+
if h.text.lower() == wanted.lower():
|
|
65
|
+
end = len(lines)
|
|
66
|
+
for later in hs[idx + 1 :]:
|
|
67
|
+
if later.level <= h.level:
|
|
68
|
+
end = later.line
|
|
69
|
+
break
|
|
70
|
+
new = lines[: h.line + 1] + ["", content.strip(), ""] + lines[end:]
|
|
71
|
+
return "\n".join(new)
|
|
72
|
+
return body.rstrip() + f"\n\n# {wanted}\n\n{content.strip()}\n"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def record(
|
|
76
|
+
root: Path,
|
|
77
|
+
cid: str,
|
|
78
|
+
*,
|
|
79
|
+
actor: str,
|
|
80
|
+
body: str | None = None,
|
|
81
|
+
meta: dict[str, Any] | None = None,
|
|
82
|
+
section: str | None = None,
|
|
83
|
+
append: str | None = None,
|
|
84
|
+
significant: bool | None = True,
|
|
85
|
+
classifier: Any = None,
|
|
86
|
+
) -> WriteResult:
|
|
87
|
+
"""Create or update a concept.
|
|
88
|
+
|
|
89
|
+
- ``meta`` keys overwrite existing keys; a ``None`` value deletes the key.
|
|
90
|
+
Unknown keys already present are preserved.
|
|
91
|
+
- ``body`` replaces the whole body; ``section`` + ``body`` replaces only that
|
|
92
|
+
section; ``append`` adds text at the end.
|
|
93
|
+
- ``significant`` edits stamp ``generated: {by, at}`` (OKF: last meaningful
|
|
94
|
+
change). Minor edits leave provenance untouched. ``None`` lets the
|
|
95
|
+
classifier decide from the before and after text.
|
|
96
|
+
"""
|
|
97
|
+
path = concept_path(root, cid)
|
|
98
|
+
created = not path.exists()
|
|
99
|
+
if created:
|
|
100
|
+
current_meta: dict[str, Any] = {}
|
|
101
|
+
current_body = ""
|
|
102
|
+
significant = True
|
|
103
|
+
else:
|
|
104
|
+
try:
|
|
105
|
+
current_meta, current_body = _read(path)
|
|
106
|
+
except FrontmatterError as exc:
|
|
107
|
+
raise StoreError(f"cannot update {path.name}: {exc}") from exc
|
|
108
|
+
|
|
109
|
+
new_meta = dict(current_meta)
|
|
110
|
+
for key, value in (meta or {}).items():
|
|
111
|
+
if value is None:
|
|
112
|
+
new_meta.pop(key, None)
|
|
113
|
+
else:
|
|
114
|
+
new_meta[key] = value
|
|
115
|
+
if not new_meta.get("type"):
|
|
116
|
+
raise StoreError("a concept needs a non-empty 'type'")
|
|
117
|
+
|
|
118
|
+
new_body = current_body
|
|
119
|
+
if body is not None:
|
|
120
|
+
new_body = replace_section(current_body, section, body) if section else body
|
|
121
|
+
if append:
|
|
122
|
+
new_body = new_body.rstrip() + "\n\n" + append.strip() + "\n"
|
|
123
|
+
|
|
124
|
+
decided_by = "caller"
|
|
125
|
+
if significant is None:
|
|
126
|
+
headline = any(new_meta.get(k) != current_meta.get(k) for k in ("type", "title", "description"))
|
|
127
|
+
if headline:
|
|
128
|
+
significant, decided_by = True, "rules"
|
|
129
|
+
else:
|
|
130
|
+
from .classify import RulesClassifier
|
|
131
|
+
|
|
132
|
+
decision = (classifier or RulesClassifier()).choose(
|
|
133
|
+
"edit_significance", ["minor", "significant"], {"before": current_body, "after": new_body})
|
|
134
|
+
significant, decided_by = decision.choice != "minor", decision.backend
|
|
135
|
+
|
|
136
|
+
if significant:
|
|
137
|
+
new_meta["generated"] = {"by": actor, "at": now()}
|
|
138
|
+
|
|
139
|
+
# Keep 'type' first for readability.
|
|
140
|
+
ordered = {"type": new_meta.pop("type"), **new_meta}
|
|
141
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
142
|
+
path.write_text(render_concept(ordered, new_body), encoding="utf-8")
|
|
143
|
+
rel = path.relative_to(root).as_posix()
|
|
144
|
+
return WriteResult(rel[:-3], rel, created, bool(significant), decided_by if not created else "new")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def verify(root: Path, cid: str, *, actor: str) -> WriteResult:
|
|
148
|
+
"""Append a verification event by ``actor`` at the current time."""
|
|
149
|
+
path = concept_path(root, cid)
|
|
150
|
+
if not path.exists():
|
|
151
|
+
raise StoreError(f"no such concept: {cid}")
|
|
152
|
+
meta, body = _read(path)
|
|
153
|
+
events = meta.get("verified")
|
|
154
|
+
if isinstance(events, dict):
|
|
155
|
+
events = [events]
|
|
156
|
+
events = list(events or [])
|
|
157
|
+
events.append({"by": actor, "at": now()})
|
|
158
|
+
meta["verified"] = events
|
|
159
|
+
path.write_text(render_concept(meta, body), encoding="utf-8")
|
|
160
|
+
rel = path.relative_to(root).as_posix()
|
|
161
|
+
return WriteResult(rel[:-3], rel, False, False)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: critic
|
|
3
|
+
description: Reviews a plan, design, change or claim critically against the project's recorded decisions, conventions and evidence. Use before committing to a plan, after a substantial change, or when a result seems too good.
|
|
4
|
+
tools: Read, Grep, Glob, mcp__rdstudio__search, mcp__rdstudio__outline, mcp__rdstudio__read
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a critic. Your job is to find what is wrong, missing or unjustified,
|
|
8
|
+
not to praise or rewrite.
|
|
9
|
+
|
|
10
|
+
Check the material you are given against:
|
|
11
|
+
1. Recorded decisions (search `type: Decision`). Flag any conflict, and whether
|
|
12
|
+
the decision's stated assumption still holds.
|
|
13
|
+
2. Recorded conventions and procedures.
|
|
14
|
+
3. Internal consistency: do the claims follow from the evidence shown?
|
|
15
|
+
4. Omissions: edge cases, untested paths, unstated assumptions.
|
|
16
|
+
|
|
17
|
+
Reply with a numbered list of issues, most serious first. For each: what is
|
|
18
|
+
wrong, why it matters, and the evidence (file:line or concept id). Separate
|
|
19
|
+
**blocking** issues from **worth considering**. If you find nothing
|
|
20
|
+
significant, say so in one line. Do not pad.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: librarian
|
|
3
|
+
description: Answers questions from the project knowledge base (OKF) without filling the main agent's context. Use for lookups that need several searches, or to check whether something is already decided, answered or documented.
|
|
4
|
+
tools: mcp__rdstudio__search, mcp__rdstudio__outline, mcp__rdstudio__read, mcp__rdstudio__list_concepts, mcp__rdstudio__backlinks
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are the librarian for this project's knowledge base. You find and report
|
|
8
|
+
what is recorded; you do not write to it and you do not speculate beyond it.
|
|
9
|
+
|
|
10
|
+
Method:
|
|
11
|
+
1. Search with several phrasings and filters (type, tags, directory).
|
|
12
|
+
2. Use outline before read, and read only the sections you need.
|
|
13
|
+
3. Follow links when the answer spans concepts.
|
|
14
|
+
|
|
15
|
+
Reply in at most ~200 words:
|
|
16
|
+
- **Answer**: what the knowledge base says, in plain terms.
|
|
17
|
+
- **Sources**: concept ids, each with its trust (human-reviewed,
|
|
18
|
+
machine-confirmed, unverified; note if changed since review).
|
|
19
|
+
- **Gaps**: what is not recorded, or where concepts disagree.
|
|
20
|
+
|
|
21
|
+
If nothing relevant is recorded, say so plainly. Never invent content.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: searcher
|
|
3
|
+
description: Finds and summarises external information (web pages, papers, documentation, PDFs) for a specific question. Use for literature or documentation lookups whose raw results would flood the main context.
|
|
4
|
+
tools: WebSearch, WebFetch, Read, Bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a research assistant. Find reliable sources that answer the question
|
|
8
|
+
you are given and summarise them compactly.
|
|
9
|
+
|
|
10
|
+
- Prefer primary sources: papers, official documentation, specifications.
|
|
11
|
+
- For PDFs available locally, extract only the relevant pages
|
|
12
|
+
(e.g. `pdftotext -f N -l M file.pdf -`).
|
|
13
|
+
- Record nothing in the knowledge base yourself; the main agent decides what to
|
|
14
|
+
keep.
|
|
15
|
+
|
|
16
|
+
Reply in at most ~300 words:
|
|
17
|
+
- **Findings**: bullet points, each with its source.
|
|
18
|
+
- **Sources**: full citation or URL for each, with a stable short id
|
|
19
|
+
(e.g. `tao2008circular`) the main agent can use in `sources`.
|
|
20
|
+
- **Confidence and gaps**: what is well supported, what is not, and what you
|
|
21
|
+
could not find.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!-- rdstudio:start (managed by rdstudio init; edits between these markers are replaced) -->
|
|
2
|
+
## Project knowledge (rdstudio)
|
|
3
|
+
|
|
4
|
+
This project keeps a knowledge base in `{knowledge}/`, in Open Knowledge Format
|
|
5
|
+
(markdown + YAML frontmatter). It records design, decisions, questions, tasks,
|
|
6
|
+
procedures, references and findings, authored jointly by the developer and
|
|
7
|
+
agents. A read-only dashboard shows it: `rdstudio serve`.
|
|
8
|
+
|
|
9
|
+
The `rdstudio` MCP server gives access to it, and the skills named below
|
|
10
|
+
(`search-okf`, `record-okf`, ...) describe how to use it; in Claude Code they
|
|
11
|
+
are also slash commands.
|
|
12
|
+
|
|
13
|
+
- **Start oriented.** If this session did not open with a knowledge-base brief,
|
|
14
|
+
call the rdstudio `brief` tool first.
|
|
15
|
+
- **Look before re-deriving.** Search the knowledge base (the `search-okf`
|
|
16
|
+
skill, or the `librarian` subagent) before reopening a question or
|
|
17
|
+
redesigning something. Recorded decisions stand unless their stated
|
|
18
|
+
assumption is shown false.
|
|
19
|
+
- **Record as you go.** Decisions (`decision`), questions (`question`),
|
|
20
|
+
findings and procedures (`record-okf`), tasks (`task`). Write through the
|
|
21
|
+
rdstudio MCP tools so provenance is stamped. Do not edit `index.md` files.
|
|
22
|
+
- **Report substantial work** as HTML in `{reports}/` (the `report` skill).
|
|
23
|
+
- **Close sessions** with the `handoff` skill.
|
|
24
|
+
- **Global knowledge:** if the developer has a cross-project knowledge base,
|
|
25
|
+
search it with `scope: "global"` (or `"all"`); suggest the `promote` skill
|
|
26
|
+
for knowledge that is not specific to this project.
|
|
27
|
+
- **Trust:** only the developer verifies concepts. Mark edits
|
|
28
|
+
`significant: false` only when trivial or dictated.
|
|
29
|
+
- The developer's actor id for provenance is `{human}`; the rdstudio tools
|
|
30
|
+
stamp yours.
|
|
31
|
+
<!-- rdstudio:end -->
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Overview
|
|
3
|
+
title: {title}
|
|
4
|
+
description: What this project is, in one sentence. (Placeholder; replace.)
|
|
5
|
+
status: draft
|
|
6
|
+
tags: [overview]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Purpose
|
|
10
|
+
|
|
11
|
+
What this project sets out to do, and why.
|
|
12
|
+
|
|
13
|
+
# Scope
|
|
14
|
+
|
|
15
|
+
What is in and out of scope.
|
|
16
|
+
|
|
17
|
+
# Where things are
|
|
18
|
+
|
|
19
|
+
The knowledge base is organised into directories. See the
|
|
20
|
+
[bootstrap task](/tasks/bootstrap-knowledge-base.md) for how to set them up.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Task
|
|
3
|
+
title: Bootstrap the knowledge base
|
|
4
|
+
description: Tailor this knowledge base to the project with the developer.
|
|
5
|
+
tags: [task, todo]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Prompt
|
|
9
|
+
|
|
10
|
+
This knowledge base was just created by `rdstudio init` and holds only
|
|
11
|
+
placeholders. Working with the developer, tailor it to this project.
|
|
12
|
+
|
|
13
|
+
1. **Learn the project.** Read the repository's README, CLAUDE.md or
|
|
14
|
+
AGENTS.md, existing docs and recent history. Ask the developer what the
|
|
15
|
+
project is for, who it is for, and what stage it is at.
|
|
16
|
+
2. **Propose a directory structure** suited to the project, and agree it with
|
|
17
|
+
the developer before creating anything. Common directories: `design/`,
|
|
18
|
+
`decisions/`, `questions/`, `tasks/`, `procedures/`, `references/`,
|
|
19
|
+
`research/`, `experiments/`. Add project-specific ones where they earn their
|
|
20
|
+
place (for example `thesis/` for a thesis project), and leave out ones that
|
|
21
|
+
do not.
|
|
22
|
+
3. **Write the overview** ([overview](/overview.md)): purpose, scope, and a map
|
|
23
|
+
of the directories. Give each directory an `overview.md` (`type: Overview`)
|
|
24
|
+
when it needs explanation.
|
|
25
|
+
4. **Seed from existing material.** Where the repository already holds design
|
|
26
|
+
notes, decision logs or references, record them as concepts. Material the
|
|
27
|
+
developer wrote is recorded with `generated.by` set to their actor id.
|
|
28
|
+
5. **Record procedures** the developer repeats (release steps, data
|
|
29
|
+
regeneration, bibliography updates) as `type: Procedure` concepts.
|
|
30
|
+
6. Write a short report (`/report`) summarising the structure and what was
|
|
31
|
+
seeded, and mark this task done.
|
|
32
|
+
|
|
33
|
+
# Plan
|
|
34
|
+
|
|
35
|
+
(To be written by the agent and approved by the developer.)
|
|
36
|
+
|
|
37
|
+
# Acceptance
|
|
38
|
+
|
|
39
|
+
- The developer has agreed the directory structure.
|
|
40
|
+
- The overview describes the project and maps the directories.
|
|
41
|
+
- `rdstudio check` passes.
|
|
42
|
+
|
|
43
|
+
# Outcome
|
|
44
|
+
|
|
45
|
+
Not started.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: decision
|
|
3
|
+
description: Record a decision with its assumption and the conditions for reopening it. Use when the developer settles a design or research choice, or asks to log a decision.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Record a decision
|
|
7
|
+
|
|
8
|
+
Decisions are recorded so they are not relitigated. Each one states what was
|
|
9
|
+
decided, the assumption it rests on, and what would justify reopening it. A
|
|
10
|
+
decision is reopened only when its assumption is shown false or the goal it
|
|
11
|
+
serves changes; an interesting alternative is not enough.
|
|
12
|
+
|
|
13
|
+
## Steps
|
|
14
|
+
|
|
15
|
+
1. Search `type: Decision` for an existing decision on the topic. If one
|
|
16
|
+
exists, update it (and say whether you are revising or reopening it).
|
|
17
|
+
2. Record at `decisions/<short-slug>` with the rdstudio `record` tool:
|
|
18
|
+
|
|
19
|
+
```markdown
|
|
20
|
+
---
|
|
21
|
+
type: Decision
|
|
22
|
+
title: <the decision, as a statement>
|
|
23
|
+
description: <one sentence>
|
|
24
|
+
tags: [<area>]
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# Decision
|
|
28
|
+
|
|
29
|
+
What was decided, concretely.
|
|
30
|
+
|
|
31
|
+
# Assumption
|
|
32
|
+
|
|
33
|
+
What must be true for this to be right, and the evidence for it.
|
|
34
|
+
|
|
35
|
+
# Alternatives considered
|
|
36
|
+
|
|
37
|
+
Briefly, and why they lost.
|
|
38
|
+
|
|
39
|
+
# Reopen if
|
|
40
|
+
|
|
41
|
+
The specific observations that would justify revisiting it.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
3. Link the decision from the concepts it governs (design, tasks) and link to
|
|
45
|
+
the evidence (references, experiments).
|
|
46
|
+
4. Decisions are the developer's. Record what they decided in their terms; if
|
|
47
|
+
you are proposing a decision, record it with `status: draft` and say so.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: handoff
|
|
3
|
+
description: Close out a working session so the next session (human or agent) can pick up without re-deriving anything. Use at the end of a session or before a long break.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Hand off
|
|
7
|
+
|
|
8
|
+
1. **Decisions** made this session: record each with the `decision` skill.
|
|
9
|
+
2. **Questions** raised and not answered: record with the `question` skill. Mark any
|
|
10
|
+
answered ones.
|
|
11
|
+
3. **Tasks**: update the `# Outcome` and state tag of every task touched.
|
|
12
|
+
4. **Findings and procedures**: record anything learned the hard way with
|
|
13
|
+
the `record-okf` skill. If a repeated process emerged, record or update a
|
|
14
|
+
`type: Procedure` concept.
|
|
15
|
+
5. **Report**: if substantial work was done, write one with the `report` skill.
|
|
16
|
+
6. Summarise to the developer in chat: what was recorded (concept ids), what is
|
|
17
|
+
waiting for their review (the rdstudio `review_queue` tool), and the suggested
|
|
18
|
+
next step.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ingest-ref
|
|
3
|
+
description: Add a paper or book to the bibliography (papis) and the knowledge base. Use when the developer names a paper, DOI, arXiv id or ISBN to add, or after syncing items captured in Zotero.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ingest a reference
|
|
7
|
+
|
|
8
|
+
Requires `[references] backend = "papis"` in `rdstudio.toml`. If a procedure
|
|
9
|
+
concept for adding references exists (search `type: Procedure`), follow it
|
|
10
|
+
with the rdstudio `procedure_next` tool instead of these generic steps; it holds
|
|
11
|
+
this project's conventions and known pitfalls.
|
|
12
|
+
|
|
13
|
+
1. **Check it is not already there:** search with the rdstudio `ref_search` tool.
|
|
14
|
+
2. **Add it to papis:** `papis add --from doi <doi>` (or `--from isbn`,
|
|
15
|
+
`--from arxiv`). Run non-interactively with `--batch` where the project's
|
|
16
|
+
procedure says so.
|
|
17
|
+
3. **Fix the citekey** to the project's convention if papis generated a
|
|
18
|
+
different one: `papis update --batch --set ref <key> <current-ref>`.
|
|
19
|
+
4. **Check the metadata:** authors split correctly into family and given names,
|
|
20
|
+
year, venue. Fix `author_list`, not the flat `author` field.
|
|
21
|
+
5. **Create the knowledge stub:** `rdstudio refs sync`.
|
|
22
|
+
6. **Summarise, if asked:** read only the pages you need with
|
|
23
|
+
the rdstudio `ref_text` tool (by page range or query), then fill the stub's
|
|
24
|
+
`# Summary` and `# Relevance` sections with the `record-okf` skill. Link the
|
|
25
|
+
decisions, questions or designs it bears on.
|
|
26
|
+
7. Regenerate any exported bibliography the project uses (for example
|
|
27
|
+
`make refs`).
|
|
28
|
+
|
|
29
|
+
## Items captured in Zotero
|
|
30
|
+
|
|
31
|
+
If the project pulls phone captures from Zotero (for example `make zotero-sync`),
|
|
32
|
+
run that first; the imported items then go through steps 3 to 6.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lint-okf
|
|
3
|
+
description: Check the knowledge base for OKF format errors and broken links, and fix what can be fixed. Use after bulk edits or when the dashboard's Review tab shows format errors.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Lint the knowledge base
|
|
7
|
+
|
|
8
|
+
1. Run `rdstudio check -w`.
|
|
9
|
+
2. Fix **errors** (missing frontmatter, missing `type`, frontmatter in a
|
|
10
|
+
non-root `index.md`) with the rdstudio `record` tool or by editing the file.
|
|
11
|
+
3. **Broken links** point at knowledge that does not exist yet. Do not delete
|
|
12
|
+
them by reflex: list them for the developer as candidate concepts, and fix
|
|
13
|
+
only the ones that are typos or moved files.
|
|
14
|
+
4. Run `rdstudio index` if indexes are out of date (the MCP server and the
|
|
15
|
+
dashboard server regenerate them automatically).
|