katalyst-taxonomy-cli 2.0.1a7__tar.gz → 2.0.1a8__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 (151) hide show
  1. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/PKG-INFO +1 -1
  2. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/pyproject.toml +35 -1
  3. katalyst_taxonomy_cli-2.0.1a8/src/Justfile +2 -0
  4. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/app.py +19 -0
  5. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/bundle.py +7 -5
  6. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/config_sources.py +45 -2
  7. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/create.py +19 -0
  8. katalyst_taxonomy_cli-2.0.1a8/src/katalyst_taxonomy_cli/commands/drafts.py +103 -0
  9. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/lifecycle.py +44 -5
  10. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/lint.py +2 -0
  11. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/plugin.py +3 -4
  12. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/query.py +29 -4
  13. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/rescaffold.py +2 -1
  14. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/show.py +10 -2
  15. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/update.py +25 -0
  16. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/context.py +6 -1
  17. katalyst_taxonomy_cli-2.0.1a8/src/katalyst_taxonomy_cli/options.py +61 -0
  18. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/releases.py +1 -1
  19. katalyst_taxonomy_cli-2.0.1a8/src/layer.yaml +23 -0
  20. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/conftest.py +1 -7
  21. katalyst_taxonomy_cli-2.0.1a8/tests/test_agents_home_mcp.py +126 -0
  22. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_cli_version.py +1 -1
  23. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_config_command.py +1 -1
  24. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_config_explain.py +1 -1
  25. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_config_plugins_command.py +1 -1
  26. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_config_profiles.py +1 -1
  27. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_config_sources.py +88 -0
  28. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_core_schema.py +1 -1
  29. katalyst_taxonomy_cli-2.0.1a8/tests/test_create_variant_field.py +158 -0
  30. katalyst_taxonomy_cli-2.0.1a8/tests/test_help_names_no_product.py +51 -0
  31. katalyst_taxonomy_cli-2.0.1a8/tests/test_k1_runtime_additions.py +283 -0
  32. katalyst_taxonomy_cli-2.0.1a8/tests/test_kind_selector_shared_path.py +160 -0
  33. katalyst_taxonomy_cli-2.0.1a8/tests/test_lifecycle_moves.py +125 -0
  34. katalyst_taxonomy_cli-2.0.1a8/tests/test_mcp_config_tool_parity.py +684 -0
  35. katalyst_taxonomy_cli-2.0.1a8/tests/test_nested_collection_and_scope.py +142 -0
  36. katalyst_taxonomy_cli-2.0.1a8/tests/test_node_under_parent_directory.py +129 -0
  37. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_plugin_gating.py +1 -1
  38. katalyst_taxonomy_cli-2.0.1a8/tests/test_qualified_filenames.py +144 -0
  39. katalyst_taxonomy_cli-2.0.1a8/tests/test_relate_properties.py +148 -0
  40. katalyst_taxonomy_cli-2.0.1a8/tests/test_relocation.py +330 -0
  41. katalyst_taxonomy_cli-2.0.1a8/tests/test_rest_shipped_plugins.py +41 -0
  42. katalyst_taxonomy_cli-2.0.1a8/tests/test_scaffold_holdings.py +232 -0
  43. katalyst_taxonomy_cli-2.0.1a8/tests/test_schema_gate.py +141 -0
  44. katalyst_taxonomy_cli-2.0.1a8/xspec/.gitignore +7 -0
  45. katalyst_taxonomy_cli-2.0.1a8/xspec/Justfile +2 -0
  46. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/01_kata_init.feature +222 -0
  47. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/02_kata_clone.feature +103 -0
  48. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/04_kata_deps.feature +219 -0
  49. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/06_kata_status.feature +35 -0
  50. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/11_kata_create_expanded.feature +179 -0
  51. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/13_kata_portfolio_org.feature +174 -0
  52. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/14_kata_resolve.feature +94 -0
  53. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/17_kata_show.feature +37 -0
  54. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/19_kata_backend_ops.feature +19 -0
  55. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/20_kata_promotion.feature +27 -0
  56. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/21_kata_roundtrip.feature +86 -0
  57. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/22_kata_error_ux.feature +97 -0
  58. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/23_kata_show_deep.feature +103 -0
  59. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/24_kata_resolve_deep.feature +85 -0
  60. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/26_kata_fresh_repo.feature +19 -0
  61. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/31_kata_config_discovery.feature +212 -0
  62. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/32_kata_config_profiles.feature +189 -0
  63. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/43_kata_relate_update.feature +59 -0
  64. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/44_kata_create_typed_fields.feature +66 -0
  65. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/51_kata_labels_annotations.feature +76 -0
  66. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/57_kata_self_channels.feature +39 -0
  67. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/58_kata_import_change_set.feature +71 -0
  68. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/59_kata_update_in_place.feature +58 -0
  69. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/63_kata_library_layers.feature +115 -0
  70. katalyst_taxonomy_cli-2.0.1a8/xspec/features/cli/64_kata_xspec_layers.feature +150 -0
  71. katalyst_taxonomy_cli-2.0.1a8/xspec/layer.yaml +23 -0
  72. katalyst_taxonomy_cli-2.0.1a8/xspec/package.json +19 -0
  73. katalyst_taxonomy_cli-2.0.1a8/xspec/playwright.config.ts +5 -0
  74. katalyst_taxonomy_cli-2.0.1a8/xspec/tsconfig.json +4 -0
  75. katalyst_taxonomy_cli-2.0.1a7/src/katalyst_taxonomy_cli/commands/drafts.py +0 -63
  76. katalyst_taxonomy_cli-2.0.1a7/src/katalyst_taxonomy_cli/options.py +0 -20
  77. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/.gitignore +0 -0
  78. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/__init__.py +0 -0
  79. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/__main__.py +0 -0
  80. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/_wiring.py +0 -0
  81. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/__init__.py +0 -0
  82. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/api.py +0 -0
  83. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/backend.py +0 -0
  84. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/clone.py +0 -0
  85. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/config.py +0 -0
  86. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/config_filters.py +0 -0
  87. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/config_home.py +0 -0
  88. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/config_plugins.py +0 -0
  89. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/config_profile.py +0 -0
  90. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/config_schemas.py +0 -0
  91. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/core.py +0 -0
  92. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/deps.py +0 -0
  93. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/diff.py +0 -0
  94. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/export.py +0 -0
  95. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/init.py +0 -0
  96. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/kinds.py +0 -0
  97. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/migrate.py +0 -0
  98. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/promote.py +0 -0
  99. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/relate.py +0 -0
  100. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/relocate.py +0 -0
  101. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/remove.py +0 -0
  102. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/resolve.py +0 -0
  103. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/rollback.py +0 -0
  104. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/self_update.py +0 -0
  105. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/status.py +0 -0
  106. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/sync.py +0 -0
  107. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/commands/versions.py +0 -0
  108. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/discovery.py +0 -0
  109. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/fields.py +0 -0
  110. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/install.py +0 -0
  111. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/installer.py +0 -0
  112. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/output.py +0 -0
  113. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/src/katalyst_taxonomy_cli/py.typed +0 -0
  114. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_add.py +0 -0
  115. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_add_bundle.py +0 -0
  116. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_api_command.py +0 -0
  117. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_backend_option.py +0 -0
  118. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_clone.py +0 -0
  119. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_command_signatures.py +0 -0
  120. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_config_home.py +0 -0
  121. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_config_schemas_command.py +0 -0
  122. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_create.py +0 -0
  123. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_create_owners.py +0 -0
  124. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_create_typed_and_variants.py +0 -0
  125. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_deps.py +0 -0
  126. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_git_backend_cli.py +0 -0
  127. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_github_backend_cli.py +0 -0
  128. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_import_change_set.py +0 -0
  129. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_import_inside_repo.py +0 -0
  130. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_install.py +0 -0
  131. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_installer.py +0 -0
  132. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_kinds.py +0 -0
  133. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_kinds_plugin_settings.py +0 -0
  134. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_labels_annotations.py +0 -0
  135. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_lifecycle_kind_selector.py +0 -0
  136. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_lint_output.py +0 -0
  137. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_lint_profile_graph.py +0 -0
  138. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_lint_strict.py +0 -0
  139. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_migrate.py +0 -0
  140. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_optional_extras.py +0 -0
  141. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_output.py +0 -0
  142. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_plugin_command.py +0 -0
  143. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_relate_update.py +0 -0
  144. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_releases.py +0 -0
  145. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_relocate.py +0 -0
  146. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_remove.py +0 -0
  147. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_repository_targets.py +0 -0
  148. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_self_channels.py +0 -0
  149. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_self_update.py +0 -0
  150. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_template_merge.py +0 -0
  151. {katalyst_taxonomy_cli-2.0.1a7 → katalyst_taxonomy_cli-2.0.1a8}/tests/test_update_in_place.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: katalyst-taxonomy-cli
