mammoth-cli 2.2.21__tar.gz → 2.2.23__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 (257) hide show
  1. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/PKG-INFO +1 -1
  2. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/__init__.py +1 -1
  3. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/ai.md +2 -2
  4. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/dataset.py +112 -2
  5. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/project.py +42 -11
  6. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/view.py +18 -5
  7. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/view_ops.py +25 -1
  8. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/errors/envelope.py +2 -0
  9. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/positionals.py +10 -2
  10. mammoth_cli-2.2.23/mammoth_cli/services/sql_check.py +87 -0
  11. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/pyproject.toml +1 -1
  12. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/ai.yaml +4 -8
  13. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/dataset.yaml +2 -0
  14. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/project.yaml +8 -2
  15. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/sdk-catalog.source.yaml +2 -2
  16. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/sdk-methods.yaml +2 -2
  17. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/.gitignore +0 -0
  18. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/LICENSE +0 -0
  19. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/README.md +0 -0
  20. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/__main__.py +0 -0
  21. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/app.py +0 -0
  22. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +0 -0
  23. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/about-mammoth.md +0 -0
  24. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +0 -0
  25. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities-misc.md +0 -0
  26. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities.md +0 -0
  27. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/command-index.md +0 -0
  28. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/activity.md +0 -0
  29. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/addon.md +0 -0
  30. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/agent.md +0 -0
  31. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/annotation.md +0 -0
  32. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/auth.md +0 -0
  33. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/automation.md +0 -0
  34. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/batch.md +0 -0
  35. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/billing.md +0 -0
  36. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/browse.md +0 -0
  37. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/calc.md +0 -0
  38. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/capability.md +0 -0
  39. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/client-app.md +0 -0
  40. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/completion.md +0 -0
  41. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/config.md +0 -0
  42. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/connector.md +0 -0
  43. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/context.md +0 -0
  44. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-qa.md +0 -0
  45. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-style.md +0 -0
  46. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-template.md +0 -0
  47. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard.md +0 -0
  48. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/data-app.md +0 -0
  49. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dataset.md +0 -0
  50. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/doctor.md +0 -0
  51. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/external-key.md +0 -0
  52. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/file.md +0 -0
  53. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/folder.md +0 -0
  54. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/job.md +0 -0
  55. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/link.md +0 -0
  56. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/log.md +0 -0
  57. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/notification.md +0 -0
  58. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/parameter.md +0 -0
  59. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/project.md +0 -0
  60. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/report.md +0 -0
  61. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schedule.md +0 -0
  62. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schema.md +0 -0
  63. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/skill.md +0 -0
  64. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/snippet.md +0 -0
  65. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/support.md +0 -0
  66. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/template.md +0 -0
  67. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/trash.md +0 -0
  68. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/upgrade.md +0 -0
  69. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/user.md +0 -0
  70. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/version.md +0 -0
  71. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view-transform.md +0 -0
  72. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view.md +0 -0
  73. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/webhook.md +0 -0
  74. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workflow.md +0 -0
  75. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workspace.md +0 -0
  76. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/handoff.md +0 -0
  77. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/input.md +0 -0
  78. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +0 -0
  79. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +0 -0
  80. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/operations.md +0 -0
  81. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/auth-scope.md +0 -0
  82. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/cleanup.md +0 -0
  83. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/dashboards.md +0 -0
  84. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/database-import.md +0 -0
  85. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/end-to-end.md +0 -0
  86. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/exports.md +0 -0
  87. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/index.md +0 -0
  88. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/need-action.md +0 -0
  89. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/resources.md +0 -0
  90. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/scheduling.md +0 -0
  91. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/transforms.md +0 -0
  92. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/typed-etl-discovery.md +0 -0
  93. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +0 -0
  94. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/report-checklist.md +0 -0
  95. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/retention.md +0 -0
  96. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +0 -0
  97. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/bundled_skill/mammoth-cli/references/task-start.md +0 -0
  98. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/__init__.py +0 -0
  99. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/activity.py +0 -0
  100. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/addon.py +0 -0
  101. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/agent.py +0 -0
  102. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/ai.py +0 -0
  103. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/annotation.py +0 -0
  104. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/auth.py +0 -0
  105. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/automation.py +0 -0
  106. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/batch.py +0 -0
  107. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/billing.py +0 -0
  108. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/browse.py +0 -0
  109. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/calc.py +0 -0
  110. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/capability.py +0 -0
  111. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/client_app.py +0 -0
  112. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/completion.py +0 -0
  113. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/config.py +0 -0
  114. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/connector.py +0 -0
  115. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/context.py +0 -0
  116. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/dashboard.py +0 -0
  117. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/data_app.py +0 -0
  118. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/doctor.py +0 -0
  119. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/external_key.py +0 -0
  120. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/file.py +0 -0
  121. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/folder.py +0 -0
  122. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/job.py +0 -0
  123. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/link.py +0 -0
  124. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/notification.py +0 -0
  125. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/parameter.py +0 -0
  126. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/registry.py +0 -0
  127. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/report.py +0 -0
  128. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/schedule.py +0 -0
  129. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/schema.py +0 -0
  130. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/skill.py +0 -0
  131. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/snippet.py +0 -0
  132. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/support.py +0 -0
  133. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/template.py +0 -0
  134. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/trash.py +0 -0
  135. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/upgrade.py +0 -0
  136. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/user.py +0 -0
  137. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/view_profile.py +0 -0
  138. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/webhook.py +0 -0
  139. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/workflow.py +0 -0
  140. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/commands/workspace.py +0 -0
  141. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/context/__init__.py +0 -0
  142. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/context/credentials.py +0 -0
  143. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/context/endpoint.py +0 -0
  144. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/context/profiles.py +0 -0
  145. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/context/resolver.py +0 -0
  146. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/contracts/__init__.py +0 -0
  147. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/contracts/auth.py +0 -0
  148. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/embed.py +0 -0
  149. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/errors/__init__.py +0 -0
  150. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/manifest/__init__.py +0 -0
  151. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/manifest/loader.py +0 -0
  152. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/messages/__init__.py +0 -0
  153. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/output/__init__.py +0 -0
  154. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/output/envelope.py +0 -0
  155. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/output/normalize.py +0 -0
  156. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/output/policy.py +0 -0
  157. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/output/progress.py +0 -0
  158. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/output/render.py +0 -0
  159. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/py.typed +0 -0
  160. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/__init__.py +0 -0
  161. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/confirm.py +0 -0
  162. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/dataset_health.py +0 -0
  163. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/dryrun.py +0 -0
  164. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/dryrun_targets.py +0 -0
  165. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/embedded.py +0 -0
  166. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/executor.py +0 -0
  167. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/input_loader.py +0 -0
  168. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/intent_only.py +0 -0
  169. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/invocation.py +0 -0
  170. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/new_data.py +0 -0
  171. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/options.py +0 -0
  172. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/parents.py +0 -0
  173. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/runlog.py +0 -0
  174. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/session.py +0 -0
  175. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/state.py +0 -0
  176. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/strict.py +0 -0
  177. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/updates.py +0 -0
  178. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/validate.py +0 -0
  179. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/runtime/verify.py +0 -0
  180. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/__init__.py +0 -0
  181. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/argspec.py +0 -0
  182. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/board_values.py +0 -0
  183. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/coerce.py +0 -0
  184. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/command_contract.py +0 -0
  185. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/conditional_format.py +0 -0
  186. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/conditions.py +0 -0
  187. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/dashboard_filters.py +0 -0
  188. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/dashboard_pages.py +0 -0
  189. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/dashboard_review.py +0 -0
  190. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/data_profile.py +0 -0
  191. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/data_quality.py +0 -0
  192. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/dispatch.py +0 -0
  193. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/factory.py +0 -0
  194. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/input_fields.py +0 -0
  195. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/listing.py +0 -0
  196. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/local_time.py +0 -0
  197. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/mapping.py +0 -0
  198. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/openapi_types.py +0 -0
  199. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/protocol.py +0 -0
  200. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/read_queries.py +0 -0
  201. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/sdk_service.py +0 -0
  202. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/stored_stats.py +0 -0
  203. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/testing.py +0 -0
  204. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/text_dates.py +0 -0
  205. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/type_system.py +0 -0
  206. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/services/write_impact.py +0 -0
  207. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/skills/__init__.py +0 -0
  208. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/skills/installer.py +0 -0
  209. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/skills/steering.py +0 -0
  210. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/mammoth_cli/testing.py +0 -0
  211. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/activity.yaml +0 -0
  212. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/addon.yaml +0 -0
  213. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/agent.yaml +0 -0
  214. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/annotation.yaml +0 -0
  215. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/auth.yaml +0 -0
  216. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/automation.yaml +0 -0
  217. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/batch.yaml +0 -0
  218. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/billing.yaml +0 -0
  219. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/browse.yaml +0 -0
  220. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/calc.yaml +0 -0
  221. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/capability.yaml +0 -0
  222. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/client-app.yaml +0 -0
  223. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/completion.yaml +0 -0
  224. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/config.yaml +0 -0
  225. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/connector.yaml +0 -0
  226. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/context.yaml +0 -0
  227. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/dashboard.yaml +0 -0
  228. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/data-app.yaml +0 -0
  229. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/doctor.yaml +0 -0
  230. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/external-key.yaml +0 -0
  231. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/file.yaml +0 -0
  232. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/folder.yaml +0 -0
  233. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/job.yaml +0 -0
  234. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/link.yaml +0 -0
  235. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/log.yaml +0 -0
  236. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/notification.yaml +0 -0
  237. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/parameter.yaml +0 -0
  238. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/report.yaml +0 -0
  239. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/schedule.yaml +0 -0
  240. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/schema.yaml +0 -0
  241. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/skill.yaml +0 -0
  242. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/snippet.yaml +0 -0
  243. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/support.yaml +0 -0
  244. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/template.yaml +0 -0
  245. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/trash.yaml +0 -0
  246. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/upgrade.yaml +0 -0
  247. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/user.yaml +0 -0
  248. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/version.yaml +0 -0
  249. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/view.yaml +0 -0
  250. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/webhook.yaml +0 -0
  251. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/workflow.yaml +0 -0
  252. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/commands/workspace.yaml +0 -0
  253. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/edits-target.source.yaml +0 -0
  254. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/openapi-operations.yaml +0 -0
  255. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/manifests/schema-v1.json +0 -0
  256. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/openapi/metadata.json +0 -0
  257. {mammoth_cli-2.2.21 → mammoth_cli-2.2.23}/spec/openapi/openapi.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mammoth-cli
