calkit-python 0.35.7__tar.gz → 0.37.0__tar.gz

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 (308) hide show
  1. {calkit_python-0.35.7 → calkit_python-0.37.0}/.gitignore +1 -1
  2. calkit_python-0.37.0/.vscode/launch.json +17 -0
  3. calkit_python-0.37.0/.vscode/tasks.json +15 -0
  4. {calkit_python-0.35.7 → calkit_python-0.37.0}/PKG-INFO +1 -1
  5. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/main/core.py +252 -16
  6. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/new.py +153 -30
  7. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/notebooks.py +119 -13
  8. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/update.py +127 -0
  9. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/core.py +6 -4
  10. calkit_python-0.37.0/calkit/dvc/zip.py +472 -0
  11. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/git.py +30 -2
  12. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/package.json +14 -0
  13. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/schemas/calkit/package.json.orig +14 -0
  14. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/models/io.py +1 -1
  15. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/models/pipeline.py +5 -6
  16. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/pipeline.py +23 -6
  17. calkit_python-0.37.0/calkit/templates/latex/__init__.py +1 -0
  18. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/cli/main/test_core.py +74 -0
  19. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/cli/test_new.py +16 -0
  20. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/cli/test_notebooks.py +36 -0
  21. calkit_python-0.37.0/calkit/tests/cli/test_update.py +22 -0
  22. calkit_python-0.35.7/calkit/tests/test_dvc.py → calkit_python-0.37.0/calkit/tests/dvc/test_core.py +1 -1
  23. calkit_python-0.37.0/calkit/tests/dvc/test_zip.py +298 -0
  24. calkit_python-0.37.0/calkit/tests/models/__init__.py +0 -0
  25. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_core.py +19 -0
  26. calkit_python-0.37.0/calkit/tests/test_git.py +387 -0
  27. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_pipeline.py +11 -0
  28. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/cli-reference.md +192 -83
  29. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/existing-project.md +6 -7
  30. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/version-control.md +51 -5
  31. {calkit_python-0.35.7 → calkit_python-0.37.0}/package.json +14 -0
  32. {calkit_python-0.35.7 → calkit_python-0.37.0}/pyproject.toml +1 -0
  33. calkit_python-0.37.0/vscode-ext/.gitignore +3 -0
  34. calkit_python-0.37.0/vscode-ext/.vscodeignore +5 -0
  35. calkit_python-0.37.0/vscode-ext/CHANGELOG.md +6 -0
  36. calkit_python-0.37.0/vscode-ext/LICENSE +19 -0
  37. calkit_python-0.37.0/vscode-ext/README.md +66 -0
  38. calkit_python-0.37.0/vscode-ext/images/calkit-no-bg.png +0 -0
  39. calkit_python-0.37.0/vscode-ext/package-lock.json +77 -0
  40. calkit_python-0.37.0/vscode-ext/package.json +138 -0
  41. calkit_python-0.37.0/vscode-ext/scripts/set-proposed-api.js +25 -0
  42. calkit_python-0.37.0/vscode-ext/src/environments.ts +224 -0
  43. calkit_python-0.37.0/vscode-ext/src/extension.ts +3510 -0
  44. calkit_python-0.37.0/vscode-ext/src/notebooks.ts +79 -0
  45. calkit_python-0.37.0/vscode-ext/src/test/environments.test.ts +130 -0
  46. calkit_python-0.37.0/vscode-ext/src/test/notebooks.test.ts +70 -0
  47. calkit_python-0.37.0/vscode-ext/src/types.ts +25 -0
  48. calkit_python-0.37.0/vscode-ext/tsconfig.json +14 -0
  49. calkit_python-0.35.7/calkit/tests/test_git.py +0 -199
  50. {calkit_python-0.35.7 → calkit_python-0.37.0}/.pre-commit-config.yaml +0 -0
  51. {calkit_python-0.35.7 → calkit_python-0.37.0}/.prettierignore +0 -0
  52. {calkit_python-0.35.7 → calkit_python-0.37.0}/.python-version +0 -0
  53. {calkit_python-0.35.7 → calkit_python-0.37.0}/.yarnrc.yml +0 -0
  54. {calkit_python-0.35.7 → calkit_python-0.37.0}/CITATION.cff +0 -0
  55. {calkit_python-0.35.7 → calkit_python-0.37.0}/CODE_OF_CONDUCT.md +0 -0
  56. {calkit_python-0.35.7 → calkit_python-0.37.0}/CONTRIBUTING.md +0 -0
  57. {calkit_python-0.35.7 → calkit_python-0.37.0}/LICENSE +0 -0
  58. {calkit_python-0.35.7 → calkit_python-0.37.0}/Makefile +0 -0
  59. {calkit_python-0.35.7 → calkit_python-0.37.0}/README.md +0 -0
  60. {calkit_python-0.35.7 → calkit_python-0.37.0}/babel.config.js +0 -0
  61. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/__init__.py +0 -0
  62. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/__main__.py +0 -0
  63. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/calc.py +0 -0
  64. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/check.py +0 -0
  65. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/__init__.py +0 -0
  66. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/check.py +0 -0
  67. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/cloud.py +0 -0
  68. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/config.py +0 -0
  69. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/core.py +0 -0
  70. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/describe.py +0 -0
  71. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/import_.py +0 -0
  72. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/latex.py +0 -0
  73. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/list.py +0 -0
  74. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/main/__init__.py +0 -0
  75. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/main/xr.py +0 -0
  76. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/office.py +0 -0
  77. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/overleaf.py +0 -0
  78. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cli/slurm.py +0 -0
  79. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/cloud.py +0 -0
  80. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/conda.py +0 -0
  81. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/config.py +0 -0
  82. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/datasets.py +0 -0
  83. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/detect.py +0 -0
  84. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/docker.py +0 -0
  85. {calkit_python-0.35.7/calkit/templates → calkit_python-0.37.0/calkit/dvc}/__init__.py +0 -0
  86. /calkit_python-0.35.7/calkit/dvc.py → /calkit_python-0.37.0/calkit/dvc/core.py +0 -0
  87. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/environments.py +0 -0
  88. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/fs.py +0 -0
  89. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/github.py +0 -0
  90. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/gui.py +0 -0
  91. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/invenio.py +0 -0
  92. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/julia.py +0 -0
  93. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/jupyter.py +0 -0
  94. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/jupyterlab/__init__.py +0 -0
  95. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/jupyterlab/routes.py +0 -0
  96. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/schemas/calkit/plugin.json +0 -0
  97. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/static/502.9a2c5772a15466e923ef.js +0 -0
  98. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/static/695.2c41003a452d43d2b358.js +0 -0
  99. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/static/867.a42a046aa5108f54f8fb.js +0 -0
  100. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/static/909.651be47ca47390b78a92.js +0 -0
  101. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js +0 -0
  102. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
  103. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/static/b2f1c3efe70cb539d121.png +0 -0
  104. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/static/remoteEntry.c091821b3d7f2d287a67.js +0 -0
  105. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/static/style.js +0 -0
  106. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/labextension/static/third-party-licenses.json +0 -0
  107. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/licenses.py +0 -0
  108. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/magics.py +0 -0
  109. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/matlab.py +0 -0
  110. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/models/__init__.py +0 -0
  111. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/models/core.py +0 -0
  112. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/models/iteration.py +0 -0
  113. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/notebooks.py +0 -0
  114. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/office.py +0 -0
  115. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/ops.py +0 -0
  116. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/overleaf.py +0 -0
  117. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/releases.py +0 -0
  118. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/server.py +0 -0
  119. {calkit_python-0.35.7/calkit/templates/latex → calkit_python-0.37.0/calkit/templates}/__init__.py +0 -0
  120. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/templates/core.py +0 -0
  121. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/templates/latex/article/paper.tex +0 -0
  122. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/templates/latex/core.py +0 -0
  123. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/templates/latex/jfm/jfm.bst +0 -0
  124. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/templates/latex/jfm/jfm.cls +0 -0
  125. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
  126. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/templates/latex/jfm/paper.tex +0 -0
  127. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/templates/latex/jfm/upmath.sty +0 -0
  128. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/__init__.py +0 -0
  129. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/cli/__init__.py +0 -0
  130. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/cli/main/__init__.py +0 -0
  131. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/cli/main/test_xr.py +0 -0
  132. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/cli/test_check.py +0 -0
  133. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/cli/test_config.py +0 -0
  134. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/cli/test_import.py +0 -0
  135. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/cli/test_latex.py +0 -0
  136. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/cli/test_list.py +0 -0
  137. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/cli/test_overleaf.py +0 -0
  138. {calkit_python-0.35.7/calkit/tests/jupyterlab → calkit_python-0.37.0/calkit/tests/dvc}/__init__.py +0 -0
  139. {calkit_python-0.35.7/calkit/tests/models → calkit_python-0.37.0/calkit/tests/jupyterlab}/__init__.py +0 -0
  140. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/jupyterlab/test_routes.py +0 -0
  141. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/models/test_iteration.py +0 -0
  142. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/models/test_pipeline.py +0 -0
  143. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_calc.py +0 -0
  144. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_check.py +0 -0
  145. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_conda.py +0 -0
  146. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_detect.py +0 -0
  147. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_docker.py +0 -0
  148. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_environments.py +0 -0
  149. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_fs.py +0 -0
  150. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_invenio.py +0 -0
  151. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_julia.py +0 -0
  152. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_jupyter.py +0 -0
  153. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_magics.py +0 -0
  154. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_matlab.py +0 -0
  155. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_notebooks.py +0 -0
  156. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_releases.py +0 -0
  157. {calkit_python-0.35.7 → calkit_python-0.37.0}/calkit/tests/test_templates.py +0 -0
  158. {calkit_python-0.35.7 → calkit_python-0.37.0}/conftest.py +0 -0
  159. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/CNAME +0 -0
  160. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/apps.md +0 -0
  161. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/calculations.md +0 -0
  162. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/calkit-yaml.md +0 -0
  163. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/cloud-integration.md +0 -0
  164. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/datasets.md +0 -0
  165. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/dependencies.md +0 -0
  166. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/environments.md +0 -0
  167. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/examples.md +0 -0
  168. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/governance.md +0 -0
  169. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/help.md +0 -0
  170. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/img/c-to-the-k-white.svg +0 -0
  171. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/img/calkit-fragmentation-compendium.png +0 -0
  172. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/img/calkit-no-bg.png +0 -0
  173. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/img/connect-zenodo.png +0 -0
  174. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/img/jupyterlab/all-green.png +0 -0
  175. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/img/jupyterlab/collect-data-stale.png +0 -0
  176. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/img/jupyterlab/new-env.png +0 -0
  177. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/img/jupyterlab/new-notebook.png +0 -0
  178. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/img/jupyterlab/pipeline-badge.png +0 -0
  179. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/img/jupyterlab-params.png +0 -0
  180. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/img/plos-osi-code-2024-03.png +0 -0
  181. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/img/vscode-nb-params.png +0 -0
  182. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/index.md +0 -0
  183. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/installation.md +0 -0
  184. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/jupyterlab.md +0 -0
  185. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/local-server.md +0 -0
  186. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/notebooks.md +0 -0
  187. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/overleaf.md +0 -0
  188. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/pipeline/index.md +0 -0
  189. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/pipeline/manual-steps.md +0 -0
  190. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/pipeline/running-and-logging.md +0 -0
  191. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/pipeline/slurm.md +0 -0
  192. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/quickstart.md +0 -0
  193. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/references.md +0 -0
  194. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/releases.md +0 -0
  195. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/reproducibility.md +0 -0
  196. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/adding-latex-pub-docker.md +0 -0
  197. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/conda-envs.md +0 -0
  198. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/first-project.md +0 -0
  199. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/github-actions.md +0 -0
  200. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/actions-repo-secrets.png +0 -0
  201. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
  202. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
  203. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
  204. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
  205. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
  206. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
  207. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
  208. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
  209. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
  210. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
  211. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/latex-codespaces/push.png +0 -0
  212. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
  213. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
  214. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/chart-more-rows.png +0 -0
  215. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/create-project.png +0 -0
  216. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
  217. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/excel-chart.png +0 -0
  218. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/excel-data.png +0 -0
  219. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
  220. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/needs-clone.png +0 -0
  221. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/new-stage.png +0 -0
  222. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
  223. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
  224. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/status-more-rows.png +0 -0
  225. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
  226. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/untracked-data.png +0 -0
  227. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/updated-publication.png +0 -0
  228. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
  229. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/office/workflow-page.png +0 -0
  230. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/openfoam/clone.png +0 -0
  231. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/openfoam/create-project.png +0 -0
  232. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
  233. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
  234. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
  235. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/openfoam/new-token.png +0 -0
  236. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/openfoam/reclone.png +0 -0
  237. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
  238. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/quick-actions.png +0 -0
  239. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/img/run-proc.png +0 -0
  240. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/index.md +0 -0
  241. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/jupyterlab.md +0 -0
  242. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/latex-codespaces.md +0 -0
  243. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/matlab.md +0 -0
  244. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/notebook-pipeline.md +0 -0
  245. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/office.md +0 -0
  246. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/openfoam.md +0 -0
  247. {calkit_python-0.35.7 → calkit_python-0.37.0}/docs/tutorials/procedures.md +0 -0
  248. {calkit_python-0.35.7 → calkit_python-0.37.0}/install.json +0 -0
  249. {calkit_python-0.35.7 → calkit_python-0.37.0}/jest.config.js +0 -0
  250. {calkit_python-0.35.7 → calkit_python-0.37.0}/jupyter-config/server-config/calkit.json +0 -0
  251. {calkit_python-0.35.7 → calkit_python-0.37.0}/mkdocs.yml +0 -0
  252. {calkit_python-0.35.7 → calkit_python-0.37.0}/schema/plugin.json +0 -0
  253. {calkit_python-0.35.7 → calkit_python-0.37.0}/scripts/generate-cli-reference.py +0 -0
  254. {calkit_python-0.35.7 → calkit_python-0.37.0}/scripts/install.ps1 +0 -0
  255. {calkit_python-0.35.7 → calkit_python-0.37.0}/scripts/install.sh +0 -0
  256. {calkit_python-0.35.7 → calkit_python-0.37.0}/scripts/make-calk9.sh +0 -0
  257. {calkit_python-0.35.7 → calkit_python-0.37.0}/scripts/sync-docs.py +0 -0
  258. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/__tests__/useQueries.spec.ts +0 -0
  259. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/calkit-config.ts +0 -0
  260. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/cell-output-marker.ts +0 -0
  261. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/components/commit-dialog.tsx +0 -0
  262. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/components/environment-editor.tsx +0 -0
  263. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/components/notebook-registration.tsx +0 -0
  264. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/components/notebook-toolbar.tsx +0 -0
  265. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/components/pipeline-status-bar.tsx +0 -0
  266. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/components/project-info-editor.tsx +0 -0
  267. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/components/sidebar-settings.tsx +0 -0
  268. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/components/sidebar.tsx +0 -0
  269. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/components/stage-editor.tsx +0 -0
  270. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/feature-flags.ts +0 -0
  271. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/file-browser-menu.ts +0 -0
  272. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/hooks/__tests__/useQueries.test.tsx +0 -0
  273. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/hooks/useQueries.ts +0 -0
  274. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/icons.ts +0 -0
  275. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/index.ts +0 -0
  276. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/io-tracker.ts +0 -0
  277. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/pipeline-state.ts +0 -0
  278. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/queryClient.ts +0 -0
  279. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/request.ts +0 -0
  280. {calkit_python-0.35.7 → calkit_python-0.37.0}/src/shims-mainmenu.d.ts +0 -0
  281. {calkit_python-0.35.7 → calkit_python-0.37.0}/style/base.css +0 -0
  282. {calkit_python-0.35.7 → calkit_python-0.37.0}/style/cell-output-marker.css +0 -0
  283. {calkit_python-0.35.7 → calkit_python-0.37.0}/style/environment-editor.css +0 -0
  284. {calkit_python-0.35.7 → calkit_python-0.37.0}/style/environment-selector.css +0 -0
  285. {calkit_python-0.35.7 → calkit_python-0.37.0}/style/img/calkit-no-bg.png +0 -0
  286. {calkit_python-0.35.7 → calkit_python-0.37.0}/style/index.css +0 -0
  287. {calkit_python-0.35.7 → calkit_python-0.37.0}/style/index.js +0 -0
  288. {calkit_python-0.35.7 → calkit_python-0.37.0}/style/notebook-toolbar.css +0 -0
  289. {calkit_python-0.35.7 → calkit_python-0.37.0}/style/pipeline-status-bar.css +0 -0
  290. {calkit_python-0.35.7 → calkit_python-0.37.0}/style/sidebar.css +0 -0
  291. {calkit_python-0.35.7 → calkit_python-0.37.0}/test/dvc-md5-dir/osx-arm64.txt +0 -0
  292. {calkit_python-0.35.7 → calkit_python-0.37.0}/test/nb-julia.ipynb +0 -0
  293. {calkit_python-0.35.7 → calkit_python-0.37.0}/test/nb-params.ipynb +0 -0
  294. {calkit_python-0.35.7 → calkit_python-0.37.0}/test/nb-subdir.ipynb +0 -0
  295. {calkit_python-0.35.7 → calkit_python-0.37.0}/test/pipeline.ipynb +0 -0
  296. {calkit_python-0.35.7 → calkit_python-0.37.0}/test/script.py +0 -0
  297. {calkit_python-0.35.7 → calkit_python-0.37.0}/test/test-log.log +0 -0
  298. {calkit_python-0.35.7 → calkit_python-0.37.0}/tsconfig.json +0 -0
  299. {calkit_python-0.35.7 → calkit_python-0.37.0}/tsconfig.test.json +0 -0
  300. {calkit_python-0.35.7 → calkit_python-0.37.0}/ui-tests/.gitignore +0 -0
  301. {calkit_python-0.35.7 → calkit_python-0.37.0}/ui-tests/README.md +0 -0
  302. {calkit_python-0.35.7 → calkit_python-0.37.0}/ui-tests/jupyter_server_test_config.py +0 -0
  303. {calkit_python-0.35.7 → calkit_python-0.37.0}/ui-tests/package.json +0 -0
  304. {calkit_python-0.35.7 → calkit_python-0.37.0}/ui-tests/playwright.config.js +0 -0
  305. {calkit_python-0.35.7 → calkit_python-0.37.0}/ui-tests/tests/calkit.spec.ts +0 -0
  306. {calkit_python-0.35.7 → calkit_python-0.37.0}/ui-tests/yarn.lock +0 -0
  307. {calkit_python-0.35.7 → calkit_python-0.37.0}/uv.lock +0 -0
  308. {calkit_python-0.35.7 → calkit_python-0.37.0}/yarn.lock +0 -0