3
- Version: 2.0.1a7
3
+ Version: 2.0.1a8
4
4
  Summary: CLI for katalyst-taxonomy — the kata command
5
5
  Author: eSimplicity
6
6
  License: Proprietary
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "katalyst-taxonomy-cli"
3
- version = "2.0.1a7"
3
+ version = "2.0.1a8"
4
4
  description = "CLI for katalyst-taxonomy — the kata command"
5
5
  license = { text = "Proprietary" }
6
6
  requires-python = ">=3.12"
@@ -41,3 +41,37 @@ packages = ["src/katalyst_taxonomy_cli"]
41
41
 
42
42
  [tool.hatch.build.targets.wheel.sources]
43
43
  "src" = ""
44
+
45
+ [dependency-groups]
46
+ dev = [
47
+ "katalyst-test-bundle",
48
+ "pytest>=8.2",
49
+ "pytest-cov>=5.0",
50
+ "ruff>=0.5",
51
+ "pyright>=1.1",
52
+ ]
53
+
54
+ [tool.pytest.ini_options]
55
+ addopts = ["-ra"]
56
+ testpaths = ["tests"]
57
+ pythonpath = ["src"]
58
+
59
+ [tool.ruff]
60
+ target-version = "py312"
61
+ line-length = 100
62
+ # As at the repository root, no first-party sources: every import sorts as a third-party one.
63
+ src = []
64
+
65
+ [tool.ruff.lint]
66
+ select = ["E4", "E7", "E9", "F", "B", "I"]
67
+
68
+ [tool.ruff.format]
69
+ quote-style = "double"
70
+
71
+ [tool.pyright]
72
+ include = ["src", "tests"]
73
+ exclude = ["**/node_modules", "**/__pycache__", "**/.*"]
74
+ venvPath = "."
75
+ venv = ".venv"
76
+ pythonVersion = "3.12"
77
+ typeCheckingMode = "strict"
@@ -0,0 +1,2 @@
1
+ # katalyst-taxonomy-cli: the Circuit's recipes, run from the package directory (cli/).
2
+ import '../../scripts/package.just'
@@ -413,6 +413,7 @@ def tax(
413
413
  profile=profile,
414
414
  )
