mammoth-cli 2.0.49__tar.gz → 2.0.51__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 (229) hide show
  1. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/PKG-INFO +2 -2
  2. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/__init__.py +1 -1
  3. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +1 -1
  4. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/schema.py +9 -1
  5. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/view.py +114 -10
  6. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/command_contract.py +7 -2
  7. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/data_quality.py +42 -3
  8. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/mapping.py +35 -0
  9. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/positionals.py +35 -1
  10. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/type_system.py +24 -1
  11. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/pyproject.toml +2 -2
  12. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/view.yaml +10 -6
  13. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/.gitignore +0 -0
  14. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/LICENSE +0 -0
  15. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/README.md +0 -0
  16. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/__main__.py +0 -0
  17. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/app.py +0 -0
  18. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/about-mammoth.md +0 -0
  19. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +0 -0
  20. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities-misc.md +0 -0
  21. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities.md +0 -0
  22. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/command-index.md +0 -0
  23. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/activity.md +0 -0
  24. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/addon.md +0 -0
  25. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/agent.md +0 -0
  26. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/ai.md +0 -0
  27. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/annotation.md +0 -0
  28. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/auth.md +0 -0
  29. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/automation.md +0 -0
  30. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/batch.md +0 -0
  31. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/billing.md +0 -0
  32. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/browse.md +0 -0
  33. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/capability.md +0 -0
  34. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/client-app.md +0 -0
  35. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/completion.md +0 -0
  36. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/config.md +0 -0
  37. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/connector.md +0 -0
  38. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/context.md +0 -0
  39. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-qa.md +0 -0
  40. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard.md +0 -0
  41. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/data-app.md +0 -0
  42. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dataset.md +0 -0
  43. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/doctor.md +0 -0
  44. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/external-key.md +0 -0
  45. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/file.md +0 -0
  46. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/folder.md +0 -0
  47. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/job.md +0 -0
  48. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/log.md +0 -0
  49. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/notification.md +0 -0
  50. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/parameter.md +0 -0
  51. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/project.md +0 -0
  52. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/report.md +0 -0
  53. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schedule.md +0 -0
  54. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schema.md +0 -0
  55. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/skill.md +0 -0
  56. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/snippet.md +0 -0
  57. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/support.md +0 -0
  58. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/template.md +0 -0
  59. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/trash.md +0 -0
  60. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/upgrade.md +0 -0
  61. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/user.md +0 -0
  62. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/version.md +0 -0
  63. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view-transform.md +0 -0
  64. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view.md +0 -0
  65. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/webhook.md +0 -0
  66. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workflow.md +0 -0
  67. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workspace.md +0 -0
  68. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/handoff.md +0 -0
  69. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/input.md +0 -0
  70. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +0 -0
  71. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +0 -0
  72. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/operations.md +0 -0
  73. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/auth-scope.md +0 -0
  74. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/cleanup.md +0 -0
  75. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/dashboards.md +0 -0
  76. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/end-to-end.md +0 -0
  77. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/exports.md +0 -0
  78. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/index.md +0 -0
  79. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/need-action.md +0 -0
  80. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/resources.md +0 -0
  81. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/scheduling.md +0 -0
  82. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/transforms.md +0 -0
  83. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/typed-etl-discovery.md +0 -0
  84. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +0 -0
  85. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/report-checklist.md +0 -0
  86. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/retention.md +0 -0
  87. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +0 -0
  88. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/bundled_skill/mammoth-cli/references/task-start.md +0 -0
  89. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/__init__.py +0 -0
  90. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/activity.py +0 -0
  91. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/addon.py +0 -0
  92. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/agent.py +0 -0
  93. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/ai.py +0 -0
  94. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/annotation.py +0 -0
  95. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/auth.py +0 -0
  96. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/automation.py +0 -0
  97. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/batch.py +0 -0
  98. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/billing.py +0 -0
  99. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/browse.py +0 -0
  100. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/capability.py +0 -0
  101. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/client_app.py +0 -0
  102. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/completion.py +0 -0
  103. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/config.py +0 -0
  104. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/connector.py +0 -0
  105. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/context.py +0 -0
  106. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/dashboard.py +0 -0
  107. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/data_app.py +0 -0
  108. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/dataset.py +0 -0
  109. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/doctor.py +0 -0
  110. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/external_key.py +0 -0
  111. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/file.py +0 -0
  112. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/folder.py +0 -0
  113. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/job.py +0 -0
  114. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/notification.py +0 -0
  115. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/parameter.py +0 -0
  116. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/project.py +0 -0
  117. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/registry.py +0 -0
  118. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/report.py +0 -0
  119. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/schedule.py +0 -0
  120. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/skill.py +0 -0
  121. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/snippet.py +0 -0
  122. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/support.py +0 -0
  123. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/template.py +0 -0
  124. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/trash.py +0 -0
  125. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/upgrade.py +0 -0
  126. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/user.py +0 -0
  127. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/view_ops.py +0 -0
  128. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/webhook.py +0 -0
  129. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/workflow.py +0 -0
  130. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/commands/workspace.py +0 -0
  131. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/context/__init__.py +0 -0
  132. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/context/credentials.py +0 -0
  133. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/context/endpoint.py +0 -0
  134. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/context/profiles.py +0 -0
  135. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/context/resolver.py +0 -0
  136. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/contracts/__init__.py +0 -0
  137. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/contracts/auth.py +0 -0
  138. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/embed.py +0 -0
  139. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/errors/__init__.py +0 -0
  140. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/errors/envelope.py +0 -0
  141. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/manifest/__init__.py +0 -0
  142. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/manifest/loader.py +0 -0
  143. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/messages/__init__.py +0 -0
  144. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/output/__init__.py +0 -0
  145. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/output/envelope.py +0 -0
  146. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/output/normalize.py +0 -0
  147. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/output/policy.py +0 -0
  148. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/output/progress.py +0 -0
  149. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/output/render.py +0 -0
  150. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/py.typed +0 -0
  151. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/__init__.py +0 -0
  152. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/confirm.py +0 -0
  153. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/dryrun.py +0 -0
  154. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/embedded.py +0 -0
  155. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/executor.py +0 -0
  156. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/input_loader.py +0 -0
  157. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/invocation.py +0 -0
  158. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/options.py +0 -0
  159. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/parents.py +0 -0
  160. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/runlog.py +0 -0
  161. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/session.py +0 -0
  162. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/strict.py +0 -0
  163. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/updates.py +0 -0
  164. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/runtime/validate.py +0 -0
  165. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/__init__.py +0 -0
  166. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/argspec.py +0 -0
  167. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/coerce.py +0 -0
  168. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/conditions.py +0 -0
  169. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/dashboard_pages.py +0 -0
  170. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/dashboard_review.py +0 -0
  171. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/dispatch.py +0 -0
  172. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/factory.py +0 -0
  173. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/input_fields.py +0 -0
  174. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/openapi_types.py +0 -0
  175. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/protocol.py +0 -0
  176. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/sdk_service.py +0 -0
  177. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/services/testing.py +0 -0
  178. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/skills/__init__.py +0 -0
  179. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/skills/installer.py +0 -0
  180. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/skills/steering.py +0 -0
  181. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/mammoth_cli/testing.py +0 -0
  182. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/activity.yaml +0 -0
  183. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/addon.yaml +0 -0
  184. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/agent.yaml +0 -0
  185. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/ai.yaml +0 -0
  186. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/annotation.yaml +0 -0
  187. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/auth.yaml +0 -0
  188. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/automation.yaml +0 -0
  189. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/batch.yaml +0 -0
  190. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/billing.yaml +0 -0
  191. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/browse.yaml +0 -0
  192. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/capability.yaml +0 -0
  193. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/client-app.yaml +0 -0
  194. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/completion.yaml +0 -0
  195. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/config.yaml +0 -0
  196. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/connector.yaml +0 -0
  197. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/context.yaml +0 -0
  198. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/dashboard.yaml +0 -0
  199. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/data-app.yaml +0 -0
  200. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/dataset.yaml +0 -0
  201. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/doctor.yaml +0 -0
  202. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/external-key.yaml +0 -0
  203. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/file.yaml +0 -0
  204. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/folder.yaml +0 -0
  205. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/job.yaml +0 -0
  206. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/log.yaml +0 -0
  207. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/notification.yaml +0 -0
  208. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/parameter.yaml +0 -0
  209. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/project.yaml +0 -0
  210. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/report.yaml +0 -0
  211. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/schedule.yaml +0 -0
  212. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/schema.yaml +0 -0
  213. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/skill.yaml +0 -0
  214. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/snippet.yaml +0 -0
  215. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/support.yaml +0 -0
  216. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/template.yaml +0 -0
  217. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/trash.yaml +0 -0
  218. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/upgrade.yaml +0 -0
  219. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/user.yaml +0 -0
  220. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/version.yaml +0 -0
  221. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/webhook.yaml +0 -0
  222. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/workflow.yaml +0 -0
  223. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/commands/workspace.yaml +0 -0
  224. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/openapi-operations.yaml +0 -0
  225. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/schema-v1.json +0 -0
  226. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/sdk-catalog.source.yaml +0 -0
  227. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/manifests/sdk-methods.yaml +0 -0
  228. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/spec/openapi/metadata.json +0 -0
  229. {mammoth_cli-2.0.49 → mammoth_cli-2.0.51}/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.49
