@proflandrigan/shards 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (397) hide show
  1. package/README.md +475 -0
  2. package/package.json +37 -0
  3. package/src/agents/academic.md +276 -0
  4. package/src/agents/ai-engineer.md +377 -0
  5. package/src/agents/analytics-engineer.md +364 -0
  6. package/src/agents/applied-ml-scientist.md +410 -0
  7. package/src/agents/backend-engineer.md +255 -0
  8. package/src/agents/bi-engineer.md +333 -0
  9. package/src/agents/data-analyst.md +343 -0
  10. package/src/agents/data-engineer.md +260 -0
  11. package/src/agents/data-modeller.md +386 -0
  12. package/src/agents/data-scientist.md +366 -0
  13. package/src/agents/deep-learning-engineer.md +389 -0
  14. package/src/agents/ml-engineer.md +424 -0
  15. package/src/agents/mlops-engineer.md +339 -0
  16. package/src/agents/researcher.md +187 -0
  17. package/src/agents/specific_instructions/academic/critical_review.md +263 -0
  18. package/src/agents/specific_instructions/academic/report.md +113 -0
  19. package/src/agents/specific_instructions/ai_engineer/advise.md +162 -0
  20. package/src/agents/specific_instructions/ai_engineer/bi_engineer_handoff.md +86 -0
  21. package/src/agents/specific_instructions/ai_engineer/experiment.md +471 -0
  22. package/src/agents/specific_instructions/ai_engineer/experiment_ui_mode.md +44 -0
  23. package/src/agents/specific_instructions/ai_engineer/phases/index.md +45 -0
  24. package/src/agents/specific_instructions/ai_engineer/phases/phase-1.md +55 -0
  25. package/src/agents/specific_instructions/ai_engineer/phases/phase-2.md +86 -0
  26. package/src/agents/specific_instructions/ai_engineer/phases/phase-3.md +96 -0
  27. package/src/agents/specific_instructions/ai_engineer/phases/phase-4.md +138 -0
  28. package/src/agents/specific_instructions/ai_engineer/phases/phase-5.md +157 -0
  29. package/src/agents/specific_instructions/ai_engineer/phases/phase-6.md +196 -0
  30. package/src/agents/specific_instructions/ai_engineer/phases/phase-7.md +313 -0
  31. package/src/agents/specific_instructions/ai_engineer/phases.md +1011 -0
  32. package/src/agents/specific_instructions/ai_engineer/prompt_lab.md +161 -0
  33. package/src/agents/specific_instructions/ai_engineer/prompt_lab_ui_mode.md +28 -0
  34. package/src/agents/specific_instructions/ai_engineer/research.md +393 -0
  35. package/src/agents/specific_instructions/ai_engineer/research_ui_mode.md +66 -0
  36. package/src/agents/specific_instructions/ai_engineer/review.md +159 -0
  37. package/src/agents/specific_instructions/ai_engineer/validation_checklist.md +182 -0
  38. package/src/agents/specific_instructions/analytics_engineer/advise.md +155 -0
  39. package/src/agents/specific_instructions/analytics_engineer/bi_engineer_handoff.md +91 -0
  40. package/src/agents/specific_instructions/analytics_engineer/data_analyst_handoff.md +84 -0
  41. package/src/agents/specific_instructions/analytics_engineer/deep_phases.md +818 -0
  42. package/src/agents/specific_instructions/analytics_engineer/phases_deep/index.md +24 -0
  43. package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-1.md +77 -0
  44. package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-2.md +106 -0
  45. package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-3.md +93 -0
  46. package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-4.md +79 -0
  47. package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-5.md +61 -0
  48. package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-6.md +45 -0
  49. package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-7.md +235 -0
  50. package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-8.md +221 -0
  51. package/src/agents/specific_instructions/analytics_engineer/phases_quick/index.md +19 -0
  52. package/src/agents/specific_instructions/analytics_engineer/phases_quick/phase-1.md +47 -0
  53. package/src/agents/specific_instructions/analytics_engineer/phases_quick/phase-2.md +78 -0
  54. package/src/agents/specific_instructions/analytics_engineer/quick_phases.md +112 -0
  55. package/src/agents/specific_instructions/analytics_engineer/review.md +167 -0
  56. package/src/agents/specific_instructions/analytics_engineer/service_mode.md +369 -0
  57. package/src/agents/specific_instructions/analytics_engineer/ui_mode.md +45 -0
  58. package/src/agents/specific_instructions/analytics_engineer/update.md +162 -0
  59. package/src/agents/specific_instructions/analytics_engineer/validation_checklist.md +121 -0
  60. package/src/agents/specific_instructions/applied_ml_scientist/advise.md +143 -0
  61. package/src/agents/specific_instructions/applied_ml_scientist/phases/index.md +21 -0
  62. package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-1.md +51 -0
  63. package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-2.md +66 -0
  64. package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-3.md +113 -0
  65. package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-4.md +104 -0
  66. package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-5.md +156 -0
  67. package/src/agents/specific_instructions/applied_ml_scientist/phases.md +428 -0
  68. package/src/agents/specific_instructions/applied_ml_scientist/research.md +379 -0
  69. package/src/agents/specific_instructions/applied_ml_scientist/review.md +142 -0
  70. package/src/agents/specific_instructions/applied_ml_scientist/validation_checklist.md +136 -0
  71. package/src/agents/specific_instructions/backend_engineer/clean.md +149 -0
  72. package/src/agents/specific_instructions/backend_engineer/review.md +91 -0
  73. package/src/agents/specific_instructions/backend_engineer/review_checklist.md +54 -0
  74. package/src/agents/specific_instructions/backend_engineer/service_mode.md +67 -0
  75. package/src/agents/specific_instructions/bi_engineer/advise.md +137 -0
  76. package/src/agents/specific_instructions/bi_engineer/data_analyst_handoff.md +77 -0
  77. package/src/agents/specific_instructions/bi_engineer/incoming_handoff.md +45 -0
  78. package/src/agents/specific_instructions/bi_engineer/phases/index.md +20 -0
  79. package/src/agents/specific_instructions/bi_engineer/phases/phase-1.md +164 -0
  80. package/src/agents/specific_instructions/bi_engineer/phases/phase-2.md +92 -0
  81. package/src/agents/specific_instructions/bi_engineer/phases/phase-3.md +121 -0
  82. package/src/agents/specific_instructions/bi_engineer/phases/phase-4.md +106 -0
  83. package/src/agents/specific_instructions/bi_engineer/phases.md +451 -0
  84. package/src/agents/specific_instructions/bi_engineer/review.md +166 -0
  85. package/src/agents/specific_instructions/bi_engineer/update.md +147 -0
  86. package/src/agents/specific_instructions/bi_engineer/validation_checklist.md +124 -0
  87. package/src/agents/specific_instructions/data_analyst/advise.md +138 -0
  88. package/src/agents/specific_instructions/data_analyst/explain.md +221 -0
  89. package/src/agents/specific_instructions/data_analyst/incoming_handoff.md +40 -0
  90. package/src/agents/specific_instructions/data_analyst/phases/index.md +20 -0
  91. package/src/agents/specific_instructions/data_analyst/phases/phase-1.md +159 -0
  92. package/src/agents/specific_instructions/data_analyst/phases/phase-2.md +112 -0
  93. package/src/agents/specific_instructions/data_analyst/phases/phase-3.md +265 -0
  94. package/src/agents/specific_instructions/data_analyst/phases/phase-4.md +100 -0
  95. package/src/agents/specific_instructions/data_analyst/phases.md +501 -0
  96. package/src/agents/specific_instructions/data_analyst/review.md +138 -0
  97. package/src/agents/specific_instructions/data_analyst/ui_mode.md +26 -0
  98. package/src/agents/specific_instructions/data_analyst/update.md +144 -0
  99. package/src/agents/specific_instructions/data_analyst/validation_checklist.md +95 -0
  100. package/src/agents/specific_instructions/data_engineer/advise.md +137 -0
  101. package/src/agents/specific_instructions/data_engineer/phases.md +466 -0
  102. package/src/agents/specific_instructions/data_engineer/phases_deep/index.md +23 -0
  103. package/src/agents/specific_instructions/data_engineer/phases_deep/phase-1.md +49 -0
  104. package/src/agents/specific_instructions/data_engineer/phases_deep/phase-2.md +93 -0
  105. package/src/agents/specific_instructions/data_engineer/phases_deep/phase-3.md +55 -0
  106. package/src/agents/specific_instructions/data_engineer/phases_deep/phase-4.md +48 -0
  107. package/src/agents/specific_instructions/data_engineer/phases_deep/phase-5.md +40 -0
  108. package/src/agents/specific_instructions/data_engineer/phases_deep/phase-6.md +102 -0
  109. package/src/agents/specific_instructions/data_engineer/phases_deep/phase-7.md +87 -0
  110. package/src/agents/specific_instructions/data_engineer/phases_quick/index.md +19 -0
  111. package/src/agents/specific_instructions/data_engineer/phases_quick/phase-1.md +45 -0
  112. package/src/agents/specific_instructions/data_engineer/phases_quick/phase-2.md +54 -0
  113. package/src/agents/specific_instructions/data_engineer/review.md +135 -0
  114. package/src/agents/specific_instructions/data_engineer/validation_checklist.md +136 -0
  115. package/src/agents/specific_instructions/data_modeller/advise.md +137 -0
  116. package/src/agents/specific_instructions/data_modeller/phases.md +581 -0
  117. package/src/agents/specific_instructions/data_modeller/phases_deep/index.md +23 -0
  118. package/src/agents/specific_instructions/data_modeller/phases_deep/phase-1.md +52 -0
  119. package/src/agents/specific_instructions/data_modeller/phases_deep/phase-2.md +113 -0
  120. package/src/agents/specific_instructions/data_modeller/phases_deep/phase-3.md +47 -0
  121. package/src/agents/specific_instructions/data_modeller/phases_deep/phase-4.md +51 -0
  122. package/src/agents/specific_instructions/data_modeller/phases_deep/phase-5.md +45 -0
  123. package/src/agents/specific_instructions/data_modeller/phases_deep/phase-6.md +105 -0
  124. package/src/agents/specific_instructions/data_modeller/phases_deep/phase-7.md +136 -0
  125. package/src/agents/specific_instructions/data_modeller/phases_quick/index.md +19 -0
  126. package/src/agents/specific_instructions/data_modeller/phases_quick/phase-1.md +47 -0
  127. package/src/agents/specific_instructions/data_modeller/phases_quick/phase-2.md +65 -0
  128. package/src/agents/specific_instructions/data_modeller/review.md +141 -0
  129. package/src/agents/specific_instructions/data_modeller/service_mode.md +218 -0
  130. package/src/agents/specific_instructions/data_modeller/validation_checklist.md +125 -0
  131. package/src/agents/specific_instructions/data_scientist/advise.md +158 -0
  132. package/src/agents/specific_instructions/data_scientist/bi_engineer_handoff.md +63 -0
  133. package/src/agents/specific_instructions/data_scientist/experiment.md +482 -0
  134. package/src/agents/specific_instructions/data_scientist/experiment_ui_mode.md +44 -0
  135. package/src/agents/specific_instructions/data_scientist/explain.md +247 -0
  136. package/src/agents/specific_instructions/data_scientist/greenfield_data.md +35 -0
  137. package/src/agents/specific_instructions/data_scientist/ml_engineer_handoff.md +52 -0
  138. package/src/agents/specific_instructions/data_scientist/notebook_walkthrough.md +76 -0
  139. package/src/agents/specific_instructions/data_scientist/phases/index.md +24 -0
  140. package/src/agents/specific_instructions/data_scientist/phases/phase-1.md +45 -0
  141. package/src/agents/specific_instructions/data_scientist/phases/phase-2.md +67 -0
  142. package/src/agents/specific_instructions/data_scientist/phases/phase-3.md +89 -0
  143. package/src/agents/specific_instructions/data_scientist/phases/phase-4.md +143 -0
  144. package/src/agents/specific_instructions/data_scientist/phases/phase-5.md +71 -0
  145. package/src/agents/specific_instructions/data_scientist/phases/phase-6.md +239 -0
  146. package/src/agents/specific_instructions/data_scientist/phases/phase-7.md +207 -0
  147. package/src/agents/specific_instructions/data_scientist/phases.md +651 -0
  148. package/src/agents/specific_instructions/data_scientist/research.md +345 -0
  149. package/src/agents/specific_instructions/data_scientist/research_ui_mode.md +52 -0
  150. package/src/agents/specific_instructions/data_scientist/review.md +136 -0
  151. package/src/agents/specific_instructions/data_scientist/service_mode.md +247 -0
  152. package/src/agents/specific_instructions/data_scientist/validation_checklist.md +183 -0
  153. package/src/agents/specific_instructions/deep_learning_engineer/advise.md +145 -0
  154. package/src/agents/specific_instructions/deep_learning_engineer/phases/index.md +21 -0
  155. package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-1.md +74 -0
  156. package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-2.md +98 -0
  157. package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-3.md +76 -0
  158. package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-4.md +128 -0
  159. package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-5.md +292 -0
  160. package/src/agents/specific_instructions/deep_learning_engineer/phases.md +567 -0
  161. package/src/agents/specific_instructions/deep_learning_engineer/research.md +389 -0
  162. package/src/agents/specific_instructions/deep_learning_engineer/review.md +155 -0
  163. package/src/agents/specific_instructions/deep_learning_engineer/validation_checklist.md +147 -0
  164. package/src/agents/specific_instructions/ml_engineer/advise.md +174 -0
  165. package/src/agents/specific_instructions/ml_engineer/bi_engineer_handoff.md +71 -0
  166. package/src/agents/specific_instructions/ml_engineer/experiment.md +474 -0
  167. package/src/agents/specific_instructions/ml_engineer/experiment_ui_mode.md +44 -0
  168. package/src/agents/specific_instructions/ml_engineer/notebook_walkthrough.md +75 -0
  169. package/src/agents/specific_instructions/ml_engineer/phases/index.md +25 -0
  170. package/src/agents/specific_instructions/ml_engineer/phases/phase-1.md +49 -0
  171. package/src/agents/specific_instructions/ml_engineer/phases/phase-2.md +75 -0
  172. package/src/agents/specific_instructions/ml_engineer/phases/phase-3.md +124 -0
  173. package/src/agents/specific_instructions/ml_engineer/phases/phase-4.md +279 -0
  174. package/src/agents/specific_instructions/ml_engineer/phases/phase-5.md +160 -0
  175. package/src/agents/specific_instructions/ml_engineer/phases/phase-6-5.md +170 -0
  176. package/src/agents/specific_instructions/ml_engineer/phases/phase-6.md +295 -0
  177. package/src/agents/specific_instructions/ml_engineer/phases/phase-7.md +337 -0
  178. package/src/agents/specific_instructions/ml_engineer/phases.md +1068 -0
  179. package/src/agents/specific_instructions/ml_engineer/research.md +437 -0
  180. package/src/agents/specific_instructions/ml_engineer/research_ui_mode.md +71 -0
  181. package/src/agents/specific_instructions/ml_engineer/review.md +187 -0
  182. package/src/agents/specific_instructions/ml_engineer/service_mode.md +273 -0
  183. package/src/agents/specific_instructions/ml_engineer/validation_checklist.md +185 -0
  184. package/src/agents/specific_instructions/mlops_engineer/advise.md +139 -0
  185. package/src/agents/specific_instructions/mlops_engineer/phases/index.md +23 -0
  186. package/src/agents/specific_instructions/mlops_engineer/phases/phase-1.md +52 -0
  187. package/src/agents/specific_instructions/mlops_engineer/phases/phase-2.md +86 -0
  188. package/src/agents/specific_instructions/mlops_engineer/phases/phase-3.md +105 -0
  189. package/src/agents/specific_instructions/mlops_engineer/phases/phase-4.md +128 -0
  190. package/src/agents/specific_instructions/mlops_engineer/phases/phase-5.md +106 -0
  191. package/src/agents/specific_instructions/mlops_engineer/phases/phase-6.md +128 -0
  192. package/src/agents/specific_instructions/mlops_engineer/phases/phase-7.md +144 -0
  193. package/src/agents/specific_instructions/mlops_engineer/phases.md +671 -0
  194. package/src/agents/specific_instructions/mlops_engineer/review.md +164 -0
  195. package/src/agents/specific_instructions/mlops_engineer/service_mode.md +81 -0
  196. package/src/agents/specific_instructions/mlops_engineer/validation_checklist.md +151 -0
  197. package/src/agents/specific_instructions/researcher/critical_review.md +292 -0
  198. package/src/agents/specific_instructions/researcher/review_checklist.md +67 -0
  199. package/src/agents/specific_instructions/researcher/service_mode.md +224 -0
  200. package/src/agents/specific_instructions/shared/auto_verify_mode.md +141 -0
  201. package/src/agents/specific_instructions/shared/autonomous_research.md +1289 -0
  202. package/src/agents/specific_instructions/shared/behavioral_rules.md +36 -0
  203. package/src/agents/specific_instructions/shared/diverge_protocol.md +387 -0
  204. package/src/agents/specific_instructions/shared/engineering_guidelines.md +136 -0
  205. package/src/agents/specific_instructions/shared/experiment_versioning.md +184 -0
  206. package/src/agents/specific_instructions/shared/goal_mode.md +187 -0
  207. package/src/agents/specific_instructions/shared/incremental_testing.md +139 -0
  208. package/src/agents/specific_instructions/shared/intent_discovery.md +223 -0
  209. package/src/agents/specific_instructions/shared/join_path_protocol.md +168 -0
  210. package/src/agents/specific_instructions/shared/knowledge_checkpoint.md +83 -0
  211. package/src/agents/specific_instructions/shared/knowledge_harvest.md +220 -0
  212. package/src/agents/specific_instructions/shared/knowledge_retrieval.md +100 -0
  213. package/src/agents/specific_instructions/shared/notebook_walkthrough_protocol.md +367 -0
  214. package/src/agents/specific_instructions/shared/reviewer_verdict_protocol.md +74 -0
  215. package/src/agents/specific_instructions/shared/swarm_protocol.md +97 -0
  216. package/src/agents/specific_instructions/shared/validation_protocol.md +139 -0
  217. package/src/agents/specific_instructions/syn/arbiter.md +140 -0
  218. package/src/agents/specific_instructions/syn/brainstorm.md +550 -0
  219. package/src/agents/specific_instructions/syn/code_review.md +232 -0
  220. package/src/agents/specific_instructions/syn/diff.md +239 -0
  221. package/src/agents/specific_instructions/syn/final_review.md +65 -0
  222. package/src/agents/specific_instructions/syn/fixer.md +240 -0
  223. package/src/agents/specific_instructions/syn/free_form.md +130 -0
  224. package/src/agents/specific_instructions/syn/knowledge.md +468 -0
  225. package/src/agents/specific_instructions/syn/notebook_walkthrough.md +78 -0
  226. package/src/agents/specific_instructions/syn/panel_review.md +634 -0
  227. package/src/agents/specific_instructions/syn/pm.md +453 -0
  228. package/src/agents/specific_instructions/syn/pr_review.md +255 -0
  229. package/src/agents/specific_instructions/syn/slides.md +417 -0
  230. package/src/agents/syn.md +729 -0
  231. package/src/commands/academic.md +41 -0
  232. package/src/commands/ai-engineer.md +45 -0
  233. package/src/commands/analytics-engineer.md +48 -0
  234. package/src/commands/applied-ml-scientist.md +45 -0
  235. package/src/commands/backend-engineer.md +35 -0
  236. package/src/commands/bi-engineer.md +40 -0
  237. package/src/commands/brainstorm.md +24 -0
  238. package/src/commands/data-analyst.md +38 -0
  239. package/src/commands/data-engineer.md +37 -0
  240. package/src/commands/data-modeller.md +38 -0
  241. package/src/commands/data-scientist.md +38 -0
  242. package/src/commands/deep-learning-engineer.md +47 -0
  243. package/src/commands/end.md +49 -0
  244. package/src/commands/knowledge.md +24 -0
  245. package/src/commands/ml-engineer.md +42 -0
  246. package/src/commands/mlops-engineer.md +47 -0
  247. package/src/commands/notebook-walkthrough.md +58 -0
  248. package/src/commands/researcher.md +40 -0
  249. package/src/commands/resume.md +57 -0
  250. package/src/commands/review-pr.md +26 -0
  251. package/src/commands/shards-guide.md +41 -0
  252. package/src/commands/shards-ui.md +32 -0
  253. package/src/commands/shards.md +41 -0
  254. package/src/docs/01-getting-started/concepts.md +109 -0
  255. package/src/docs/01-getting-started/first-session.md +79 -0
  256. package/src/docs/01-getting-started/install.md +61 -0
  257. package/src/docs/02-agents/academic.md +71 -0
  258. package/src/docs/02-agents/ai-engineer.md +78 -0
  259. package/src/docs/02-agents/analytics-engineer.md +58 -0
  260. package/src/docs/02-agents/applied-ml-scientist.md +59 -0
  261. package/src/docs/02-agents/backend-engineer.md +58 -0
  262. package/src/docs/02-agents/bi-engineer.md +65 -0
  263. package/src/docs/02-agents/data-analyst.md +67 -0
  264. package/src/docs/02-agents/data-engineer.md +57 -0
  265. package/src/docs/02-agents/data-modeller.md +51 -0
  266. package/src/docs/02-agents/data-scientist.md +78 -0
  267. package/src/docs/02-agents/deep-learning-engineer.md +64 -0
  268. package/src/docs/02-agents/ml-engineer.md +80 -0
  269. package/src/docs/02-agents/mlops-engineer.md +59 -0
  270. package/src/docs/02-agents/overview.md +62 -0
  271. package/src/docs/02-agents/researcher.md +73 -0
  272. package/src/docs/02-agents/syn.md +88 -0
  273. package/src/docs/03-protocols/auto-verify.md +82 -0
  274. package/src/docs/03-protocols/autonomous-research.md +59 -0
  275. package/src/docs/03-protocols/behavioral-rules.md +35 -0
  276. package/src/docs/03-protocols/diverge.md +50 -0
  277. package/src/docs/03-protocols/engineering-guidelines.md +56 -0
  278. package/src/docs/03-protocols/experiment-versioning.md +38 -0
  279. package/src/docs/03-protocols/gate-pattern.md +65 -0
  280. package/src/docs/03-protocols/incremental-testing.md +68 -0
  281. package/src/docs/03-protocols/join-path.md +46 -0
  282. package/src/docs/03-protocols/knowledge-ledger.md +70 -0
  283. package/src/docs/03-protocols/reviewer-verdicts.md +39 -0
  284. package/src/docs/03-protocols/swarm.md +40 -0
  285. package/src/docs/03-protocols/validation.md +174 -0
  286. package/src/docs/04-ui/activity-bar.md +70 -0
  287. package/src/docs/04-ui/chat-pane.md +80 -0
  288. package/src/docs/04-ui/code-intel.md +62 -0
  289. package/src/docs/04-ui/file-editing.md +61 -0
  290. package/src/docs/04-ui/git.md +54 -0
  291. package/src/docs/04-ui/keybindings.md +79 -0
  292. package/src/docs/04-ui/knowledge-map.md +76 -0
  293. package/src/docs/04-ui/overview.md +93 -0
  294. package/src/docs/04-ui/panels.md +49 -0
  295. package/src/docs/04-ui/pinboard-selection.md +66 -0
  296. package/src/docs/04-ui/quick-open-palette.md +56 -0
  297. package/src/docs/04-ui/sessions.md +81 -0
  298. package/src/docs/04-ui/settings-permissions.md +56 -0
  299. package/src/docs/05-commands/reference.md +59 -0
  300. package/src/docs/06-outputs/directory-map.md +116 -0
  301. package/src/docs/07-workflows/ai-eval-first.md +57 -0
  302. package/src/docs/07-workflows/deep-study-to-production.md +76 -0
  303. package/src/docs/07-workflows/diverge-exploration.md +77 -0
  304. package/src/docs/07-workflows/quick-analysis.md +45 -0
  305. package/src/docs/08-integrations/claude-code-auto-mode.md +191 -0
  306. package/src/docs/08-integrations/google-slides.md +175 -0
  307. package/src/docs/README.md +30 -0
  308. package/src/docs/manifest.json +108 -0
  309. package/src/templates/analysis-template.md +20 -0
  310. package/src/templates/branch-report.md +46 -0
  311. package/src/templates/diff-report.md +88 -0
  312. package/src/templates/knowledge-index.md +7 -0
  313. package/src/templates/model-card-schema.json +186 -0
  314. package/src/templates/model-card-schema.md +88 -0
  315. package/src/templates/model-card.md +124 -0
  316. package/src/templates/project-plan.md +47 -0
  317. package/src/templates/project-specs.md +81 -0
  318. package/src/templates/report-template.md +43 -0
  319. package/src/templates/study-template.md +25 -0
  320. package/src/ui/cc-readonly.js +181 -0
  321. package/src/ui/chat-session.js +466 -0
  322. package/src/ui/css/base.css +136 -0
  323. package/src/ui/css/brainstorm.css +525 -0
  324. package/src/ui/css/chat.css +1405 -0
  325. package/src/ui/css/editor.css +546 -0
  326. package/src/ui/css/eval-dashboard.css +157 -0
  327. package/src/ui/css/experiment.css +237 -0
  328. package/src/ui/css/guide.css +186 -0
  329. package/src/ui/css/knowledge-map.css +383 -0
  330. package/src/ui/css/layout.css +431 -0
  331. package/src/ui/css/model-card.css +161 -0
  332. package/src/ui/css/notebook-walkthrough.css +271 -0
  333. package/src/ui/css/pr-review.css +403 -0
  334. package/src/ui/css/prompt-lab.css +325 -0
  335. package/src/ui/css/sessions.css +258 -0
  336. package/src/ui/css/sidebar.css +661 -0
  337. package/src/ui/css/terminal.css +113 -0
  338. package/src/ui/css/theme-light.css +542 -0
  339. package/src/ui/index.html +389 -0
  340. package/src/ui/js/agents.js +32 -0
  341. package/src/ui/js/bookmarks.js +230 -0
  342. package/src/ui/js/chat.js +1776 -0
  343. package/src/ui/js/code-intel.js +328 -0
  344. package/src/ui/js/command-palette.js +142 -0
  345. package/src/ui/js/events.js +591 -0
  346. package/src/ui/js/explorer.js +317 -0
  347. package/src/ui/js/file-view.js +477 -0
  348. package/src/ui/js/git.js +536 -0
  349. package/src/ui/js/guide.js +198 -0
  350. package/src/ui/js/hud.js +75 -0
  351. package/src/ui/js/init.js +351 -0
  352. package/src/ui/js/knowledge-map.js +906 -0
  353. package/src/ui/js/markdown.js +114 -0
  354. package/src/ui/js/monaco.js +164 -0
  355. package/src/ui/js/notebook-walkthrough.js +272 -0
  356. package/src/ui/js/notebook.js +448 -0
  357. package/src/ui/js/panels.js +2681 -0
  358. package/src/ui/js/pinboard.js +186 -0
  359. package/src/ui/js/quick-open.js +164 -0
  360. package/src/ui/js/selection-context.js +131 -0
  361. package/src/ui/js/sessions.js +256 -0
  362. package/src/ui/js/settings.js +476 -0
  363. package/src/ui/js/split-view.js +82 -0
  364. package/src/ui/js/state.js +343 -0
  365. package/src/ui/js/table.js +161 -0
  366. package/src/ui/js/tabs.js +284 -0
  367. package/src/ui/js/tabular.js +125 -0
  368. package/src/ui/js/terminal.js +354 -0
  369. package/src/ui/js/timeline.js +137 -0
  370. package/src/ui/js/utils.js +293 -0
  371. package/src/ui/notebook-kernel.py +790 -0
  372. package/src/ui/open-browser.js +55 -0
  373. package/src/ui/permission-pattern.js +42 -0
  374. package/src/ui/relay.js +513 -0
  375. package/src/ui/server.js +3072 -0
  376. package/src/ui/session-index.js +225 -0
  377. package/src/ui/shards_icon.png +0 -0
  378. package/src/ui/spawn-server.js +41 -0
  379. package/src/ui/symbol-index.js +813 -0
  380. package/src/ui/ui-push.js +177 -0
  381. package/tools/gate-hook/VALIDATION_SPEC.md +273 -0
  382. package/tools/gate-hook/__tests__/auto-verify.test.js +343 -0
  383. package/tools/gate-hook/auto-allowlist.js +179 -0
  384. package/tools/gate-hook/auto-state.js +68 -0
  385. package/tools/gate-hook/classify.js +21 -0
  386. package/tools/gate-hook/log.js +57 -0
  387. package/tools/gate-hook/parser.js +205 -0
  388. package/tools/gate-hook/sql-guard.js +230 -0
  389. package/tools/gate-hook/state.js +170 -0
  390. package/tools/gate-hook/sweep.js +139 -0
  391. package/tools/gate-hook/transcript.js +45 -0
  392. package/tools/gate-hook/validation.js +321 -0
  393. package/tools/gate-hook.js +475 -0
  394. package/tools/install.js +914 -0
  395. package/tools/shards-gates.js +311 -0
  396. package/tools/shards-sessions.js +261 -0
  397. package/tools/shards-ui.js +377 -0