415
415
  _register_source_providers(profile)
416
+ _report_pending_sources()
416
417
  if ctx.invoked_subcommand == "plugin":
417
418
  # `kata tax plugin remove` is how a broken source plugin is taken out; a plugin
418
419
  # that will not activate must not stop the command that removes it.
@@ -469,6 +470,24 @@ def _register_source_providers(profile: str | None) -> None:
469
470
  ensure_source_providers(True, profile=profile)
470
471
 
471
472
 
473
+ def _report_pending_sources() -> None:
474
+ """Say which plugins and schema bundles a sourced profile declares that are not loaded.
475
+
476
+ Loading one runs its code, so it waits for the user to accept it
477
+ (:mod:`katalyst_taxonomy.services.source_consent`). This only reports, on stderr and
478
+ never failing the command; ``kata config sync`` is where it is accepted.
479
+ """
480
+ from katalyst_taxonomy.adapters.orchestration import pending_source_declarations
481
+
482
+ for declaration in pending_source_declarations():
483
+ click.echo(
484
+ f"{declaration.describe()}; it is not loaded, because it runs code. Run "
485
+ f"'kata config sync {declaration.profile}' to review and accept it, or "
486
+ f"'{declaration.install_command} {declaration.shown_source}' to install it yourself.",
487
+ err=True,
488
+ )
489
+
490
+
472
491
  # ---------------------------------------------------------------------------
473
492
  # Plugin activation (#219)
474
493
  # ---------------------------------------------------------------------------
@@ -45,13 +45,11 @@ def add_bundle(ctx: CliContext, source_uri: str, yes: bool) -> None:
45
45
  that depend solely on katalyst-engine are accepted.