3
- Version: 2.2.21
3
+ Version: 2.2.23
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
@@ -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.2.21"
9
+ __version__ = "2.2.23"
10
10
 
11
11
  # The versioned machine output envelope contract.
12
12
  SCHEMA_VERSION = 1
@@ -8,7 +8,7 @@ Run: `mammoth ai condition generate`. Exact input fields: `mammoth schema get ai
8
8
 
9
9
  Example: `mammoth ai condition generate 123 --input '{"intent": "Summarize revenue by region"}'`. Placeholders are illustrative; resolve IDs and input from observed reads.
10
10
 
11
- Result: `AiConditionGenerateResult`; mutation `benign_mutation`, confirmation `none`, wait policy `not_async`.
11
+ Result: `AiConditionGenerateResult`; mutation `read`, confirmation `none`, wait policy `not_async`.
12
12
 
13
13
  Status on release: ran once on CLI 2.0.14 — write sweep 2026-09-18: exit 0 on release with CLI 2.0.14. Generated: lower("region") = 'east' (CD01). First attempt without dataview_id failed with job_failed/CD02 Dataview 4 not found (defaulted to wrong dataview) -- dataview_id must be passed explicitly. S…
14
14
 
@@ -18,7 +18,7 @@ Run: `mammoth ai expression generate`. Exact input fields: `mammoth schema get a
18
18
 