@@ -1,7 +1,7 @@
1
1
  dev.ipynb
2
2
  *.pyc
3
3
  .DS_Store
4
- .vscode
4
+ .vscode/settings.json
5
5
  site
6
6
  .env
7
7
  .venv
@@ -0,0 +1,17 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "Run Calkit VS Code Extension",
6
+ "type": "extensionHost",
7
+ "request": "launch",
8
+ "runtimeExecutable": "${execPath}",
9
+ "args": [
10
+ "--extensionDevelopmentPath=${workspaceFolder}/vscode-ext",
11
+ "--enable-proposed-api=calkit.calkit-vscode"
12
+ ],
13
+ "outFiles": ["${workspaceFolder}/vscode-ext/out/**/*.js"],
14
+ "preLaunchTask": "npm: compile (vscode-ext)"
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "type": "shell",
6
+ "command": "npm",
7
+ "args": ["run", "dev:compile"],
8
+ "options": {
9
+ "cwd": "${workspaceFolder}/vscode-ext"
10
+ },
11
+ "label": "npm: compile (vscode-ext)",
12
+ "problemMatcher": ["$tsc"]
13
+ }
14
+ ]
15
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: calkit-python
3
- Version: 0.35.7
3
+ Version: 0.37.0
4
4
  Summary: Reproducibility simplified.
