mammoth-cli 2.0.30__tar.gz → 2.0.31__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 (221) hide show
  1. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/PKG-INFO +1 -1
  2. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/__init__.py +1 -1
  3. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/app.py +162 -60
  4. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +1 -1
  5. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/exports.md +4 -1
  6. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/need-action.md +7 -3
  7. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +7 -0
  8. mammoth_cli-2.0.31/mammoth_cli/manifest/loader.py +164 -0
  9. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/pyproject.toml +1 -1
  10. mammoth_cli-2.0.30/mammoth_cli/manifest/loader.py +0 -96
  11. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/.gitignore +0 -0
  12. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/LICENSE +0 -0
  13. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/README.md +0 -0
  14. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/__main__.py +0 -0
  15. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +0 -0
  16. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities-misc.md +0 -0
  17. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities.md +0 -0
  18. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/command-index.md +0 -0
  19. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/activity.md +0 -0
  20. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/addon.md +0 -0
  21. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/agent.md +0 -0
  22. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/ai.md +0 -0
  23. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/annotation.md +0 -0
  24. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/auth.md +0 -0
  25. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/automation.md +0 -0
  26. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/batch.md +0 -0
  27. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/billing.md +0 -0
  28. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/browse.md +0 -0
  29. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/capability.md +0 -0
  30. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/client-app.md +0 -0
  31. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/completion.md +0 -0
  32. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/config.md +0 -0
  33. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/connector.md +0 -0
  34. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/context.md +0 -0
  35. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard.md +0 -0
  36. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/data-app.md +0 -0
  37. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dataset.md +0 -0
  38. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/doctor.md +0 -0
  39. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/external-key.md +0 -0
  40. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/file.md +0 -0
  41. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/folder.md +0 -0
  42. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/job.md +0 -0
  43. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/log.md +0 -0
  44. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/notification.md +0 -0
  45. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/parameter.md +0 -0
  46. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/project.md +0 -0
  47. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/report.md +0 -0
  48. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schedule.md +0 -0
  49. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schema.md +0 -0
  50. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/skill.md +0 -0
  51. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/snippet.md +0 -0
  52. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/support.md +0 -0
  53. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/template.md +0 -0
  54. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/trash.md +0 -0
  55. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/upgrade.md +0 -0
  56. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/user.md +0 -0
  57. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/version.md +0 -0
  58. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view.md +0 -0
  59. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/webhook.md +0 -0
  60. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workflow.md +0 -0
  61. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workspace.md +0 -0
  62. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/handoff.md +0 -0
  63. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/input.md +0 -0
  64. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +0 -0
  65. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +0 -0
  66. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/operations.md +0 -0
  67. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/auth-scope.md +0 -0
  68. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/cleanup.md +0 -0
  69. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/dashboards.md +0 -0
  70. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/end-to-end.md +0 -0
  71. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/index.md +0 -0
  72. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/resources.md +0 -0
  73. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/transforms.md +0 -0
  74. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/typed-etl-discovery.md +0 -0
  75. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +0 -0
  76. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/report-checklist.md +0 -0
  77. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/retention.md +0 -0
  78. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/bundled_skill/mammoth-cli/references/task-start.md +0 -0
  79. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/__init__.py +0 -0
  80. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/activity.py +0 -0
  81. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/addon.py +0 -0
  82. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/agent.py +0 -0
  83. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/ai.py +0 -0
  84. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/annotation.py +0 -0
  85. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/auth.py +0 -0
  86. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/automation.py +0 -0
  87. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/batch.py +0 -0
  88. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/billing.py +0 -0
  89. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/browse.py +0 -0
  90. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/capability.py +0 -0
  91. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/client_app.py +0 -0
  92. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/completion.py +0 -0
  93. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/config.py +0 -0
  94. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/connector.py +0 -0
  95. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/context.py +0 -0
  96. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/dashboard.py +0 -0
  97. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/data_app.py +0 -0
  98. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/dataset.py +0 -0
  99. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/doctor.py +0 -0
  100. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/external_key.py +0 -0
  101. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/file.py +0 -0
  102. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/folder.py +0 -0
  103. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/job.py +0 -0
  104. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/notification.py +0 -0
  105. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/parameter.py +0 -0
  106. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/project.py +0 -0
  107. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/registry.py +0 -0
  108. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/report.py +0 -0
  109. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/schedule.py +0 -0
  110. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/schema.py +0 -0
  111. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/skill.py +0 -0
  112. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/snippet.py +0 -0
  113. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/support.py +0 -0
  114. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/template.py +0 -0
  115. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/trash.py +0 -0
  116. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/upgrade.py +0 -0
  117. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/user.py +0 -0
  118. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/view.py +0 -0
  119. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/view_ops.py +0 -0
  120. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/webhook.py +0 -0
  121. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/workflow.py +0 -0
  122. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/commands/workspace.py +0 -0
  123. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/context/__init__.py +0 -0
  124. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/context/credentials.py +0 -0
  125. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/context/endpoint.py +0 -0
  126. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/context/profiles.py +0 -0
  127. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/context/resolver.py +0 -0
  128. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/contracts/__init__.py +0 -0
  129. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/contracts/auth.py +0 -0
  130. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/errors/__init__.py +0 -0
  131. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/errors/envelope.py +0 -0
  132. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/manifest/__init__.py +0 -0
  133. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/messages/__init__.py +0 -0
  134. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/output/__init__.py +0 -0
  135. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/output/envelope.py +0 -0
  136. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/output/normalize.py +0 -0
  137. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/output/policy.py +0 -0
  138. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/output/progress.py +0 -0
  139. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/output/render.py +0 -0
  140. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/py.typed +0 -0
  141. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/__init__.py +0 -0
  142. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/confirm.py +0 -0
  143. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/dryrun.py +0 -0
  144. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/executor.py +0 -0
  145. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/input_loader.py +0 -0
  146. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/invocation.py +0 -0
  147. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/options.py +0 -0
  148. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/parents.py +0 -0
  149. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/runlog.py +0 -0
  150. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/session.py +0 -0
  151. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/strict.py +0 -0
  152. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/updates.py +0 -0
  153. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/runtime/validate.py +0 -0
  154. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/__init__.py +0 -0
  155. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/argspec.py +0 -0
  156. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/coerce.py +0 -0
  157. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/command_contract.py +0 -0
  158. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/conditions.py +0 -0
  159. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/dispatch.py +0 -0
  160. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/factory.py +0 -0
  161. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/input_fields.py +0 -0
  162. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/mapping.py +0 -0
  163. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/openapi_types.py +0 -0
  164. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/positionals.py +0 -0
  165. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/protocol.py +0 -0
  166. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/sdk_service.py +0 -0
  167. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/testing.py +0 -0
  168. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/services/type_system.py +0 -0
  169. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/skills/__init__.py +0 -0
  170. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/skills/installer.py +0 -0
  171. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/skills/steering.py +0 -0
  172. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/mammoth_cli/testing.py +0 -0
  173. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/activity.yaml +0 -0
  174. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/addon.yaml +0 -0
  175. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/agent.yaml +0 -0
  176. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/ai.yaml +0 -0
  177. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/annotation.yaml +0 -0
  178. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/auth.yaml +0 -0
  179. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/automation.yaml +0 -0
  180. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/batch.yaml +0 -0
  181. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/billing.yaml +0 -0
  182. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/browse.yaml +0 -0
  183. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/capability.yaml +0 -0
  184. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/client-app.yaml +0 -0
  185. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/completion.yaml +0 -0
  186. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/config.yaml +0 -0
  187. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/connector.yaml +0 -0
  188. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/context.yaml +0 -0
  189. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/dashboard.yaml +0 -0
  190. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/data-app.yaml +0 -0
  191. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/dataset.yaml +0 -0
  192. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/doctor.yaml +0 -0
  193. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/external-key.yaml +0 -0
  194. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/file.yaml +0 -0
  195. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/folder.yaml +0 -0
  196. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/job.yaml +0 -0
  197. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/log.yaml +0 -0
  198. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/notification.yaml +0 -0
  199. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/parameter.yaml +0 -0
  200. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/project.yaml +0 -0
  201. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/report.yaml +0 -0
  202. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/schedule.yaml +0 -0
  203. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/schema.yaml +0 -0
  204. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/skill.yaml +0 -0
  205. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/snippet.yaml +0 -0
  206. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/support.yaml +0 -0
  207. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/template.yaml +0 -0
  208. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/trash.yaml +0 -0
  209. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/upgrade.yaml +0 -0
  210. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/user.yaml +0 -0
  211. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/version.yaml +0 -0
  212. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/view.yaml +0 -0
  213. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/webhook.yaml +0 -0
  214. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/workflow.yaml +0 -0
  215. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/commands/workspace.yaml +0 -0
  216. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/openapi-operations.yaml +0 -0
  217. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/schema-v1.json +0 -0
  218. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/sdk-catalog.source.yaml +0 -0
  219. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/manifests/sdk-methods.yaml +0 -0
  220. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/spec/openapi/metadata.json +0 -0
  221. {mammoth_cli-2.0.30 → mammoth_cli-2.0.31}/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.30