46
46
  """
47
47
  from katalyst_taxonomy.services.bundle_service import install_bundle
48
+ from katalyst_taxonomy.services.source_consent import CONFIRM_PROMPT, consent_notice
48
49
 
49
50
  if not yes:
50
- click.echo(
51
- "Installing a remote bundle path-imports and executes its Python code.\n"
52
- f"Source: {source_uri}"
53
- )
54
- if not click.confirm("Proceed?", default=False):
51
+ click.echo(consent_notice("schemas", source_uri))
52
+ if not click.confirm(CONFIRM_PROMPT, default=False):
55
53
  ctx.output.echo("Aborted.")
56
54
  raise SystemExit(1)
57
55
 
@@ -90,6 +88,10 @@ def list_bundles(ctx: CliContext) -> None:
90
88
  f" {b.name or b.fqn or '(unnamed)'}{version} [{shown_source(b.source)}] "
91
89
  f"{pin}, {b.state} kinds: {kinds}"
92
90
  )
91
+ if b.error:
92
+ ctx.output.echo(f" error: {b.error}")
93
+ if any(b.state == "failed" for b in bundles):
94
+ ctx.output.echo("A failed bundle is skipped. `kata tax bundle remove <name>` drops it.")
93
95
 
94
96
 
95
97
  @bundle.command("remove")
@@ -15,6 +15,7 @@ other command reads the pinned copy.
15
15
 
16
16
  from __future__ import annotations
17
17
 
18
+ from collections.abc import Callable
18
19
  from typing import TYPE_CHECKING, Any
19
20
 
20
21
  import click
@@ -22,6 +23,7 @@ import click
22
23
  from ..output import ConsoleOutput, OutputPort
23
24
 
24
25
  if TYPE_CHECKING: # pragma: no cover - typing only
26
+ from katalyst_taxonomy.config.profile_sources import SourceDeclaration
25
27
  from katalyst_taxonomy.services.config_sources import SourceStatus, SourceSync
26
28
 
27
29
  __all__ = ["attach"]
@@ -42,22 +44,55 @@ def _short(pin: str | None) -> str:
42
44
  return pin[:12] if len(pin) in (40, 64) else pin
43
45
 
44
46
 
47
+ def _asker(out: OutputPort, *, yes: bool, interactive: bool) -> Callable[[SourceDeclaration], bool]:
48
+ """What decides, per declaration, whether the user accepts code from a source."""
49
+ from katalyst_taxonomy.services.source_consent import CONFIRM_PROMPT, consent_notice
50
+
51
+ def ask(declaration: SourceDeclaration) -> bool:
52
+ if yes:
53
+ return True
54
+ if not interactive:
55
+ return False
56
+ out.info(declaration.describe() + ".")
57
+ click.echo(consent_notice(declaration.section, declaration.source))
58
+ try:
59
+ return click.confirm(CONFIRM_PROMPT, default=False)
60
+ except click.Abort:
61
+ click.echo()
62
+ return False
63
+
64
+ return ask
65
+
66
+
45
67
  @click.command("sync")
46
68
  @click.argument("names", nargs=-1, metavar="[PROFILE]...")
47
69
  @click.option("--format", "fmt", type=_FORMAT_CHOICE, default="text")
48
- def config_sync(names: tuple[str, ...], fmt: str) -> None:
70
+ @click.option(
71
+ "-y",
72
+ "--yes",
73
+ is_flag=True,
74
+ help="Accept every plugin and schema bundle a profile declares, without asking.",
75
+ )
76
+ def config_sync(names: tuple[str, ...], fmt: str, yes: bool) -> None:
49
77
  """Fetch every sourced profile (or each PROFILE) and pin it.
50
78
 
51
79
  A git source is pinned to its commit, a kata API to its ETag or content hash. Each
52
80
  repository a sourced profile's filesystem backends name is found under
53
81
  spec.workspace by its remote URL, or cloned there. Exits 1 if any fetch failed; a
54
82
  failed one keeps its previous pin.
83
+
84
+ A plugin or schema bundle with a source that a profile declares runs code when it is
85
+ loaded, so it is not loaded until you accept it: this asks about each one that is new
86
+ (or whose pin changed), as 'kata tax plugin add' does. A declined one stays unloaded
87
+ and is asked about again at the next sync. With --format json nothing is asked; only
88
+ --yes accepts.
55
89
  """
56
90
  from katalyst_taxonomy.adapters.orchestration import sync_config_sources
57
91
 
58
92
  out = _output()
93
+ accept = _asker(out, yes=yes, interactive=fmt == "text")
59
94
  try:
60
- results: list[SourceSync] = sync_config_sources(names)
95
+ results: list[SourceSync] = sync_config_sources(names, accept=accept)
61
96
  except ValueError as exc:
62
97
  raise click.ClickException(str(exc)) from exc
63
98
  if fmt == "json":
@@ -79,6 +114,14 @@ def config_sync(names: tuple[str, ...], fmt: str) -> None:
79
114
  for clone in result.clones:
80
115
  how = "cloned to" if clone.cloned else "using"
81
116
  out.info(f" {clone.repository}: {how} {clone.path}")
117
+ for declaration in result.accepted:
118
+ out.info(f" Accepted {declaration.kind} {declaration.name!r}.")
119
+ for declaration in result.pending:
120
+ out.warning(
121
+ f"Not accepted: {declaration.kind} {declaration.name!r} from "
122
+ f"{declaration.shown_source}. It is not loaded; 'kata config sync "
123
+ f"{result.profile}' asks again."
124
+ )
82
125
  if any(not r.ok for r in results):
83
126
  raise SystemExit(1)
84
127
 
@@ -71,6 +71,16 @@ from katalyst_taxonomy_cli.context import CliContext
71
71
  "it must be named."
72
72
  ),
73
73
  )
74
+ @click.option(
75
+ "--force",
76
+ is_flag=True,
77
+ default=False,
78
+ help=(
79
+ "Create it in a state the lifecycle does not start a node in, or without the "
80
+ "fields and links the state's gate needs. Needs --reason; recorded in the node."
81
+ ),
82
+ )
83
+ @click.option("--reason", default="", help="With --force: why the lifecycle is overridden.")
74
84
  @click.option(
75
85
  "--dry-run",
76
86
  is_flag=True,
@@ -96,6 +106,8 @@ def create(
96
106
  annotation_pairs: tuple[str, ...],
97
107
  target_dir: str | None,
98
108
  backend_target: str | None,
109
+ force: bool,
110
+ reason: str,
99
111
  dry_run: bool,
100
112
  ) -> None:
101
113
  """Create a NODE_TYPE node called NAME, in the backend --backend names.