5
5
  Project-URL: Homepage, https://calkit.org
6
6
  Project-URL: Issues, https://github.com/calkit/calkit/issues
@@ -25,6 +25,7 @@ from git.exc import InvalidGitRepositoryError
25
25
  from typing_extensions import Annotated, Optional
26
26
 
27
27
  import calkit
28
+ import calkit.dvc.zip
28
29
  import calkit.matlab
29
30
  import calkit.pipeline
30
31
  from calkit import (
@@ -225,6 +226,69 @@ def clone(
225
226
  raise_error("Failed to pull from DVC remote(s)")
226
227
  except Exception as e:
227
228
  raise_error(f"Failed to pull from DVC remote(s): {e}")
229
+ calkit.dvc.zip.sync_all(direction="to-workspace")
230
+
231
+
232
+ def _format_dvc_data_status(status: dict, zip_path_map: dict) -> str:
233
+ """Format DVC data status, substituting zip paths with workspace paths."""
234
+ reverse_zip = {zip_p: ws for ws, zip_p in zip_path_map.items()}
235
+ color_map = {
236
+ "added": typer.colors.GREEN,
237
+ "modified": typer.colors.YELLOW,
238
+ "deleted": typer.colors.RED,
239
+ "renamed": typer.colors.CYAN,
240
+ }
241
+
242
+ def transform(path: str) -> str | None:
243
+ if path in reverse_zip:
244
+ return reverse_zip[path] + " (zipped)"
245
+ return path
246
+
247
+ def format_section(changes: dict, title: str) -> list[str]:
248
+ entries = []
249
+ for change_type, color in color_map.items():
250
+ for item in changes.get(change_type, []):
251
+ if isinstance(item, dict):
252
+ old = transform(item.get("old", ""))
253
+ new = transform(item.get("new", ""))
254
+ if old is not None and new is not None:
255
+ line = typer.style(
256
+ f" {change_type}: {old} -> {new}", fg=color
257
+ )
258
+ entries.append(line)
259
+ else:
260
+ display = transform(item)
261
+ if display is not None:
262
+ line = typer.style(
263
+ f" {change_type}: {display}", fg=color
264
+ )
265
+ entries.append(line)
266
+ if not entries:
267
+ return []
268
+ return [title] + entries
269
+
270
+ lines = []
271
+ lines += format_section(
272
+ status.get("committed", {}), "DVC committed changes:"
273
+ )
274
+ lines += format_section(
275
+ status.get("uncommitted", {}), "DVC uncommitted changes:"
276
+ )
277
+ not_in_cache = [transform(p) for p in status.get("not_in_cache", [])]
278
+ not_in_cache = [p for p in not_in_cache if p is not None]
279
+ if not_in_cache:
280
+ lines.append(typer.style("Files not in cache:", bold=True))
281
+ for p in not_in_cache:
282
+ lines.append(f" {p}")
283
+ not_in_remote = [transform(p) for p in status.get("not_in_remote", [])]
284
+ not_in_remote = [p for p in not_in_remote if p is not None]
285
+ if not_in_remote:
286
+ lines.append(typer.style("Files not in remote:", bold=True))
287
+ for p in not_in_remote:
288
+ lines.append(f" {p}")
289
+ if not lines:
290
+ return "No changes.\n"
291
+ return "\n".join(lines) + "\n"
228
292
 
229
293
 
230
294
  @app.command(name="status")
@@ -249,6 +313,9 @@ def get_status(
249
313
  ),
250
314
  ),
251
315
  ] = None,
