mammoth-cli 2.2.13__tar.gz → 2.2.15__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 (255) hide show
  1. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/PKG-INFO +2 -2
  2. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/__init__.py +1 -1
  3. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/activity.py +12 -15
  4. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/browse.py +27 -3
  5. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/dataset.py +74 -9
  6. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/registry.py +1 -1
  7. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/schema.py +2 -1
  8. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/view.py +84 -6
  9. mammoth_cli-2.2.15/mammoth_cli/services/local_time.py +19 -0
  10. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/positionals.py +3 -2
  11. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/read_queries.py +42 -3
  12. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/pyproject.toml +2 -2
  13. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/dataset.yaml +4 -3
  14. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/sdk-methods.yaml +3 -3
  15. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/.gitignore +0 -0
  16. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/LICENSE +0 -0
  17. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/README.md +0 -0
  18. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/__main__.py +0 -0
  19. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/app.py +0 -0
  20. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +0 -0
  21. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/about-mammoth.md +0 -0
  22. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +0 -0
  23. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities-misc.md +0 -0
  24. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities.md +0 -0
  25. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/command-index.md +0 -0
  26. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/activity.md +0 -0
  27. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/addon.md +0 -0
  28. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/agent.md +0 -0
  29. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/ai.md +0 -0
  30. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/annotation.md +0 -0
  31. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/auth.md +0 -0
  32. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/automation.md +0 -0
  33. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/batch.md +0 -0
  34. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/billing.md +0 -0
  35. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/browse.md +0 -0
  36. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/calc.md +0 -0
  37. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/capability.md +0 -0
  38. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/client-app.md +0 -0
  39. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/completion.md +0 -0
  40. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/config.md +0 -0
  41. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/connector.md +0 -0
  42. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/context.md +0 -0
  43. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-qa.md +0 -0
  44. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-style.md +0 -0
  45. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-template.md +0 -0
  46. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard.md +0 -0
  47. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/data-app.md +0 -0
  48. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dataset.md +0 -0
  49. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/doctor.md +0 -0
  50. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/external-key.md +0 -0
  51. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/file.md +0 -0
  52. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/folder.md +0 -0
  53. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/job.md +0 -0
  54. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/link.md +0 -0
  55. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/log.md +0 -0
  56. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/notification.md +0 -0
  57. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/parameter.md +0 -0
  58. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/project.md +0 -0
  59. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/report.md +0 -0
  60. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schedule.md +0 -0
  61. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schema.md +0 -0
  62. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/skill.md +0 -0
  63. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/snippet.md +0 -0
  64. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/support.md +0 -0
  65. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/template.md +0 -0
  66. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/trash.md +0 -0
  67. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/upgrade.md +0 -0
  68. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/user.md +0 -0
  69. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/version.md +0 -0
  70. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view-transform.md +0 -0
  71. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view.md +0 -0
  72. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/webhook.md +0 -0
  73. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workflow.md +0 -0
  74. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workspace.md +0 -0
  75. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/handoff.md +0 -0
  76. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/input.md +0 -0
  77. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +0 -0
  78. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +0 -0
  79. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/operations.md +0 -0
  80. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/auth-scope.md +0 -0
  81. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/cleanup.md +0 -0
  82. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/dashboards.md +0 -0
  83. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/database-import.md +0 -0
  84. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/end-to-end.md +0 -0
  85. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/exports.md +0 -0
  86. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/index.md +0 -0
  87. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/need-action.md +0 -0
  88. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/resources.md +0 -0
  89. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/scheduling.md +0 -0
  90. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/transforms.md +0 -0
  91. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/typed-etl-discovery.md +0 -0
  92. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +0 -0
  93. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/report-checklist.md +0 -0
  94. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/retention.md +0 -0
  95. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +0 -0
  96. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/bundled_skill/mammoth-cli/references/task-start.md +0 -0
  97. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/__init__.py +0 -0
  98. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/addon.py +0 -0
  99. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/agent.py +0 -0
  100. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/ai.py +0 -0
  101. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/annotation.py +0 -0
  102. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/auth.py +0 -0
  103. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/automation.py +0 -0
  104. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/batch.py +0 -0
  105. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/billing.py +0 -0
  106. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/calc.py +0 -0
  107. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/capability.py +0 -0
  108. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/client_app.py +0 -0
  109. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/completion.py +0 -0
  110. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/config.py +0 -0
  111. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/connector.py +0 -0
  112. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/context.py +0 -0
  113. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/dashboard.py +0 -0
  114. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/data_app.py +0 -0
  115. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/doctor.py +0 -0
  116. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/external_key.py +0 -0
  117. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/file.py +0 -0
  118. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/folder.py +0 -0
  119. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/job.py +0 -0
  120. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/link.py +0 -0
  121. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/notification.py +0 -0
  122. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/parameter.py +0 -0
  123. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/project.py +0 -0
  124. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/report.py +0 -0
  125. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/schedule.py +0 -0
  126. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/skill.py +0 -0
  127. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/snippet.py +0 -0
  128. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/support.py +0 -0
  129. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/template.py +0 -0
  130. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/trash.py +0 -0
  131. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/upgrade.py +0 -0
  132. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/user.py +0 -0
  133. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/view_ops.py +0 -0
  134. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/view_profile.py +0 -0
  135. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/webhook.py +0 -0
  136. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/workflow.py +0 -0
  137. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/commands/workspace.py +0 -0
  138. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/context/__init__.py +0 -0
  139. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/context/credentials.py +0 -0
  140. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/context/endpoint.py +0 -0
  141. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/context/profiles.py +0 -0
  142. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/context/resolver.py +0 -0
  143. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/contracts/__init__.py +0 -0
  144. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/contracts/auth.py +0 -0
  145. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/embed.py +0 -0
  146. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/errors/__init__.py +0 -0
  147. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/errors/envelope.py +0 -0
  148. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/manifest/__init__.py +0 -0
  149. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/manifest/loader.py +0 -0
  150. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/messages/__init__.py +0 -0
  151. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/output/__init__.py +0 -0
  152. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/output/envelope.py +0 -0
  153. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/output/normalize.py +0 -0
  154. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/output/policy.py +0 -0
  155. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/output/progress.py +0 -0
  156. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/output/render.py +0 -0
  157. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/py.typed +0 -0
  158. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/__init__.py +0 -0
  159. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/confirm.py +0 -0
  160. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/dataset_health.py +0 -0
  161. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/dryrun.py +0 -0
  162. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/dryrun_targets.py +0 -0
  163. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/embedded.py +0 -0
  164. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/executor.py +0 -0
  165. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/input_loader.py +0 -0
  166. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/intent_only.py +0 -0
  167. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/invocation.py +0 -0
  168. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/new_data.py +0 -0
  169. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/options.py +0 -0
  170. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/parents.py +0 -0
  171. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/runlog.py +0 -0
  172. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/session.py +0 -0
  173. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/state.py +0 -0
  174. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/strict.py +0 -0
  175. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/updates.py +0 -0
  176. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/validate.py +0 -0
  177. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/runtime/verify.py +0 -0
  178. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/__init__.py +0 -0
  179. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/argspec.py +0 -0
  180. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/board_values.py +0 -0
  181. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/coerce.py +0 -0
  182. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/command_contract.py +0 -0
  183. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/conditions.py +0 -0
  184. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/dashboard_filters.py +0 -0
  185. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/dashboard_pages.py +0 -0
  186. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/dashboard_review.py +0 -0
  187. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/data_profile.py +0 -0
  188. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/data_quality.py +0 -0
  189. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/dispatch.py +0 -0
  190. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/factory.py +0 -0
  191. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/input_fields.py +0 -0
  192. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/listing.py +0 -0
  193. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/mapping.py +0 -0
  194. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/openapi_types.py +0 -0
  195. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/protocol.py +0 -0
  196. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/sdk_service.py +0 -0
  197. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/stored_stats.py +0 -0
  198. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/testing.py +0 -0
  199. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/text_dates.py +0 -0
  200. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/type_system.py +0 -0
  201. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/services/write_impact.py +0 -0
  202. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/skills/__init__.py +0 -0
  203. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/skills/installer.py +0 -0
  204. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/skills/steering.py +0 -0
  205. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/mammoth_cli/testing.py +0 -0
  206. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/activity.yaml +0 -0
  207. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/addon.yaml +0 -0
  208. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/agent.yaml +0 -0
  209. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/ai.yaml +0 -0
  210. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/annotation.yaml +0 -0
  211. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/auth.yaml +0 -0
  212. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/automation.yaml +0 -0
  213. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/batch.yaml +0 -0
  214. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/billing.yaml +0 -0
  215. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/browse.yaml +0 -0
  216. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/calc.yaml +0 -0
  217. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/capability.yaml +0 -0
  218. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/client-app.yaml +0 -0
  219. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/completion.yaml +0 -0
  220. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/config.yaml +0 -0
  221. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/connector.yaml +0 -0
  222. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/context.yaml +0 -0
  223. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/dashboard.yaml +0 -0
  224. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/data-app.yaml +0 -0
  225. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/doctor.yaml +0 -0
  226. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/external-key.yaml +0 -0
  227. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/file.yaml +0 -0
  228. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/folder.yaml +0 -0
  229. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/job.yaml +0 -0
  230. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/link.yaml +0 -0
  231. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/log.yaml +0 -0
  232. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/notification.yaml +0 -0
  233. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/parameter.yaml +0 -0
  234. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/project.yaml +0 -0
  235. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/report.yaml +0 -0
  236. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/schedule.yaml +0 -0
  237. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/schema.yaml +0 -0
  238. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/skill.yaml +0 -0
  239. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/snippet.yaml +0 -0
  240. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/support.yaml +0 -0
  241. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/template.yaml +0 -0
  242. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/trash.yaml +0 -0
  243. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/upgrade.yaml +0 -0
  244. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/user.yaml +0 -0
  245. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/version.yaml +0 -0
  246. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/view.yaml +0 -0
  247. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/webhook.yaml +0 -0
  248. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/workflow.yaml +0 -0
  249. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/commands/workspace.yaml +0 -0
  250. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/edits-target.source.yaml +0 -0
  251. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/openapi-operations.yaml +0 -0
  252. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/schema-v1.json +0 -0
  253. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/manifests/sdk-catalog.source.yaml +0 -0
  254. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/spec/openapi/metadata.json +0 -0
  255. {mammoth_cli-2.2.13 → mammoth_cli-2.2.15}/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.13