@@ -115,6 +127,9 @@ def create(
115
127
  a value of its type (boolean, integer, list, map) and allowed values, a reference
116
128
  naming a node of the referenced kind.
117
129
 
130
+ A node starts active, or as a draft (--set lifecycle_state=draft); the lifecycle and
131
+ the gates of its kind are held on create as on update.
132
+
118
133
  --label KEY=VALUE and --annotation KEY=VALUE go in the node's metadata, on any kind.
119
134
  A key is a name of letters, digits, '-', '_' and '.' (at most 63), optionally after a
120
135
  DNS prefix and '/' (example.org/team); a label's value is such a name (or empty), an
@@ -139,6 +154,8 @@ def create(
139
154
 
140
155
  from .._wiring import resolve_backend
141
156
 
157
+ if force and not reason.strip():
158
+ raise click.UsageError("--force needs --reason: a forced move is recorded with why.")
142
159
  resolved_parent = parent or parent_alt
143
160
  node_file = read_node_file(from_file)
144
161
  labels = {**node_file.labels, **parse_pairs(label_pairs, option="--label")}
@@ -171,6 +188,8 @@ def create(
171
188
  target_dir=target_dir,
172
189
  profile=ctx.profile,
173
190
  dry_run=dry_run,
191
+ force=force,
192
+ reason=reason,
174
193
  )
175
194
  if exit_code != 0:
176
195
  raise SystemExit(exit_code)
@@ -0,0 +1,103 @@
1
+ """kata tax drafts — List the drafts: nodes in a draft state, and a promotable backend's draft rows."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+ from katalyst_taxonomy_cli.context import CliContext
8
+
9
+
10
+ @click.command("drafts")
11
+ @click.option(
12
+ "--schema", "show_schema", is_flag=True, help="Show draft schema definitions instead of nodes."
13
+ )
14
+ @click.option(
15
+ "--backend",
16
+ "backend_target",
17
+ default=None,
18
+ help="Backend to operate on.",
19
+ )
20
+ @click.pass_obj
21
+ def drafts(
22
+ ctx: CliContext,
23
+ show_schema: bool,
24
+ backend_target: str | None,
25
+ ) -> None:
26
+ """List the drafts.
27
+
28
+ On any backend, a draft is a node in a draft state of its lifecycle
29
+ (``lifecycle_state: draft``): written, not accepted, and left out of normal reads.
30
+ Move it on with ``kata tax update --set lifecycle_state=…``. Without --backend, the
31
+ drafts of every backend the profile reads.
32
+
33
+ On a promotable backend (Databricks) the rows synced or created but not yet promoted
34
+ to active are listed too, and --schema lists draft schema definitions. Those rows are
35
+ a backend revision, not a lifecycle state; ``kata tax promote`` moves them.
36
+
37
+ Examples:
38
+
39
+ kata tax drafts
40
+
41
+ kata tax drafts --backend local
42
+
43
+ kata tax drafts --schema
44
+ """
45
+ from katalyst_taxonomy.ports.backend import PromotableBackend
46
+
47
+ from .._wiring import resolve_backend, resolve_promotable_backend
48
+
49
+ if not show_schema and (
50
+ backend_target is None
51
+ or not isinstance(resolve_backend(ctx, backend_target).backend, PromotableBackend)
52
+ ):
53
+ _list_draft_nodes(ctx, backend_target)
54
+ return
55
+
56
+ _, backend, _ = resolve_promotable_backend(ctx, backend_target)
57
+
58
+ if show_schema:
59
+ items = backend.list_draft_schema()
60
+ label = "Draft schema definitions"
61
+ else:
62
+ items = backend.list_drafts()
63
+ label = "Draft nodes"
64
+
65
+ if not items:
66
+ ctx.output.echo(f"No {label.lower()} found.")
67
+ return
68
+
69
+ ctx.output.echo(f"{label} ({len(items)}):")
70
+ ctx.output.echo("")
71
+ for item in items:
72
+ if show_schema:
73
+ kind = item.get("kind", "?")
74
+ wing = item.get("wing", "?")
75
+ ctx.output.echo(f" [{kind:15s}] wing={wing}")
76
+ else:
77
+ fqtn = item.get("fqtn", "?")
78
+ kind = item.get("kind", "?")
79
+ name = item.get("name", "?")
80
+ ctx.output.echo(f" [{kind:15s}] {name:30s} ({fqtn})")
81
+
82
+
83
+ def _list_draft_nodes(ctx: CliContext, backend_target: str | None) -> None:
84
+ """The nodes of the backend that are in a state the lifecycle hides."""
85
+ from katalyst_taxonomy.engine.accessors import get_identity_fqtn
86
+ from katalyst_taxonomy.engine.bridge import get_default_registry
87
+ from katalyst_taxonomy.services.filters import StateView, narrow_to_states
88
+
89
+ from .._wiring import resolve_read
90
+
91
+ lifecycle = get_default_registry().lifecycle
92
+ resolved = resolve_read(ctx, backend_target)
93
+ nodes = narrow_to_states(
94
+ tuple(node for _, backend in resolved.sources for node in backend.list_nodes()),
95
+ StateView(states=lifecycle.hidden),
96
+ )
97
+ if not nodes:
98
+ ctx.output.echo("No draft nodes found.")
99
+ return
100
+ ctx.output.echo(f"Draft nodes ({len(nodes)}):")
101
+ ctx.output.echo("")
102
+ for node in nodes:
103
+ ctx.output.echo(f" [{node.kind:15s}] {node.name:30s} ({get_identity_fqtn(node)})")
@@ -24,6 +24,24 @@ from katalyst_taxonomy_cli.context import CliContext
24
24
  __all__ = ["delete", "import_", "restore"]
25
25
 
26
26
 
27
+ _FORCE = click.option(
28
+ "--force",
29
+ is_flag=True,
30
+ help=(
31
+ "Move the node against the lifecycle (or past a gate of its kind). Needs --reason; "
32
+ "recorded in the node's provenance."
33
+ ),
34
+ )
35
+ _REASON = click.option(
36
+ "--reason", default="", help="With --force: why the lifecycle is overridden."
37
+ )
38
+
39
+
40
+ def _check_force(force: bool, reason: str) -> None:
41
+ if force and not reason.strip():
42
+ raise click.UsageError("--force needs --reason: a forced move is recorded with why.")
43
+
44
+
27
45
  def _service(ctx: CliContext, backend_target: str | None, address: str) -> tuple[Any, Any]:
28
46
  from katalyst_taxonomy.engine.bridge import get_default_registry
29
47
  from katalyst_taxonomy.services.node_lifecycle import NodeLifecycleService
@@ -45,21 +63,33 @@ def _service(ctx: CliContext, backend_target: str | None, address: str) -> tuple
45
63
  help="Take the node out of the backend instead of retiring it.",
46
64
  )
47
65
  @click.option("--dry-run", is_flag=True, help="With --remove: show the impact; change nothing.")
66
+ @_FORCE
67
+ @_REASON
48
68
  @click.pass_obj
49
69
  def delete(
50
- ctx: CliContext, address: str, backend_target: str | None, remove: bool, dry_run: bool
70
+ ctx: CliContext,
71
+ address: str,
72
+ backend_target: str | None,
73
+ remove: bool,
74
+ dry_run: bool,
75
+ force: bool,
76
+ reason: str,
51
77
  ) -> None:
52
78
  """Delete the node ADDRESS (``backend:fqtn``) names: retire it, or with --remove, remove it.
53
79
 
54
80
  A removal follows each referring relationship's delete policy: block (refused),
55
81
  cascade (removed too), detach (dropped from the referrer) or redirect (moved to the
56
82
  node's replacement). It is shown first with --dry-run, and is all or nothing.
83
+
84
+ A retire is a move to the lifecycle's retired state, held to the lifecycle as an
85
+ update is (--force --reason overrides).
57
86
  """
87
+ _check_force(force, reason)
58
88
  service, resolved = _service(ctx, backend_target, address)
59
89
  if not remove:
60
90
  if dry_run:
61
91
  raise click.UsageError("--dry-run goes with --remove; a retire changes one field.")
62
- outcome = service.retire(resolved.address)
92
+ outcome = service.retire(resolved.address, force=force, reason=reason)
63
93
  if not outcome.success:
64
94
  ctx.output.echo(f"Error: {outcome.error}")
65
95
  raise SystemExit(1)
@@ -81,11 +111,20 @@ def delete(
81
111
  @click.option(
82
112
  "--backend", "backend_target", default=None, help="The backend, when ADDRESS has no prefix."
83
113
  )
114
+ @_FORCE
115
+ @_REASON
84
116
  @click.pass_obj
85
- def restore(ctx: CliContext, address: str, backend_target: str | None) -> None:
86
- """Return the retired node ADDRESS to service, after the local shapes pass on it."""
117
+ def restore(
118
+ ctx: CliContext, address: str, backend_target: str | None, force: bool, reason: str
119
+ ) -> None:
120
+ """Return the retired node ADDRESS to service, after the local shapes pass on it.
121
+
122
+ Only a retired node is restored; any other state moves with ``kata tax update --set
123
+ lifecycle_state=STATE``.
124
+ """
125
+ _check_force(force, reason)
87
126
  service, resolved = _service(ctx, backend_target, address)
88
- outcome = service.restore(resolved.address)
127
+ outcome = service.restore(resolved.address, force=force, reason=reason)
89
128
  if not outcome.success:
90
129
  ctx.output.echo(f"Error: {outcome.error}")
91
130
  raise SystemExit(1)
@@ -126,6 +126,7 @@ def lint(ctx: CliContext, verbose: bool, strict: bool, backend: str | None) -> N
126
126
  for title, found in (
127
127
  ("Undeclared spec keys", result.undeclared_fields),
128
128
  ("Misplaced documents", result.misplaced_documents),
129
+ ("Forced lifecycle moves", result.forced_moves),
129
130
  ("Relationships no kind carries", result.uncarried_relationships),
130
131
  ("Shape findings", result.shape_findings),
131
132
  ("Shape warnings", result.shape_warnings),
@@ -151,6 +152,7 @@ def lint(ctx: CliContext, verbose: bool, strict: bool, backend: str | None) -> N
151
152
  + len(result.duplicate_names)
152
153
  + len(result.undeclared_fields)
153
154
  + len(result.misplaced_documents)
155
+ + len(result.forced_moves)
154
156
  + len(result.uncarried_relationships)
155
157
  + len(result.shape_findings)
156
158
  + len(result.shape_warnings)
@@ -50,13 +50,12 @@ def add_plugin(ctx: CliContext, source_uri: str, yes: bool) -> None:
50
50
  katalyst_taxonomy.plugins entry point and depends only on katalyst-engine and
51
51
  katalyst-taxonomy. Installing executes the plugin's Python code.
52
52
  """
53
+ from katalyst_taxonomy.services.source_consent import CONFIRM_PROMPT, consent_notice
53
54
  from katalyst_taxonomy.sources.schemes import BUNDLE_SOURCE_SCHEMES, reject_unknown_scheme
54
55
 
55
56
  if not yes:
56
- click.echo(
57
- f"Installing a plugin path-imports and executes its Python code.\nSource: {source_uri}"
58
- )
59
- if not click.confirm("Proceed?", default=False):
57
+ click.echo(consent_notice("plugins", source_uri))
58
+ if not click.confirm(CONFIRM_PROMPT, default=False):
60
59
  ctx.output.echo("Aborted.")
61
60
  raise SystemExit(1)
62
61
  try:
@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING
7
7
  import click
8
8
 
9
9
  from katalyst_taxonomy_cli.context import CliContext
10
+ from katalyst_taxonomy_cli.options import state_options, state_view
10
11
 
11
12
  if TYPE_CHECKING: # pragma: no cover - typing only
12
13
  from katalyst_engine.model.node import Node
@@ -55,6 +56,7 @@ if TYPE_CHECKING: # pragma: no cover - typing only
55
56
  "(its declaration's holds); each is shown with that directory."
56
57
  ),
57
58
  )
59
+ @state_options
58
60
  @click.pass_obj
59
61
  def query(
60
62
  ctx: CliContext,
@@ -64,12 +66,18 @@ def query(
64
66
  fqtn: str | None,
65
67
  name: str | None,
66
68
  holds: str | None,
69
+ include_drafts: bool,
70
+ state_names: tuple[str, ...],
67
71
  ) -> None:
68
72
  """Query nodes and relationships from a backend.
69
73
 
70
74
  Uses the unified TaxonomyBackend.list_nodes() and list_relationships()
71
75
  interface — works against any backend implementation.
72
76
 
77
+ A node in a state other than active is marked with it, `(draft)`, `(retired)`. Drafts
78
+ are left out unless --include-drafts or --state draft asks for them. Asked about one
79
+ node (--fqtn naming exactly one), the output also says where it may move next.
80
+
73
81
  Examples:
74
82
 
75
83
  kata tax query --kind tool
@@ -85,6 +93,10 @@ def query(
85
93
  kata tax query --fqtn prod-graph:payments
86
94
 
87
95
  kata tax query --fqtn repo:shop.web --holds ROLE
96
+
97
+ kata tax query --state draft
98
+
99
+ kata tax query --include-drafts --kind capability
88
100
  """
89
101
  from .._wiring import resolve_read
90
102
 
@@ -131,7 +143,9 @@ def query(
131
143
  from katalyst_taxonomy.services.filters import visible_nodes
132
144
 
133
145
  try:
134
- nodes = visible_nodes(ctx.cwd, nodes, profile=ctx.profile)
146
+ nodes = visible_nodes(
147
+ ctx.cwd, nodes, profile=ctx.profile, states=state_view(include_drafts, state_names)
148
+ )
135
149
  except ValueError as exc:
136
150
  raise click.ClickException(str(exc)) from exc
137
151
  held: dict[int, str] = {}
@@ -148,6 +162,12 @@ def query(
148
162
  ctx.output.echo(f"Nodes ({len(nodes)}):")
149
163
  ctx.output.echo("")
150
164
  from katalyst_taxonomy.engine.accessors import get_identity_fqtn
165
+ from katalyst_taxonomy.engine.bridge import get_default_registry
166
+ from katalyst_taxonomy.services.lifecycle_rule import LifecycleRule
167
+
168
+ registry = get_default_registry()
169
+ rule = LifecycleRule(registry)
170
+ lifecycle = registry.lifecycle
151
171
 
152
172
  for node in nodes:
153
173
  # Qualified, because the same name in two backends is two nodes.
@@ -157,9 +177,14 @@ def query(
157
177
  )
158
178
  continue
159
179
  desc = (node.description or "")[:55]
160
- # A retired node is still in the graph; say so (katalyst-core's lifecycle).
161
- retired = " (retired)" if node.spec.get("lifecycle_state") == "retired" else ""
162
- ctx.output.echo(f" [{node.kind:15s}] {get_identity_fqtn(node):40s} {desc}{retired}")
180
+ # A node out of the default state says so (katalyst-core's lifecycle): `(draft)`,
181
+ # `(retired)`. Retired nodes are still in the graph; drafts only when asked for.
182
+ marked = f" ({state})" if (state := rule.state_of(node)) != lifecycle.default else ""
183
+ ctx.output.echo(f" [{node.kind:15s}] {get_identity_fqtn(node):40s} {desc}{marked}")
184
+ if len(nodes) == 1:
185
+ moves = ", ".join(rule.next_moves(nodes[0])) or "none"
186
+ ctx.output.echo("")
187
+ ctx.output.echo(f"State: {rule.state_of(nodes[0])}; may move to: {moves}")
163
188
 
164
189
 
165
190
  def _narrow_to_selected_subtree(
@@ -80,6 +80,7 @@ def rescaffold(
80
80
  from katalyst_taxonomy.engine.accessors import get_source_path
81
81
  from katalyst_taxonomy.engine.qualification import match_fqtn_in
82
82
  from katalyst_taxonomy.ports.backend import repository_root_of
83
+ from katalyst_taxonomy.services.filters import SHOW_ALL_STATES
83
84
  from katalyst_taxonomy.services.rescaffold import rescaffold_node
84
85
  from katalyst_taxonomy.services.template_install import template_base_resolver
85
86
 
@@ -118,7 +119,7 @@ def rescaffold(
118
119
  mode=mode,
119
120
  dry_run=dry_run,
120
121
  create_hooks=create_hooks(),
121
- load_taxonomy=lambda: load_taxonomy(root, profile=ctx.profile),
122
+ load_taxonomy=lambda: load_taxonomy(root, profile=ctx.profile, states=SHOW_ALL_STATES),
122
123
  )
123
124
  if not outcome.success:
124
125
  ctx.output.echo(f"Error: {outcome.error}")
@@ -5,18 +5,26 @@ from __future__ import annotations
5
5
  import click
6
6
 
7
7
  from katalyst_taxonomy_cli.context import CliContext
8
+ from katalyst_taxonomy_cli.options import state_options, state_view
8
9
 
9
10
 
10
11
  @click.command("show")
11
12
  @click.option("--format", "fmt", type=click.Choice(["tree", "list", "json"]), default=None)
13
+ @state_options
12
14
  @click.pass_obj
13
- def show(ctx: CliContext, fmt: str | None) -> None:
14
- """Show the taxonomy structure."""
15
+ def show(
16
+ ctx: CliContext, fmt: str | None, include_drafts: bool, state_names: tuple[str, ...]
17
+ ) -> None:
18
+ """Show the taxonomy structure.
19
+
20
+ Drafts are left out unless --include-drafts or --state asks for them.
21
+ """
15
22
  import json as json_module
16
23
 
17
24
  from katalyst_taxonomy.services.serialization import build_tree_lines, serialize_taxonomy
18
25
 
19
26
  fmt = fmt or ctx.output_format or "tree"
27
+ ctx.states = state_view(include_drafts, state_names)
20
28
  service = ctx.taxonomy_service
21
29
 
22
30
  if fmt == "json":