3
+ Version: 2.0.51
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
@@ -17,7 +17,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
17
  Requires-Python: <3.15,>=3.12
18
18
  Requires-Dist: jsonschema>=4.20
19
19
  Requires-Dist: keyring<26,>=25.7
20
- Requires-Dist: mammoth-io<0.8,>=0.7.21
20
+ Requires-Dist: mammoth-io<0.8,>=0.7.23
21
21
  Requires-Dist: platformdirs<5,>=4.10
22
22
  Requires-Dist: pydantic<3,>=2.13
23
23
  Requires-Dist: pyyaml<7,>=6
@@ -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.49"
9
+ __version__ = "2.0.51"
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.49
3
+ version: 2.0.51
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 (join, merge, pivot, filter, clean), dashboards, exports, and handoffs."
5
5
  ---
6
6
 
@@ -92,6 +92,13 @@ _COMMAND_DISCOVERY_PURPOSES = {
92
92
  "view.export.sftp": "sftp secure file transfer server",
93
93
  "view.export.sharepoint": "share point sharepoint microsoft",
94
94
  "view.export.tableau": "tableau server dashboard",
95
+ # "Run"/"execute"/"apply"/"refresh" the pipeline is ambiguous between
96
+ # re-running its tasks and applying a pending draft; both need to surface.
97
+ "view.pipeline.rerun": (
98
+ "run execute rerun re-run refresh recompute apply pipeline tasks "
99
+ "from a point in the task sequence"
100
+ ),
101
+ "view.draft.submit": "apply submit run execute a pending draft changes",
95
102
  "webhook.create": "webhook http endpoint api push create",
96
103
  "webhook.update": "webhook http endpoint api update",
97
104
  "webhook.get": "webhook http endpoint api get",
@@ -142,7 +149,8 @@ _COMMAND_DISCOVERY_PURPOSES = {
142
149
  "fill missing null empty blank blanks impute carry forward fill down values"
143
150
  ),
144
151
  "view.transform.filter": (
145
- "filter rows keep drop exclude remove delete rows where condition subset"
152
+ "filter rows keep drop exclude remove delete rows where condition subset "
153
+ "bind bound binding parameter"
146
154
  ),
147
155
  "view.transform.generate-sql": "generate write sql query from natural language intent question",
148
156
  "view.transform.increment-date": "add subtract days months years to a date column shift",
@@ -286,15 +286,39 @@ def brief_view_record(record: Any) -> Any:
286
286
  return {key: record[key] for key in BRIEF_VIEW_FIELDS if key in record}
287
287
 
288
288
 
289
+ #: dataset.list_all's SDK symbol, reused from the pagination-safe helper item
290
+ #: A's discovery fix relies on (mammoth/api/datasets.py) rather than a
291
+ #: single unpaginated page, so a project with many datasets is not silently
292
+ #: truncated here either.
293
+ _DATASETS_LIST_ALL_SYMBOL = "mammoth.api.datasets.DatasetsAPI.list_all"
294
+ #: Soft floor for the no-DATASET_ID ``view list`` walk: whole per-dataset
295
+ #: pages are pulled and appended until at least this many views have been
296
+ #: collected (or every dataset in the project has been visited), so one call
297
+ #: never silently returns a single dataset's worth from a large project.
298
+ _VIEW_LIST_ALL_DATASETS_MIN_VIEWS = 100
299
+
300
+
289
301
  def view_list(invocation: Invocation) -> HandlerResult:
290
- """List dataviews for a dataset in the active project."""
302
+ """List dataviews for a dataset, or every dataset in the active project.
303
+
304
+ Omitting DATASET_ID used to fail outright with ``missing_argument`` --
305
+ an agent's very first move is often ``view list`` before it knows any
306
+ dataset id. It now walks every dataset in the active project instead
307
+ (paged; see :data:`_VIEW_LIST_ALL_DATASETS_MIN_VIEWS`), same as passing
308
+ an explicit id still does for a single dataset.
309
+ """
291
310
  project_id = require_project(invocation)
292
- dataset_id = _require_int_positional_at(invocation, 0, "dataset id")
311
+ dataset_id = _int_positional_at(invocation, 0, "dataset id")
293
312
  document = invocation.load_input() or {}
294
- kwargs: dict[str, Any] = {"dataset_id": dataset_id, "project_id": project_id}
295
- _forward_optional(document, kwargs, ("limit", "sort"))
313
+ if dataset_id is None and document.get(_DATASET_ID_FIELD) is not None:
314
+ dataset_id = int(document[_DATASET_ID_FIELD])
296
315
  with open_service(invocation) as (service, auth):
297
- data = service.call(_symbol(invocation), **kwargs)
316
+ if dataset_id is None:
317
+ data = _view_list_across_project(service, _symbol(invocation), document, project_id)
318
+ else:
319
+ kwargs: dict[str, Any] = {"dataset_id": dataset_id, "project_id": project_id}
320
+ _forward_optional(document, kwargs, ("limit", "sort"))
321
+ data = service.call(_symbol(invocation), **kwargs)
298
322
  parents.remember_records(
299
323
  _profile_name(invocation), auth.workspace_id, data, project_id=project_id
300
324
  )
@@ -309,6 +333,45 @@ def view_list(invocation: Invocation) -> HandlerResult:
309
333
  return data, _meta(invocation, auth.workspace_id, project_id)
310
334
 
311
335
 
336
+ def _view_list_across_project(
337
+ service: Any, view_list_symbol: str, document: dict[str, Any], project_id: int
338
+ ) -> dict[str, Any]:
339
+ """Aggregate dataviews from every dataset in the project (paged).
340
+
341
+ Starts at ``dataset_offset`` (default 0) into the project's dataset
342
+ list, pulling whole per-dataset view pages until either every dataset
343
+ has been visited or at least ``_VIEW_LIST_ALL_DATASETS_MIN_VIEWS`` views
344
+ have been collected. ``next_dataset_offset`` names where to resume when
345
+ the project holds more datasets than were visited.
346
+ """
347
+ dataset_offset = int(document.get("dataset_offset", 0))
348
+ datasets_page = service.call(_DATASETS_LIST_ALL_SYMBOL, project_id=project_id)
349
+ datasets = datasets_page.get("datasets", []) if isinstance(datasets_page, dict) else []
350
+ view_kwargs: dict[str, Any] = {}
351
+ _forward_optional(document, view_kwargs, ("sort",))
352
+ dataviews: list[Any] = []
353
+ visited = dataset_offset
354
+ for index in range(dataset_offset, len(datasets)):
355
+ dataset = datasets[index]
356
+ visited = index + 1
357
+ dataset_id = dataset.get("id") if isinstance(dataset, dict) else None
358
+ if not isinstance(dataset_id, int):
359
+ continue
360
+ page = service.call(
361
+ view_list_symbol, dataset_id=dataset_id, project_id=project_id, **view_kwargs
362
+ )
363
+ for item in page.get("dataviews", []) if isinstance(page, dict) else []:
364
+ if isinstance(item, dict):
365
+ item = {**item, "dataset_id": dataset_id}
366
+ dataviews.append(item)
367
+ if len(dataviews) >= _VIEW_LIST_ALL_DATASETS_MIN_VIEWS:
368
+ break
369
+ result: dict[str, Any] = {"dataviews": dataviews, "datasets_visited": visited - dataset_offset}
370
+ if visited < len(datasets):
371
+ result["next_dataset_offset"] = visited
372
+ return result
373
+
374
+
312
375
  def view_bulk_delete(invocation: Invocation) -> HandlerResult:
313
376
  """Permanently delete several dataviews by id. Prompt or ``--yes`` required."""
314
377
  project_id = require_project(invocation)
@@ -778,15 +841,17 @@ def _relabel_and_check(
778
841
  if mapping is None or types is None:
779
842
  mapping, types = _column_profile(service, dataset_id, view_id, project_id)
780
843
  data = _relabel_columns(service, dataset_id, view_id, project_id, data, mapping)
781
- return _with_column_warnings(data, types, view_id)
844
+ return _with_column_warnings(data, types, view_id, dataset_id)
782
845
 
783
846
 
784
- def _with_column_warnings(data: Any, types: dict[str, str], view_id: int) -> Any:
847
+ def _with_column_warnings(
848
+ data: Any, types: dict[str, str], view_id: int, dataset_id: int | None = None
849
+ ) -> Any:
785
850
  """Add ``column_warnings`` for the rows of a data page (never fatal)."""
786
851
  if not isinstance(data, dict) or not isinstance(data.get(_ROWS_KEY), list) or not types:
787
852
  return data
788
853
  try:
789
- warnings = column_warnings(data[_ROWS_KEY], types, view_id)
854
+ warnings = column_warnings(data[_ROWS_KEY], types, view_id, dataset_id)
790
855
  except Exception: # noqa: BLE001 -- a presentation aid must not fail the read
791
856
  return data
792
857
  return {**data, "column_warnings": warnings} if warnings else data
@@ -1635,6 +1700,31 @@ def view_pipeline_edit(invocation: Invocation) -> HandlerResult:
1635
1700
  return data, _meta(invocation, auth.workspace_id, None)
1636
1701
 
1637
1702
 
1703
+ #: OpenAPI ``PipelineInfo`` (the exact ``get_pipeline`` payload) pins
1704
+ #: ``draft_mode`` as a top-level string enum and ``auto_run`` as a top-level
1705
+ #: bool. "dirty" means unsubmitted draft changes are pending; ``auto_run``
1706
+ #: false means new tasks stop short of computing. Both leave a dataview
1707
+ #: looking finished when it is not (WPP/T3 evidence: agents that never ran
1708
+ #: 'view pipeline get' had no way to notice either).
1709
+ _DRAFT_MODE_DIRTY = "dirty"
1710
+
1711
+
1712
+ def _pipeline_action_hint(view_id: int, data: dict[str, Any]) -> str | None:
1713
+ """Best-effort hint naming the exact command a stale pipeline needs."""
1714
+ if data.get("draft_mode") == _DRAFT_MODE_DIRTY:
1715
+ return (
1716
+ f"This view has an unsubmitted draft with pending changes; run "
1717
+ f"'mammoth view draft submit {view_id}' to apply them."
1718
+ )
1719
+ if data.get("auto_run") is False:
1720
+ return (
1721
+ f"Auto-run is off for this pipeline; new tasks will not compute "
1722
+ f"automatically. Run 'mammoth view pipeline rerun {view_id}' to "
1723
+ f"compute pending tasks now."
1724
+ )
1725
+ return None
1726
+
1727
+
1638
1728
  def view_pipeline_get(invocation: Invocation) -> HandlerResult:
1639
1729
  """Get a dataview's full pipeline."""
1640
1730
  dataview_id = _require_int_positional_at(invocation, 0, "dataview id")
@@ -1643,6 +1733,9 @@ def view_pipeline_get(invocation: Invocation) -> HandlerResult:
1643
1733
  _forward_optional(document, kwargs, ("dataset_id",))
1644
1734
  with open_service(invocation) as (service, auth):
1645
1735
  data = service.call(_symbol(invocation), **kwargs)
1736
+ hint = _pipeline_action_hint(dataview_id, data) if isinstance(data, dict) else None
1737
+ if hint is not None:
1738
+ data = {**data, "hint": hint}
1646
1739
  return data, _meta(invocation, auth.workspace_id, None)
1647
1740
 
1648
1741
 
@@ -2149,7 +2242,18 @@ def view_export_specialized(invocation: Invocation) -> HandlerResult:
2149
2242
  for name, parameter in signature.parameters.items()
2150
2243
  if name != "self" and parameter.kind is not inspect.Parameter.VAR_KEYWORD
2151
2244
  }
2152
- allowed = explicit_fields | set(_SPECIAL_EXPORT_COMMON_FIELDS) | {_DATASET_ID_FIELD}
2245
+ # The six common trigger controls only actually reach the SDK call when
2246
+ # the route's method has a **kwargs sink to carry them; ``to_dataset`` has
2247
+ # a closed signature (no **kwargs) and does not accept any of them.
2248
+ # Allowing them anyway let a field like ``end_of_pipeline`` pass this
2249
+ # check and then crash the call itself with an opaque "unexpected keyword
2250
+ # argument" TypeError instead of a clear unknown_input_field.
2251
+ accepts_var_keyword = any(
2252
+ parameter.kind is inspect.Parameter.VAR_KEYWORD
2253
+ for parameter in signature.parameters.values()
2254
+ )
2255
+ common_fields = _SPECIAL_EXPORT_COMMON_FIELDS if accepts_var_keyword else frozenset()
2256
+ allowed = explicit_fields | common_fields | {_DATASET_ID_FIELD}
2153
2257
  unknown = sorted(set(document) - allowed)
2154
2258
  if unknown:
2155
2259
  raise CliError(
@@ -2290,7 +2394,7 @@ def upload_preview(service: Any, dataset_id: int, project_id: int | None) -> dic
2290
2394
  "sample_rows": rows[:_UPLOAD_SAMPLE_ROWS],
2291
2395
  }
2292
2396
  try:
2293
- warnings = column_warnings(rows, types, view_id) if rows else []
2397
+ warnings = column_warnings(rows, types, view_id, dataset_id) if rows else []
2294
2398
  except Exception: # noqa: BLE001
2295
2399
  warnings = []
2296
2400
  if warnings:
@@ -592,8 +592,13 @@ _S7_ADDITIONAL_INPUT_FIELDS: dict[str, tuple[FieldSpec, ...]] = {
592
592
  # The dataview GET takes a server-side projection: "__min", "__standard",
593
593
  # "__full" or a comma-separated field list.
594
594
  "view.get": (FieldSpec("fields", required=False, annotation=str | None, default=None),),
595
- # ``view list`` records are trimmed to the brief shape; ``full`` keeps them.
596
- "view.list": (FieldSpec("full", required=False, annotation=bool, default=False),),
595
+ # ``view list`` records are trimmed to the brief shape; ``full`` keeps
596
+ # them. ``dataset_offset`` resumes the no-DATASET_ID, every-dataset-in-
597
+ # the-project walk (item G) at a later dataset index.
598
+ "view.list": (
599
+ FieldSpec("full", required=False, annotation=bool, default=False),
600
+ FieldSpec("dataset_offset", required=False, annotation=int, default=0),
601
+ ),
597
602
  "activity.list": (
598
603
  FieldSpec("project_id", required=False, annotation=int | None, default=None),
599
604
  FieldSpec("workspace_id", required=False, annotation=int | None, default=None),
@@ -61,10 +61,43 @@ def _convert_hint(view_id: int | None, column: str, to: str) -> str:
61
61
  return f"mammoth view transform convert-type {target} --input '{spec}'"
62
62
 
63
63
 
64
+ def _duplicate_rows_warning(
65
+ rows: list[Mapping[str, Any]],
66
+ view_id: int | None,
67
+ dataset_id: int | None,
68
+ ) -> dict[str, Any] | None:
69
+ """Return a table-level warning when the page itself holds exact duplicates.
70
+
71
+ Scoped honestly to the rows actually read: an agent must not read this as
72
+ table-wide duplication without separately checking the full table (e.g.
73
+ ``view data aggregate`` COUNT vs a distinct count).
74
+ """
75
+ fingerprints = [json.dumps(row, sort_keys=True, default=str) for row in rows]
76
+ counts: dict[str, int] = {}
77
+ for fingerprint in fingerprints:
78
+ counts[fingerprint] = counts.get(fingerprint, 0) + 1
79
+ duplicate_count = sum(count - 1 for count in counts.values() if count > 1)
80
+ if not duplicate_count:
81
+ return None
82
+ warning: dict[str, Any] = {
83
+ "issue": "duplicate_rows",
84
+ "detail": (
85
+ f"{duplicate_count} of the {len(rows)} rows read are exact duplicates "
86
+ "of another row in this page (not checked table-wide)."
87
+ ),
88
+ "rows_checked": len(rows),
89
+ }
90
+ if view_id is not None and dataset_id is not None:
91
+ spec = json.dumps({"dataset_id": dataset_id})
92
+ warning["fix"] = f"mammoth view transform discard-duplicates {view_id} --input '{spec}'"
93
+ return warning
94
+
95
+
64
96
  def column_warnings(
65
97
  rows: Iterable[Mapping[str, Any]],
66
98
  column_types: Mapping[str, str],
67
99
  view_id: int | None = None,
100
+ dataset_id: int | None = None,
68
101
  ) -> list[dict[str, Any]]:
69
102
  """Return warnings for text columns that hold numbers or dates, and for blanks.
70
103
 
@@ -72,18 +105,24 @@ def column_warnings(
72
105
  rows: Row objects keyed by display name (a data page after relabeling).
73
106
  column_types: Display name to Mammoth type (``TEXT``, ``NUMERIC``, ...).
74
107
  view_id: The view the rows came from, used in the suggested command.
108
+ dataset_id: The view's dataset, used for the duplicate-rows fix
109
+ command (a mutation, so it needs the exact parent, not discovery).
75
110
 
76
111
  Returns:
77
112
  One record per finding: ``column``, ``issue``
78
- (``numbers_stored_as_text``, ``dates_stored_as_text`` or
79
- ``blank_values``), ``detail`` and, where one command fixes it, ``fix``.
80
- Counts are over the rows given (``rows_checked`` on each record).
113
+ (``numbers_stored_as_text``, ``dates_stored_as_text``,
114
+ ``blank_values`` or the table-level ``duplicate_rows``), ``detail``
115
+ and, where one command fixes it, ``fix``. Counts are over the rows
116
+ given (``rows_checked`` on each record).
81
117
  """
82
118
  materialised = [row for row in rows if isinstance(row, Mapping)]
83
119
  if not materialised:
84
120
  return []
85
121
  checked = len(materialised)
86
122
  warnings: list[dict[str, Any]] = []
123
+ duplicate_warning = _duplicate_rows_warning(materialised, view_id, dataset_id)
124
+ if duplicate_warning is not None:
125
+ warnings.append(duplicate_warning)
87
126
  for column, col_type in column_types.items():
88
127
  values = [row.get(column) for row in materialised if column in row]
89
128
  if not values:
@@ -109,6 +109,38 @@ def _job_recovery(job_id: object, *, profile: str | None = None) -> list[str]:
109
109
 
110
110
  _PROJECT_DELETE = re.compile(r"/workspaces/\d+/projects/(\d+)/?$")
111
111
  _DASHBOARD_WRITE = re.compile(r"/dashboards/(\d+)(?:/|$)")
112
+ _ENDPOINT_WORKSPACE = re.compile(r"/workspaces/(\d+)")
113
+
114
+
115
+ def _workspace_mismatch_error(exc: MammothAuthError, workspace_id: int | None) -> CliError | None:
116
+ """A 401 whose endpoint targets a workspace other than the profile's own
117
+ is a scoping mistake, not invalid credentials, so it must not tell the
118
+ user to reauthenticate (release evidence T4-L-002 — the in-product agent
119
+ parroted "reauthenticate" to a web user for exactly this case).
120
+ """
121
+ if workspace_id is None:
122
+ return None
123
+ endpoint = getattr(exc, "endpoint", None)
124
+ if not isinstance(endpoint, str):
125
+ return None
126
+ match = _ENDPOINT_WORKSPACE.search(endpoint)
127
+ if match is None:
128
+ return None
129
+ target_workspace_id = int(match.group(1))
130
+ if target_workspace_id == workspace_id:
131
+ return None
132
+ return CliError(
133
+ code=CODE_AUTHORIZATION_REQUIRED,
134
+ message=(
135
+ f"No access to workspace {target_workspace_id} "
136
+ f"(this sign-in is for workspace {workspace_id})."
137
+ ),
138
+ exit_status=EXIT_AUTH,
139
+ hint="Pass --workspace or use a profile signed in for that workspace.",
140
+ details=_metadata(exc),
141
+ request_id=exc.request_id,
142
+ authorization_required=True,
143
+ )
112
144
 
113
145
 
114
146
  def _resource_recovery(
@@ -161,6 +193,9 @@ def map_sdk_exception(
161
193
  )
162
194
 
163
195
  if isinstance(exc, MammothAuthError):
196
+ mismatch = _workspace_mismatch_error(exc, workspace_id)
197
+ if mismatch is not None:
198
+ return mismatch
164
199
  return CliError(
165
200
  code=CODE_AUTHENTICATION_FAILED,
166
201
  message="Mammoth rejected the provided credentials.",
@@ -168,6 +168,23 @@ POSITIONAL_OVERRIDES: dict[str, tuple[PositionalSpec, ...]] = {
168
168
  "dashboard.tags.merge": (
169
169
  PositionalSpec(name="tag_id", type=int, required=True, help="ID of the source tag."),
170
170
  ),
171
+ # Item G: an agent's first move is often `view list` before it has any
172
+ # dataset id; omitting it now walks every dataset in the active project
173
+ # (paged) instead of failing with missing_argument.
174
+ "view.list": (
175
+ PositionalSpec(
176
+ name="dataset_id",
177
+ type=int,
178
+ required=False,
179
+ falls_back_to_field="dataset_id",
180
+ help=(
181
+ "ID of the dataset. Omit it to list dataviews across every "
182
+ "dataset in the active project instead (paged; see the "
183
+ "'dataset_offset' input field and the 'next_dataset_offset' "
184
+ "result field)."
185
+ ),
186
+ ),
187
+ ),
171
188
  # Explicit parent avoids rich-view discovery for the release-scoped GET.
172
189
  "view.get": (
173
190
  PositionalSpec(name="view_id", type=int, required=True, help="ID of the view."),
@@ -913,6 +930,22 @@ EXACT_PARENT_HELP = (
913
930
  "'dataset_id' input field; only read commands may omit it and discover the parent."
914
931
  )
915
932
 
933
+ # Per-command overrides of ``EXACT_PARENT_HELP`` for a command whose own
934
+ # fields could otherwise be mistaken for this positional. ``view.export.dataset``
935
+ # also takes a ``target_ds_id`` input field naming the destination dataset;
936
+ # an agent that read only the generic text passed a destination id here
937
+ # instead and hit a not-found on the (unrelated) source view's own parent
938
+ # (WPP evidence c38/c39).
939
+ _EXACT_PARENT_HELP_OVERRIDES: dict[str, str] = {
940
+ "view.export.dataset": (
941
+ "Exact parent dataset ID of the SOURCE view being exported -- not the "
942
+ "destination. Required for this command: pass it here or as the "
943
+ "'dataset_id' input field. To write into an existing destination dataset "
944
+ "instead of creating one, use the 'target_ds_id' input field, not this "
945
+ "positional."
946
+ ),
947
+ }
948
+
916
949
 
917
950
  def _with_exact_parent_help(
918
951
  command_id: str, specs: tuple[PositionalSpec, ...]
@@ -926,13 +959,14 @@ def _with_exact_parent_help(
926
959
  record = command_by_id(command_id)
927
960
  if record is None or record.get("mutation_class", "read") == "read":
928
961
  return specs
962
+ help_text = _EXACT_PARENT_HELP_OVERRIDES.get(command_id, EXACT_PARENT_HELP)
929
963
  return tuple(
930
964
  (
931
965
  PositionalSpec(
932
966
  name=spec.name,
933
967
  type=spec.type,
934
968
  required=spec.required,
935
- help=EXACT_PARENT_HELP,
969
+ help=help_text,
936
970
  falls_back_to_field=spec.falls_back_to_field,
937
971
  fills_sdk_param=spec.fills_sdk_param,
938
972
  example_value=spec.example_value,
@@ -91,13 +91,36 @@ def is_opaque_mapping(annotation: Any) -> bool:
91
91
  return not args or (len(args) == 2 and args[1] is Any)
92
92
 
93
93
 
94
+ #: A leaf condition's ``value`` may be a literal, or a marker that binds it to
95
+ #: a project/workspace parameter's live value instead. The server deep-walks
96
+ #: task params for this exact shape and substitutes the parameter's current
97
+ #: value at execution time (``api/api/parameters/binding_extractor.py`` /
98
+ #: ``consts.py`` in mvc-service); the SDK's ``Condition.value`` already
99
+ #: forwards it untouched, so no SDK change is needed — only this schema was
100
+ #: silent about the shape existing.
101
+ _PARAMETER_BINDING_SCHEMA = {
102
+ "type": "object",
103
+ "description": (
104
+ "Bind to a project/workspace parameter's live value instead of a literal. "
105
+ "Create one first with 'parameter create' if it does not exist yet."
106
+ ),
107
+ "properties": {
108
+ "type": {"const": "parameter"},
109
+ "parameter_id": {"type": "integer"},
110
+ },
111
+ "required": ["type", "parameter_id"],
112
+ "additionalProperties": False,
113
+ "example": {"type": "parameter", "parameter_id": 12},
114
+ }
115
+
116
+
94
117
  def _condition_schema() -> dict[str, Any]:
95
118
  leaf = {
96
119
  "type": "object",
97
120
  "properties": {
98
121
  "column": {"type": "string", "example": "Status"},
99
122
  "operator": {"type": "string", "example": "EQ"},
100
- "value": {"example": "Active"},
123
+ "value": {"anyOf": [{"example": "Active"}, _PARAMETER_BINDING_SCHEMA]},
101
124
  "case_sensitive": {"type": "boolean"},
102
125
  "value_is_column": {"type": "boolean"},
103
126
  "component": {"type": "string"},
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "mammoth-cli"
3
- version = "2.0.49"
3
+ version = "2.0.51"
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"
7
7
  license = "LicenseRef-Proprietary"
8
8
  requires-python = ">=3.12,<3.15"
9
9
  dependencies = [
10
- "mammoth-io (>=0.7.21,<0.8)",
10
+ "mammoth-io (>=0.7.23,<0.8)",
11
11
  "typer (>=0.27,<0.28)",
12
12
  "rich (>=15,<16)",
13
13
  "platformdirs (>=4.10,<5)",
@@ -1881,8 +1881,10 @@ commands:
1881
1881
  required: false
1882
1882
  metavar: DATASET_ID
1883
1883
  falls_back_to_field: dataset_id
1884
- help: 'Exact parent dataset ID. Required for this command: pass it here or as the ''dataset_id'' input
1885
- field; only read commands may omit it and discover the parent.'
1884
+ help: 'Exact parent dataset ID of the SOURCE view being exported -- not the destination.
1885
+ Required for this command: pass it here or as the ''dataset_id'' input field. To write into
1886
+ an existing destination dataset instead of creating one, use the ''target_ds_id'' input field,
1887
+ not this positional.'
1886
1888
  options: []
1887
1889
  sdk_symbol: mammoth.view.ViewExport.to_dataset
1888
1890
  sdk_conversion: Call mammoth.view.ViewExport.to_dataset with validated request fields.
@@ -2994,10 +2996,12 @@ commands:
2994
2996
  positionals:
2995
2997
  - name: dataset_id
2996
2998
  type: int
2997
- required: true
2999
+ required: false
2998
3000
  metavar: DATASET_ID
2999
- falls_back_to_field: null
3000
- help: ID of the dataset.
3001
+ falls_back_to_field: dataset_id
3002
+ help: 'ID of the dataset. Omit it to list dataviews across every dataset in
3003
+ the active project instead (paged; see the ''dataset_offset'' input field
3004
+ and the ''next_dataset_offset'' result field).'
3001
3005
  options: []
3002
3006
  sdk_symbol: mammoth.api.dataviews.DataviewsAPI.list
3003
3007
  sdk_conversion: Call mammoth.api.dataviews.DataviewsAPI.list with validated request fields.
@@ -3013,7 +3017,7 @@ commands:
3013
3017
  contract_fixture: null
3014
3018
  required_fixture_guard: null
3015
3019
  secret_fields: []
3016
- human_example: mammoth view list DATASET_ID --help
3020
+ human_example: mammoth view list --help
3017
3021
  agent_example: mammoth view list 123
3018
3022
  unit_tests:
3019
3023
  - UT-VIEW-LIST
File without changes
File without changes
File without changes