@@ -0,0 +1,790 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ notebook-kernel.py — agent-driven Jupyter kernel helper for the Notebook
4
+ Walkthrough mode.
5
+
6
+ Subcommands:
7
+ start <session_id> <notebook_path> spawn a kernel, persist its connection
8
+ file under .shards/notebooks/<session>/,
9
+ create the walkthrough state JSON.
10
+ exec <session_id> <cell_index> execute a single cell against the live
11
+ kernel; stream outputs into the
12
+ walkthrough state JSON and the .ipynb
13
+ file on disk.
14
+ restart <session_id> restart the kernel; mark all previously
15
+ executed cells stale.
16
+ run-all <session_id> restart the kernel, then execute every
17
+ code cell top-to-bottom on the fresh
18
+ kernel (the "Restart & Run All"
19
+ reproducibility check); stop at the
20
+ first failing cell.
21
+ stop <session_id> shut the kernel down and clean up.
22
+ status <session_id> print "alive" / "dead" / "missing".
23
+
24
+ The agent calls this via Bash. The helper is the only piece that touches the
25
+ Jupyter kernel — agents never speak to jupyter_client directly. Each subcommand
26
+ is a one-shot process that connects to the persistent kernel via the saved
27
+ connection file.
28
+
29
+ State layout (per walkthrough session):
30
+
31
+ .shards/notebooks/<session_id>/
32
+ kernel.json jupyter_client connection file
33
+ kernel.pid PID of the kernel process
34
+ notebook.path absolute path of the .ipynb under walkthrough
35
+ state.json walkthrough state (mirrored to <project>/.shards/
36
+ notebook-walkthrough.json by the agent for the UI)
37
+
38
+ Output: every subcommand prints a single JSON object on stdout for the agent
39
+ to parse. Errors are reported with {"ok": false, "error": "..."} — exit code
40
+ is still 0 unless the helper itself can't run (missing python deps).
41
+ """
42
+
43
+ from __future__ import annotations
44
+
45
+ import argparse
46
+ import datetime as _dt
47
+ import json
48
+ import os
49
+ import signal
50
+ import subprocess
51
+ import sys
52
+ import time
53
+ import uuid
54
+ from pathlib import Path
55
+
56
+
57
+ # ────────────────────────────────────────────────────────────────────────────
58
+ # Paths
59
+ # ────────────────────────────────────────────────────────────────────────────
60
+
61
+
62
+ def project_root() -> Path:
63
+ """Walk up from cwd until .shards/ is found, else fall back to cwd."""
64
+ cwd = Path.cwd().resolve()
65
+ for d in [cwd, *cwd.parents]:
66
+ if (d / ".shards").is_dir():
67
+ return d
68
+ return cwd
69
+
70
+
71
+ def session_dir(session_id: str) -> Path:
72
+ root = project_root() / ".shards" / "notebooks" / session_id
73
+ root.mkdir(parents=True, exist_ok=True)
74
+ return root
75
+
76
+
77
+ def conn_file(session_id: str) -> Path:
78
+ return session_dir(session_id) / "kernel.json"
79
+
80
+
81
+ def pid_file(session_id: str) -> Path:
82
+ return session_dir(session_id) / "kernel.pid"
83
+
84
+
85
+ def notebook_path_file(session_id: str) -> Path:
86
+ return session_dir(session_id) / "notebook.path"
87
+
88
+
89
+ def state_file(session_id: str) -> Path:
90
+ return session_dir(session_id) / "state.json"
91
+
92
+
93
+ # ────────────────────────────────────────────────────────────────────────────
94
+ # Output helpers
95
+ # ────────────────────────────────────────────────────────────────────────────
96
+
97
+
98
+ def emit(obj: dict) -> None:
99
+ sys.stdout.write(json.dumps(obj, ensure_ascii=False))
100
+ sys.stdout.write("\n")
101
+ sys.stdout.flush()
102
+
103
+
104
+ def fail(msg: str, **extra) -> None:
105
+ payload = {"ok": False, "error": msg}
106
+ payload.update(extra)
107
+ emit(payload)
108
+
109
+
110
+ def now_iso() -> str:
111
+ return _dt.datetime.utcnow().replace(microsecond=0).isoformat() + "Z"
112
+
113
+
114
+ # ────────────────────────────────────────────────────────────────────────────
115
+ # Notebook on-disk I/O
116
+ # ────────────────────────────────────────────────────────────────────────────
117
+
118
+
119
+ def read_notebook(nb_path: Path) -> dict:
120
+ return json.loads(nb_path.read_text(encoding="utf-8"))
121
+
122
+
123
+ def write_notebook(nb_path: Path, nb: dict) -> None:
124
+ nb_path.write_text(json.dumps(nb, indent=1, ensure_ascii=False), encoding="utf-8")
125
+
126
+
127
+ def cell_source(cell: dict) -> str:
128
+ src = cell.get("source", "")
129
+ return "".join(src) if isinstance(src, list) else str(src)
130
+
131
+
132
+ def cells_summary(nb: dict) -> list:
133
+ cells = nb.get("cells", []) or []
134
+ out = []
135
+ for i, c in enumerate(cells):
136
+ out.append(
137
+ {
138
+ "index": i,
139
+ "type": c.get("cell_type", "code"),
140
+ "executed": c.get("execution_count") is not None,
141
+ "stale": False,
142
+ "explained": False,
143
+ "lastRunAt": None,
144
+ "explanation": None,
145
+ "outputSummary": None,
146
+ }
147
+ )
148
+ return out
149
+
150
+
151
+ # ────────────────────────────────────────────────────────────────────────────
152
+ # Walkthrough state
153
+ # ────────────────────────────────────────────────────────────────────────────
154
+
155
+
156
+ def load_state(session_id: str) -> dict:
157
+ f = state_file(session_id)
158
+ if not f.exists():
159
+ return {}
160
+ try:
161
+ return json.loads(f.read_text(encoding="utf-8"))
162
+ except Exception:
163
+ return {}
164
+
165
+
166
+ def save_state(session_id: str, state: dict) -> None:
167
+ f = state_file(session_id)
168
+ f.parent.mkdir(parents=True, exist_ok=True)
169
+ f.write_text(json.dumps(state, indent=2, ensure_ascii=False), encoding="utf-8")
170
+
171
+
172
+ # ────────────────────────────────────────────────────────────────────────────
173
+ # Kernel lifecycle
174
+ # ────────────────────────────────────────────────────────────────────────────
175
+
176
+
177
+ def is_pid_alive(pid: int) -> bool:
178
+ try:
179
+ os.kill(pid, 0)
180
+ except OSError:
181
+ return False
182
+ return True
183
+
184
+
185
+ def kernel_alive(session_id: str) -> bool:
186
+ pf = pid_file(session_id)
187
+ if not pf.exists():
188
+ return False
189
+ try:
190
+ pid = int(pf.read_text().strip())
191
+ except Exception:
192
+ return False
193
+ return is_pid_alive(pid)
194
+
195
+
196
+ def spawn_kernel(session_id: str) -> int:
197
+ """Spawn a new ipykernel process bound to a connection file under the
198
+ session dir. Returns the kernel PID. Does not block on startup — the
199
+ first `exec` call waits for the kernel to be ready."""
200
+ cf = conn_file(session_id)
201
+ if cf.exists():
202
+ try:
203
+ cf.unlink()
204
+ except Exception:
205
+ pass
206
+
207
+ cmd = [
208
+ sys.executable,
209
+ "-m",
210
+ "ipykernel_launcher",
211
+ "-f",
212
+ str(cf),
213
+ ]
214
+ proc = subprocess.Popen(
215
+ cmd,
216
+ stdout=subprocess.DEVNULL,
217
+ stderr=subprocess.DEVNULL,
218
+ cwd=str(project_root()),
219
+ start_new_session=True,
220
+ )
221
+ pid_file(session_id).write_text(str(proc.pid))
222
+
223
+ # Wait up to ~10s for the connection file to appear.
224
+ for _ in range(100):
225
+ if cf.exists():
226
+ break
227
+ time.sleep(0.1)
228
+ else:
229
+ try:
230
+ proc.terminate()
231
+ except Exception:
232
+ pass
233
+ raise RuntimeError("kernel failed to write connection file within 10s")
234
+
235
+ return proc.pid
236
+
237
+
238
+ def stop_kernel(session_id: str) -> bool:
239
+ pf = pid_file(session_id)
240
+ if not pf.exists():
241
+ return False
242
+ try:
243
+ pid = int(pf.read_text().strip())
244
+ except Exception:
245
+ return False
246
+ if is_pid_alive(pid):
247
+ try:
248
+ os.kill(pid, signal.SIGTERM)
249
+ except Exception:
250
+ pass
251
+ # Give it a moment, then SIGKILL if still alive.
252
+ for _ in range(20):
253
+ if not is_pid_alive(pid):
254
+ break
255
+ time.sleep(0.1)
256
+ if is_pid_alive(pid):
257
+ try:
258
+ os.kill(pid, signal.SIGKILL)
259
+ except Exception:
260
+ pass
261
+ try:
262
+ pf.unlink()
263
+ except Exception:
264
+ pass
265
+ cf = conn_file(session_id)
266
+ if cf.exists():
267
+ try:
268
+ cf.unlink()
269
+ except Exception:
270
+ pass
271
+ return True
272
+
273
+
274
+ def kernel_client(session_id: str):
275
+ """Return a connected BlockingKernelClient for this session. The kernel
276
+ must already be running."""
277
+ from jupyter_client import BlockingKernelClient # type: ignore
278
+
279
+ cf = conn_file(session_id)
280
+ if not cf.exists():
281
+ raise RuntimeError(f"no connection file at {cf}")
282
+ kc = BlockingKernelClient()
283
+ kc.load_connection_file(str(cf))
284
+ kc.start_channels()
285
+ # Wait for kernel to be alive — first start may need a moment.
286
+ for _ in range(100):
287
+ try:
288
+ kc.kernel_info(reply=True, timeout=0.5)
289
+ break
290
+ except Exception:
291
+ time.sleep(0.1)
292
+ return kc
293
+
294
+
295
+ # ────────────────────────────────────────────────────────────────────────────
296
+ # Output capture
297
+ # ────────────────────────────────────────────────────────────────────────────
298
+
299
+
300
+ def capture_outputs(kc, msg_id: str, timeout_s: float = 120.0) -> tuple[list, str, int | None]:
301
+ """Drain iopub until the kernel reports idle for our msg_id. Returns
302
+ (outputs_in_jupyter_format, status, execution_count)."""
303
+ outputs: list = []
304
+ status = "ok"
305
+ exec_count: int | None = None
306
+ deadline = time.time() + timeout_s
307
+
308
+ while True:
309
+ if time.time() > deadline:
310
+ status = "timeout"
311
+ break
312
+ try:
313
+ msg = kc.get_iopub_msg(timeout=1.0)
314
+ except Exception:
315
+ continue
316
+
317
+ parent = (msg.get("parent_header") or {}).get("msg_id")
318
+ if parent != msg_id:
319
+ continue
320
+
321
+ msg_type = msg.get("msg_type") or msg.get("header", {}).get("msg_type")
322
+ content = msg.get("content") or {}
323
+
324
+ if msg_type == "stream":
325
+ outputs.append(
326
+ {
327
+ "output_type": "stream",
328
+ "name": content.get("name", "stdout"),
329
+ "text": content.get("text", ""),
330
+ }
331
+ )
332
+ elif msg_type == "execute_result":
333
+ exec_count = content.get("execution_count", exec_count)
334
+ outputs.append(
335
+ {
336
+ "output_type": "execute_result",
337
+ "execution_count": content.get("execution_count"),
338
+ "data": content.get("data", {}),
339
+ "metadata": content.get("metadata", {}),
340
+ }
341
+ )
342
+ elif msg_type == "display_data":
343
+ outputs.append(
344
+ {
345
+ "output_type": "display_data",
346
+ "data": content.get("data", {}),
347
+ "metadata": content.get("metadata", {}),
348
+ }
349
+ )
350
+ elif msg_type == "error":
351
+ status = "error"
352
+ outputs.append(
353
+ {
354
+ "output_type": "error",
355
+ "ename": content.get("ename", ""),
356
+ "evalue": content.get("evalue", ""),
357
+ "traceback": content.get("traceback", []),
358
+ }
359
+ )
360
+ elif msg_type == "execute_input":
361
+ exec_count = content.get("execution_count", exec_count)
362
+ elif msg_type == "status":
363
+ if content.get("execution_state") == "idle":
364
+ break
365
+
366
+ return outputs, status, exec_count
367
+
368
+
369
+ def short_summary(outputs: list, max_chars: int = 200) -> str:
370
+ """Compact text summary of cell outputs for the walkthrough state."""
371
+ parts: list[str] = []
372
+ for o in outputs:
373
+ t = o.get("output_type")
374
+ if t == "stream":
375
+ txt = o.get("text", "") or ""
376
+ if isinstance(txt, list):
377
+ txt = "".join(txt)
378
+ parts.append(txt.strip())
379
+ elif t == "execute_result" or t == "display_data":
380
+ data = o.get("data", {}) or {}
381
+ if "text/plain" in data:
382
+ v = data["text/plain"]
383
+ if isinstance(v, list):
384
+ v = "".join(v)
385
+ parts.append(str(v).strip())
386
+ elif "image/png" in data:
387
+ parts.append("[image/png]")
388
+ elif "image/svg+xml" in data:
389
+ parts.append("[image/svg]")
390
+ elif "text/html" in data:
391
+ parts.append("[text/html]")
392
+ elif t == "error":
393
+ parts.append(f"{o.get('ename','Error')}: {o.get('evalue','')}".strip())
394
+ joined = " ".join(p for p in parts if p)
395
+ if len(joined) > max_chars:
396
+ joined = joined[: max_chars - 1] + "…"
397
+ return joined
398
+
399
+
400
+ # ────────────────────────────────────────────────────────────────────────────
401
+ # Subcommand: start
402
+ # ────────────────────────────────────────────────────────────────────────────
403
+
404
+
405
+ def cmd_start(session_id: str, nb_path_str: str) -> None:
406
+ nb_path = Path(nb_path_str).expanduser().resolve()
407
+ if not nb_path.exists() or nb_path.suffix != ".ipynb":
408
+ fail(f"notebook not found or not .ipynb: {nb_path}")
409
+ return
410
+
411
+ # Tear down any prior session under this id so start is idempotent.
412
+ if kernel_alive(session_id):
413
+ stop_kernel(session_id)
414
+
415
+ try:
416
+ pid = spawn_kernel(session_id)
417
+ except Exception as e:
418
+ fail(f"failed to spawn kernel: {e}")
419
+ return
420
+
421
+ notebook_path_file(session_id).write_text(str(nb_path))
422
+
423
+ nb = read_notebook(nb_path)
424
+ state = {
425
+ "sessionId": session_id,
426
+ "notebookPath": str(nb_path),
427
+ "kernelPid": pid,
428
+ "kernelStartedAt": now_iso(),
429
+ "currentCellIndex": 0,
430
+ "status": "ready",
431
+ "cells": cells_summary(nb),
432
+ "transcript": [],
433
+ }
434
+ save_state(session_id, state)
435
+
436
+ emit(
437
+ {
438
+ "ok": True,
439
+ "sessionId": session_id,
440
+ "kernelPid": pid,
441
+ "notebookPath": str(nb_path),
442
+ "cellCount": len(nb.get("cells", []) or []),
443
+ "stateFile": str(state_file(session_id)),
444
+ "connectionFile": str(conn_file(session_id)),
445
+ }
446
+ )
447
+
448
+
449
+ # ────────────────────────────────────────────────────────────────────────────
450
+ # Subcommand: exec
451
+ # ────────────────────────────────────────────────────────────────────────────
452
+
453
+
454
+ def cmd_exec(session_id: str, cell_index: int) -> None:
455
+ nb_pf = notebook_path_file(session_id)
456
+ if not nb_pf.exists():
457
+ fail("session not started — run `start` first")
458
+ return
459
+ nb_path = Path(nb_pf.read_text().strip())
460
+ if not nb_path.exists():
461
+ fail(f"notebook missing on disk: {nb_path}")
462
+ return
463
+
464
+ if not kernel_alive(session_id):
465
+ fail("kernel not alive — run `restart` or `start`")
466
+ return
467
+
468
+ nb = read_notebook(nb_path)
469
+ cells = nb.get("cells", []) or []
470
+ if cell_index < 0 or cell_index >= len(cells):
471
+ fail(f"cell_index out of range: {cell_index} (notebook has {len(cells)} cells)")
472
+ return
473
+
474
+ cell = cells[cell_index]
475
+ if cell.get("cell_type") != "code":
476
+ # Markdown cells "execute" trivially — just mark them in state.
477
+ state = load_state(session_id)
478
+ state.setdefault("cells", cells_summary(nb))
479
+ if cell_index < len(state["cells"]):
480
+ state["cells"][cell_index]["executed"] = True
481
+ state["cells"][cell_index]["stale"] = False
482
+ state["cells"][cell_index]["lastRunAt"] = now_iso()
483
+ state["cells"][cell_index]["outputSummary"] = "(markdown)"
484
+ state["currentCellIndex"] = cell_index
485
+ save_state(session_id, state)
486
+ emit(
487
+ {
488
+ "ok": True,
489
+ "sessionId": session_id,
490
+ "cellIndex": cell_index,
491
+ "cellType": "markdown",
492
+ "status": "ok",
493
+ "outputs": [],
494
+ "outputSummary": "(markdown)",
495
+ "executionCount": None,
496
+ }
497
+ )
498
+ return
499
+
500
+ code = cell_source(cell)
501
+
502
+ try:
503
+ kc = kernel_client(session_id)
504
+ except Exception as e:
505
+ fail(f"failed to connect to kernel: {e}")
506
+ return
507
+
508
+ try:
509
+ msg_id = kc.execute(code, store_history=True)
510
+ outputs, status, exec_count = capture_outputs(kc, msg_id)
511
+ finally:
512
+ try:
513
+ kc.stop_channels()
514
+ except Exception:
515
+ pass
516
+
517
+ # Update notebook on disk so the file pane reflects fresh outputs.
518
+ cell["outputs"] = outputs
519
+ if exec_count is not None:
520
+ cell["execution_count"] = exec_count
521
+ cells[cell_index] = cell
522
+ nb["cells"] = cells
523
+ write_notebook(nb_path, nb)
524
+
525
+ # Update walkthrough state.
526
+ summary = short_summary(outputs)
527
+ state = load_state(session_id)
528
+ if not state.get("cells") or len(state["cells"]) != len(cells):
529
+ state["cells"] = cells_summary(nb)
530
+ state["cells"][cell_index]["executed"] = True
531
+ state["cells"][cell_index]["stale"] = False
532
+ state["cells"][cell_index]["lastRunAt"] = now_iso()
533
+ state["cells"][cell_index]["outputSummary"] = summary
534
+ state["currentCellIndex"] = cell_index
535
+ state["status"] = "ready" if status == "ok" else status
536
+ save_state(session_id, state)
537
+
538
+ emit(
539
+ {
540
+ "ok": True,
541
+ "sessionId": session_id,
542
+ "cellIndex": cell_index,
543
+ "cellType": "code",
544
+ "status": status,
545
+ "executionCount": exec_count,
546
+ "outputs": outputs,
547
+ "outputSummary": summary,
548
+ }
549
+ )
550
+
551
+
552
+ # ────────────────────────────────────────────────────────────────────────────
553
+ # Subcommand: restart
554
+ # ────────────────────────────────────────────────────────────────────────────
555
+
556
+
557
+ def cmd_restart(session_id: str) -> None:
558
+ nb_pf = notebook_path_file(session_id)
559
+ if not nb_pf.exists():
560
+ fail("session not started — run `start` first")
561
+ return
562
+
563
+ stop_kernel(session_id)
564
+ try:
565
+ pid = spawn_kernel(session_id)
566
+ except Exception as e:
567
+ fail(f"failed to respawn kernel: {e}")
568
+ return
569
+
570
+ state = load_state(session_id)
571
+ for c in state.get("cells", []) or []:
572
+ if c.get("executed"):
573
+ c["stale"] = True
574
+ state["kernelPid"] = pid
575
+ state["kernelStartedAt"] = now_iso()
576
+ state["status"] = "ready"
577
+ save_state(session_id, state)
578
+
579
+ emit({"ok": True, "sessionId": session_id, "kernelPid": pid, "restarted": True})
580
+
581
+
582
+ # ────────────────────────────────────────────────────────────────────────────
583
+ # Subcommand: run-all
584
+ # ────────────────────────────────────────────────────────────────────────────
585
+
586
+
587
+ def cmd_run_all(session_id: str) -> None:
588
+ """Restart the kernel, then execute every code cell top-to-bottom against a
589
+ single fresh kernel — the "Restart & Run All" reproducibility check. This is
590
+ the only true test that a notebook runs clean from a cold start; incremental
591
+ `exec` proves cells in isolation but not in order on a fresh kernel. Stops at
592
+ the first failing cell, writes outputs back into the .ipynb, and updates
593
+ state.json per cell as it goes."""
594
+ nb_pf = notebook_path_file(session_id)
595
+ if not nb_pf.exists():
596
+ fail("session not started — run `start` first")
597
+ return
598
+ nb_path = Path(nb_pf.read_text().strip())
599
+ if not nb_path.exists():
600
+ fail(f"notebook missing on disk: {nb_path}")
601
+ return
602
+
603
+ # Fresh kernel — this is what makes the run a true reproducibility check.
604
+ stop_kernel(session_id)
605
+ try:
606
+ pid = spawn_kernel(session_id)
607
+ except Exception as e:
608
+ fail(f"failed to respawn kernel: {e}")
609
+ return
610
+
611
+ nb = read_notebook(nb_path)
612
+ cells = nb.get("cells", []) or []
613
+
614
+ state = load_state(session_id)
615
+ if not state.get("cells") or len(state["cells"]) != len(cells):
616
+ state["cells"] = cells_summary(nb)
617
+ state["kernelPid"] = pid
618
+ state["kernelStartedAt"] = now_iso()
619
+ # Nothing has run on the fresh kernel yet — clear prior execution flags.
620
+ for c in state.get("cells", []) or []:
621
+ c["executed"] = False
622
+ c["stale"] = False
623
+
624
+ per_cell: list = []
625
+ cells_run = 0
626
+ cells_passed = 0
627
+ first_error = None
628
+
629
+ try:
630
+ kc = kernel_client(session_id)
631
+ except Exception as e:
632
+ fail(f"failed to connect to kernel: {e}")
633
+ return
634
+
635
+ # One client for the whole run (closer to a real run-all than re-handshaking
636
+ # per cell, and cheaper). Persist partial progress in the finally block.
637
+ try:
638
+ for idx, cell in enumerate(cells):
639
+ if cell.get("cell_type") != "code":
640
+ state["cells"][idx]["executed"] = True
641
+ state["cells"][idx]["stale"] = False
642
+ state["cells"][idx]["lastRunAt"] = now_iso()
643
+ state["cells"][idx]["outputSummary"] = "(markdown)"
644
+ continue
645
+
646
+ code = cell_source(cell)
647
+ msg_id = kc.execute(code, store_history=True)
648
+ outputs, status, exec_count = capture_outputs(kc, msg_id)
649
+
650
+ cell["outputs"] = outputs
651
+ if exec_count is not None:
652
+ cell["execution_count"] = exec_count
653
+ cells[idx] = cell
654
+
655
+ summary = short_summary(outputs)
656
+ state["cells"][idx]["executed"] = True
657
+ state["cells"][idx]["stale"] = False
658
+ state["cells"][idx]["lastRunAt"] = now_iso()
659
+ state["cells"][idx]["outputSummary"] = summary
660
+ state["currentCellIndex"] = idx
661
+
662
+ cells_run += 1
663
+ per_cell.append({"index": idx, "status": status})
664
+
665
+ if status == "ok":
666
+ cells_passed += 1
667
+ else:
668
+ err = next(
669
+ (o for o in outputs if o.get("output_type") == "error"), {}
670
+ )
671
+ first_error = {
672
+ "cellIndex": idx,
673
+ "ename": err.get("ename", status),
674
+ "evalue": err.get("evalue", "")
675
+ if status == "error"
676
+ else "execution timed out",
677
+ }
678
+ state["status"] = status
679
+ break
680
+ finally:
681
+ try:
682
+ kc.stop_channels()
683
+ except Exception:
684
+ pass
685
+ # Persist whatever ran into the notebook + state, even on early stop.
686
+ nb["cells"] = cells
687
+ write_notebook(nb_path, nb)
688
+ if first_error is None:
689
+ state["status"] = "ready"
690
+ save_state(session_id, state)
691
+
692
+ cells_total = sum(1 for c in cells if c.get("cell_type") == "code")
693
+ emit(
694
+ {
695
+ "ok": True,
696
+ "sessionId": session_id,
697
+ "restarted": True,
698
+ "cellsTotal": cells_total,
699
+ "cellsRun": cells_run,
700
+ "cellsPassed": cells_passed,
701
+ "firstError": first_error,
702
+ "perCell": per_cell,
703
+ }
704
+ )
705
+
706
+
707
+ # ────────────────────────────────────────────────────────────────────────────
708
+ # Subcommand: stop
709
+ # ────────────────────────────────────────────────────────────────────────────
710
+
711
+
712
+ def cmd_stop(session_id: str) -> None:
713
+ stopped = stop_kernel(session_id)
714
+ state = load_state(session_id)
715
+ if state:
716
+ state["status"] = "ended"
717
+ save_state(session_id, state)
718
+ emit({"ok": True, "sessionId": session_id, "stopped": bool(stopped)})
719
+
720
+
721
+ # ────────────────────────────────────────────────────────────────────────────
722
+ # Subcommand: status
723
+ # ────────────────────────────────────────────────────────────────────────────
724
+
725
+
726
+ def cmd_status(session_id: str) -> None:
727
+ if not session_dir(session_id).exists() or not pid_file(session_id).exists():
728
+ emit({"ok": True, "sessionId": session_id, "kernel": "missing"})
729
+ return
730
+ alive = kernel_alive(session_id)
731
+ emit({"ok": True, "sessionId": session_id, "kernel": "alive" if alive else "dead"})
732
+
733
+
734
+ # ────────────────────────────────────────────────────────────────────────────
735
+ # CLI
736
+ # ────────────────────────────────────────────────────────────────────────────
737
+
738
+
739
+ def main(argv: list[str]) -> int:
740
+ parser = argparse.ArgumentParser(prog="notebook-kernel.py", add_help=True)
741
+ sub = parser.add_subparsers(dest="cmd", required=True)
742
+
743
+ sp_start = sub.add_parser("start")
744
+ sp_start.add_argument("session_id")
745
+ sp_start.add_argument("notebook_path")
746
+
747
+ sp_exec = sub.add_parser("exec")
748
+ sp_exec.add_argument("session_id")
749
+ sp_exec.add_argument("cell_index", type=int)
750
+
751
+ sp_restart = sub.add_parser("restart")
752
+ sp_restart.add_argument("session_id")
753
+
754
+ sp_runall = sub.add_parser("run-all")
755
+ sp_runall.add_argument("session_id")
756
+
757
+ sp_stop = sub.add_parser("stop")
758
+ sp_stop.add_argument("session_id")
759
+
760
+ sp_status = sub.add_parser("status")
761
+ sp_status.add_argument("session_id")
762
+
763
+ sp_uuid = sub.add_parser("new-session") # convenience: print a fresh uuid
764
+
765
+ args = parser.parse_args(argv)
766
+
767
+ try:
768
+ if args.cmd == "start":
769
+ cmd_start(args.session_id, args.notebook_path)
770
+ elif args.cmd == "exec":
771
+ cmd_exec(args.session_id, args.cell_index)
772
+ elif args.cmd == "restart":
773
+ cmd_restart(args.session_id)
774
+ elif args.cmd == "run-all":
775
+ cmd_run_all(args.session_id)
776
+ elif args.cmd == "stop":
777
+ cmd_stop(args.session_id)
778
+ elif args.cmd == "status":
779
+ cmd_status(args.session_id)
780
+ elif args.cmd == "new-session":
781
+ emit({"ok": True, "sessionId": str(uuid.uuid4())})
782
+ except SystemExit:
783
+ raise
784
+ except Exception as e:
785
+ fail(f"unexpected error: {e}")
786
+ return 0
787
+
788
+
789
+ if __name__ == "__main__":
790
+ sys.exit(main(sys.argv[1:]))