3
+ Version: 2.2.15
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.9,>=0.8.13
20
+ Requires-Dist: mammoth-io<0.9,>=0.8.15
21
21
  Requires-Dist: platformdirs<5,>=4.10
22
22
  Requires-Dist: pydantic<3,>=2.12.5
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.2.13"
9
+ __version__ = "2.2.15"
10
10
 
11
11
  # The versioned machine output envelope contract.
12
12
  SCHEMA_VERSION = 1
@@ -26,6 +26,7 @@ from mammoth_cli.manifest.loader import command_by_id
26
26
  from mammoth_cli.runtime.invocation import Invocation
27
27
  from mammoth_cli.runtime.session import open_service, resolved_project
28
28
  from mammoth_cli.services.command_contract import bind_command_inputs
29
+ from mammoth_cli.services.local_time import local_time
29
30
 
30
31
  HandlerResult = tuple[Any, dict[str, Any]]
31
32
 
@@ -164,6 +165,9 @@ def _meta(invocation: Invocation, workspace_id: int, project_id: int | None) ->
164
165
  _WORKSPACE_USERS_SYMBOL = "mammoth.api.workspace.WorkspaceAPI.list_users"
165
166
  _TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
166
167
  _EPOCH = "1970-01-01 00:00:00"
168
+ # The route's default fields carry every entry's full details (tens of KB per page);
169
+ # a change list needs only these. ``path`` names the dataset and view by name and id.
170
+ _LIST_FIELDS = "id,category,name_key,primary_object,result,created_at,user_id,path"
167
171
 