19
19
  Example: `mammoth ai expression generate 123 --input '{"intent": "Summarize revenue by region", "mode": "sample"}'`. Placeholders are illustrative; resolve IDs and input from observed reads.
20
20
 
21
- Result: `AiExpressionGenerateResult`; mutation `benign_mutation`, confirmation `none`, wait policy `not_async`.
21
+ Result: `AiExpressionGenerateResult`; mutation `read`, confirmation `none`, wait policy `not_async`.
22
22
 
23
23
  Status on release: ran once on CLI 2.0.15 — re-verification 2026-09-18: exit 2 on release with CLI 2.0.15. Fix held: invalid mode surfaced as invalid_arguments with the SDK message before any request was sent: {"error":{"code":"invalid_arguments","message":"`mode` must be 'math' or 'metric', got 'sampl…
24
24
 
@@ -10,7 +10,7 @@ seam to the public SDK method named by the command's reviewed manifest
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
- from typing import Any
13
+ from typing import Any, Protocol
14
14
 
15
15
  from mammoth_cli.errors.envelope import (
16
16
  CODE_INVALID_ARGUMENT,
@@ -44,6 +44,13 @@ from mammoth_cli.services.listing import (
44
44
  HandlerResult = tuple[Any, dict[str, Any]]
45
45
 
46
46
 
47
+ _DATASET_GET_SYMBOL = "mammoth.api.datasets.DatasetsAPI.get"
48
+
49
+
50
+ class _SdkCaller(Protocol):
51
+ def call(self, sdk_symbol: str, /, **kwargs: object) -> object: ...
52
+
53
+
47
54
  def _symbol(invocation: Invocation) -> str:
48
55
  """Return the reviewed backing SDK symbol for this command."""
49
56
  record = command_by_id(invocation.command_id)
@@ -803,12 +810,16 @@ def dataset_create(invocation: Invocation) -> HandlerResult:
803
810
  assert document is not None
804
811
  _forward_optional(document, kwargs, ("folder_resource_id",))
805
812
  with open_service(invocation) as (service, auth):
813
+ if invocation.dry_run:
814
+ _predict_final_name(invocation, service, dataset_spec, project_id)
806
815
  data = service.call(_symbol(invocation), **kwargs)
807
816
  # ``datasets.create`` returns a bare job handle and never waits. Block on
808
817
  # it here (honoring ``--job-timeout``) so the command reports a finished
809
818
  # dataset id instead of a job id the caller must poll separately.
810
819
  settled = service.wait_if_job(data)
811
- return _created_dataset(data, settled), _meta(invocation, auth.workspace_id, project_id)
820
+ result = _created_dataset(data, settled)
821
+ _add_final_name(service, result, dataset_spec, project_id)
822
+ return result, _meta(invocation, auth.workspace_id, project_id)
812
823
 
813
824
 
814
825
  def _created_dataset(handle: Any, settled: Any) -> dict[str, Any]:
@@ -836,6 +847,105 @@ def _created_dataset(handle: Any, settled: Any) -> dict[str, Any]:
836
847
  return result
837
848
 
838
849
 
850
+ #: Pages (of 100) read to predict a final name; past it the answer is "a suffix may be added".
851
+ _NAME_PROBE_PAGES = 20
852
+
853
+
854
+ def _requested_name(dataset_spec: object) -> str | None:
855
+ """The name the caller asked for: ``ds_name`` (clone, cloud) or ``name`` (sketch)."""
856
+ if not isinstance(dataset_spec, dict):
857
+ return None
858
+ for key in ("ds_name", "name"):
859
+ if isinstance(dataset_spec.get(key), str) and dataset_spec[key]:
860
+ return str(dataset_spec[key])
861
+ return None
862
+
863
+
864
+ def _project_dataset_names(service: _SdkCaller, project_id: int) -> tuple[set[str], bool]:
865
+ """Every dataset name in the project and whether all of them were read."""
866
+ names: set[str] = set()
867
+ for page in range(_NAME_PROBE_PAGES):
868
+ data = service.call(
869
+ "mammoth.api.datasets.DatasetsAPI.list",
870
+ project_id=project_id,
871
+ limit=100,
872
+ offset=page * 100,
873
+ )
874
+ rows = data.get("datasets", []) if isinstance(data, dict) else []
875
+ names.update(r["name"] for r in rows if isinstance(r, dict) and "name" in r)
876
+ if len(rows) < 100:
877
+ return names, True
878
+ return names, False
879
+
880
+
881
+ def _name_impact(requested: str, names: set[str], complete: bool) -> dict[str, Any]:
882
+ """The dry-run prediction for ``requested`` against the project's dataset ``names``."""
883
+ if requested not in names:
884
+ free: dict[str, Any] = {"requested_name": requested, "name_taken": False}
885
+ if complete:
886
+ return {**free, "final_name": requested}
887
+ return {**free, "note": "Not all datasets were read; a numeric suffix is added if taken."}
888
+ impact: dict[str, Any] = {"requested_name": requested, "name_taken": True}
889
+ if not complete:
890
+ impact["note"] = f"'{requested}' is taken; a numeric suffix will be added to the name."
891
+ return impact
892
+ suffix = next(i for i in range(2, len(names) + 3) if f"{requested} {i}" not in names)
893
+ impact["final_name"] = f"{requested} {suffix}"
894
+ impact["note"] = (
895
+ f"'{requested}' is taken; the dataset will be named '{impact['final_name']}'. "
896
+ "Ask the user whether to use a different name."
897
+ )
898
+ return impact
899
+
900
+
901
+ def _predict_final_name(
902
+ invocation: Invocation, service: _SdkCaller, dataset_spec: object, project_id: int
903
+ ) -> None:
904
+ """Dry run: say whether the requested name is taken and what the server will name it.
905
+
906
+ The server (``Datasource.get_unique_name``) tries ``name``, ``name 2``, ``name 3``...
907
+ against the project's live dataset names, so the answer is computed the same way.
908
+ """
909
+ requested = _requested_name(dataset_spec)
910
+ if requested is not None:
911
+ names, complete = _project_dataset_names(service, project_id)
912
+ object.__setattr__(invocation, "predicted_impact", _name_impact(requested, names, complete))
913
+
914
+
915
+ def _add_final_name(
916
+ service: _SdkCaller, result: dict[str, Any], dataset_spec: object, project_id: int
917
+ ) -> None:
918
+ """Add the name the server actually gave the new dataset to a create result.
919
+
920
+ The server appends a suffix when the requested name is taken, so the
921
+ requested name is not the dataset's name. The dataset already exists, so a failed
922
+ lookup must not fail (and invite a duplicate) create; it is reported in ``note``.
923
+ """
924
+ dataset_id = result.get("dataset_id")
925
+ if dataset_id is None:
926
+ return
927
+ try:
928
+ record = service.call(
929
+ _DATASET_GET_SYMBOL, dataset_id=dataset_id, project_id=project_id, fields="id,name"
930
+ )
931
+ except CliError as exc: # the service maps every SDK API/HTTP error to CliError
932
+ result["note"] = (
933
+ f"Created dataset {dataset_id}; its final name could not be read back "
934
+ f"({exc.code}). Run dataset get {dataset_id}."
935
+ )
936
+ return
937
+ if not isinstance(record, dict):
938
+ return
939
+ name = record.get("dataset", record).get("name")
940
+ if not isinstance(name, str):
941
+ return
942
+ result["name"] = name
943
+ requested = _requested_name(dataset_spec)
944
+ if requested is not None and requested != name:
945
+ result["requested_name"] = requested
946
+ result["note"] = f"The name '{requested}' was taken, so the dataset is named '{name}'."
947
+
948
+
839
949
  def dataset_create_from_pdf(invocation: Invocation) -> HandlerResult:
840
950
  """Create dataset(s) from tables extracted out of a PDF file."""
841
951
  project_id = require_project(invocation)
@@ -580,9 +580,9 @@ def _report_line(where: str, warning: dict[str, Any]) -> str:
580
580
  return f"{subject}: {warning.get('issue')}. {warning.get('detail', '')}".strip()
581
581
 
582
582
 
583
- def _scoped_dataset_id(invocation: Invocation) -> int | None:
584
- """The DATASET_ID that narrows ``project check`` to one dataset, or None."""
585
- raw = invocation.positional("dataset_id")
583
+ def _scoped_id(invocation: Invocation, name: str) -> int | None:
584
+ """The integer positional ``name`` (``dataset_id``, ``view_id``), or None if omitted."""
585
+ raw = invocation.positional(name)
586
586
  if raw is None:
587
587
  return None
588
588
  try:
@@ -590,11 +590,28 @@ def _scoped_dataset_id(invocation: Invocation) -> int | None:
590
590
  except ValueError as exc:
591
591
  raise CliError(
592
592
  code=CODE_INVALID_ARGUMENT,
593
- message=f"The dataset id argument '{raw}' is not an integer.",
593
+ message=f"The {name.replace('_', ' ')} argument '{raw}' is not an integer.",
594
594
  exit_status=EXIT_USAGE,
595
595
  ) from exc
596
596
 
597
597
 
598
+ def _scoped_dataset_id(invocation: Invocation) -> int | None:
599
+ """The DATASET_ID that narrows ``project check`` to one dataset, or None."""
600
+ return _scoped_id(invocation, "dataset_id")
601
+
602
+
603
+ def _scoped_view_id(invocation: Invocation, dataset_id: int | None) -> int | None:
604
+ """The VIEW_ID that picks which view of DATASET_ID ``project check`` reads, or None."""
605
+ view_id = _scoped_id(invocation, "view_id")
606
+ if view_id is not None and dataset_id is None:
607
+ raise CliError(
608
+ code=CODE_INVALID_ARGUMENT,
609
+ message="A view id needs its dataset: project check PROJECT_ID DATASET_ID VIEW_ID.",
610
+ exit_status=EXIT_USAGE,
611
+ )
612
+ return view_id
613
+
614
+
598
615
  def _datasets_to_check(service: Any, project_id: int, scoped: int | None) -> list[Any]:
599
616
  """Every dataset of the project, or only the named one -- never a walk when scoped."""
600
617
  if scoped is not None:
@@ -647,14 +664,16 @@ def _map_checks[T](
647
664
  )
648
665
 
649
666
 
650
- def _preview_dataset(service: Any, _auth: Any, args: tuple[Any, int | None]) -> dict[str, Any]:
667
+ def _preview_dataset(
668
+ service: Any, _auth: Any, args: tuple[Any, int | None, int | None]
669
+ ) -> dict[str, Any]:
651
670
  from mammoth_cli.commands.view import upload_preview
652
671
 
653
- dataset, project_id = args
672
+ dataset, project_id, view_id = args
654
673
  dataset_id = dataset.get("id") if isinstance(dataset, dict) else None
655
674
  if not isinstance(dataset_id, int):
656
675
  return {"preview_error": "dataset entry has no integer id"}
657
- return upload_preview(service, dataset_id, project_id)
676
+ return upload_preview(service, dataset_id, project_id, view_id)
658
677
 
659
678
 
660
679
  def _check_dashboard(invocation: Invocation) -> Callable[[Any, Any, Any], dict[str, Any]]:
@@ -678,6 +697,7 @@ def _dataset_entry(dataset: dict[str, Any], preview: dict[str, Any]) -> dict[str
678
697
  "dataset_id": dataset.get("id"),
679
698
  "dataset_name": dataset.get("name"),
680
699
  "view_id": preview["view_id"],
700
+ "view_name": preview.get("view_name"),
681
701
  "row_count": preview.get("row_count"),
682
702
  "column_warnings": preview.get("column_warnings", []),
683
703
  "column_checks": preview["column_checks"],
@@ -711,6 +731,7 @@ def _dataset_status(dataset: dict[str, Any], preview: dict[str, Any]) -> dict[st
711
731
  return {
712
732
  **row,
713
733
  "view_id": preview["view_id"],
734
+ "view_name": preview.get("view_name"),
714
735
  "freshness": preview.get("freshness", {"state": "unknown"}),
715
736
  "data_quality": {
716
737
  "warnings": len(preview.get("column_warnings", [])),
@@ -745,7 +766,9 @@ def project_check(invocation: Invocation) -> HandlerResult:
745
766
  one line per finding; ``resources`` has one status row per dataset and
746
767
  dashboard (freshness apart from data quality, or the error that stopped
747
768
  its check). Resources are checked several at once. Given a DATASET_ID it
748
- reads only that dataset's first view (no dataset list, no dashboards).
769
+ reads only that dataset's first view (no dataset list, no dashboards);
770
+ given VIEW_ID as well it reads that view instead. Every status row names
771
+ the ``view_id`` and ``view_name`` it checked.
749
772
  Cold-agent evals (2.0.41) left one column's blanks undecided in every
750
773
  run, because the warning sat in an earlier result.
751
774
  """
@@ -758,6 +781,7 @@ def project_check(invocation: Invocation) -> HandlerResult:
758
781
  skipped: list[dict[str, Any]] = []
759
782
  statuses: list[dict[str, Any]] = []
760
783
  scoped_dataset = _scoped_dataset_id(invocation)
784
+ scoped_view = _scoped_view_id(invocation, scoped_dataset)
761
785
  with open_service(invocation) as (service, auth):
762
786
  every_dataset = _datasets_to_check(service, project_id, scoped_dataset)
763
787
  datasets = every_dataset[:_CHECK_MAX_DATASETS]
@@ -773,7 +797,7 @@ def project_check(invocation: Invocation) -> HandlerResult:
773
797
  invocation,
774
798
  service,
775
799
  auth,
776
- [(dataset, project_id) for dataset in datasets],
800
+ [(dataset, project_id, scoped_view) for dataset in datasets],
777
801
  _preview_dataset,
778
802
  )
779
803
  for dataset, preview in zip(datasets, previews, strict=True):
@@ -788,13 +812,14 @@ def project_check(invocation: Invocation) -> HandlerResult:
788
812
  )
789
813
  continue
790
814
  entry = _dataset_entry(dataset, preview)
791
- if "other_views" in preview:
815
+ if "other_views" in preview and scoped_view is None:
792
816
  to_report.append(_other_views_line(dataset, preview))
793
817
  views.append(entry)
794
818
  checked.append(
795
819
  {
796
820
  "dataset_id": dataset["id"],
797
821
  "view_id": preview["view_id"],
822
+ "view_name": preview.get("view_name"),
798
823
  "rows_checked": preview["column_checks"]["rows_checked"],
799
824
  }
800
825
  )
@@ -834,7 +859,13 @@ def project_check(invocation: Invocation) -> HandlerResult:
834
859
  "checked": checked,
835
860
  "skipped": skipped,
836
861
  **(
837
- {"scope": {"dataset_id": scoped_dataset, "dashboards": "not checked"}}
862
+ {
863
+ "scope": {
864
+ "dataset_id": scoped_dataset,
865
+ **({"view_id": scoped_view} if scoped_view else {}),
866
+ "dashboards": "not checked",
867
+ }
868
+ }
838
869
  if scoped_dataset
839
870
  else {}
840
871
  ),
@@ -4096,8 +4096,17 @@ _DATAVIEW_LIST_SYMBOL = "mammoth.api.dataviews.DataviewsAPI.list"
4096
4096
  _UPLOAD_SAMPLE_ROWS = 3
4097
4097
 
4098
4098
 
4099
- def upload_preview(service: Any, dataset_id: int, project_id: int | None) -> dict[str, Any]:
4100
- """The first view of a new dataset: columns, types, sample rows, warnings.
4099
+ def _select_view(views: list[Any], view_id: int | None) -> dict[str, Any] | None:
4100
+ """The view with ``view_id``, or the first (most recent) one when none is named."""
4101
+ if view_id is None:
4102
+ return views[0]
4103
+ return next((v for v in views if isinstance(v, dict) and v.get("id") == view_id), None)
4104
+
4105
+
4106
+ def upload_preview(
4107
+ service: Any, dataset_id: int, project_id: int | None, view_id: int | None = None
4108
+ ) -> dict[str, Any]:
4109
+ """A view of a dataset (the first unless ``view_id``): columns, types, sample rows, warnings.
4101
4110
 
4102
4111
  ``before_dashboard`` names the columns to add before any dashboard is
4103
4112
  made (revenue from a unit price and a quantity), since a dashboard does
@@ -4116,16 +4125,19 @@ def upload_preview(service: Any, dataset_id: int, project_id: int | None) -> dic
4116
4125
  views = listing.get("dataviews") if isinstance(listing, dict) else None
4117
4126
  if not isinstance(views, list) or not views or not isinstance(views[0], dict):
4118
4127
  return {"preview_error": f"dataset {dataset_id} has no view to preview"}
4128
+ chosen = _select_view(views, view_id)
4129
+ if chosen is None:
4130
+ return {"preview_error": f"dataset {dataset_id} has no view {view_id}"}
4119
4131
  # A dataset with more than one live view has one previewed here; the
4120
4132
  # rest are named (id + name) so a caller (``project.check``) can say
4121
4133
  # a view besides the one checked exists, rather than silently acting
4122
4134
  # on the single view this function happens to preview.
4123
4135
  other_views = [
4124
4136
  {"id": other.get("id"), "name": other.get("name")}
4125
- for other in views[1:]
4126
- if isinstance(other, dict) and isinstance(other.get("id"), int)
4137
+ for other in views
4138
+ if other is not chosen and isinstance(other, dict) and isinstance(other.get("id"), int)
4127
4139
  ]
4128
- record = apply_column_renames(views[0])
4140
+ record = apply_column_renames(chosen)
4129
4141
  view_id = record.get("id")
4130
4142
  if not isinstance(view_id, int):
4131
4143
  return {"preview_error": f"dataset {dataset_id}'s first view has no id"}
@@ -4150,6 +4162,7 @@ def upload_preview(service: Any, dataset_id: int, project_id: int | None) -> dic
4150
4162
  checks, warnings = _check_columns(rows, types, view_id, dataset_id)
4151
4163
  preview: dict[str, Any] = {
4152
4164
  "view_id": view_id,
4165
+ "view_name": record.get("name"),
4153
4166
  "row_count": record.get("row_count"),
4154
4167
  "columns": types,
4155
4168
  "sample_rows": rows[:_UPLOAD_SAMPLE_ROWS],
@@ -61,6 +61,7 @@ from mammoth_cli.runtime.session import open_service, resolved_project
61
61
  from mammoth_cli.services.command_contract import bind_command_inputs
62
62
  from mammoth_cli.services.conditions import CONDITION_KWARG
63
63
  from mammoth_cli.services.input_fields import TASK_COUNT_FIELD
64
+ from mammoth_cli.services.sql_check import check_sql_binds
64
65
  from mammoth_cli.services.write_impact import (
65
66
  ImpactRead,
66
67
  Measure,
@@ -923,7 +924,30 @@ def view_transform_add_sql(invocation: Invocation) -> HandlerResult:
923
924
  _require_field(document, "query")
924
925
  assert document is not None
925
926
  kwargs = _bind_transform_inputs(invocation, document)
926
- return _dispatch_view(invocation, view_id, "add_sql", **kwargs)
927
+ return _dispatch_view(
928
+ invocation, view_id, "add_sql", prepare=_sql_binding_check(invocation, view_id), **kwargs
929
+ )
930
+
931
+
932
+ def _sql_binding_check(
933
+ invocation: Invocation, view_id: int
934
+ ) -> Callable[[Any, int, dict[str, Any]], Any]:
935
+ """A ``prepare`` hook: a dry run fails with ``would_fail`` when the SQL names a column
936
+ the step cannot bind (see :mod:`mammoth_cli.services.sql_check`). A real run is unchanged."""
937
+
938
+ def check(service: Any, dataset_id: int, kwargs: dict[str, Any]) -> Any:
939
+ if invocation.dry_run:
940
+ view = service.call(
941
+ _DATAVIEW_GET_SYMBOL,
942
+ dataset_id=dataset_id,
943
+ dataview_id=view_id,
944
+ project_id=resolved_project(invocation),
945
+ )
946
+ if isinstance(view, dict):
947
+ check_sql_binds(str(kwargs.get("query", "")), view)
948
+ return None
949
+
950
+ return check
927
951
 
928
952
 
929
953
  def view_transform_ai(invocation: Invocation) -> HandlerResult:
@@ -56,6 +56,7 @@ CODE_JOB_FAILED = "job_failed"
56
56
  CODE_INTERRUPTED = "interrupted"
57
57
  CODE_UNSUPPORTED_CONTRACT = "unsupported_contract"
58
58
  CODE_NO_OP = "no_op"
59
+ CODE_WOULD_FAIL = "would_fail"
59
60
 
60
61
  # User-safe, one-line summaries shown to the customer instead of the model-facing
61
62
  # ``message``/``hint``. Plain language only: no command names, ids, field names
@@ -173,6 +174,7 @@ ERROR_SUMMARIES: dict[str, str] = {
173
174
  "upgrade_failed": "The update didn't complete.",
174
175
  "usage_error": _S_BAD_REQUEST,
175
176
  "view_in_draft": "This view is still a draft and can't be used yet.",
177
+ "would_fail": "This step would fail when it runs, so it was not added.",
176
178
  }
177
179
 
178
180
  #: Shown for a code with no entry (a runtime backstop only; the exhaustiveness
@@ -395,14 +395,22 @@ POSITIONAL_OVERRIDES: dict[str, tuple[PositionalSpec, ...]] = {
395
395
  # active project). The SDK signature marks it required or omits it, so the
396
396
  # dual-sourced optional locator is authored here.
397
397
  "project.get": _optional_project_id(),
398
- # ``project check PROJECT_ID DATASET_ID`` narrows the check to one dataset.
398
+ # ``project check PROJECT_ID DATASET_ID [VIEW_ID]`` narrows the check to one dataset,
399
+ # and to one of its views (its first, most recent one when VIEW_ID is omitted).
399
400
  "project.check": (
400
401
  *_optional_project_id(),
401
402
  PositionalSpec(
402
403
  name="dataset_id",
403
404
  type=int,
404
405
  required=False,
405
- help="Check only this dataset's first view; no other dataset or dashboard is read.",
406
+ help="Check only this dataset (its first view unless VIEW_ID is given); "
407
+ "no other dataset or dashboard is read.",
408
+ ),
409
+ PositionalSpec(
410
+ name="view_id",
411
+ type=int,
412
+ required=False,
413
+ help="Check this view of DATASET_ID instead of its first (most recent) one.",
406
414
  ),
407
415
  ),
408
416
  "project.needs-attention": _optional_project_id(),
@@ -0,0 +1,87 @@
1
+ """Check a SQL step's column names against what it will bind to, before it is added.
2
+
3
+ ``FROM "view:<id>"`` binds to the names the pipeline produced (the ``metadata`` of the
4
+ view's latest data), not to a display-only rename (``display_properties.COLUMN_NAMES``),
5
+ which ``view get`` shows. A query naming the renamed column is accepted by the CLI and
6
+ fails in the backend's DuckDB with a Binder Error, so a dry run says so first.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import re
12
+ from collections.abc import Mapping
13
+
14
+ from mammoth_cli.errors.envelope import CODE_WOULD_FAIL, EXIT_USAGE, CliError
15
+
16
+ _STRING = re.compile(r"'(?:[^']|'')*'")
17
+ _QUOTED = re.compile(r'"((?:[^"]|"")+)"')
18
+ _ALIAS = re.compile(r'\bAS\s+("(?:[^"]|"")+"|\w+)|("(?:[^"]|"")+"|\w+)\s+AS\s*\(', re.IGNORECASE)
19
+ _TABLE = re.compile(r'\b(?:FROM|JOIN)\s+("(?:[^"]|"")+")', re.IGNORECASE)
20
+ _VIEW_REF = re.compile(r"view:\d+")
21
+
22
+
23
+ def _unquote(name: str) -> str:
24
+ return name[1:-1].replace('""', '"') if name.startswith('"') else name
25
+
26
+
27
+ def _declared(query: str) -> set[str]:
28
+ """Names the query itself introduces (aliases, CTEs) or reads as a table."""
29
+ aliases = {_unquote(a or b) for a, b in _ALIAS.findall(query)}
30
+ tables = {_unquote(t) for t in _TABLE.findall(query)}
31
+ return {name.casefold() for name in aliases | tables}
32
+
33
+
34
+ def _identifiers(query: str, renamed: Mapping[str, str]) -> list[str]:
35
+ """Quoted identifiers, plus any bare word that is a renamed column's new name."""
36
+ found = [m for m in _QUOTED.findall(query) if not _VIEW_REF.fullmatch(m)]
37
+ words = {w.casefold() for w in re.findall(r"\w+", _QUOTED.sub(" ", query))}
38
+ found += [new for new in renamed if new.casefold() in words]
39
+ return list(dict.fromkeys(name.replace('""', '"') for name in found))
40
+
41
+
42
+ def _renames(view: Mapping[str, object]) -> dict[str, str]:
43
+ """``{new name: pipeline name}`` for each display-only rename of the view."""
44
+ display = view.get("display_properties")
45
+ names = display.get("COLUMN_NAMES") if isinstance(display, dict) else None
46
+ metadata = view.get("metadata")
47
+ if not isinstance(names, dict) or not isinstance(metadata, list):
48
+ return {}
49
+ by_internal = {c.get("internal_name"): c.get("display_name") for c in metadata}
50
+ return {new: by_internal[i] for i, new in names.items() if by_internal.get(i)}
51
+
52
+
53
+ def check_sql_binds(query: str, view: Mapping[str, object]) -> None:
54
+ """Raise ``would_fail`` when ``query`` names a column the step cannot bind.
55
+
56
+ Args:
57
+ query: The step's SQL.
58
+ view: The raw ``dataview get`` record (its ``metadata`` is the bindable schema).
59
+
60
+ Raises:
61
+ CliError: ``would_fail``, with the unknown name and the candidate columns.
62
+ """
63
+ metadata = view.get("metadata")
64
+ rows = metadata if isinstance(metadata, list) else []
65
+ columns = [
66
+ str(c["display_name"]) for c in rows if isinstance(c, dict) and c.get("display_name")
67
+ ]
68
+ if not columns:
69
+ return
70
+ renamed = _renames(view)
71
+ text = _STRING.sub("''", query)
72
+ known = {c.casefold() for c in columns} | _declared(text)
73
+ for name in _identifiers(text, renamed):
74
+ if name.casefold() not in known:
75
+ raise _unbound(name, columns, renamed.get(name))
76
+
77
+
78
+ def _unbound(name: str, columns: list[str], was: str | None) -> CliError:
79
+ hint = f'Use "{was}": "{name}" is only a display name for it.' if was else None
80
+ return CliError(
81
+ code=CODE_WOULD_FAIL,
82
+ message=f'Referenced column "{name}" not found. Candidate bindings: '
83
+ + ", ".join(f'"{c}"' for c in columns),
84
+ exit_status=EXIT_USAGE,
85
+ hint=hint or "Use one of the candidate columns; nothing was added.",
86
+ details={"unknown_column": name, "candidate_columns": columns},
87
+ )
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mammoth-cli"
3
- version = "2.2.21"
3
+ version = "2.2.23"
4
4
  description = "Command-line interface for the Mammoth Analytics platform"
5
5
  authors = [{name = "Mammoth Analytics", email = "support@mammoth.io"}]
6
6
  readme = "README.md"
@@ -18,12 +18,11 @@ commands:
18
18
  sdk_conversion: Call mammoth.api.ai.AIAPI.condition_generate with validated request fields.
19
19
  request_model: AiConditionGenerateRequest
20
20
  result_model: AiConditionGenerateResult
21
- mutation_class: benign_mutation
22
- edits_target: false
21
+ mutation_class: read
23
22
  confirmation: none
24
23
  wait_policy: not_async
25
24
  pagination_policy: none
26
- acceptance_evidence: live_disposable_project
25
+ acceptance_evidence: live_read_only
27
26
  live_test: LT-AI-CONDITION-GENERATE
28
27
  live_exemption_reason: null
29
28
  contract_fixture: null
@@ -41,7 +40,6 @@ commands:
41
40
  undo_test: null
42
41
  known_restrictions: null
43
42
  reviewed_by: primary
44
- no_readback: "generates a condition preview; nothing is persisted to read back"
45
43
  - command_id: ai.expression.generate
46
44
  command_path: ai expression generate
47
45
  disposition: command
@@ -60,12 +58,11 @@ commands:
60
58
  sdk_conversion: Call mammoth.api.ai.AIAPI.expression_generate with validated request fields.
61
59
  request_model: AiExpressionGenerateRequest
62
60
  result_model: AiExpressionGenerateResult
63
- mutation_class: benign_mutation
64
- edits_target: false
61
+ mutation_class: read
65
62
  confirmation: none
66
63
  wait_policy: not_async
67
64
  pagination_policy: none
68
- acceptance_evidence: live_disposable_project
65
+ acceptance_evidence: live_read_only
69
66
  live_test: LT-AI-EXPRESSION-GENERATE
70
67
  live_exemption_reason: null
71
68
  contract_fixture: null
@@ -84,7 +81,6 @@ commands:
84
81
  undo_test: null
85
82
  known_restrictions: null
86
83
  reviewed_by: primary
87
- no_readback: "generates an expression preview; nothing is persisted to read back"
88
84
  - command_id: ai.retention.condition
89
85
  command_path: ai retention condition
90
86
  disposition: command
@@ -252,6 +252,8 @@ commands:
252
252
  dataset/view IDs and delete only IDs created by this task. A weburl dataset is a one-time copy that
253
253
  keeps no source details to refresh from, so automation's run_data_retrieval task cannot refresh it
254
254
  (only a cloud-connected dataset can be refreshed); say so rather than scheduling a refresh for it.
255
+ The result's name is the dataset's real name; when the requested name was taken the server adds a suffix,
256
+ and requested_name and note say so. Use name, not the name you asked for.
255
257
  reviewed_by: primary
256
258
  - command_id: dataset.create-from-pdf
257
259
  command_path: dataset create-from-pdf
@@ -98,7 +98,13 @@ commands:
98
98
  required: false
99
99
  metavar: DATASET_ID
100
100
  falls_back_to_field: null
101
- help: Check only this dataset's first view; no other dataset or dashboard is read.
101
+ help: Check only this dataset (its first view unless VIEW_ID is given); no other dataset or dashboard is read.
102
+ - name: view_id
103
+ type: int
104
+ required: false
105
+ metavar: VIEW_ID
106
+ falls_back_to_field: null
107
+ help: Check this view of DATASET_ID instead of its first (most recent) one.
102
108
  options: []
103
109
  sdk_symbol: mammoth_cli.commands.project.project_check
104
110
  sdk_conversion: Call mammoth_cli.commands.project.project_check with validated request fields.
@@ -124,7 +130,7 @@ commands:
124
130
  - CT-PROJECT-CHECK-ERROR
125
131
  draft_test: null
126
132
  undo_test: null
127
- known_restrictions: Reads the first view of each dataset (the one uploads and the playbooks use) and one data page per view, six resources at a time. `resources` has one status row per dataset and dashboard with its freshness (in_sync, out_of_sync or running, with data_updated_at) kept apart from its data quality (warning count), or the error that stopped its check. Given DATASET_ID it reads only that dataset's first view and skips the dashboards.
133
+ known_restrictions: Reads the first view of each dataset (the one uploads and the playbooks use) and one data page per view, six resources at a time. `resources` has one status row per dataset and dashboard with its freshness (in_sync, out_of_sync or running, with data_updated_at) kept apart from its data quality (warning count), or the error that stopped its check. Given DATASET_ID it reads only that dataset's first view and skips the dashboards; also given VIEW_ID it reads that view instead. Every status row names the view_id and view_name it checked, so compare it with the view the work was on.
128
134
  reviewed_by: primary
129
135
  - command_id: project.checkpoint.list
130
136
  command_path: project checkpoint list
@@ -421,14 +421,14 @@ sdk_methods:
421
421
  - sdk_symbol: mammoth.api.ai.AIAPI.condition_generate
422
422
  canonical_command: ai.condition.generate
423
423
  alias_of: null
424
- mutation_class: null
424
+ mutation_class: read
425
425
  wait_policy: null
426
426
  pagination_policy: null
427
427
  openapi_operation_ids: []
428
428
  - sdk_symbol: mammoth.api.ai.AIAPI.expression_generate
429
429
  canonical_command: ai.expression.generate
430
430
  alias_of: null
431
- mutation_class: null
431
+ mutation_class: read
432
432
  wait_policy: null
433
433
  pagination_policy: null
434
434
  openapi_operation_ids: []