3
+ Version: 2.0.31
4
4
  Summary: Command-line interface for the Mammoth Analytics platform
5
5
  Project-URL: homepage, https://mammoth.io
6
6
  Project-URL: repository, https://github.com/EdgeMetric/mammothsdk
@@ -6,7 +6,7 @@ not implement a second HTTP client and does not call private SDK members.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- __version__ = "2.0.30"
9
+ __version__ = "2.0.31"
10
10
 
11
11
  # The versioned machine output envelope contract.
12
12
  SCHEMA_VERSION = 1
@@ -330,6 +330,13 @@ class _LeafGroup(TyperGroup):
330
330
  class _EnvelopeGroup(TyperGroup):
331
331
  """Root group that renders Click usage errors as the machine error envelope.
332
332
 
333
+ It also materialises top-level command groups lazily (see
334
+ :func:`_load_top_level_group`): ``list_commands`` and ``get_command`` know
335
+ every group by name from the manifest, but a group's Click tree is only
336
+ built when it is invoked, completed or listed in ``--help``. Reading
337
+ ``commands`` directly materialises them all, so tools that walk the tree
338
+ still see the full surface.
339
+
333
340
  Click's standalone error handling prints a human ``Usage: ... Error: ...``
334
341
  message and exits, even under ``--output json``: an agent driving the CLI
335
342
  then receives un-parseable prose (or, for a leaf that also parents
@@ -345,6 +352,28 @@ class _EnvelopeGroup(TyperGroup):
345
352
  on.
346
353
  """