168
172
 
169
173
  def _close_time_window(kwargs: dict[str, Any]) -> str | None:
@@ -184,19 +188,6 @@ def _close_time_window(kwargs: dict[str, Any]) -> str | None:
184
188
  return f"start_time was not given; the window starts at {_EPOCH} UTC."
185
189
 
186
190
 
187
- def _local_time(stamp: Any) -> str | None:
188
- """A ``created_at`` (UTC if it has no offset) as ISO in this machine's timezone."""
189
- if not isinstance(stamp, str):
190
- return None
191
- try:
192
- moment = datetime.fromisoformat(stamp)
193
- except ValueError:
194
- return stamp
195
- if moment.tzinfo is None:
196
- moment = moment.replace(tzinfo=UTC)
197
- return moment.astimezone().isoformat(timespec="seconds")
198
-
199
-
200
191
  def _user_emails(service: Any) -> dict[Any, str]:
201
192
  """Workspace users by id; empty when the caller may not list them."""
202
193
  try:
@@ -206,6 +197,11 @@ def _user_emails(service: Any) -> dict[Any, str]:
206
197
  return {u["id"]: u["email"] for u in users if isinstance(u, dict) and u.get("email")}
207
198
 
208
199
 
200
+ def _readable_path(path: Any) -> str | None:
201
+ """The entry's ``workspace:name:id/project:name:id/dataset:name:id/...`` chain."""
202
+ return path.strip('{}"') if isinstance(path, str) else None
203
+
204
+
209
205
  def _change(entry: dict[str, Any], emails: dict[Any, str]) -> dict[str, Any]:
210
206
  """One log entry as a line of a change list: when, who, what, on which object."""
211
207
  raw_details = entry.get("details")
@@ -214,13 +210,14 @@ def _change(entry: dict[str, Any], emails: dict[Any, str]) -> dict[str, Any]:
214
210
  primary: dict[str, Any] = raw_primary if isinstance(raw_primary, dict) else {}
215
211
  user_id = entry.get("user_id")
216
212
  change = {
217
- "when": _local_time(entry.get("created_at")),
213
+ "when": local_time(entry.get("created_at")),
218
214
  "user_id": user_id,
219
215
  "user": emails.get(user_id),
220
216
  "action": entry.get("name_key"),
221
217
  "category": entry.get("category"),
222
218
  "object": {"name": primary.get("name"), "resource_id": primary.get("resource_id")},
223
219
  "result": entry.get("result"),
220
+ "path": _readable_path(entry.get("path")),
224
221
  }
225
222
  if details.get("task_name"):
226
223
  change["task"] = details["task_name"]
@@ -248,7 +245,7 @@ def activity_list(invocation: Invocation) -> HandlerResult:
248
245
  """
249
246
  document = _bound_document(invocation)
250
247
  _refuse_bare_resource_id(document)
251
- kwargs: dict[str, Any] = {}
248
+ kwargs: dict[str, Any] = {"fields": _LIST_FIELDS}
252
249
  _forward_optional(document, kwargs, _LIST_OPTIONAL)
253
250
  note = _close_time_window(kwargs)
254
251
  with open_service(invocation) as (service, auth):
@@ -86,6 +86,26 @@ def _forward_optional(
86
86
  kwargs[field] = document[field]
87
87
 
88
88
 
89
+ #: The id a dataset or a view is known by elsewhere in the CLI; a hit's ``id`` is its tree id.
90
+ _OBJECT_ID_KEYS = {"datasource": "dataset_id", "dataview": "view_id"}
91
+
92
+
93
+ def _with_object_ids(data: Any) -> Any:
94
+ """Name the id a command takes: ``dataset_id`` / ``view_id`` beside the tree ``id``."""
95
+ rows = data.get("resources") if isinstance(data, dict) else None
96
+ if not isinstance(rows, list):
97
+ return data
98
+ named = [
99
+ (
100
+ {**row, key: row.get("object_id")}
101
+ if isinstance(row, dict) and (key := _OBJECT_ID_KEYS.get(str(row.get("resource_type"))))
102
+ else row
103
+ )
104
+ for row in rows
105
+ ]
106
+ return {**data, "resources": named}
107
+
108
+
89
109
  def browse_folder(invocation: Invocation) -> HandlerResult:
90
110
  """Browse resources inside one folder of the active project.
91
111
 
@@ -173,7 +193,7 @@ def browse_resources(invocation: Invocation) -> HandlerResult:
173
193
  )
174
194
  with open_service(invocation) as (service, auth):
175
195
  data = service.call(_symbol(invocation), **kwargs)
176
- return data, _meta(invocation, auth.workspace_id, project_id)
196
+ return _with_object_ids(data), _meta(invocation, auth.workspace_id, project_id)
177
197
 
178
198
 
179
199
  def browse_resource(invocation: Invocation) -> HandlerResult:
