mammoth-cli 2.0.18__tar.gz → 2.0.19__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 (217) hide show
  1. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/PKG-INFO +7 -1
  2. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/README.md +6 -0
  3. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/__init__.py +1 -1
  4. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +1 -1
  5. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities.md +1 -1
  6. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/command-index.md +2 -2
  7. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/project.md +10 -0
  8. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view.md +1 -1
  9. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +5 -1
  10. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/end-to-end.md +1 -1
  11. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/resources.md +46 -4
  12. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/transforms.md +6 -0
  13. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/task-start.md +3 -1
  14. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/doctor.py +17 -11
  15. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/file.py +27 -0
  16. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/project.py +71 -0
  17. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/registry.py +1 -0
  18. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/upgrade.py +5 -2
  19. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/output/envelope.py +4 -0
  20. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/runtime/executor.py +11 -1
  21. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/runtime/strict.py +28 -7
  22. mammoth_cli-2.0.19/mammoth_cli/runtime/updates.py +264 -0
  23. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/command_contract.py +2 -0
  24. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/input_fields.py +9 -0
  25. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/mapping.py +16 -0
  26. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/positionals.py +10 -0
  27. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/pyproject.toml +1 -1
  28. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/file.yaml +8 -4
  29. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/project.yaml +41 -0
  30. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/view.yaml +5 -2
  31. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/.gitignore +0 -0
  32. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/LICENSE +0 -0
  33. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/__main__.py +0 -0
  34. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/app.py +0 -0
  35. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +0 -0
  36. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities-misc.md +0 -0
  37. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/activity.md +0 -0
  38. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/addon.md +0 -0
  39. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/agent.md +0 -0
  40. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/ai.md +0 -0
  41. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/annotation.md +0 -0
  42. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/auth.md +0 -0
  43. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/automation.md +0 -0
  44. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/batch.md +0 -0
  45. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/billing.md +0 -0
  46. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/browse.md +0 -0
  47. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/capability.md +0 -0
  48. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/client-app.md +0 -0
  49. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/completion.md +0 -0
  50. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/config.md +0 -0
  51. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/connector.md +0 -0
  52. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/context.md +0 -0
  53. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard.md +0 -0
  54. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/data-app.md +0 -0
  55. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dataset.md +0 -0
  56. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/doctor.md +0 -0
  57. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/external-key.md +0 -0
  58. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/file.md +0 -0
  59. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/folder.md +0 -0
  60. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/job.md +0 -0
  61. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/log.md +0 -0
  62. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/notification.md +0 -0
  63. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/parameter.md +0 -0
  64. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/report.md +0 -0
  65. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schedule.md +0 -0
  66. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schema.md +0 -0
  67. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/skill.md +0 -0
  68. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/snippet.md +0 -0
  69. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/support.md +0 -0
  70. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/template.md +0 -0
  71. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/trash.md +0 -0
  72. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/upgrade.md +0 -0
  73. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/user.md +0 -0
  74. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/version.md +0 -0
  75. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/webhook.md +0 -0
  76. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workflow.md +0 -0
  77. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workspace.md +0 -0
  78. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/handoff.md +0 -0
  79. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/input.md +0 -0
  80. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +0 -0
  81. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/operations.md +0 -0
  82. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/auth-scope.md +0 -0
  83. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/cleanup.md +0 -0
  84. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/dashboards.md +0 -0
  85. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/exports.md +0 -0
  86. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/index.md +0 -0
  87. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/need-action.md +0 -0
  88. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/typed-etl-discovery.md +0 -0
  89. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +0 -0
  90. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/report-checklist.md +0 -0
  91. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/retention.md +0 -0
  92. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +0 -0
  93. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/__init__.py +0 -0
  94. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/activity.py +0 -0
  95. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/addon.py +0 -0
  96. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/agent.py +0 -0
  97. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/ai.py +0 -0
  98. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/annotation.py +0 -0
  99. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/auth.py +0 -0
  100. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/automation.py +0 -0
  101. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/batch.py +0 -0
  102. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/billing.py +0 -0
  103. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/browse.py +0 -0
  104. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/capability.py +0 -0
  105. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/client_app.py +0 -0
  106. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/completion.py +0 -0
  107. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/config.py +0 -0
  108. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/connector.py +0 -0
  109. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/context.py +0 -0
  110. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/dashboard.py +0 -0
  111. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/data_app.py +0 -0
  112. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/dataset.py +0 -0
  113. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/external_key.py +0 -0
  114. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/folder.py +0 -0
  115. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/job.py +0 -0
  116. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/notification.py +0 -0
  117. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/parameter.py +0 -0
  118. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/report.py +0 -0
  119. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/schedule.py +0 -0
  120. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/schema.py +0 -0
  121. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/skill.py +0 -0
  122. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/snippet.py +0 -0
  123. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/support.py +0 -0
  124. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/template.py +0 -0
  125. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/trash.py +0 -0
  126. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/user.py +0 -0
  127. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/view.py +0 -0
  128. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/view_ops.py +0 -0
  129. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/webhook.py +0 -0
  130. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/workflow.py +0 -0
  131. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/commands/workspace.py +0 -0
  132. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/context/__init__.py +0 -0
  133. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/context/credentials.py +0 -0
  134. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/context/endpoint.py +0 -0
  135. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/context/profiles.py +0 -0
  136. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/context/resolver.py +0 -0
  137. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/contracts/__init__.py +0 -0
  138. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/contracts/auth.py +0 -0
  139. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/errors/__init__.py +0 -0
  140. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/errors/envelope.py +0 -0
  141. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/manifest/__init__.py +0 -0
  142. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/manifest/loader.py +0 -0
  143. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/messages/__init__.py +0 -0
  144. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/output/__init__.py +0 -0
  145. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/output/normalize.py +0 -0
  146. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/output/policy.py +0 -0
  147. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/output/progress.py +0 -0
  148. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/output/render.py +0 -0
  149. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/py.typed +0 -0
  150. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/runtime/__init__.py +0 -0
  151. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/runtime/confirm.py +0 -0
  152. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/runtime/input_loader.py +0 -0
  153. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/runtime/invocation.py +0 -0
  154. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/runtime/options.py +0 -0
  155. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/runtime/runlog.py +0 -0
  156. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/runtime/session.py +0 -0
  157. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/runtime/validate.py +0 -0
  158. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/__init__.py +0 -0
  159. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/argspec.py +0 -0
  160. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/coerce.py +0 -0
  161. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/conditions.py +0 -0
  162. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/dispatch.py +0 -0
  163. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/factory.py +0 -0
  164. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/openapi_types.py +0 -0
  165. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/protocol.py +0 -0
  166. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/sdk_service.py +0 -0
  167. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/testing.py +0 -0
  168. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/services/type_system.py +0 -0
  169. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/skills/__init__.py +0 -0
  170. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/skills/installer.py +0 -0
  171. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/mammoth_cli/testing.py +0 -0
  172. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/activity.yaml +0 -0
  173. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/addon.yaml +0 -0
  174. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/agent.yaml +0 -0
  175. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/ai.yaml +0 -0
  176. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/annotation.yaml +0 -0
  177. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/auth.yaml +0 -0
  178. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/automation.yaml +0 -0
  179. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/batch.yaml +0 -0
  180. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/billing.yaml +0 -0
  181. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/browse.yaml +0 -0
  182. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/capability.yaml +0 -0
  183. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/client-app.yaml +0 -0
  184. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/completion.yaml +0 -0
  185. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/config.yaml +0 -0
  186. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/connector.yaml +0 -0
  187. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/context.yaml +0 -0
  188. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/dashboard.yaml +0 -0
  189. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/data-app.yaml +0 -0
  190. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/dataset.yaml +0 -0
  191. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/doctor.yaml +0 -0
  192. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/external-key.yaml +0 -0
  193. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/folder.yaml +0 -0
  194. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/job.yaml +0 -0
  195. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/log.yaml +0 -0
  196. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/notification.yaml +0 -0
  197. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/parameter.yaml +0 -0
  198. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/report.yaml +0 -0
  199. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/schedule.yaml +0 -0
  200. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/schema.yaml +0 -0
  201. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/skill.yaml +0 -0
  202. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/snippet.yaml +0 -0
  203. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/support.yaml +0 -0
  204. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/template.yaml +0 -0
  205. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/trash.yaml +0 -0
  206. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/upgrade.yaml +0 -0
  207. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/user.yaml +0 -0
  208. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/version.yaml +0 -0
  209. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/webhook.yaml +0 -0
  210. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/workflow.yaml +0 -0
  211. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/commands/workspace.yaml +0 -0
  212. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/openapi-operations.yaml +0 -0
  213. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/schema-v1.json +0 -0
  214. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/sdk-catalog.source.yaml +0 -0
  215. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/manifests/sdk-methods.yaml +0 -0
  216. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/openapi/metadata.json +0 -0
  217. {mammoth_cli-2.0.18 → mammoth_cli-2.0.19}/spec/openapi/openapi.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mammoth-cli