347
354
 
355
+ @property # type: ignore[override]
356
+ def commands(self) -> dict[str, Any]:
357
+ loaded: dict[str, Any] = self.__dict__.setdefault("_eager_commands", {})
358
+ for name in _LAZY_GROUP_NAMES:
359
+ if name not in loaded:
360
+ loaded[name] = _load_top_level_group(name)
361
+ return loaded
362
+
363
+ @commands.setter
364
+ def commands(self, value: Any) -> None:
365
+ self.__dict__["_eager_commands"] = dict(value or {})
366
+
367
+ def list_commands(self, ctx: Any) -> list[str]:
368
+ eager = self.__dict__.get("_eager_commands", {})
369
+ return [*eager, *(name for name in _LAZY_GROUP_NAMES if name not in eager)]
370
+
371
+ def get_command(self, ctx: Any, cmd_name: str) -> Any:
372
+ eager: dict[str, Any] = self.__dict__.setdefault("_eager_commands", {})
373
+ if cmd_name not in eager and cmd_name in _LAZY_GROUP_NAMES:
374
+ eager[cmd_name] = _load_top_level_group(cmd_name)
375
+ return eager.get(cmd_name)
376
+
348
377
  def main(self, *args: Any, **kwargs: Any) -> Any:
349
378
  if not kwargs.get("standalone_mode", True):
350
379
  return super().main(*args, **kwargs)
@@ -888,68 +917,55 @@ def _short_type(type_name: str) -> str:
888
917
  return " | ".join(part.strip().rsplit(".", 1)[-1] for part in text.split("|"))[:40]
889
918
 
890
919
 