316
+ as_json: Annotated[
317
+ bool, typer.Option("--json", help="Output status as JSON.")
318
+ ] = False,
252
319
  ):
253
320
  """View status (project, version control, and/or pipeline)."""
254
321
  ck_info = calkit.load_calkit_info()
@@ -270,6 +337,9 @@ def get_status(
270
337
  categories = valid_categories
271
338
  pipeline_status = None
272
339
  if "pipeline" in categories or "dvc" in categories:
340
+ # Sync zips so the zip files reflect current workspace state before
341
+ # reporting status
342
+ calkit.dvc.zip.sync_all(direction="to-zip")
273
343
  pipeline_status = calkit.pipeline.get_status(
274
344
  ck_info=ck_info,
275
345
  targets=targets,
@@ -284,6 +354,78 @@ def get_status(
284
354
  "Failed pipeline environment checks for: "
285
355
  + ", ".join(pipeline_status.failed_environment_checks)
286
356
  )
357
+ if as_json:
358
+ status_dict = {}
359
+ if "project" in categories:
360
+ status = calkit.get_latest_project_status()
361
+ status_dict["project"] = (
362
+ None
363
+ if status is None
364
+ else {
365
+ "status": status.status,
366
+ "message": status.message,
367
+ "timestamp": status.timestamp.isoformat(),
368
+ }
369
+ )
370
+ if "git" in categories:
371
+ try:
372
+ repo = git.Repo()
373
+ changed_files = calkit.git.get_changed_files(repo=repo)
374
+ staged_files = calkit.git.get_staged_files(repo=repo)
375
+ untracked_files = calkit.git.get_untracked_files(repo=repo)
376
+ if targets:
377
+ target_prefixes = [
378
+ Path(target).as_posix().rstrip("/")
379
+ for target in targets
380
+ ]
381
+
382
+ def _matches_target(path: str) -> bool:
383
+ path = Path(path).as_posix()
384
+ return any(
385
+ path == prefix or path.startswith(prefix + "/")
386
+ for prefix in target_prefixes
387
+ )
388
+
389
+ changed_files = [
390
+ path for path in changed_files if _matches_target(path)
391
+ ]
392
+ staged_files = [
393
+ path for path in staged_files if _matches_target(path)
394
+ ]
395
+ untracked_files = [
396
+ path
397
+ for path in untracked_files
398
+ if _matches_target(path)
399
+ ]
400
+ status_dict["git"] = {
401
+ "branch": None
402
+ if repo.head.is_detached
403
+ else repo.active_branch.name,
404
+ "is_dirty": repo.is_dirty(untracked_files=True),
405
+ "changed_files": changed_files,
406
+ "staged_files": staged_files,
407
+ "untracked_files": untracked_files,
408
+ }
409
+ except InvalidGitRepositoryError:
410
+ status_dict["git"] = {"error": "Not a Git repository"}
411
+ if "dvc" in categories:
412
+ try:
413
+ dvc_repo = calkit.dvc.get_dvc_repo()
414
+ data_status = dvc_repo.data_status()
415
+ if isinstance(data_status, dict):
416
+ data_status.pop("git", None)
417
+ status_dict["dvc"] = data_status
418
+ except Exception as e:
419
+ status_dict["dvc"] = {"error": f"{e.__class__.__name__}: {e}"}
420
+ if "pipeline" in categories or "dvc" in categories:
421
+ if pipeline_status is None:
422
+ status_dict["pipeline"] = None
423
+ else:
424
+ status_dict["pipeline"] = pipeline_status.model_dump(
425
+ mode="json"
426
+ )
427
+ print(json.dumps(status_dict, indent=2, default=str))
428
+ return
287
429
  if "project" in categories:
288
430
  print_sep("Project")
289
431
  # Print latest status
@@ -313,37 +455,49 @@ def get_status(
313
455
  typer.echo()
314
456
  if "dvc" in categories:
315
457
  print_sep("DVC")
316
- dvc_data_cmd = ["data", "status"]
317
- if targets:
318
- dvc_data_cmd += targets
319
- run_dvc_command(dvc_data_cmd)
320
- typer.echo()
458
+ try:
459
+ get_dvc_repo()
460
+ except Exception:
461
+ typer.echo("This is not a DVC repository.\n")
462
+ else:
463
+ zip_path_map = calkit.dvc.zip.get_zip_path_map()
464
+ dvc_repo = get_dvc_repo()
465
+ raw = dict(dvc_repo.data_status())
466
+ raw.pop("git", None)
467
+ typer.echo(_format_dvc_data_status(raw, zip_path_map))
321
468
  if "pipeline" in categories or "dvc" in categories:
322
469
  print_sep("Pipeline")
323
470
  # Nicely format the results from pipeline status
324
471
  if pipeline_status and pipeline_status.errors:
325
- typer.echo("Pipeline status unavailable due to errors.")
472
+ warn("Pipeline status unavailable due to errors.")
473
+ return
474
+ if pipeline_status and not pipeline_status.has_pipeline:
475
+ typer.echo("This project has no pipeline.")
476
+ return
326
477
  elif pipeline_status and pipeline_status.is_stale:
478
+ typer.echo("Stale stages:")
327
479
  for stage_name in pipeline_status.stale_stage_names:
328
480
  stale_stage = pipeline_status.stale_stages.get(stage_name)
329
481
  if stale_stage is None:
330
482
  continue
331
- typer.echo(f"{typer.style(stage_name, fg='yellow')}:")
483
+ typer.echo(f" {typer.style(stage_name, fg='yellow')}:")
484
+ if stale_stage.modified_command:
485
+ typer.echo(" modified command")
332
486
  # Show stale outputs for this stage
333
487
  if stale_stage.stale_outputs:
334
- typer.echo(" stale outputs:")
488
+ typer.echo(" stale outputs:")
335
489
  for output_path in stale_stage.stale_outputs:
336
- typer.echo(f" - {output_path}")
490
+ typer.echo(f" {output_path}")
337
491
  # Show modified outputs from this stage
338
492
  if stale_stage.modified_outputs:
339
- typer.echo(" modified outputs:")
493
+ typer.echo(" modified outputs:")
340
494
  for output_path in stale_stage.modified_outputs:
341
- typer.echo(f" - {output_path}")
495
+ typer.echo(f" {output_path}")
342
496
  # Show modified inputs making the stage stale
343
497
  if stale_stage.modified_inputs:
344
- typer.echo(" modified inputs:")
498
+ typer.echo(" modified inputs:")
345
499
  for input_path in stale_stage.modified_inputs:
346
- typer.echo(f" - {input_path}")
500
+ typer.echo(f" {input_path}")
347
501
  elif pipeline_status:
348
502
  typer.echo("Pipeline is up to date.")
349
503
 
@@ -395,7 +549,9 @@ def add(
395
549
  to: Annotated[
396
550
  str | None,
397
551
  typer.Option(
398
- "--to", "-t", help="System with which to add (git or dvc)."
552
+ "--to",
553
+ "-t",
554
+ help="System with which to add (git, dvc, or dvc-zip).",
399
555
  ),
400
556
  ] = None,
401
557
  ):
@@ -421,7 +577,7 @@ def add(
421
577
  raise_error(
422
578
  "Cannot auto-generate commit message for more than one path"
423
579
  )
424
- if to is not None and to not in ["git", "dvc"]:
580
+ if to is not None and to not in ["git", "dvc", "dvc-zip"]:
425
581
  raise_error(f"Invalid option for 'to': {to}")
426
582
  try:
427
583
  repo = git.Repo()
@@ -464,6 +620,10 @@ def add(
464
620
  subprocess.call(["git", "add"] + paths)
465
621
  elif to == "dvc":
466
622
  run_dvc_command(["add"] + paths)
623
+ elif to == "dvc-zip":
624
+ for path in paths:
625
+ typer.echo(f"Adding {path} as a DVC zip")
626
+ calkit.dvc.zip.add(path)
467
627
  else:
468
628
  raise_error(f"Invalid option for 'to': {to}")
469
629
  else:
@@ -512,7 +672,14 @@ def add(
512
672
  d.a_path for d in repo.index.diff(None) if d.a_path is not None
513
673
  ]:
514
674
  paths.append(changed_file)
675
+ zip_path_map = calkit.dvc.zip.get_zip_path_map()
515
676
  for path in paths:
677
+ # Check if this path is already registered as a zip
678
+ posix_path = Path(path).as_posix()
679
+ if posix_path in zip_path_map:
680
+ typer.echo(f"Adding {path} via DVC zip")
681
+ calkit.dvc.zip.add(path)
682
+ continue
516
683
  # Detect if this file should be tracked with Git or DVC
517
684
  # First see if it's in Git
518
685
  if repo.git.ls_files(path):
@@ -528,6 +695,12 @@ def add(
528
695
  elif os.path.splitext(path)[-1] in DVC_EXTENSIONS:
529
696
  typer.echo(f"Adding {path} to DVC per its extension")
530
697
  run_dvc_command(["add", path])
698
+ elif calkit.dvc.zip.is_zip_candidate(path):
699
+ typer.echo(
700
+ f"Adding {path} as a DVC zip "
701
+ "(large directory of small files)"
702
+ )
703
+ calkit.dvc.zip.add(path)
531
704
  elif calkit.get_size(path) > DVC_SIZE_THRESH_BYTES:
532
705
  typer.echo(
533
706
  f"Adding {path} to DVC since it's greater than 1 MB"
@@ -763,6 +936,7 @@ def pull(
763
936
  result = run_dvc_command(["pull"] + dvc_args)
764
937
  if result != 0:
765
938
  raise_error("DVC pull failed")
939
+ calkit.dvc.zip.sync_all(direction="to-workspace")
766
940
 
767
941
 
768
942
  @app.command(name="push")
@@ -1098,6 +1272,7 @@ def run(
1098
1272
  import dvc.ui
1099
1273
  from dvc.cli import main as dvc_cli_main
1100
1274
 
1275
+ import calkit.dvc.zip
1101
1276
  import calkit.environments
1102
1277
  import calkit.pipeline
1103
1278
  from calkit.cli.overleaf import sync as overleaf_sync
@@ -1297,10 +1472,38 @@ def run(
1297
1472
  dvc.ui.ui.write = lambda *args, **kwargs: None
1298
1473
  res = dvc_cli_main(["repro"] + args)
1299
1474
  failed = res != 0
1300
- # Parse log to get timing
1475
+ # Parse log to get timing and which stages ran
1301
1476
  with open(log_fpath, "r") as f:
1302
1477
  log_content = f.read()
1303
1478
  stage_run_info = _stage_run_info_from_log_content(log_content)
1479
+ # Zip dvc-zip outputs for stages that actually ran
1480
+ if stage_run_info:
1481
+ from calkit.models.io import PathOutput
1482
+ from calkit.models.pipeline import Pipeline
1483
+
1484
+ ck_info = calkit.load_calkit_info()
1485
+ pipeline_cfg = ck_info.get("pipeline", {})
1486
+ if pipeline_cfg:
1487
+ try:
1488
+ ck_pipeline = Pipeline.model_validate(pipeline_cfg)
1489
+ zip_input_paths = []
1490
+ for stage_name in stage_run_info:
1491
+ stage = ck_pipeline.stages.get(stage_name)
1492
+ if stage is None:
1493
+ continue
1494
+ for out in stage.outputs:
1495
+ if (
1496
+ isinstance(out, PathOutput)
1497
+ and out.storage == "dvc-zip"
1498
+ ):
1499
+ zip_input_paths.append(out.path)
1500
+ if zip_input_paths:
1501
+ calkit.dvc.zip.sync_some(
1502
+ zip_input_paths, direction="to-zip"
1503
+ )
1504
+ except Exception:
1505
+ # Fall back to syncing all zips if pipeline parsing fails
1506
+ calkit.dvc.zip.sync_all(direction="to-zip")
1304
1507
  # Close logger file handler to prevent permissions issues if deleting
1305
1508
  dvc.log.logger.removeHandler(file_handler)
1306
1509
  file_handler.close()
@@ -2166,6 +2369,39 @@ def switch_branch(name: Annotated[str, typer.Argument(help="Branch name.")]):
2166
2369
  else:
2167
2370
  cmd = [name]
2168
2371
  repo.git.checkout(cmd)
2372
+ # After switching branches, DVC-tracked zips may have changed; check out
2373
+ # the new branch's DVC data and unzip to the workspace
2374
+ run_dvc_command(["checkout"])
2375
+ calkit.dvc.zip.sync_all(direction="to-workspace")
2376
+
2377
+
2378
+ @app.command(name="stash")
2379
+ def stash(
2380
+ pop: Annotated[
2381
+ bool, typer.Option("--pop", help="Pop the most recent stash.")
2382
+ ] = False,
2383
+ ):
2384
+ """Stash or restore workspace changes including dvc-zip tracked dirs.
2385
+
2386
+ Without --pop: zips any modified workspace dirs into the DVC cache, then
2387
+ git-stashes (saving the updated .dvc files), checks out the committed DVC
2388
+ state, and unzips it to the workspace.
2389
+
2390
+ With --pop: pops the git stash (restoring the saved .dvc files), checks
2391
+ out the stashed DVC state, and unzips it to the workspace.
2392
+ """
2393
+ if pop:
2394
+ subprocess.check_call(["git", "stash", "pop"])
2395
+ run_dvc_command(["checkout"])
2396
+ calkit.dvc.zip.sync_all(direction="to-workspace")
2397
+ else:
2398
+ # Zip any modified workspace dirs so their current state is in the DVC
2399
+ # cache (the updated .dvc file will be captured by git stash)
2400
+ calkit.dvc.zip.sync_all(direction="to-zip")
2401
+ subprocess.check_call(["git", "stash"])
2402
+ # Restore the committed zip versions and unzip them
2403
+ run_dvc_command(["checkout"])
2404
+ calkit.dvc.zip.sync_all(direction="to-workspace")
2169
2405
 
2170
2406
 
2171
2407
  @app.command(
@@ -1333,6 +1333,75 @@ def new_uv_env(
1333
1333
  repo.git.commit(["-m", f"Add uv environment {name}"])
1334
1334
 
1335
1335
 
1336
+ @new_app.command("slurm-env")
1337
+ def new_slurm_env(
1338
+ name: Annotated[
1339
+ str, typer.Option("--name", "-n", help="Environment name.")
1340
+ ],
1341
+ host: Annotated[
1342
+ str,
1343
+ typer.Option("--host", help="Host where SLURM commands should run."),
1344
+ ] = "localhost",
1345
+ default_options: Annotated[
1346
+ list[str],
1347
+ typer.Option(
1348
+ "--default-option",
1349
+ help=(
1350
+ "Default sbatch/srun option string (for example --gpus=1). "
1351
+ "Repeat for multiple options."
1352
+ ),
1353
+ ),
1354
+ ] = [],
1355
+ description: Annotated[
1356
+ str | None, typer.Option("--description", help="Description.")
1357
+ ] = None,
1358
+ overwrite: Annotated[
1359
+ bool,
1360
+ typer.Option(
1361
+ "--overwrite",
1362
+ "-f",
1363
+ help="Overwrite any existing environment with this name.",
1364
+ ),
1365
+ ] = False,
1366
+ no_commit: Annotated[
1367
+ bool, typer.Option("--no-commit", help="Do not commit changes.")
1368
+ ] = False,
1369
+ ):
1370
+ """Create a new SLURM environment."""
1371
+ host = host.strip()
1372
+ if not host:
1373
+ raise_error("Host is required")
1374
+ ck_info = calkit.load_calkit_info()
1375
+ envs = ck_info.get("environments", {})
1376
+ if isinstance(envs, list):
1377
+ typer.echo("Converting environments from list to dict")
1378
+ envs = {env.pop("name"): env for env in envs}
1379
+ if name in envs and not overwrite:
1380
+ raise_error(
1381
+ f"Environment with name {name} already exists "
1382
+ "(use -f to overwrite)"
1383
+ )
1384
+ normalized_default_options = [
1385
+ opt.strip() for opt in default_options if opt.strip()
1386
+ ]
1387
+ env = {"kind": "slurm", "host": host}
1388
+ if normalized_default_options:
1389
+ env["default_options"] = normalized_default_options # type: ignore
1390
+ if description is not None:
1391
+ env["description"] = description
1392
+ envs[name] = env
1393
+ ck_info["environments"] = envs
1394
+ with open("calkit.yaml", "w") as f:
1395
+ ryaml.dump(ck_info, f)
1396
+ if not no_commit:
1397
+ repo = git.Repo()
1398
+ repo.git.add("calkit.yaml")
1399
+ if repo.git.diff(["--staged", "calkit.yaml"]):
1400
+ repo.git.commit(
1401
+ ["calkit.yaml", "-m", f"Add SLURM environment {name}"]
1402
+ )
1403
+
1404
+
1336
1405
  @new_app.command("uv-venv")
1337
1406
  def new_uv_venv(
1338
1407
  name: Annotated[
@@ -1615,21 +1684,29 @@ def new_pixi_env(
1615
1684
  @new_app.command("julia-env")
1616
1685
  def new_julia_env(
1617
1686
  packages: Annotated[
1618
- list[str],
1619
- typer.Argument(help="Packages to include in the environment."),
1620
- ],
1687
+ list[str] | None,
1688
+ typer.Argument(
1689
+ help="Optional packages to include in the environment."
1690
+ ),
1691
+ ] = None,
1621
1692
  name: Annotated[
1622
1693
  str, typer.Option("--name", "-n", help="Environment name.")
1623
- ],
1694
+ ] = "main",
1624
1695
  path: Annotated[
1625
- str, typer.Option("--path", help="Path for Project.toml file.")
1626
- ] = "Project.toml",
1696
+ str | None,
1697
+ typer.Option("--path", help="Path for Project.toml file."),
1698
+ ] = None,
1627
1699
  description: Annotated[
1628
1700
  str | None, typer.Option("--description", help="Description.")
1629
1701
  ] = None,
1630
1702
  julia_version: Annotated[
1631
- str, typer.Option("--julia", "-j", help="Julia version.")
1632
- ] = "1.11",
1703
+ str | None,
1704
+ typer.Option(
1705
+ "--julia",
1706
+ "-j",
1707
+ help="Julia version. Auto-detected if not supplied.",
1708
+ ),
1709
+ ] = None,
1633
1710
  overwrite: Annotated[
1634
1711
  bool,
1635
1712
  typer.Option(
@@ -1641,8 +1718,17 @@ def new_julia_env(
1641
1718
  no_commit: Annotated[
1642
1719
  bool, typer.Option("--no-commit", help="Do not commit changes.")
1643
1720
  ] = False,
1721
+ no_check: Annotated[
1722
+ bool,
1723
+ typer.Option(
1724
+ "--no-check",
1725
+ help="Do not check environment is up-to-date after creation.",
1726
+ ),
1727
+ ] = False,
1644
1728
  ):
1645
- """Create a new Julia environment."""
1729
+ """Create a new Julia environment or add an existing one to calkit.yaml."""
1730
+ if path is None:
1731
+ path = "Project.toml"
1646
1732
  if not os.path.basename(path) == "Project.toml":
1647
1733
  raise_error(
1648
1734
  "Julia environment paths must point to a Project.toml file"
@@ -1665,27 +1751,57 @@ def new_julia_env(
1665
1751
  f"Environment with name {name} already exists "
1666
1752
  "(use -f to overwrite)"
1667
1753
  )
1668
- # Create the environment now
1754
+ # Detect Julia version if not supplied
1755
+ if julia_version is None:
1756
+ try:
1757
+ julia_version = calkit.julia.get_version()
1758
+ except ValueError as e:
1759
+ raise_error(
1760
+ f"Failed to detect Julia version: {e}. "
1761
+ "Use --julia to specify a version."
1762
+ )
1763
+ # Check for duplicate Julia environments with same path and version
1764
+ for env_name, env_config in envs.items():
1765
+ if (
1766
+ env_config.get("kind") == "julia"
1767
+ and env_config.get("path") == path
1768
+ and env_config.get("julia") == julia_version
1769
+ ):
1770
+ raise_error(
1771
+ f"Julia environment '{env_name}' already exists with path "
1772
+ f"'{path}' and version '{julia_version}'; "
1773
+ f"Use a different path, version, or --overwrite to replace it"
1774
+ )
1669
1775
  env_dir = os.path.dirname(path)
1670
1776
  if env_dir:
1671
1777
  os.makedirs(env_dir, exist_ok=True)
1672
1778
  else:
1673
1779
  env_dir = "."
1674
- # First make sure the Julia version is installed
1675
- cmd = ["juliaup", "add", julia_version]
1676
- try:
1677
- subprocess.run(cmd, check=True)
1678
- except subprocess.CalledProcessError:
1679
- raise_error(f"Failed to install Julia version {julia_version}")
1680
- cmd = ["julia", f"+{julia_version}", f"--project={env_dir}", "-e"]
1681
- install_cmd = "using Pkg;"
1682
- for package in packages:
1683
- install_cmd += f' Pkg.add("{package}");'
1684
- cmd.append(install_cmd)
1685
- try:
1686
- subprocess.run(cmd, check=True)
1687
- except subprocess.CalledProcessError:
1688
- raise_error("Failed to create new Julia environment")
1780
+ project_exists = os.path.isfile(path)
1781
+ created_project_file = False
1782
+ # If packages are provided, initialize via Julia/Pkg.
1783
+ if packages:
1784
+ # First make sure the Julia version is installed
1785
+ cmd = ["juliaup", "add", julia_version]
1786
+ try:
1787
+ subprocess.run(cmd, check=True)
1788
+ except subprocess.CalledProcessError:
1789
+ raise_error(f"Failed to install Julia version {julia_version}")
1790
+ cmd = ["julia", f"+{julia_version}", f"--project={env_dir}", "-e"]
1791
+ install_cmd = f'using Pkg; Pkg.activate("{env_dir}");'
1792
+ for package in packages or []:
1793
+ install_cmd += f' Pkg.add("{package}");'
1794
+ cmd.append(install_cmd)
1795
+ try:
1796
+ subprocess.run(cmd, check=True)
1797
+ except subprocess.CalledProcessError:
1798
+ raise_error("Failed to create new Julia environment")
1799
+ elif not project_exists:
1800
+ # Allow creating a Julia environment scaffold without requiring Julia
1801
+ # tooling at creation time. IJulia or other packages can be added later.
1802
+ with open(path, "w") as f:
1803
+ f.write("[deps]\n")
1804
+ created_project_file = True
1689
1805
  typer.echo("Adding environment to calkit.yaml")
1690
1806
  env = dict(kind="julia", path=path, julia=julia_version)
1691
1807
  if description is not None:
@@ -1694,11 +1810,18 @@ def new_julia_env(
1694
1810
  ck_info["environments"] = envs
1695
1811
  with open("calkit.yaml", "w") as f:
1696
1812
  ryaml.dump(ck_info, f)
1697
- repo.git.add(path)
1698
- repo.git.add(os.path.join(env_dir, "Manifest.toml"))
1699
- repo.git.add("calkit.yaml")
1700
- if not no_commit and repo.git.diff("--staged"):
1701
- repo.git.commit(["-m", f"Add Julia env {name}"])
1813
+ if not no_commit:
1814
+ repo.git.add("calkit.yaml")
1815
+ if packages or created_project_file:
1816
+ repo.git.add(path)
1817
+ if packages:
1818
+ repo.git.add(os.path.join(env_dir, "Manifest.toml"))
1819
+ if not no_check and packages:
1820
+ env_lock_fpath = check_environment(env_name=name)
1821
+ if env_lock_fpath is not None:
1822
+ repo.git.add(env_lock_fpath)
1823
+ if repo.git.diff("--staged"):
1824
+ repo.git.commit(["-m", f"Add Julia env {name}"])
1702
1825
 
1703
1826
 
1704
1827
  @new_app.command("renv")