@@ -208,7 +228,11 @@ def browse_ancestors(invocation: Invocation) -> HandlerResult:
208
228
 
209
229
 
210
230
  def browse_search(invocation: Invocation) -> HandlerResult:
211
- """Search resources across every project of the workspace (resources v2)."""
231
+ """Search resources across every project of the workspace (resources v2).
232
+
233
+ ``resource_type`` ``dataset`` means the route's ``datasource``. Each dataset hit
234
+ carries ``dataset_id`` and each view hit ``view_id``: pass those, never the tree ``id``.
235
+ """
212
236
  document = invocation.load_input() or {}
213
237
  kwargs: dict[str, Any] = {}
214
238
  _forward_optional(
@@ -216,7 +240,7 @@ def browse_search(invocation: Invocation) -> HandlerResult:
216
240
  )
217
241
  with open_service(invocation) as (service, auth):
218
242
  data = service.call(_symbol(invocation), **kwargs)
219
- return data, _meta(invocation, auth.workspace_id, None)
243
+ return _with_object_ids(data), _meta(invocation, auth.workspace_id, None)
220
244
 
221
245
 
222
246
  def browse_resources_bulk(invocation: Invocation) -> HandlerResult:
@@ -33,6 +33,7 @@ from mammoth_cli.runtime.session import open_service, require_project
33
33
  from mammoth_cli.services.listing import (
34
34
  DATASET_ROW_FIELDS,
35
35
  ambiguity_note,
36
+ compact_columns,
36
37
  dataset_summary,
37
38
  fit_budget,
38
39
  name_hit,
@@ -202,7 +203,11 @@ _SEARCH_PAGE_SIZE = 100
202
203
 
203
204
 
204
205
  def search_hits(
205
- service: Any, resource_type: str, needle: str, project_id: int | None = None
206
+ service: Any,
207
+ resource_type: str,
208
+ needle: str,
209
+ project_id: int | None = None,
210
+ fields: str = "minimal",
206
211
  ) -> tuple[list[dict[str, Any]], bool]:
207
212
  """Resources of ``resource_type`` whose name contains ``needle``, from the search route.
208
213
 
@@ -222,11 +227,11 @@ def search_hits(
222
227
  for _ in range(_SEARCH_PAGES):
223
228
  page = service.call(
224
229
  symbol,
225
- search=needle,
230
+ search=needle or None,
226
231
  resource_type=resource_type,
227
232
  limit=_SEARCH_PAGE_SIZE,
228
233
  cursor=cursor,
229
- fields="minimal",
234
+ fields=fields,
230
235
  **scope,
231
236
  )
232
237
  rows += [r for r in page.get("resources", []) if isinstance(r, dict)]
@@ -274,6 +279,61 @@ def named_project(service: Any, project_id: int, visible: list[dict[str, Any]])
274
279
  return {"id": project_id, "name": record.get("name")}
275
280
 
276
281
 
282
+ def _requested_columns(document: dict[str, Any]) -> list[str]:
283
+ """The ``columns`` input as a list of names (a comma-separated string is split)."""
284
+ raw = document.get("columns")
285
+ if raw is None:
286
+ return []
287
+ names = raw.split(",") if isinstance(raw, str) else raw
288
+ if not isinstance(names, list) or not all(isinstance(n, str) for n in names):
289
+ raise CliError(
290
+ code=CODE_INVALID_ARGUMENT,
291
+ message="'columns' must be a list of column names (or one comma-separated string).",
292
+ exit_status=EXIT_USAGE,
293
+ )
294
+ return [n.strip() for n in names if n.strip()]
295
+
296
+
297
+ def _column_names(hit: dict[str, Any]) -> list[str]:
298
+ """Display names of the columns a full-field search hit carries."""
299
+ properties = hit.get("object_properties")
300
+ metadata = properties.get("metadata") if isinstance(properties, dict) else None
301
+ return [
302
+ str(c["display_name"])
303
+ for c in metadata or []
304
+ if isinstance(c, dict) and c.get("display_name")
305
+ ]
306
+
307
+
308
+ def _find_by_columns(
309
+ service: Any,
310
+ needle: str,
311
+ columns: list[str],
312
+ visible: list[dict[str, Any]],
313
+ ) -> tuple[list[dict[str, Any]], bool]:
314
+ """Datasets (named like ``needle``, when given) that have every one of ``columns``."""
315
+ hits, cut = search_hits(service, "datasource", needle, fields="full")
316
+ names = {p.get("id"): p.get("name") for p in visible}
317
+ wanted = [c.lower() for c in columns]
318
+ matches: list[dict[str, Any]] = []
319
+ for hit in hits:
320
+ have = _column_names(hit)
321
+ if not set(wanted) <= {c.lower() for c in have}:
322
+ continue
323
+ matches.append(
324
+ {
325
+ "project_id": hit.get("project_id"),
326
+ "project_name": names.get(hit.get("project_id")),
327
+ "id": hit.get("object_id"),
328
+ "name": hit.get("name"),
329
+ "rows": hit.get("row_count"),
330
+ "cols": len(have),
331
+ "columns": compact_columns([(c, None) for c in have], limit=None),
332
+ }
333
+ )
334
+ return matches, cut
335
+
336
+
277
337
  def dataset_find(invocation: Invocation) -> HandlerResult:
278
338
  """Search dataset names for a substring across every visible project.
279
339
 
@@ -326,9 +386,11 @@ def dataset_find(invocation: Invocation) -> HandlerResult:
326
386
  def dataset_list(invocation: Invocation) -> HandlerResult:
327
387
  """List datasets in the active project, newest first, each with what tells them apart.
328
388
 
329
- Every item carries its size, created/updated time, how its data arrived, and
330
- its column names and types (from the list route's stored ``stats``,
331
- ``sources`` and ``data_schema``), cut to fit the agent tool output cap.
389
+ Every item carries its created/updated time and how its data arrived (from the
390
+ list route's ``sources``), cut to fit the agent tool output cap. The list asks
391
+ only the cheap fields, so it holds no row counts or column names: ``view list
392
+ DATASET_ID`` shows a dataset's columns, and ``dataset find --input '{"columns":
393
+ [...]}'`` finds the dataset that has given columns.
332
394
  ``full: true`` returns the raw ``{id, name}`` list instead. ``name`` keeps only datasets
333
395
  whose name contains it (case-insensitive) across every page, as short rows without
334
396
  column lists; ``limit``/``offset`` then page over the matches.
@@ -347,7 +409,9 @@ def dataset_list(invocation: Invocation) -> HandlerResult:
347
409
  with open_service(invocation) as (service, auth):
348
410
  data = service.call(_symbol(invocation), **kwargs)
349
411
  if compact:
350
- data = _compact_dataset_list(data, kwargs.get("offset", 0), kwargs.get("sort"))
412
+ data = _compact_dataset_list(
413
+ data, kwargs.get("offset", 0), kwargs.get("sort"), kwargs.get("limit", 100)
414
+ )
351
415
  return data, _meta(invocation, auth.workspace_id, project_id)
352
416
 
353
417
 
@@ -388,7 +452,7 @@ def _dataset_name_search(
388
452
  return page, _meta(invocation, auth.workspace_id, project_id)
389
453
 
390
454
 
391
- def _compact_dataset_list(data: Any, offset: int, sort: str | None) -> Any:
455
+ def _compact_dataset_list(data: Any, offset: int, sort: str | None, limit: int) -> Any:
392
456
  """Summarise a dataset-list page and fit it to the tool output cap."""
393
457
  datasets = data.get("datasets") if isinstance(data, dict) else None
394
458
  if not isinstance(datasets, list):
@@ -404,7 +468,8 @@ def _compact_dataset_list(data: Any, offset: int, sort: str | None) -> Any:
404
468
  "stored sample_values per view (all_columns: true lists every column)."
405
469
  ),
406
470
  }
407
- if omitted or data.get("next"):
471
+ # The list route sends no ``next``: a page of ``limit`` rows may have a successor.
472
+ if omitted or data.get("next") or len(datasets) >= limit:
408
473
  result["more"] = True
409
474
  result["next_offset"] = int(offset) + len(kept)
410
475
  return result
@@ -718,7 +718,7 @@ HANDLERS: dict[str, Handler] = {
718
718
  "view.update": view_cmd.view_update,
719
719
  "view.data.get": view_cmd.view_data_get,
720
720
  "view.data.query": view_cmd.view_data_query,
721
- "view.data.aggregate": view_cmd.view_data_aggregate,
721
+ "view.data.aggregate": view_cmd.view_data_aggregate_rounded,
722
722
  "view.data.compare": view_cmd.view_data_compare,
723
723
  "view.data.explore": view_cmd.view_data_explore,
724
724
  "view.data.profile": view_profile_cmd.view_data_profile,
@@ -306,7 +306,8 @@ _COMMAND_DISCOVERY_PURPOSES = {
306
306
  "view.data.explore": (
307
307
  "explore trend trends trending over time per day week month quarter year by date "
308
308
  "distribution spread histogram top most common frequent values breakdown share "
309
- "percentage profile period year quarter month date range coverage figure"
309
+ "percentage profile period year quarter month date range coverage figure "
310
+ "cumulative"
310
311
  ),
311
312
  # "Give the West team their own copy they can change" / "duplicate this
312
313
  # dataset as an independent copy" / "clone it without changing the
@@ -22,6 +22,7 @@ from __future__ import annotations
22
22
 
23
23
  import dataclasses
24
24
  import inspect
25
+ import shlex
25
26
  from datetime import UTC, datetime
26
27
  from decimal import Decimal, InvalidOperation
27
28
  from typing import Any
@@ -56,6 +57,7 @@ from mammoth_cli.services.conditions import CONDITION_KWARG, compile_condition
56
57
  from mammoth_cli.services.dashboard_review import UPLOAD_NOTE, upload_hints
57
58
  from mammoth_cli.services.data_quality import column_warnings, duplicate_rows_fact
58
59
  from mammoth_cli.services.listing import DATASET_LIST_FIELDS, compact_view_list
60
+ from mammoth_cli.services.local_time import local_time
59
61
  from mammoth_cli.services.read_queries import ReadContext
60
62
 
61
63
  HandlerResult = tuple[Any, dict[str, Any]]
@@ -1690,6 +1692,7 @@ def view_data_aggregate(invocation: Invocation) -> HandlerResult:
1690
1692
  TEXT-date) column groups by the named part of the date -- ``Monday``..``Sunday``,
1691
1693
  ``January``..``December``, ``Q1``..``Q4``, the year -- in calendar order: the
1692
1694
  read-only way to answer by-weekday and by-month questions.
1695
+ Figures are rounded for display (2 decimals; 4 below 1).
1693
1696
  Never use ``view transform pivot`` just to read a number -- it mutates the
1694
1697
  view's pipeline.
1695
1698
  """
@@ -1745,6 +1748,15 @@ def view_data_aggregate(invocation: Invocation) -> HandlerResult:
1745
1748
  return data, meta
1746
1749
 
1747
1750
 
1751
+ def view_data_aggregate_rounded(invocation: Invocation) -> HandlerResult:
1752
+ """``view data aggregate`` as the user reads it: floats rounded (2 decimals; 4 below 1).
1753
+
1754
+ ``view data compare`` calls :func:`view_data_aggregate` itself, at full precision.
1755
+ """
1756
+ data, meta = view_data_aggregate(invocation)
1757
+ return read_queries.round_result_rows(data), meta
1758
+
1759
+
1748
1760
  def _compare_key_and_value_columns(document: dict[str, Any]) -> tuple[list[str], list[str]]:
1749
1761
  """Return the (key, value) display-column names an aggregate call over
1750
1762
  ``document`` will produce, without making the call -- mirrors the label
@@ -1930,7 +1942,9 @@ def _explore_text_dates(
1930
1942
  condition=condition,
1931
1943
  )
1932
1944
  page = (document.get("offset"), document.get("limit"))
1933
- rows = read_queries.apply_explore_order(response["data"], document.get("sort"), page)
1945
+ rows = read_queries.apply_explore_order(
1946
+ response["data"], document.get("sort"), page, bool(document.get("cumulative"))
1947
+ )
1934
1948
  return {**response, "data": rows}
1935
1949
 
1936
1950
 
@@ -1953,7 +1967,13 @@ def view_data_explore(invocation: Invocation) -> HandlerResult:
1953
1967
  An optional ``metric`` ``{"column": ..., "function": ...}`` (SUM, COUNT,
1954
1968
  AVG, MIN, MAX, STDDEV or DISTINCT_COUNT) adds a second aggregate per bucket over another column,
1955
1969
  and an optional ``condition`` filters rows first -- same as ``view data
1956
- aggregate``, which this command wraps for the raw PIVOT shape. Never use
1970
+ aggregate``, which this command wraps for the raw PIVOT shape. A date range
1971
+ is one ``and`` condition (``>=`` a start, ``<=`` an end); one year is
1972
+ ``{"column": ..., "operator": "=", "value": 2017, "component": "year"}``.
1973
+ ``cumulative: true`` adds a running total of the metric (of ``count`` without
1974
+ one) over the buckets in date order, whatever ``sort`` or ``limit`` shows:
1975
+ the answer to "cumulative / running total through X". Figures are rounded for
1976
+ display (2 decimals; 4 below 1). Never use
1957
1977
  ``view transform pivot`` just to explore a column; it mutates the
1958
1978
  pipeline.
1959
1979
  """
@@ -1989,6 +2009,8 @@ def view_data_explore(invocation: Invocation) -> HandlerResult:
1989
2009
  if isinstance(metric, dict):
1990
2010
  resolved_metric = _resolved_aggregate_item(metric, display_to_internal)
1991
2011
  as_map["agg_1"] = resolved_metric["as_name"]
2012
+ if document.get("cumulative"):
2013
+ as_map["cumulative"] = f"{as_map.get('agg_1', 'count')} (running total)"
1992
2014
  if bucket_dates:
1993
2015
  data = _explore_text_dates(reads, document, internal_column, resolved_metric)
1994
2016
  else:
@@ -2003,7 +2025,7 @@ def view_data_explore(invocation: Invocation) -> HandlerResult:
2003
2025
  data = read_queries.with_assumptions(reads, data)
2004
2026
  if not bucket_dates:
2005
2027
  data = read_queries.with_variant_hint(reads, data, column_arg)
2006
- return data, meta
2028
+ return read_queries.round_result_rows(data), meta
2007
2029
 
2008
2030
 
2009
2031
  def _explore_on_backend(
@@ -2028,7 +2050,9 @@ def _explore_on_backend(
2028
2050
  kwargs[CONDITION_KWARG] = compile_condition(spec).build(
2029
2051
  reads.display_to_internal or None, reads.column_types or None
2030
2052
  )
2031
- _forward_optional(document, kwargs, ("level", "sequence", "limit", "offset", "sort"))
2053
+ _forward_optional(
2054
+ document, kwargs, ("level", "sequence", "limit", "offset", "sort", "cumulative")
2055
+ )
2032
2056
  return reads.service.call(_symbol(invocation), **kwargs)
2033
2057
 
2034
2058
 
@@ -2581,10 +2605,30 @@ def view_version_list(invocation: Invocation) -> HandlerResult:
2581
2605
  _forward_optional(document, kwargs, ("fields", "sort", "limit", "offset", "name"))
2582
2606
  with open_service(invocation) as (service, auth):
2583
2607
  kwargs["dataset_id"] = _resolve_dataset_id(service, invocation, dataview_id, document)
2584
- data = service.call(_symbol(invocation), **kwargs)
2608
+ data = _with_version_times(service.call(_symbol(invocation), **kwargs))
2585
2609
  return data, _meta(invocation, auth.workspace_id, project_id)
2586
2610
 
2587
2611
 
2612
+ def _with_version_times(data: Any) -> Any:
2613
+ """Give each version a ``when`` with an explicit offset.
2614
+
2615
+ ``created_at`` is naive UTC and the server-made ``name`` ("30-Sep-2026 11:17 AM")
2616
+ is that same UTC wall time, so neither can be shown to a user as local time.
2617
+ """
2618
+ versions = data.get("versions") if isinstance(data, dict) else None
2619
+ if not isinstance(versions, list):
2620
+ return data
2621
+ stamped = [
2622
+ {**v, "when": local_time(v.get("created_at"))} if isinstance(v, dict) else v
2623
+ for v in versions
2624
+ ]
2625
+ return {
2626
+ **data,
2627
+ "versions": stamped,
2628
+ "time_note": "created_at and the version name are UTC; `when` carries its offset.",
2629
+ }
2630
+
2631
+
2588
2632
  def view_version_update(invocation: Invocation) -> HandlerResult:
2589
2633
  """Update one pipeline version by id. ``body`` is required."""
2590
2634
  project_id = require_project(invocation)
@@ -3474,7 +3518,14 @@ def view_export_specialized(invocation: Invocation) -> HandlerResult:
3474
3518
  int(target_ds_id),
3475
3519
  kwargs.get("column_mapping"),
3476
3520
  )
3477
- data = service.call_view(dataview_id, method, dataset_id=dataset_id, **kwargs)
3521
+ try:
3522
+ data = service.call_view(dataview_id, method, dataset_id=dataset_id, **kwargs)
3523
+ except CliError as error:
3524
+ if not (is_dataset_route and error.details.get("export_pending")):
3525
+ raise
3526
+ return _pending_dataset_export(error, dataview_id, result_project_id), _meta(
3527
+ invocation, auth.workspace_id, project_id
3528
+ )
3478
3529
  if is_dataset_route and isinstance(data, int):
3479
3530
  # The SDK returns the bare id of the dataset written to; name it, and
3480
3531
  # say which project it landed in, so the agent's next read is obvious.
@@ -3535,6 +3586,33 @@ def view_export_specialized(invocation: Invocation) -> HandlerResult:
3535
3586
  return data, _meta(invocation, auth.workspace_id, project_id)
3536
3587
 
3537
3588
 
3589
+ def _pending_dataset_export(error: CliError, view_id: int, project_id: int) -> dict[str, Any]:
3590
+ """A dataset export that was accepted but whose dataset id is not known yet.
3591
+
3592
+ The export was submitted and the server is writing it (or has written it); only
3593
+ the lookup of its id ran out of time. That is not a failure, so say what is known
3594
+ and how to read the dataset when it lands, never an error.
3595
+ """
3596
+ name = error.details.get("dataset_name")
3597
+ pending: dict[str, Any] = {
3598
+ "status": "pending",
3599
+ "source_view_id": view_id,
3600
+ "project_id": project_id,
3601
+ "refreshes_on_pipeline_run": True,
3602
+ "note": (
3603
+ "The export was submitted and is still being written, so its dataset id is not "
3604
+ "known yet. Do not export again: that would create a second dataset. Look it up "
3605
+ f"in a moment with the next command (its source reads 'export of view {view_id}')."
3606
+ ),
3607
+ }
3608
+ if name is not None:
3609
+ pending["dataset_name"] = name
3610
+ pending["next"] = f"mammoth dataset find {shlex.quote(str(name))} --project {project_id}"
3611
+ if error.details.get("dataset_id") is not None:
3612
+ pending["dataset_id"] = error.details["dataset_id"]
3613
+ return pending
3614
+
3615
+
3538
3616
  _EXPORTS_LIST_SYMBOL = "mammoth.api.exports.ExportsAPI.list"
3539
3617
  _INTERNAL_DATASET_HANDLER_TYPE = "internal_dataset"
3540
3618
 
@@ -0,0 +1,19 @@
1
+ """Timestamps from the backend are naive UTC; show them with an explicit offset."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import UTC, datetime
6
+ from typing import Any
7
+
8
+
9
+ def local_time(stamp: Any) -> str | None:
10
+ """A timestamp (UTC if it has no offset) as ISO in this machine's timezone."""
11
+ if not isinstance(stamp, str):
12
+ return None
13
+ try:
14
+ moment = datetime.fromisoformat(stamp)
15
+ except ValueError:
16
+ return stamp
17
+ if moment.tzinfo is None:
18
+ moment = moment.replace(tzinfo=UTC)
19
+ return moment.astimezone().isoformat(timespec="seconds")
@@ -644,8 +644,9 @@ POSITIONAL_OVERRIDES: dict[str, tuple[PositionalSpec, ...]] = {
644
644
  PositionalSpec(
645
645
  name="name_substring",
646
646
  type=str,
647
- required=True,
648
- help="Case-insensitive substring to match against dataset names.",
647
+ required=False,
648
+ help="Case-insensitive substring to match against dataset names; optional "
649
+ "when --input gives 'columns'.",
649
650
  example_value="sales",
650
651
  ),
651
652
  ),
@@ -19,13 +19,18 @@ from dataclasses import dataclass, field
19
19
  from datetime import date
20
20
  from typing import Any
21
21
 
22
- from mammoth.api.dataviews import _add_explore_percentages, _explore_sort_and_limit
22
+ from mammoth.api.dataviews import (
23
+ _add_explore_cumulative,
24
+ _add_explore_percentages,
25
+ _explore_sort_and_limit,
26
+ )
23
27
 
24
28
  from mammoth_cli.errors.envelope import (
25
29
  CODE_INVALID_ARGUMENTS,
26
30
  EXIT_USAGE,
27
31
  CliError,
28
32
  )
33
+ from mammoth_cli.output.normalize import DataRows
29
34
  from mammoth_cli.services import data_profile as dp
30
35
  from mammoth_cli.services import text_dates
31
36
  from mammoth_cli.services.conditions import CONDITION_KWARG, _resolve_operator
@@ -382,10 +387,15 @@ def pivot_with_text_dates(
382
387
 
383
388
 
384
389
  def apply_explore_order(
385
- rows: list[dict[str, Any]], sort: str | None, page: tuple[int | None, int | None]
390
+ rows: list[dict[str, Any]],
391
+ sort: str | None,
392
+ page: tuple[int | None, int | None],
393
+ cumulative: bool = False,
386
394
  ) -> list[dict[str, Any]]:
387
- """Percentages, order and paging for a bucketed explore, like the SDK's own."""
395
+ """Percentages, running total, order and paging for a bucketed explore, like the SDK's own."""
388
396
  _add_explore_percentages(rows)
397
+ if cumulative:
398
+ _add_explore_cumulative(rows)
389
399
  return _explore_sort_and_limit(rows, "DATE", sort, page)
390
400
 
391
401
 
@@ -628,3 +638,32 @@ def with_assumptions(ctx: ReadContext, data: Any) -> Any:
628
638
  if not ctx.assumptions or not isinstance(data, dict):
629
639
  return data
630
640
  return {**data, "text_dates": list(ctx.assumptions.values())}
641
+
642
+
643
+ # ---------------------------------------------------------------------------
644
+ # Display rounding
645
+ # ---------------------------------------------------------------------------
646
+
647
+
648
+ def _round_for_display(value: Any) -> Any:
649
+ """A float rounded to what a person reads: 2 decimals, 4 below 1 so a rate survives."""
650
+ if isinstance(value, bool) or not isinstance(value, float):
651
+ return value
652
+ return round(value, 2 if abs(value) >= 1 else 4)
653
+
654
+
655
+ def round_result_rows(data: Any) -> Any:
656
+ """``data`` with every float in its result rows rounded for display.
657
+
658
+ Sums and averages come back with float noise (``428257.2699000002``) that
659
+ would be quoted to a user as is. Only the answer rows are rounded; a caller
660
+ that joins or diffs figures reads the unrounded handler instead.
661
+ """
662
+ rows = data.get("data") if isinstance(data, dict) else None
663
+ if not isinstance(rows, list):
664
+ return data
665
+ rounded = [
666
+ {k: _round_for_display(v) for k, v in row.items()} if isinstance(row, dict) else row
667
+ for row in rows
668
+ ]
669
+ return {**data, "data": DataRows(rounded) if isinstance(rows, DataRows) else rounded}
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "mammoth-cli"
3
- version = "2.2.13"
3
+ version = "2.2.15"
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.8.13,<0.9)",
10
+ "mammoth-io (>=0.8.15,<0.9)",
11
11
  "typer (>=0.27,<0.28)",
12
12
  "rich (>=15,<16)",
13
13
  "platformdirs (>=4.10,<5)",
@@ -524,10 +524,11 @@ commands:
524
524
  positionals:
525
525
  - name: name_substring
526
526
  type: str
527
- required: true
527
+ required: false
528
528
  metavar: NAME_SUBSTRING
529
529
  falls_back_to_field: null
530
- help: Case-insensitive substring to match against dataset names.
530
+ help: Case-insensitive substring to match against dataset names; optional when --input
531
+ gives 'columns'.
531
532
  options: []
532
533
  sdk_symbol: mammoth_cli.commands.dataset.find
533
534
  sdk_conversion: Call mammoth_cli.commands.dataset.find with validated request fields.
@@ -543,7 +544,7 @@ commands:
543
544
  contract_fixture: null
544
545
  required_fixture_guard: null
545
546
  secret_fields: []
546
- human_example: mammoth dataset find NAME_SUBSTRING --help
547
+ human_example: mammoth dataset find --help
547
548
  agent_example: mammoth dataset find sales
548
549
  unit_tests:
549
550
  - UT-DATASET-FIND
@@ -428,8 +428,8 @@ methods:
428
428
  ''int | None'' = None, categories: ''list[Any] | None'' = None, activities: ''list[Any] | None''
429
429
  = None, resource_id: ''str | None'' = None, result: ''str | None'' = None, start_time: ''str |
430
430
  None'' = None, end_time: ''str | None'' = None, origin: ''str | None'' = None, user_ids: ''list[Any]
431
- | None'' = None, parent_id: ''int | None'' = None, search_text: ''str | None'' = None) -> ''dict[str,
432
- Any]'''
431
+ | None'' = None, parent_id: ''int | None'' = None, search_text: ''str | None'' = None, fields: ''str | None'' = None)
432
+ -> ''dict[str, Any]'''
433
433
  openapi_operation_ids: []
434
434
  canonical_command: activity.list
435
435
  alias_of: null
@@ -3997,7 +3997,7 @@ methods:
3997
3997
  reviewed_by: primary
3998
3998
  - sdk_symbol: mammoth.api.dataviews.DataviewsAPI.explore
3999
3999
  implementation_origin: mammoth.api.dataviews.DataviewsAPI.explore
4000
- signature: 'explore(self, dataset_id: ''int'', dataview_id: ''int'', column: ''str'', column_type: ''str | None'' = None, level: ''str | None'' = None, metric: ''dict[str, Any] | None'' = None, condition: ''dict[str, Any] | None'' = None, sequence: ''int | None'' = None, limit: ''int | None'' = None, offset: ''int | None'' = None, sort: ''str | None'' = None, workspace_id: ''int | None'' = None, project_id: ''int | None'' = None, timeout: ''int | None'' = None, poll_interval: ''int'' = 2) -> ''dict[str, Any]'''
4000
+ signature: 'explore(self, dataset_id: ''int'', dataview_id: ''int'', column: ''str'', column_type: ''str | None'' = None, level: ''str | None'' = None, metric: ''dict[str, Any] | None'' = None, condition: ''dict[str, Any] | None'' = None, sequence: ''int | None'' = None, limit: ''int | None'' = None, offset: ''int | None'' = None, sort: ''str | None'' = None, cumulative: ''bool'' = False, workspace_id: ''int | None'' = None, project_id: ''int | None'' = None, timeout: ''int | None'' = None, poll_interval: ''int'' = 2) -> ''dict[str, Any]'''
4001
4001
  openapi_operation_ids: []
4002
4002
  canonical_command: view.data.explore
4003
4003
  alias_of: null