3
- Version: 2.0.18
3
+ Version: 2.0.19
4
4
  Summary: Command-line interface for the Mammoth Analytics platform
5
5
  Project-URL: homepage, https://mammoth.io
6
6
  Project-URL: repository, https://github.com/EdgeMetric/mammothsdk
@@ -217,6 +217,12 @@ mammoth skill install --input '{"agents": ["codex"], "scope": "project"}'
217
217
  Use `mammoth skill update` after a CLI upgrade. It refreshes copies owned by the
218
218
  installer and reports modified copies instead of silently replacing them.
219
219
 
220
+ The CLI checks PyPI for a newer release at most once a day, after a command
221
+ has finished: every JSON envelope carries `meta.update_available` and human
222
+ output adds one stderr line. Set `MAMMOTH_NO_UPDATE_CHECK=1` to turn it off,
223
+ or `MAMMOTH_AUTO_UPGRADE=1` to have it upgrade itself (opt-in; see
224
+ [Upgrade](https://github.com/EdgeMetric/mammothsdk/blob/main/mammoth-cli/docs/upgrade.md)).
225
+
220
226
  ## What you can do
221
227
 
222
228
  | Area | Command families |
@@ -189,6 +189,12 @@ mammoth skill install --input '{"agents": ["codex"], "scope": "project"}'
189
189
  Use `mammoth skill update` after a CLI upgrade. It refreshes copies owned by the
190
190
  installer and reports modified copies instead of silently replacing them.
191
191
 
192
+ The CLI checks PyPI for a newer release at most once a day, after a command
193
+ has finished: every JSON envelope carries `meta.update_available` and human
194
+ output adds one stderr line. Set `MAMMOTH_NO_UPDATE_CHECK=1` to turn it off,
195
+ or `MAMMOTH_AUTO_UPGRADE=1` to have it upgrade itself (opt-in; see
196
+ [Upgrade](https://github.com/EdgeMetric/mammothsdk/blob/main/mammoth-cli/docs/upgrade.md)).
197
+
192
198
  ## What you can do
193
199
 
194
200
  | Area | Command families |
@@ -6,7 +6,7 @@ not implement a second HTTP client and does not call private SDK members.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- __version__ = "2.0.18"
9
+ __version__ = "2.0.19"
10
10
 
11
11
  # The versioned machine output envelope contract.
12
12
  SCHEMA_VERSION = 1
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: mammoth-cli
3
- version: 2.0.18
3
+ version: 2.0.19
4
4
  description: "Use Mammoth Analytics from a terminal: install or authenticate the CLI, discover its live command contract, and safely manage projects, data, views, pipelines, dashboards, exports, and handoffs."
5
5
  ---
6
6
 
@@ -1,7 +1,7 @@
1
1
  # What is proven on release
2
2
 
3
3
  Generated from `docs/release-capability-matrix.json`; do not edit by hand.
4
- The CLI publishes 551 commands. 455 of them bind one of the 528 API operations in the matrix; the remainder are local commands (`schema`, `auth`, `doctor`, `log`, ...) or typed variants that share an operation (every `view transform *` command submits through `view.task.add`). 258 bound commands ran once successfully on release, 2 are not supported there, and the rest are untried. Untried is not broken: discover the contract with `mammoth schema get COMMAND_ID --output json --no-input`, run it, and treat the structured error envelope as the answer.
4
+ The CLI publishes 552 commands. 455 of them bind one of the 528 API operations in the matrix; the remainder are local commands (`schema`, `auth`, `doctor`, `log`, ...) or typed variants that share an operation (every `view transform *` command submits through `view.task.add`). 258 bound commands ran once successfully on release, 2 are not supported there, and the rest are untried. Untried is not broken: discover the contract with `mammoth schema get COMMAND_ID --output json --no-input`, run it, and treat the structured error envelope as the answer.
5
5
 
6
6
  Status meanings:
7
7
 
@@ -1,6 +1,6 @@
1
1
  # Published CLI command catalog
2
2
 
3
- This generated catalog is a lookup table for every command in the published CLI manifest, currently 551; each entry carries its release status line. It is a command-contract index, not an assertion that every backend/OpenAPI operation has a CLI binding. For an exact local CLI contract, run `mammoth schema get COMMAND_ID --output json --no-input`; `mammoth capability list` is an API-binding inventory and can omit typed/local CLI routes. For focused workflows, read the [recipes index](recipes/index.md). Sensitive structured input must come from a private file or pipe; never put secrets in literal argv.
3
+ This generated catalog is a lookup table for every command in the published CLI manifest, currently 552; each entry carries its release status line. It is a command-contract index, not an assertion that every backend/OpenAPI operation has a CLI binding. For an exact local CLI contract, run `mammoth schema get COMMAND_ID --output json --no-input`; `mammoth capability list` is an API-binding inventory and can omit typed/local CLI routes. For focused workflows, read the [recipes index](recipes/index.md). Sensitive structured input must come from a private file or pipe; never put secrets in literal argv.
4
4
 
5
5
  Load only the applicable domain file:
6
6
 
@@ -31,7 +31,7 @@ Load only the applicable domain file:
31
31
  - [log](commands/log.md) — 2 published commands
32
32
  - [notification](commands/notification.md) — 5 published commands
33
33
  - [parameter](commands/parameter.md) — 14 published commands
34
- - [project](commands/project.md) — 18 published commands
34
+ - [project](commands/project.md) — 19 published commands
35
35
  - [report](commands/report.md) — 1 published commands
36
36
  - [schedule](commands/schedule.md) — 5 published commands
37
37
  - [schema](commands/schema.md) — 3 published commands
@@ -62,6 +62,16 @@ Result: `ProjectDeleteResult`; mutation `high_impact`, confirmation `confirm_tar
62
62
 
63
63
  Status on release: ran once on CLI 2.0.18 — fixture-lifecycle sweep 2026-09-19: exit 0 on release with CLI 2.0.18. Deleted project 41 (sweep-218-1224), read back with project get 41 beforehand. Single invocation only.
64
64
 
65
+ ### `project.ensure`
66
+
67
+ Run: `mammoth project ensure`. Exact input fields: `mammoth schema get project.ensure --output json --no-input`.
68
+
69
+ Example: `mammoth project ensure 'Revenue report' --output json --no-input`. Placeholders are illustrative; resolve IDs and input from observed reads.
70
+
71
+ Result: `ProjectEnsureResult`; mutation `benign_mutation`, confirmation `none`, wait policy `not_async`.
72
+
73
+ Status on release: untried; no live run recorded.
74
+
65
75
  ### `project.get`
66
76
 
67
77
  Run: `mammoth project get`. Exact input fields: `mammoth schema get project.get --output json --no-input`.
@@ -66,7 +66,7 @@ Status on release: ran once on CLI 2.0.14 — write sweep 2026-09-18: exit 0 on
66
66
 
67
67
  Run: `mammoth view checkpoint create`. Exact input fields: `mammoth schema get view.checkpoint.create --output json --no-input`.
68
68
 
69
- Example: `mammoth view checkpoint create 123 123 --input '{"body": {"checkpoint_name": "Revenue report", "checkpoint_type": "alert"}}' --output json --no-input`. Placeholders are illustrative; resolve IDs and input from observed reads.
69
+ Example: `mammoth view checkpoint create 123 123 --input '{"body": {"checkpoint_name": "Revenue report", "checkpoint_type": "alert", "pinned_to_end": true}}' --output json --no-input`. Placeholders are illustrative; resolve IDs and input from observed reads.
70
70
 
71
71
  Result: `ViewCheckpointCreateResult`; mutation `benign_mutation`, confirmation `none`, wait policy `not_async`.
72
72
 
@@ -2,9 +2,13 @@
2
2
 
3
3
  ## Success envelope (stdout)
4
4
  ```json
5
- {"schema_version": 1, "data": <result>, "meta": {"command": "project list", "profile": "default", "workspace_id": 4, "project_id": 180, "pagination": null}}
5
+ {"schema_version": 1, "data": <result>, "meta": {"command": "project list", "profile": "default", "workspace_id": 4, "project_id": 180, "pagination": null, "update_available": null}}
6
6
  ```
7
7
 
8
+ `meta.update_available` is `null` or `{"current", "latest", "command"}` when a
9
+ newer CLI is on PyPI (checked once a day, after a command, never blocking).
10
+ Run its `command` before starting a task, not in the middle of one.
11
+
8
12
  ## Error envelope (stderr)
9
13
  ```json
10
14
  {"schema_version": 1, "error": {"code": "resource_not_found", "message": "...", "hint": "...", "details": {}, "request_id": null, "retryable": false, "authorization_required": false, "recovery_commands": ["..."], "log_ref": {"file": ".../logs/2026-09-19.jsonl", "run_id": "b6bc6bf6d166"}}}
@@ -11,7 +11,7 @@ mammoth doctor --output json --no-input # must succeed
11
11
  mammoth project list --output json --no-input # baseline: ids that exist before you start
12
12
 
13
13
  # 1. own project, upload, find the view ids
14
- mammoth project create 'orders-etl' --output json --no-input # -> PROJECT_ID
14
+ mammoth project ensure 'From Claude' --output json --no-input # -> PROJECT_ID (data.project_id; reused next time)
15
15
  mammoth file upload orders.csv --project PROJECT_ID --output json --no-input # -> data.datasets[].id/status
16
16
  mammoth file upload customers.csv --project PROJECT_ID --output json --no-input
17
17
  # a dataset in need_action is not usable yet: follow need-action.md (file-settings update), then re-read
@@ -6,11 +6,30 @@ id instead. Run `view list DATASET_ID --project PROJECT_ID` to get it. Do not
6
6
  dig through `dataset get` and its `dependencies` field for a view id; `view
7
7
  list` is the supported route.
8
8
 
9
- Discover exact contracts, then upload and read back explicit parents. A new
10
- project takes its name as the positional argument:
9
+ Discover exact contracts, then upload and read back explicit parents.
10
+
11
+ ## Where uploads land
12
+
13
+ Unless the task names a project, work in one project of your own and put
14
+ every dataset of the task there, so the user's real projects stay clean and
15
+ one sweep removes everything. `project ensure` is idempotent get-or-create
16
+ by exact name: re-running it (or a second task) returns the same id
17
+ (`created: false`) instead of making another project. A one-off task can
18
+ still use `project create NAME` for a throwaway project.
19
+
20
+ ```bash
21
+ mammoth project ensure 'From Claude' --output json --no-input # -> data.project_id, data.created
22
+ mammoth project ensure 'From Claude' --output json --no-input # same id again, created: false
23
+ ```
24
+
25
+ Moving a dataset between projects is not a single API call on release;
26
+ re-upload into the target project when the task asks for that. To sweep the
27
+ working project when the task says it was temporary, delete it (one id per
28
+ call, read back) with `project delete ID --yes --confirm ID`; otherwise delete
29
+ only the datasets you created and leave the project for the next task.
11
30
 
12
31
  ```bash
13
- mammoth project create 'PROJECT NAME' --output json --no-input
32
+ mammoth project ensure 'PROJECT NAME' --output json --no-input
14
33
  mammoth schema get file.upload --output json --no-input
15
34
  mammoth file upload ./SOURCE.csv --project PROJECT_ID --output json --no-input
16
35
  mammoth dataset list --project PROJECT_ID --output json --no-input
@@ -18,8 +37,31 @@ mammoth view list DATASET_ID --project PROJECT_ID --output json --no-input
18
37
  mammoth view get VIEW_ID --project PROJECT_ID --output json --no-input
19
38
  ```
20
39
 
40
+ ## What `file upload` accepts
41
+
42
+ `file upload` reads the file from local disk and streams it; nothing passes
43
+ through your context, so size costs nothing but time. Two boundaries hold:
44
+
45
+ - Format, by extension: `csv tsv psv txt xls xlsx xml pdf tiff jpeg jpg png
46
+ heic webp`, or an archive of those (`zip gz bz2 tar 7z`). `.json` is not
47
+ accepted; convert to CSV first. Other extensions fail before any upload.
48
+ - Size, at the ingress in front of the API: a 60 MB upload is refused with
49
+ HTTP 413 (`invalid_argument`, "larger than Mammoth accepts") before the API
50
+ sees it; 16 MB is accepted. The exact cap between those was not pinned
51
+ down. Keep one upload well under 16 MB: split a larger file by rows and
52
+ upload the parts, or compress it (a `.zip`/`.gz` of a CSV is accepted and
53
+ expanded server-side).
54
+
55
+ Scanning and parsing time grows with size; pass `--timeout 300` on anything
56
+ past a few MB and, when the command returns `timeout` with a `job_handle`,
57
+ poll with the `job get` recovery command it prints instead of uploading
58
+ again. A bare HTTP 500 from this route (`outcome_unknown`, empty body) means
59
+ the upload service is degraded; check `job get` for a job first, do not
60
+ retry in a loop.
61
+
21
62
  `file upload` takes the local path as its positional argument; there is no
22
- `--source`/`--file` option. Its result carries the status the platform holds
63
+ `--source`/`--file` option. A path that does not exist is a usage error
64
+ (`Local file not found`) before any request. Its result carries the status the platform holds
23
65
  for each created dataset: `ready`, or `need_action` with a `next_command`, in
24
66
  which case follow [need-action](need-action.md) before looking for a view. `dataset delete` is asynchronous: re-read
25
67
  `dataset list` (the id disappears once the job completes) before reporting.
@@ -89,6 +89,12 @@ but the copy answers every read with `4DTVW019` and its copied tasks never
89
89
  execute. A plain `view create DATASET_ID` (no `clone_from`) gives a fresh view
90
90
  of the raw upload, without the pipeline:
91
91
 
92
+ Every pivot `as_name` must be a display name that does not already exist on
93
+ the view (`4DTVW018 Same display name cannot be reused`): to carry a joined
94
+ column such as `monthly_target` into the summary, aggregate it under a new
95
+ name (`{"column": "monthly_target", "function": "MAX", "as_name": "target"}`)
96
+ or use the SQL task with `MAX(monthly_target) AS monthly_target`.
97
+
92
98
  ```bash
93
99
  mammoth schema get view.transform.pivot --output json --no-input
94
100
  mammoth view transform add-sql VIEW_ID --project PROJECT_ID \
@@ -16,7 +16,9 @@ or secrets in an argument, prompt, transcript, or checkpoint.
16
16
  Pin `mammoth-cli==X.Y.Z` when the task names an approved release. Use the
17
17
  host installer script (`installers/mammoth-install.sh` in the repository)
18
18
  only when the operator names it; do not pipe a remote script to a shell on
19
- your own initiative. The SDK (`mammoth-io`) comes with the CLI.
19
+ your own initiative. The SDK (`mammoth-io`) comes with the CLI. If a later
20
+ envelope shows `meta.update_available`, run the `command` it names before
21
+ the next task step, then continue on the new version.
20
22
  2. Locate the installed guidance with `mammoth skill path --output json
21
23
  --no-input` and read `SKILL.md`; open a reference only when the routing
22
24
  table sends you there. `references/commands/*.md` are per-command lookups,
@@ -20,7 +20,7 @@ from mammoth_cli import __version__
20
20
  from mammoth_cli.context import credentials, profiles
21
21
  from mammoth_cli.context.resolver import resolve_auth
22
22
  from mammoth_cli.errors.envelope import CliError
23
- from mammoth_cli.runtime import runlog
23
+ from mammoth_cli.runtime import runlog, updates
24
24
  from mammoth_cli.runtime.invocation import Invocation
25
25
  from mammoth_cli.runtime.session import open_service, resolved_project
26
26
 
@@ -57,15 +57,13 @@ def _safe_connection_diagnostics(error: CliError, *, debug: bool) -> dict[str, A
57
57
  if isinstance(retry_after, str):
58
58
  if len(retry_after) > 10 or not retry_after.isascii() or not retry_after.isdigit():
59
59
  retry_after = None
60
- elif (
61
- not isinstance(retry_after, int)
62
- or isinstance(retry_after, bool)
63
- or retry_after < 0
64
- ):
60
+ elif not isinstance(retry_after, int) or isinstance(retry_after, bool) or retry_after < 0:
65
61
  retry_after = None
66
62
  request_id = error.request_id or raw.get("request_id")
67
- if not isinstance(request_id, str) or len(request_id) > 128 or not re.fullmatch(
68
- r"[A-Za-z0-9._:-]+", request_id
63
+ if (
64
+ not isinstance(request_id, str)
65
+ or len(request_id) > 128
66
+ or not re.fullmatch(r"[A-Za-z0-9._:-]+", request_id)
69
67
  ):
70
68
  request_id = None
71
69
 
@@ -166,6 +164,16 @@ def doctor(invocation: Invocation) -> HandlerResult:
166
164
  )
167
165
  )
168
166
 
167
+ latest = updates.latest_from_pypi()
168
+ if latest is None:
169
+ version_detail = f"{__version__} installed; PyPI not reachable to compare"
170
+ elif updates.is_newer(latest):
171
+ version_detail = f"{__version__} installed; {latest} available: {updates.UPGRADE_COMMAND}"
172
+ else:
173
+ version_detail = f"{__version__} installed; latest on PyPI"
174
+ # Informational: an older CLI still works, so the check never fails.
175
+ checks.append(_check("cli_version", True, version_detail))
176
+
169
177
  record = profiles.get_profile(profile_name)
170
178
  checks.append(
171
179
  _check(
@@ -266,9 +274,7 @@ def doctor(invocation: Invocation) -> HandlerResult:
266
274
  recommendations.append("mammoth context project use PROJECT_ID")
267
275
  if not connection_ok and auth_ok and not invocation.debug:
268
276
  debug_profile = f" --profile {shlex.quote(profile_name)}" if profile_name else ""
269
- recommendations.append(
270
- f"mammoth doctor --debug{debug_profile} --output json --no-input"
271
- )
277
+ recommendations.append(f"mammoth doctor --debug{debug_profile} --output json --no-input")
272
278
 
273
279
  data = {
274
280
  "cli_version": __version__,
@@ -12,6 +12,7 @@ SDK method named by the command's reviewed manifest ``sdk_symbol``.
12
12
  from __future__ import annotations
13
13
 
14
14
  from collections.abc import Callable
15
+ from pathlib import Path
15
16
  from typing import Any
16
17
 
17
18
  from mammoth_cli.errors.envelope import (
@@ -213,6 +214,7 @@ def file_upload(invocation: Invocation) -> HandlerResult:
213
214
  document,
214
215
  **({"files": list(invocation.extra_args)} if invocation.extra_args else {}),
215
216
  )
217
+ _require_local_files(kwargs.get("files"))
216
218
  with open_service(invocation) as (service, auth):
217
219
  data = service.call(_symbol(invocation), **kwargs)
218
220
  # The SDK waits for the upload job and returns the created dataset
@@ -227,6 +229,31 @@ def file_upload(invocation: Invocation) -> HandlerResult:
227
229
  return result, _meta(invocation, auth.workspace_id, resolved_project(invocation))
228
230
 
229
231
 
232
+ def _require_local_files(files: Any) -> None:
233
+ """Fail as a usage error, naming the path, when a local file is missing.
234
+
235
+ The SDK raises a bare ``ValueError`` for an unreadable path, which the
236
+ generic mapping renders as an opaque ``api_error``; a relative path from
237
+ the wrong working directory is the usual cause and must say so.
238
+ """
239
+ candidates = files if isinstance(files, list) else [files] if files else []
240
+ for candidate in candidates:
241
+ if not isinstance(candidate, str):
242
+ continue
243
+ path = Path(candidate).expanduser()
244
+ if not path.is_file():
245
+ raise CliError(
246
+ code=CODE_INVALID_ARGUMENT,
247
+ message=f"Local file not found: {candidate}",
248
+ exit_status=EXIT_USAGE,
249
+ hint=(
250
+ f"Resolved against the working directory {Path.cwd()}; pass an absolute "
251
+ "path or run the command from the directory that holds the file."
252
+ ),
253
+ details={"path": candidate, "cwd": str(Path.cwd())},
254
+ )
255
+
256
+
230
257
  _DATASET_GET_SYMBOL = "mammoth.api.datasets.DatasetsAPI.get"
231
258
 
232
259
 
@@ -14,6 +14,7 @@ from __future__ import annotations
14
14
  from typing import Any
15
15
 
16
16
  from mammoth_cli.errors.envelope import (
17
+ CODE_CONFLICT,
17
18
  CODE_INVALID_ARGUMENT,
18
19
  CODE_MISSING_ARGUMENT,
19
20
  CODE_MISSING_FIELD,
@@ -216,6 +217,76 @@ def project_create(invocation: Invocation) -> HandlerResult:
216
217
  return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
217
218
 
218
219
 
220
+ #: The projects route caps ``limit`` at 100 (backend 4GENR007 above it) and
221
+ #: the SDK exposes no offset, so this is the largest listing one call can see.
222
+ _ENSURE_PROJECT_LIST_LIMIT = 100
223
+ _ENSURE_CREATE_SYMBOL = "mammoth.api.projects.ProjectsAPI.create"
224
+
225
+
226
+ def project_ensure(invocation: Invocation) -> HandlerResult:
227
+ """Return the project with exactly this name, creating it when absent.
228
+
229
+ Idempotent get-or-create for a scratch or working project (the "From
230
+ Claude" convention: pick one project once, land every task's datasets
231
+ there, sweep it with ``project delete``). Matching is exact and
232
+ case-sensitive on the name; when several projects share the name the
233
+ lowest id wins and ``duplicates`` lists the rest, so a re-run never
234
+ creates a second one.
235
+ """
236
+ document = invocation.load_input() or {}
237
+ name = _string_positional(invocation) or document.get("name")
238
+ if not isinstance(name, str) or not name.strip():
239
+ raise CliError(
240
+ code=CODE_MISSING_ARGUMENT,
241
+ message="A project name is required.",
242
+ exit_status=EXIT_USAGE,
243
+ hint="Pass the name as a positional argument or a 'name' input field.",
244
+ )
245
+ with open_service(invocation) as (service, auth):
246
+ listing = service.list_projects(limit=_ENSURE_PROJECT_LIST_LIMIT)
247
+ projects = list(listing.get("projects", [])) if isinstance(listing, dict) else []
248
+ same_name = sorted(
249
+ (p for p in projects if isinstance(p, dict) and p.get("name") == name),
250
+ key=lambda p: int(p.get("id") or 0),
251
+ )
252
+ if same_name:
253
+ chosen = same_name[0]
254
+ data: dict[str, Any] = {
255
+ "project": chosen,
256
+ "project_id": chosen.get("id"),
257
+ "created": False,
258
+ "duplicates": [p.get("id") for p in same_name[1:]],
259
+ }
260
+ return data, _meta(invocation, auth.workspace_id, chosen.get("id"))
261
+ truncated = bool(listing.get("next")) if isinstance(listing, dict) else False
262
+ if truncated or len(projects) >= _ENSURE_PROJECT_LIST_LIMIT:
263
+ # A name beyond the first page may exist; creating blindly would
264
+ # break the idempotency this command promises.
265
+ raise CliError(
266
+ code=CODE_CONFLICT,
267
+ message=(
268
+ f"Workspace has more than {_ENSURE_PROJECT_LIST_LIMIT} projects; "
269
+ f"cannot prove {name!r} is absent."
270
+ ),
271
+ exit_status=EXIT_USAGE,
272
+ hint="Find the project with 'project list' and pass --project, "
273
+ "or create it explicitly with 'project create'.",
274
+ details={"listed": len(projects)},
275
+ )
276
+ created = service.call(_ENSURE_CREATE_SYMBOL, name=name)
277
+ record = created.get("project", created) if isinstance(created, dict) else {}
278
+ project_id = record.get("id") if isinstance(record, dict) else None
279
+ if project_id is None and isinstance(created, dict):
280
+ project_id = created.get("project_id") or created.get("id")
281
+ data = {
282
+ "project": record if isinstance(record, dict) else created,
283
+ "project_id": project_id,
284
+ "created": True,
285
+ "duplicates": [],
286
+ }
287
+ return data, _meta(invocation, auth.workspace_id, project_id)
288
+
289
+
219
290
  def project_update(invocation: Invocation) -> HandlerResult:
220
291
  """Update a project's name or color from the ``--input`` document."""
221
292
  project_id = _project_id(invocation)
@@ -187,6 +187,7 @@ HANDLERS: dict[str, Handler] = {
187
187
  "project.publish-credentials": project_cmd.project_publish_credentials,
188
188
  # project family (mutations)
189
189
  "project.create": project_cmd.project_create,
190
+ "project.ensure": project_cmd.project_ensure,
190
191
  "project.update": project_cmd.project_update,
191
192
  "project.delete": project_cmd.project_delete,
192
193
  "project.bulk-delete": project_cmd.project_bulk_delete,
@@ -166,9 +166,12 @@ def build_upgrade_command(manager: str, target_version: str | None) -> list[str]
166
166
  return [sys.executable, "-m", "pip", "install", "--upgrade", PACKAGE_NAME]
167
167
 
168
168
 
169
- def _run_upgrade(command: list[str]) -> subprocess.CompletedProcess[str]:
169
+ def run_upgrade(command: list[str]) -> subprocess.CompletedProcess[str]:
170
170
  """Run the upgrade command as a subprocess, capturing its output.
171
171
 
172
+ Public so the opt-in automatic upgrade (:mod:`mammoth_cli.runtime.updates`)
173
+ goes through the same seam tests patch here.
174
+
172
175
  Raises:
173
176
  CliError: ``upgrade_failed`` when the command cannot be launched.
174
177
  """
@@ -269,7 +272,7 @@ def perform(invocation: Invocation, *, check: bool, target_version: str | None)
269
272
  policy=POLICY_PROMPT_OR_YES,
270
273
  action=f"upgrade {PACKAGE_NAME} to {action_target} using {manager}",
271
274
  )
272
- result = _run_upgrade(command)
275
+ result = run_upgrade(command)
273
276
  if result.returncode != 0:
274
277
  raise CliError(
275
278
  code="upgrade_failed",
@@ -17,6 +17,9 @@ class Meta:
17
17
  workspace_id: int | None = None
18
18
  project_id: int | None = None
19
19
  pagination: dict[str, Any] | None = None
20
+ #: ``{"current", "latest", "command"}`` when a newer CLI is on PyPI
21
+ #: (from the daily cached check), else None.
22
+ update_available: dict[str, Any] | None = None
20
23
 
21
24
  def to_dict(self) -> dict[str, Any]:
22
25
  return {
@@ -25,6 +28,7 @@ class Meta:
25
28
  "workspace_id": self.workspace_id,
26
29
  "project_id": self.project_id,
27
30
  "pagination": self.pagination,
31
+ "update_available": self.update_available,
28
32
  }
29
33
 
30
34
 
@@ -20,6 +20,7 @@ from mammoth_cli.errors.envelope import EXIT_USAGE, CliError
20
20
  from mammoth_cli.output.envelope import Meta, Result
21
21
  from mammoth_cli.output.policy import MACHINE_OUTPUTS, VALID_OUTPUTS
22
22
  from mammoth_cli.output.render import render
23
+ from mammoth_cli.runtime import updates
23
24
  from mammoth_cli.runtime.invocation import Invocation
24
25
  from mammoth_cli.runtime.runlog import RunLog
25
26
  from mammoth_cli.services.mapping import map_sdk_exception
@@ -73,6 +74,7 @@ def emit_success(
73
74
  workspace_id: int | None = None,
74
75
  project_id: int | None = None,
75
76
  pagination: dict[str, Any] | None = None,
77
+ update_available: dict[str, Any] | None = None,
76
78
  ) -> None:
77
79
  """Render one success envelope to stdout.
78
80
 
@@ -85,6 +87,7 @@ def emit_success(
85
87
  workspace_id: The resolved workspace id, if any.
86
88
  project_id: The resolved project id, if any.
87
89
  pagination: Pagination metadata, if any.
90
+ update_available: The cached newer-release notice, if any.
88
91
  """
89
92
  meta = Meta(
90
93
  command=command_id.replace(".", " "),
@@ -92,6 +95,7 @@ def emit_success(
92
95
  workspace_id=workspace_id,
93
96
  project_id=project_id,
94
97
  pagination=pagination,
98
+ update_available=update_available,
95
99
  )
96
100
  envelope = Result(data=data, meta=meta).to_envelope()
97
101
  render(envelope, output=output)
@@ -171,10 +175,15 @@ def run(
171
175
  run_log.finish(error.exit_status, error_code=error.code)
172
176
  emit_error(error, machine=machine_error, output=output)
173
177
 
178
+ # The update notice is read from the daily cache (no network); the cache
179
+ # itself is refreshed only after the command has produced its output.
180
+ update = updates.available_update(command_id)
174
181
  try:
175
182
  _validate_output(output)
183
+ updates.auto_upgrade(command_id, run_log)
176
184
  data, meta_extra = producer()
177
- emit_success(command_id, data, output, **meta_extra)
185
+ emit_success(command_id, data, output, update_available=update, **meta_extra)
186
+ updates.emit_hint(update, output=output)
178
187
  except CliError as error:
179
188
  fail(error)
180
189
  raise typer.Exit(error.exit_status) from None
@@ -194,6 +203,7 @@ def run(
194
203
  raise typer.Exit(mapped_error.exit_status) from None
195
204
  if run_log is not None:
196
205
  run_log.finish(0)
206
+ updates.refresh_if_stale(command_id)
197
207
 
198
208
 
199
209
  def _open_run_log(command_id: str, invocation: Invocation | None) -> RunLog | None:
@@ -60,6 +60,7 @@ CODE_INVALID_INPUT_FIELD_TYPE = "invalid_input_field_type"
60
60
  _TRUE_STRINGS = frozenset({"true", "1", "yes", "y", "on"})
61
61
  _FALSE_STRINGS = frozenset({"false", "0", "no", "n", "off"})
62
62
 
63
+
63
64
  def _unknown_option_hint(command_id: str, token: str) -> tuple[str, dict[str, Any]]:
64
65
  """Name the accepted form when an unknown ``--option`` matches a real field.
65
66
 
@@ -138,8 +139,7 @@ def _invalid_field_type_error(command_id: str, field: str, value: Any, expected:
138
139
  return CliError(
139
140
  code=CODE_INVALID_INPUT_FIELD_TYPE,
140
141
  message=(
141
- f"Input field '{field}' for '{command_id.replace('.', ' ')}' must be a "
142
- f"{expected}."
142
+ f"Input field '{field}' for '{command_id.replace('.', ' ')}' must be a " f"{expected}."
143
143
  ),
144
144
  exit_status=EXIT_USAGE,
145
145
  hint=f"Pass '{field}' as a {expected} value.",
@@ -193,6 +193,30 @@ def _coerce_float(value: Any, *, command_id: str, field: str) -> float:
193
193
  raise _invalid_field_type_error(command_id, field, value, "number")
194
194
 
195
195
 
196
+ def _relax_numeric_oneof(schema: Any) -> Any:
197
+ """Turn ``oneOf: [number, integer, ...]`` into ``anyOf`` throughout ``schema``.
198
+
199
+ The backend's generated schemas declare numeric fields (``threshold`` on a
200
+ data check, for one) as ``oneOf`` over ``number`` and ``integer``. An
201
+ integral value satisfies both branches, so strict ``oneOf`` rejects
202
+ exactly the values the backend accepts (``5`` fails, ``0.5`` passes).
203
+ The intent is clearly "any of these"; validate it that way.
204
+ """
205
+ if isinstance(schema, list):
206
+ return [_relax_numeric_oneof(item) for item in schema]
207
+ if not isinstance(schema, dict):
208
+ return schema
209
+ relaxed: dict[str, Any] = {}
210
+ for key, value in schema.items():
211
+ if key == "oneOf" and isinstance(value, list):
212
+ types = {b.get("type") for b in value if isinstance(b, dict)}
213
+ if {"number", "integer"} <= types:
214
+ relaxed["anyOf"] = _relax_numeric_oneof(value)
215
+ continue
216
+ relaxed[key] = _relax_numeric_oneof(value)
217
+ return relaxed
218
+
219
+
196
220
  def _coerce_document_fields(
197
221
  command_id: str,
198
222
  document: dict[str, Any],
@@ -208,15 +232,12 @@ def _coerce_document_fields(
208
232
  key == "body"
209
233
  and is_opaque_mapping(field.annotation)
210
234
  and input_schema is not None
211
- and (
212
- body_schema := input_schema.get("properties", {}).get("body")
213
- )
214
- is not None
235
+ and (body_schema := input_schema.get("properties", {}).get("body")) is not None
215
236
  ):
216
237
  from jsonschema import ValidationError, validate # type: ignore[import-untyped]
217
238
 
218
239
  try:
219
- validate(document[key], thaw_contract_metadata(body_schema))
240
+ validate(document[key], _relax_numeric_oneof(thaw_contract_metadata(body_schema)))
220
241
  except ValidationError as error:
221
242
  path = ".".join(str(part) for part in error.absolute_path)
222
243
  field_path = f"body.{path}" if path else "body"