891
- def build_app() -> typer.Typer:
892
- """Construct the full Typer command tree from the command manifests."""
893
- root = typer.Typer(
894
- name="mammoth",
895
- help=(
896
- "Command-line interface for the Mammoth Analytics platform.\n\n"
897
- "Start with 'mammoth doctor' to check access, 'mammoth schema find QUERY' "
898
- "to locate a command, and 'mammoth schema get COMMAND_ID' for its full input schema."
899
- ),
920
+ def _command_records() -> list[dict[str, Any]]:
921
+ return [r for r in load_commands() if r.get("disposition") != "alias"]
922
+
923
+
924
+ @cache
925
+ def _command_tree() -> tuple[dict[tuple[str, ...], str], frozenset[tuple[str, ...]]]:
926
+ """Map every command path to its id, and name the container nodes.
927
+
928
+ A "container command" sits at a node that also has deeper subcommands
929
+ (e.g. ``dataset file-settings``, which also has ``... undo`` / ``... update``).
930
+ """
931
+ path_to_command = {
932
+ tuple(r["command_path"].split()): r["command_id"] for r in _command_records()
933
+ }
934
+ prefixes: set[tuple[str, ...]] = set()
935
+ for tokens in path_to_command:
936
+ for depth in range(1, len(tokens)):
937
+ prefixes.add(tokens[:depth])
938
+ return path_to_command, frozenset(prefixes & path_to_command.keys())
939
+
940
+
941
+ def _group_typer(tokens: tuple[str, ...], command_id: str | None) -> typer.Typer:
942
+ if command_id is not None:
943
+ # This node is both a group and an invocable command.
944
+ sub = typer.Typer(
945
+ cls=_LeafGroup,
946
+ no_args_is_help=False,
947
+ invoke_without_command=True,
948
+ context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
949
+ )
950
+ sub.callback()(_build_leaf(command_id, is_group_callback=True))
951
+ return sub
952
+ return typer.Typer(
953
+ help=_GROUP_DESCRIPTIONS.get(tokens[0], f"Commands for {' '.join(tokens)}."),
900
954
  no_args_is_help=True,
901
- add_completion=True,
902
- cls=_EnvelopeGroup,
903
955
  context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
904
956
  )
905
957
 
906
- @root.callback()
907
- def _root(
908
- version: bool = typer.Option(
909
- False, "--version", callback=_version_callback, is_eager=True, help="Show version."
910
- ),
911
- ) -> None:
912
- """Root callback holding eager global options."""
913
958
 
914
- records = [r for r in load_commands() if r.get("disposition") != "alias"]
915
- path_to_command: dict[tuple[str, ...], str] = {
916
- tuple(r["command_path"].split()): r["command_id"] for r in records
917
- }
918
- all_paths = set(path_to_command)
919
- # A "container command" sits at a node that also has deeper subcommands
920
- # (e.g. `dataset file-settings`, which also has `... undo` / `... update`).
921
- containers = {
922
- tokens
923
- for tokens in all_paths
924
- if any(other != tokens and other[: len(tokens)] == tokens for other in all_paths)
925
- }
926
-
927
- groups: dict[tuple[str, ...], typer.Typer] = {(): root}
928
-
929
- def _make_group(tokens: tuple[str, ...]) -> typer.Typer:
930
- command_id = path_to_command.get(tokens)
931
- if command_id is not None:
932
- # This node is both a group and an invocable command.
933
- sub = typer.Typer(
934
- cls=_LeafGroup,
935
- no_args_is_help=False,
936
- invoke_without_command=True,
937
- context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
938
- )
939
- sub.callback()(_build_leaf(command_id, is_group_callback=True))
940
- else:
941
- sub = typer.Typer(
942
- help=_GROUP_DESCRIPTIONS.get(tokens[0], f"Commands for {' '.join(tokens)}."),
943
- no_args_is_help=True,
944
- context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
945
- )
946
- return sub
959
+ def _populate(base: tuple[str, ...], group: typer.Typer) -> None:
960
+ """Register every command below ``base`` (exclusive) on ``group``."""
961
+ path_to_command, containers = _command_tree()
962
+ groups: dict[tuple[str, ...], typer.Typer] = {base: group}
947
963
 
948
964
  def _group_for(tokens: tuple[str, ...]) -> typer.Typer:
949
965
  if tokens in groups:
950
966
  return groups[tokens]
951
967
  parent = _group_for(tokens[:-1])
952
- sub = _make_group(tokens)
968
+ sub = _group_typer(tokens, path_to_command.get(tokens))
953
969
  top_level = tokens[0]
954
970
  parent.add_typer(
955
971
  sub,
@@ -960,15 +976,16 @@ def build_app() -> typer.Typer:
960
976
  groups[tokens] = sub
961
977
  return sub
962
978
 
979
+ below = {t: c for t, c in path_to_command.items() if t[: len(base)] == base and t != base}
963
980
  # Ensure every container group exists (parents before children).
964
- for tokens in sorted(containers, key=len):
981
+ for tokens in sorted((t for t in below if t in containers), key=len):
965
982
  _group_for(tokens)
966
983
 
967
984
  # Register every non-container command as a leaf under its parent group.
968
985
  # A command_id with a bespoke, fully-typed callback overrides the generic
969
986
  # leaf at the same registered name and path; the manifest-driven surface
970
987
  # is otherwise unchanged.
971
- for tokens, command_id in sorted(path_to_command.items()):
988
+ for tokens, command_id in sorted(below.items()):
972
989
  if tokens in containers:
973
990
  continue
974
991
  record = command_by_id(command_id)
@@ -980,11 +997,88 @@ def build_app() -> typer.Typer:
980
997
  context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
981
998
  )(callback)
982
999
 
1000
+
1001
+ @cache
1002
+ def _top_level_typer(name: str) -> typer.Typer:
1003
+ """Build (once) the Typer sub-app for one top-level command group."""
1004
+ path_to_command, _ = _command_tree()
1005
+ sub = _group_typer((name,), path_to_command.get((name,)))
1006
+ _populate((name,), sub)
1007
+ return sub
1008
+
1009
+
1010
+ # Top-level groups are converted to Click on first use. Building the leaf
1011
+ # callbacks and ``--help`` text for all ~550 commands, then letting Typer
1012
+ # convert every one of them, used to cost most of the CLI's start-up time on
1013
+ # every invocation; a run touches one group.
1014
+ _LAZY_GROUP_NAMES: list[str] = []
1015
+ _LAZY_SETTINGS: dict[str, Any] = {}
1016
+
1017
+
1018
+ def _load_top_level_group(name: str) -> Any:
1019
+ from typer.models import TyperInfo
1020
+
1021
+ info = TyperInfo(
1022
+ _top_level_typer(name),
1023
+ name=name,
1024
+ help=_GROUP_DESCRIPTIONS.get(name, f"Commands for {name}."),
1025
+ rich_help_panel=_ROOT_HELP_PANELS.get(name),
1026
+ )
1027
+ return typer.main.get_group_from_info(info, **_LAZY_SETTINGS)
1028
+
1029
+
1030
+ def build_app() -> typer.Typer:
1031
+ """Construct the Typer command tree from the command manifests.
1032
+
1033
+ Top-level leaves (``doctor``, ``version``, ...) are registered here; every
1034
+ top-level group is materialised by :class:`_EnvelopeGroup` on demand.
1035
+ """
1036
+ root = typer.Typer(
1037
+ name="mammoth",
1038
+ help=(
1039
+ "Command-line interface for the Mammoth Analytics platform.\n\n"
1040
+ "Start with 'mammoth doctor' to check access, 'mammoth schema find QUERY' "
1041
+ "to locate a command, and 'mammoth schema get COMMAND_ID' for its full input schema."
1042
+ ),
1043
+ no_args_is_help=True,
1044
+ add_completion=True,
1045
+ cls=_EnvelopeGroup,
1046
+ context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
1047
+ )
1048
+
1049
+ @root.callback()
1050
+ def _root(
1051
+ version: bool = typer.Option(
1052
+ False, "--version", callback=_version_callback, is_eager=True, help="Show version."
1053
+ ),
1054
+ ) -> None:
1055
+ """Root callback holding eager global options."""
1056
+
1057
+ path_to_command, containers = _command_tree()
1058
+ top_names = sorted({tokens[0] for tokens in path_to_command})
1059
+ lazy: list[str] = []
1060
+ for name in top_names:
1061
+ if (name,) in containers or (name,) not in path_to_command:
1062
+ lazy.append(name)
1063
+ continue
1064
+ command_id = path_to_command[(name,)]
1065
+ root.command(
1066
+ name=name,
1067
+ help=_command_help(command_id, command_by_id(command_id)),
1068
+ rich_help_panel=_ROOT_HELP_PANELS.get(name),
1069
+ context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
1070
+ )(BESPOKE.get(command_id) or _build_leaf(command_id))
1071
+ _LAZY_GROUP_NAMES[:] = lazy
1072
+ _LAZY_SETTINGS.update(
1073
+ pretty_exceptions_short=root.pretty_exceptions_short,
1074
+ rich_markup_mode=root.rich_markup_mode,
1075
+ suggest_commands=root.suggest_commands,
1076
+ )
983
1077
  return root
984
1078
 
985
1079
 
986
1080
  def registered_command_paths() -> set[str]:
987
- """Return every registered command path by walking the built Typer tree.
1081
+ """Return every registered command path by walking the built Typer trees.
988
1082
 
989
1083
  Container commands (a node that is both a group and invocable) are counted
990
1084
  by their own path as well as their subcommands.
@@ -998,14 +1092,20 @@ def registered_command_paths() -> set[str]:
998
1092
  paths.add(" ".join((*prefix, name)))
999
1093
  for group in instance.registered_groups:
1000
1094
  sub = group.typer_instance
1001
- if sub is None or not group.name:
1095
+ name = group.name
1096
+ if sub is None or not name:
1002
1097
  continue
1003
- node = (*prefix, group.name)
1004
- if getattr(sub.info, "invoke_without_command", False) and sub.registered_callback:
1098
+ node = (*prefix, name)
1099
+ if sub.registered_callback is not None:
1005
1100
  paths.add(" ".join(node))
1006
1101
  walk(sub, node)
1007
1102
 
1008
- walk(build_app(), ())
1103
+ walk(app, ())
1104
+ for name in _LAZY_GROUP_NAMES:
1105
+ sub = _top_level_typer(name)
1106
+ if sub.registered_callback is not None:
1107
+ paths.add(name)
1108
+ walk(sub, (name,))
1009
1109
  return paths
1010
1110
 
1011
1111
 
@@ -1020,7 +1120,9 @@ def _root_click_command() -> Any:
1020
1120
  the tree is fixed for the process lifetime (:data:`app` is built once at
1021
1121
  import time).
1022
1122
  """
1023
- return typer.main.get_command(app)
1123
+ root: Any = typer.main.get_command(app)
1124
+ root.commands # noqa: B018 -- materialise every lazy top-level group
1125
+ return root
1024
1126
 
1025
1127
 
1026
1128
  def command_option_names(command_path: str) -> set[str]:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: mammoth-cli
3
- version: 2.0.30
3
+ version: 2.0.31
4
4
  description: "Use Mammoth Analytics from a terminal: install or authenticate the CLI, discover its live command contract, and safely manage projects, data, views, pipelines, dashboards, exports, and handoffs."
5
5
  ---
6
6
 
@@ -3,10 +3,13 @@
3
3
  ```bash
4
4
  mammoth schema find "view export"
5
5
  mammoth schema get view.export.csv
6
- mammoth view export csv VIEW_ID --project PROJECT_ID
6
+ mammoth view export csv VIEW_ID --project PROJECT_ID --input '{"dataset_id": DATASET_ID, "output_path": "out.csv"}'
7
7
  mammoth view export list VIEW_ID DATASET_ID --project PROJECT_ID
8
8
  ```
9
9
 
10
+ `view export csv` takes one positional (the view); `dataset_id` and
11
+ `output_path` go in `--input`. It is a download, not a pipeline step.
12
+
10
13
  If the export or its source dataset/view is requested as a deliverable, mark
11
14
  both as retained before starting. Wait/reconcile returned jobs. Verify local
12
15
  artifact headers, rows and hash;
@@ -30,9 +30,13 @@ mammoth dataset file-settings update DATASET_ID --project PROJECT_ID \
30
30
  ```
31
31
 
32
32
  The response is a job (`operation: "understand_csv"`). Poll `dataset get`
33
- until `status` leaves `need_action`; it normally reaches `ready` within a few
34
- seconds, after which `view list` returns the generated view and typed
35
- transforms can proceed. If the status becomes `error`, read `status_info`
33
+ (the record is under `data.dataset`, so read `data.dataset.status`) until
34
+ the status leaves `need_action`. It usually reaches `ready` within seconds;
35
+ on prague an ISO `YYYY-MM-DD` column was flagged ambiguous and took about
36
+ three minutes, so poll for up to five minutes before treating it as stuck.
37
+ Then `view list` returns the generated view and typed transforms can
38
+ proceed. Which environments ask differs: release processed the same file
39
+ straight to `ready`. If the status becomes `error`, read `status_info`
36
40
  and stop; do not retry with guessed settings.
37
41
 
38
42
  ## All-text CSV: `ready` without a view
@@ -44,6 +44,13 @@ mammoth view transform filter 144 --dry-run \
44
44
  # data.would_call.sdk_symbol = ...FilterOpsMixin.filter_rows, view_id 144, dataset_id 122
45
45
  ```
46
46
 
47
+ A dry run is a full process with the same reads as the real run. On a
48
+ hosted environment budget roughly 2 s per request (about 0.7 s of it TLS
49
+ setup from a cold process, the rest server time), so `view task list` →
50
+ `--dry-run` → write costs about 30–40 s per pipeline step. That is the
51
+ price of the procedure, not a hang; do not shorten `--timeout` below the
52
+ default to speed it up.
53
+
47
54
  ## Add-only edits to a pipeline someone else may touch
48
55
 
49
56
  Every `view transform *` and `view task add` accepts `expected_task_count`:
@@ -0,0 +1,164 @@
1
+ """Load and validate the reviewed parity and command manifests.
2
+
3
+ These manifests are the single source of truth for capability discovery, schema
4
+ discovery, help generation, the agent skill, and the parity report. Runtime code
5
+ loads them read-only; the build scripts generate them.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import functools
11
+ import hashlib
12
+ import json
13
+ import os
14
+ import tempfile
15
+ from dataclasses import dataclass
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ import platformdirs
20
+ import yaml
21
+
22
+ SPEC_ROOT = Path(__file__).resolve().parent.parent.parent / "spec"
23
+ MANIFEST_DIR = SPEC_ROOT / "manifests"
24
+ COMMANDS_DIR = MANIFEST_DIR / "commands"
25
+ SCHEMA_PATH = MANIFEST_DIR / "schema-v1.json"
26
+ OPENAPI_OPERATIONS_PATH = MANIFEST_DIR / "openapi-operations.yaml"
27
+ SDK_METHODS_PATH = MANIFEST_DIR / "sdk-methods.yaml"
28
+
29
+ MANIFEST_SCHEMA_VERSION = 1
30
+ CACHE_ENV = "MAMMOTH_CLI_MANIFEST_CACHE"
31
+
32
+ # libyaml parses the 800 KiB of command manifests in ~0.2 s; the pure-Python
33
+ # loader takes ~2 s, which used to be most of the CLI's start-up time.
34
+ _YAML_LOADER = getattr(yaml, "CSafeLoader", yaml.SafeLoader)
35
+
36
+
37
+ def _read_yaml(path: Path) -> Any:
38
+ if not path.exists():
39
+ return None
40
+ return yaml.load(path.read_text(encoding="utf-8"), Loader=_YAML_LOADER) # noqa: S506
41
+
42
+
43
+ def cache_dir() -> Path:
44
+ """Return the directory the parsed-manifest cache lives in (not created)."""
45
+ return Path(platformdirs.user_cache_dir("mammoth-cli", "Mammoth"))
46
+
47
+
48
+ def _cache_enabled() -> bool:
49
+ return os.environ.get(CACHE_ENV, "1").strip().lower() not in {"0", "false", "no", "off"}
50
+
51
+
52
+ def _commands_fingerprint(paths: list[Path]) -> str:
53
+ """Identify the exact set of manifest files by name, size and mtime."""
54
+ digest = hashlib.sha256()
55
+ digest.update(str(MANIFEST_SCHEMA_VERSION).encode())
56
+ for path in paths:
57
+ stat = path.stat()
58
+ digest.update(f"{path.name}\0{stat.st_size}\0{stat.st_mtime_ns}\n".encode())
59
+ return digest.hexdigest()[:24]
60
+
61
+
62
+ def _read_commands_cache(path: Path) -> list[dict[str, Any]] | None:
63
+ try:
64
+ data = json.loads(path.read_text(encoding="utf-8"))
65
+ except (OSError, ValueError):
66
+ return None
67
+ if not isinstance(data, list) or not all(isinstance(item, dict) for item in data):
68
+ return None
69
+ return data
70
+
71
+
72
+ def _write_commands_cache(path: Path, records: list[dict[str, Any]]) -> None:
73
+ """Write the cache atomically; a cache that cannot be written is simply skipped."""
74
+ try:
75
+ path.parent.mkdir(parents=True, exist_ok=True)
76
+ fd, tmp = tempfile.mkstemp(dir=path.parent, prefix=path.name, suffix=".tmp")
77
+ with os.fdopen(fd, "w", encoding="utf-8") as handle:
78
+ json.dump(records, handle, separators=(",", ":"))
79
+ os.replace(tmp, path)
80
+ except OSError:
81
+ return
82
+
83
+
84
+ @functools.lru_cache(maxsize=1)
85
+ def load_schema() -> dict[str, Any]:
86
+ schema: dict[str, Any] = json.loads(SCHEMA_PATH.read_text(encoding="utf-8"))
87
+ return schema
88
+
89
+
90
+ @functools.lru_cache(maxsize=1)
91
+ def load_operations() -> list[dict[str, Any]]:
92
+ data = _read_yaml(OPENAPI_OPERATIONS_PATH)
93
+ if not data:
94
+ return []
95
+ return list(data.get("operations", []))
96
+
97
+
98
+ @functools.lru_cache(maxsize=1)
99
+ def load_sdk_methods() -> list[dict[str, Any]]:
100
+ data = _read_yaml(SDK_METHODS_PATH)
101
+ if not data:
102
+ return []
103
+ return list(data.get("methods", []))
104
+
105
+
106
+ @functools.lru_cache(maxsize=1)
107
+ def load_commands() -> list[dict[str, Any]]:
108
+ """Load every command record across all command group files, sorted by id.
109
+
110
+ The parsed records are cached as JSON under :func:`cache_dir`, keyed by the
111
+ manifest files' names, sizes and mtimes, so a normal invocation skips YAML
112
+ parsing entirely. Set ``MAMMOTH_CLI_MANIFEST_CACHE=0`` to bypass the cache.
113
+ """
114
+ if not COMMANDS_DIR.exists():
115
+ return []
116
+ paths = sorted(COMMANDS_DIR.glob("*.yaml"))
117
+ cache_path: Path | None = None
118
+ if _cache_enabled():
119
+ try:
120
+ cache_path = cache_dir() / f"commands-{_commands_fingerprint(paths)}.json"
121
+ except OSError:
122
+ cache_path = None
123
+ if cache_path is not None:
124
+ cached = _read_commands_cache(cache_path)
125
+ if cached is not None:
126
+ return cached
127
+ records: list[dict[str, Any]] = []
128
+ for path in paths:
129
+ data = _read_yaml(path)
130
+ if not data:
131
+ continue
132
+ records.extend(data.get("commands", []))
133
+ records.sort(key=lambda record: record["command_id"])
134
+ if cache_path is not None:
135
+ _write_commands_cache(cache_path, records)
136
+ return records
137
+
138
+
139
+ @dataclass(frozen=True)
140
+ class ManifestSet:
141
+ operations: list[dict[str, Any]]
142
+ sdk_methods: list[dict[str, Any]]
143
+ commands: list[dict[str, Any]]
144
+
145
+
146
+ def load_all() -> ManifestSet:
147
+ return ManifestSet(
148
+ operations=load_operations(),
149
+ sdk_methods=load_sdk_methods(),
150
+ commands=load_commands(),
151
+ )
152
+
153
+
154
+ def command_by_id(command_id: str) -> dict[str, Any] | None:
155
+ for record in load_commands():
156
+ if record["command_id"] == command_id:
157
+ return record
158
+ return None
159
+
160
+
161
+ def clear_cache() -> None:
162
+ """Reset cached manifests (used in tests that rewrite manifest files)."""
163
+ for func in (load_schema, load_operations, load_sdk_methods, load_commands):
164
+ func.cache_clear()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mammoth-cli"
3
- version = "2.0.30"
3
+ version = "2.0.31"
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"
@@ -1,96 +0,0 @@
1
- """Load and validate the reviewed parity and command manifests.
2
-
3
- These manifests are the single source of truth for capability discovery, schema
4
- discovery, help generation, the agent skill, and the parity report. Runtime code
5
- loads them read-only; the build scripts generate them.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- import functools
11
- import json
12
- from dataclasses import dataclass
13
- from pathlib import Path
14
- from typing import Any
15
-
16
- import yaml
17
-
18
- SPEC_ROOT = Path(__file__).resolve().parent.parent.parent / "spec"
19
- MANIFEST_DIR = SPEC_ROOT / "manifests"
20
- COMMANDS_DIR = MANIFEST_DIR / "commands"
21
- SCHEMA_PATH = MANIFEST_DIR / "schema-v1.json"
22
- OPENAPI_OPERATIONS_PATH = MANIFEST_DIR / "openapi-operations.yaml"
23
- SDK_METHODS_PATH = MANIFEST_DIR / "sdk-methods.yaml"
24
-
25
- MANIFEST_SCHEMA_VERSION = 1
26
-
27
-
28
- def _read_yaml(path: Path) -> Any:
29
- if not path.exists():
30
- return None
31
- return yaml.safe_load(path.read_text(encoding="utf-8"))
32
-
33
-
34
- @functools.lru_cache(maxsize=1)
35
- def load_schema() -> dict[str, Any]:
36
- schema: dict[str, Any] = json.loads(SCHEMA_PATH.read_text(encoding="utf-8"))
37
- return schema
38
-
39
-
40
- @functools.lru_cache(maxsize=1)
41
- def load_operations() -> list[dict[str, Any]]:
42
- data = _read_yaml(OPENAPI_OPERATIONS_PATH)
43
- if not data:
44
- return []
45
- return list(data.get("operations", []))
46
-
47
-
48
- @functools.lru_cache(maxsize=1)
49
- def load_sdk_methods() -> list[dict[str, Any]]:
50
- data = _read_yaml(SDK_METHODS_PATH)
51
- if not data:
52
- return []
53
- return list(data.get("methods", []))
54
-
55
-
56
- @functools.lru_cache(maxsize=1)
57
- def load_commands() -> list[dict[str, Any]]:
58
- """Load every command record across all command group files, sorted by id."""
59
- records: list[dict[str, Any]] = []
60
- if not COMMANDS_DIR.exists():
61
- return []
62
- for path in sorted(COMMANDS_DIR.glob("*.yaml")):
63
- data = _read_yaml(path)
64
- if not data:
65
- continue
66
- records.extend(data.get("commands", []))
67
- records.sort(key=lambda record: record["command_id"])
68
- return records
69
-
70
-
71
- @dataclass(frozen=True)
72
- class ManifestSet:
73
- operations: list[dict[str, Any]]
74
- sdk_methods: list[dict[str, Any]]
75
- commands: list[dict[str, Any]]
76
-
77
-
78
- def load_all() -> ManifestSet:
79
- return ManifestSet(
80
- operations=load_operations(),
81
- sdk_methods=load_sdk_methods(),
82
- commands=load_commands(),
83
- )
84
-
85
-
86
- def command_by_id(command_id: str) -> dict[str, Any] | None:
87
- for record in load_commands():
88
- if record["command_id"] == command_id:
89
- return record
90
- return None
91
-
92
-
93
- def clear_cache() -> None:
94
- """Reset cached manifests (used in tests that rewrite manifest files)."""
95
- for func in (load_schema, load_operations, load_sdk_methods, load_commands):
96
- func.cache_clear()
File without changes
File without changes
File without changes