core-runtime-engine 11.5.1__py3-none-any.whl

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 (96) hide show
  1. core_runtime/__init__.py +10 -0
  2. core_runtime/__version__.py +17 -0
  3. core_runtime/cli/__init__.py +7 -0
  4. core_runtime/cli/__main__.py +22 -0
  5. core_runtime/cli/bump_version.py +176 -0
  6. core_runtime/cli/contract_preflight.py +69 -0
  7. core_runtime/cli/create_domain.py +66 -0
  8. core_runtime/cli/doctor.py +44 -0
  9. core_runtime/cli/inventory.py +85 -0
  10. core_runtime/cli/lint.py +8 -0
  11. core_runtime/cli/main.py +579 -0
  12. core_runtime/cli/release_check.py +65 -0
  13. core_runtime/cli/repair_artifact_paths.py +48 -0
  14. core_runtime/cli/sync_template.py +67 -0
  15. core_runtime/cli/validate.py +73 -0
  16. core_runtime/core/__init__.py +34 -0
  17. core_runtime/core/audit_event.py +760 -0
  18. core_runtime/core/audit_trail_index.py +100 -0
  19. core_runtime/core/canonicalization.py +55 -0
  20. core_runtime/core/contract_evaluator.py +945 -0
  21. core_runtime/core/contract_executability.py +307 -0
  22. core_runtime/core/contract_loader.py +57 -0
  23. core_runtime/core/contract_probes.py +630 -0
  24. core_runtime/core/contract_program.py +131 -0
  25. core_runtime/core/contract_program_registry.py +104 -0
  26. core_runtime/core/contract_program_v2.py +126 -0
  27. core_runtime/core/dsk_v3.py +142 -0
  28. core_runtime/core/explainability.py +2115 -0
  29. core_runtime/core/numeric_normalization.py +120 -0
  30. core_runtime/core/rule_anchor.py +1388 -0
  31. core_runtime/core/schema_fingerprint.py +69 -0
  32. core_runtime/core/sensor_evidence.py +548 -0
  33. core_runtime/data/contracts/CoreAnchor.sol +109 -0
  34. core_runtime/data/contracts/CoreRuleAnchor.abi.json +111 -0
  35. core_runtime/data/contracts/CoreRuleAnchor.bin +1 -0
  36. core_runtime/data/contracts/CoreRuleAnchor.build.json +20 -0
  37. core_runtime/data/contracts/CoreRuleAnchor.runtime.bin +1 -0
  38. core_runtime/data/contracts/CoreRuleAnchor.sol +74 -0
  39. core_runtime/data/package_data_manifest.v1.json +173 -0
  40. core_runtime/data/schemas/core/causal_trace.v1.json +141 -0
  41. core_runtime/data/schemas/core/context_gate.v1.json +38 -0
  42. core_runtime/data/schemas/core/context_threshold.v1.json +46 -0
  43. core_runtime/data/schemas/core/contract_program.v1.json +186 -0
  44. core_runtime/data/schemas/core/contract_program.v2.json +187 -0
  45. core_runtime/data/schemas/core/control_decision.v1.json +114 -0
  46. core_runtime/data/schemas/core/dsk.v3.json +105 -0
  47. core_runtime/data/schemas/core/effect_result.v1.json +39 -0
  48. core_runtime/data/schemas/core/entropy_signal.v1.json +108 -0
  49. core_runtime/data/schemas/core/execution_receipt.v1.json +93 -0
  50. core_runtime/data/schemas/core/frozen_release_manifest.v1.json +87 -0
  51. core_runtime/data/schemas/core/frozen_release_manifest.v2.json +72 -0
  52. core_runtime/data/schemas/core/frozen_release_manifest.v3.json +38 -0
  53. core_runtime/data/schemas/core/frozen_release_manifest.v4.json +72 -0
  54. core_runtime/data/schemas/core/frozen_release_manifest.v5.json +38 -0
  55. core_runtime/data/schemas/core/frozen_release_manifest.v6.json +116 -0
  56. core_runtime/data/schemas/core/frozen_release_manifest.v7.json +37 -0
  57. core_runtime/data/schemas/core/frozen_release_manifest.v8.json +114 -0
  58. core_runtime/data/schemas/core/frozen_rule_set.v1.json +282 -0
  59. core_runtime/data/schemas/core/memory_artifact.v1.json +120 -0
  60. core_runtime/data/schemas/core/memory_generation_result.v1.json +37 -0
  61. core_runtime/data/schemas/core/operational_learning_event.v1.json +63 -0
  62. core_runtime/data/schemas/core/pattern_candidate.v1.json +114 -0
  63. core_runtime/data/schemas/core/physical_safety_assurance_case.v1.json +676 -0
  64. core_runtime/data/schemas/core/policy_lifecycle.v1.json +99 -0
  65. core_runtime/data/schemas/core/retention_manifest.v1.json +53 -0
  66. core_runtime/data/schemas/core/reversibility_policy.v1.json +107 -0
  67. core_runtime/data/schemas/core/rule_anchor_batch.v1.json +93 -0
  68. core_runtime/data/schemas/core/rule_anchor_chain_evidence.v1.json +56 -0
  69. core_runtime/data/schemas/core/rule_approval.v1.json +49 -0
  70. core_runtime/data/schemas/core/rule_approval_request.v1.json +42 -0
  71. core_runtime/data/schemas/core/state_transition.v1.json +115 -0
  72. core_runtime/data/schemas/core/task_closeout.v1.json +47 -0
  73. core_runtime/data/schemas/core/template_promotion_candidate.v1.json +83 -0
  74. core_runtime/data/schemas/core/unsigned_rule_anchor_deployment.v1.json +106 -0
  75. core_runtime/data/schemas/core/unsigned_rule_anchor_transaction.v1.json +116 -0
  76. core_runtime/tooling/__init__.py +48 -0
  77. core_runtime/tooling/bump_version.py +900 -0
  78. core_runtime/tooling/contract_preflight.py +293 -0
  79. core_runtime/tooling/create_domain.py +254 -0
  80. core_runtime/tooling/diagnostics.py +129 -0
  81. core_runtime/tooling/doctor.py +482 -0
  82. core_runtime/tooling/file_inventory.py +182 -0
  83. core_runtime/tooling/json_checks.py +100 -0
  84. core_runtime/tooling/release_check.py +1017 -0
  85. core_runtime/tooling/repair_artifact_paths.py +458 -0
  86. core_runtime/tooling/report_writer.py +176 -0
  87. core_runtime/tooling/repository_inventory.py +399 -0
  88. core_runtime/tooling/safety_checks.py +172 -0
  89. core_runtime/tooling/sync_template.py +303 -0
  90. core_runtime/tooling/validation.py +507 -0
  91. core_runtime/tooling/version_inventory.py +256 -0
  92. core_runtime_engine-11.5.1.dist-info/METADATA +35 -0
  93. core_runtime_engine-11.5.1.dist-info/RECORD +96 -0
  94. core_runtime_engine-11.5.1.dist-info/WHEEL +5 -0
  95. core_runtime_engine-11.5.1.dist-info/entry_points.txt +2 -0
  96. core_runtime_engine-11.5.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,10 @@
1
+ """CORE — deterministic contract, schema and validation engine.
2
+
3
+ CORE validates artifacts against public contracts: schemas, fingerprints,
4
+ manifests and bounded evidence. It never executes domain logic, never
5
+ holds private semantics and never decides legal truth.
6
+ """
7
+
8
+ from core_runtime.__version__ import __version__
9
+
10
+ __all__ = ["__version__"]
@@ -0,0 +1,17 @@
1
+ """CORE Runtime - Single Source of Truth for Version.
2
+
3
+ This file is the canonical version source. All other version references
4
+ (pyproject.toml, core_runtime/__init__.py, documentation) should be
5
+ kept in sync with this value.
6
+
7
+ DO NOT EDIT THIS FILE MANUALLY.
8
+ Use scripts/bump_version.py to update versions consistently.
9
+ """
10
+
11
+ __version__ = "11.5.1"
12
+
13
+ # Component versions
14
+ CORE_VERSION = "11.5.1"
15
+
16
+ # Parsed version tuple for programmatic comparison
17
+ VERSION_INFO = tuple(int(x) for x in __version__.split("."))
@@ -0,0 +1,7 @@
1
+ """CORE Runtime CLI - tooling commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from core_runtime.__version__ import __version__
6
+
7
+ __all__ = ["__version__"]
@@ -0,0 +1,22 @@
1
+ """CLI entry point for core_runtime.tool commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ from core_runtime.cli.main import build_parser
8
+
9
+
10
+ def main() -> int:
11
+ parser = build_parser()
12
+ args = parser.parse_args()
13
+
14
+ if not hasattr(args, "func"):
15
+ parser.print_help()
16
+ return 2
17
+
18
+ return args.func(args)
19
+
20
+
21
+ if __name__ == "__main__":
22
+ sys.exit(main())
@@ -0,0 +1,176 @@
1
+ """CLI command for bump-version — dry-run and controlled apply."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from core_runtime.tooling.bump_version import BumpVersionPlanner
10
+ from core_runtime.tooling.diagnostics import DiagnosticCollection, ExitCode
11
+ from core_runtime.tooling.version_inventory import VersionInventory
12
+
13
+
14
+ def cmd_bump_version(args: object) -> int:
15
+ """Execute the bump-version command (dry-run or controlled apply)."""
16
+ # Access args safely (argparse Namespace or test mock)
17
+ target_version_raw = getattr(args, "target_version", None)
18
+ dry_run: bool = getattr(args, "dry_run", False)
19
+ apply_mode: bool = getattr(args, "apply", False)
20
+ confirm_current: str | None = getattr(args, "confirm_current", None)
21
+ fmt: str = getattr(args, "format", "json")
22
+ output = getattr(args, "output", None)
23
+
24
+ diagnostics = DiagnosticCollection()
25
+
26
+ # ---- Mutual exclusion: --dry-run and --apply cannot both be set ----
27
+ if dry_run and apply_mode:
28
+ blocked_msg = {
29
+ "code": "core.bump_version.mutual_exclusion",
30
+ "severity": "blocked",
31
+ "message": "--dry-run and --apply are mutually exclusive. Specify one.",
32
+ "mutation_allowed": False,
33
+ }
34
+ if fmt == "json":
35
+ print(json.dumps(blocked_msg, indent=2, ensure_ascii=False))
36
+ else:
37
+ print("# BLOCKED")
38
+ print("")
39
+ print("--dry-run and --apply are mutually exclusive. Specify one.")
40
+ return ExitCode.BLOCKED.value
41
+
42
+ # ---- Default mode: if neither flag is set, default to dry-run ----
43
+ if not dry_run and not apply_mode:
44
+ dry_run = True
45
+
46
+ # ---- Apply mode: --confirm-current is required ----
47
+ if apply_mode and not confirm_current:
48
+ blocked_msg = {
49
+ "code": "core.bump_version.confirm_current_required",
50
+ "severity": "blocked",
51
+ "message": "--confirm-current is required when using --apply.",
52
+ "mutation_allowed": False,
53
+ }
54
+ if fmt == "json":
55
+ print(json.dumps(blocked_msg, indent=2, ensure_ascii=False))
56
+ else:
57
+ print("# BLOCKED")
58
+ print("")
59
+ print("--confirm-current is required when using --apply.")
60
+ print("Usage: python -m core_runtime.cli bump-version <target> --apply --confirm-current <current>")
61
+ return ExitCode.BLOCKED.value
62
+
63
+ # Guard against None (argparse should always provide this)
64
+ if target_version_raw is None:
65
+ print("Error: target_version is required", file=sys.stderr)
66
+ return ExitCode.INTERNAL_ERROR.value
67
+
68
+ target_version: str = target_version_raw
69
+
70
+ repo_root = Path(__file__).resolve().parents[2] # core_runtime/cli/ -> repo_root
71
+
72
+ # Discover current version before planning/applying
73
+ version_inv = VersionInventory(repo_root)
74
+ current_version = version_inv.get_canonical_version()
75
+ if current_version is None:
76
+ diagnostics.add_blocked(
77
+ code="core.bump_version.canonical_missing",
78
+ message="Cannot determine current canonical version",
79
+ path="core_runtime/__version__.py",
80
+ )
81
+ base_report = {
82
+ "tool": "core-runtime bump-version",
83
+ "mode": "apply" if apply_mode else "dry-run",
84
+ "status": "blocked",
85
+ "mutation_performed": False,
86
+ "current_version": None,
87
+ "target_version": target_version,
88
+ "summary": {"files_checked": 0, "files_that_would_change": 0,
89
+ "replacement_count": 0, "info": 0, "warning": 0,
90
+ "error": 0, "blocked": 1},
91
+ "changes": [],
92
+ "diagnostics": [d.to_dict() for d in diagnostics.diagnostics],
93
+ }
94
+ if fmt == "json":
95
+ print(json.dumps(base_report, indent=2, ensure_ascii=False))
96
+ else:
97
+ print("# BLOCKED: Cannot determine current version")
98
+ return ExitCode.BLOCKED.value
99
+
100
+ planner = BumpVersionPlanner(repo_root)
101
+ output_path = Path(output) if output else None
102
+
103
+ # ================================================================
104
+ # DRY-RUN MODE
105
+ # ================================================================
106
+ if dry_run:
107
+ changes, summary = planner.plan(target_version, diagnostics)
108
+ if fmt == "json":
109
+ report = planner.report_json(
110
+ target_version=target_version,
111
+ current_version=current_version,
112
+ changes=changes,
113
+ summary=summary,
114
+ diagnostics=diagnostics,
115
+ output_path=output_path,
116
+ mode="dry-run",
117
+ mutation_performed=False,
118
+ )
119
+ if not output_path:
120
+ print(json.dumps(report, indent=2, ensure_ascii=False))
121
+ else:
122
+ md = planner.report_markdown(
123
+ target_version=target_version,
124
+ current_version=current_version,
125
+ changes=changes,
126
+ summary=summary,
127
+ diagnostics=diagnostics,
128
+ output_path=output_path,
129
+ mode="dry-run",
130
+ mutation_performed=False,
131
+ )
132
+ if not output_path:
133
+ print(md)
134
+ return diagnostics.compute_exit_code().value
135
+
136
+ # ================================================================
137
+ # APPLY MODE
138
+ # ================================================================
139
+ if apply_mode:
140
+ applied_changes, summary = planner.apply(
141
+ target_version=target_version,
142
+ confirm_current=confirm_current, # type: ignore[arg-type]
143
+ diagnostics=diagnostics,
144
+ )
145
+ if fmt == "json":
146
+ report = planner.report_json(
147
+ target_version=target_version,
148
+ current_version=current_version,
149
+ changes=[], # not used in apply mode
150
+ summary=summary,
151
+ diagnostics=diagnostics,
152
+ output_path=output_path,
153
+ mode="apply",
154
+ mutation_performed=(diagnostics.compute_exit_code() == ExitCode.OK),
155
+ applied_changes=applied_changes,
156
+ )
157
+ if not output_path:
158
+ print(json.dumps(report, indent=2, ensure_ascii=False))
159
+ else:
160
+ md = planner.report_markdown(
161
+ target_version=target_version,
162
+ current_version=current_version,
163
+ changes=[], # not used in apply mode
164
+ summary=summary,
165
+ diagnostics=diagnostics,
166
+ output_path=output_path,
167
+ mode="apply",
168
+ mutation_performed=(diagnostics.compute_exit_code() == ExitCode.OK),
169
+ applied_changes=applied_changes,
170
+ )
171
+ if not output_path:
172
+ print(md)
173
+ return diagnostics.compute_exit_code().value
174
+
175
+ # Should never reach here
176
+ return ExitCode.INTERNAL_ERROR.value
@@ -0,0 +1,69 @@
1
+ """CLI command for `core-runtime contract-preflight`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ from core_runtime.tooling.contract_preflight import ContractPreflightReport, RepositoryContractPreflight
9
+ from core_runtime.tooling.diagnostics import ExitCode
10
+
11
+
12
+ def cmd_contract_preflight(args: object) -> int:
13
+ """Execute the advisory-only contract preflight command."""
14
+ fmt: str = getattr(args, "format", "json")
15
+ output = getattr(args, "output", None)
16
+ candidate = getattr(args, "candidate", None)
17
+ compare = getattr(args, "compare", None)
18
+ repo_root = Path(__file__).resolve().parents[2]
19
+ preflight = RepositoryContractPreflight(repo_root)
20
+
21
+ if compare:
22
+ report = preflight.build_compare_report(compare[0], compare[1])
23
+ elif candidate:
24
+ report = preflight.build_candidate_report(candidate)
25
+ else:
26
+ from core_runtime.tooling.diagnostics import DiagnosticCollection
27
+
28
+ diagnostics = DiagnosticCollection()
29
+ diagnostics.add_blocked(
30
+ code="core.contract_preflight.mode_required",
31
+ message="contract-preflight requires --candidate or --compare",
32
+ path="contract-preflight",
33
+ expected="--candidate <name> or --compare <a> <b>",
34
+ actual="missing mode",
35
+ )
36
+ report = ContractPreflightReport(
37
+ tool="core-runtime contract-preflight",
38
+ command="contract-preflight",
39
+ status="blocked",
40
+ summary={"mode": "missing", "item_count": 0},
41
+ selection={"mode": "missing"},
42
+ diagnostics=diagnostics,
43
+ )
44
+
45
+ output_path = Path(output) if output else None
46
+ if fmt == "json":
47
+ payload = report.to_dict()
48
+ text = json.dumps(payload, indent=2, ensure_ascii=False)
49
+ if output_path:
50
+ output_path.parent.mkdir(parents=True, exist_ok=True)
51
+ output_path.write_text(text + "\n", encoding="utf-8")
52
+ else:
53
+ print(text)
54
+ else:
55
+ markdown = report.to_markdown()
56
+ if output_path:
57
+ output_path.parent.mkdir(parents=True, exist_ok=True)
58
+ output_path.write_text(markdown, encoding="utf-8")
59
+ else:
60
+ print(markdown)
61
+
62
+ status_to_exit = {
63
+ "pass": ExitCode.OK.value,
64
+ "warning": ExitCode.OK.value,
65
+ "error": ExitCode.ERROR.value,
66
+ "blocked": ExitCode.BLOCKED.value,
67
+ "internal_error": ExitCode.INTERNAL_ERROR.value,
68
+ }
69
+ return status_to_exit.get(report.status, ExitCode.INTERNAL_ERROR.value)
@@ -0,0 +1,66 @@
1
+ """CLI command for `core-runtime create-domain`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ from core_runtime.tooling.create_domain import CreateDomainReport, DomainScaffolder
9
+ from core_runtime.tooling.diagnostics import DiagnosticCollection, ExitCode
10
+
11
+
12
+ def cmd_create_domain(args: object) -> int:
13
+ """Execute the dry-run-first domain scaffolding preflight."""
14
+ fmt: str = getattr(args, "format", "json")
15
+ output = getattr(args, "output", None)
16
+ name = getattr(args, "name", None)
17
+ template = getattr(args, "template", "generic")
18
+ dry_run = bool(getattr(args, "dry_run", False))
19
+ repo_root = Path(__file__).resolve().parents[2]
20
+ scaffolder = DomainScaffolder(repo_root)
21
+
22
+ if not dry_run:
23
+ diagnostics = DiagnosticCollection()
24
+ diagnostics.add_blocked(
25
+ code="core.create_domain.dry_run_required",
26
+ message="create-domain is dry-run only in this slice",
27
+ path=name or "create-domain",
28
+ expected="--dry-run",
29
+ actual="missing dry-run",
30
+ )
31
+ report = CreateDomainReport(
32
+ tool="core-runtime create-domain",
33
+ command="create-domain {0}".format(name or ""),
34
+ status="blocked",
35
+ summary={"mode": "missing_dry_run", "item_count": 0},
36
+ selection={"domain": name, "template": template, "mode": "missing_dry_run"},
37
+ diagnostics=diagnostics,
38
+ )
39
+ else:
40
+ report = scaffolder.build_plan(name=name, template=template)
41
+
42
+ output_path = Path(output) if output else None
43
+ if fmt == "json":
44
+ payload = report.to_dict()
45
+ text = json.dumps(payload, indent=2, ensure_ascii=False)
46
+ if output_path:
47
+ output_path.parent.mkdir(parents=True, exist_ok=True)
48
+ output_path.write_text(text + "\n", encoding="utf-8")
49
+ else:
50
+ print(text)
51
+ else:
52
+ markdown = report.to_markdown()
53
+ if output_path:
54
+ output_path.parent.mkdir(parents=True, exist_ok=True)
55
+ output_path.write_text(markdown, encoding="utf-8")
56
+ else:
57
+ print(markdown)
58
+
59
+ status_to_exit = {
60
+ "pass": ExitCode.OK.value,
61
+ "warning": ExitCode.OK.value,
62
+ "error": ExitCode.ERROR.value,
63
+ "blocked": ExitCode.BLOCKED.value,
64
+ "internal_error": ExitCode.INTERNAL_ERROR.value,
65
+ }
66
+ return status_to_exit.get(report.status, ExitCode.INTERNAL_ERROR.value)
@@ -0,0 +1,44 @@
1
+ """CLI command for `core-runtime doctor`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ from core_runtime.tooling.diagnostics import ExitCode
9
+ from core_runtime.tooling.doctor import RepositoryDoctor
10
+
11
+
12
+ def cmd_doctor(args: object) -> int:
13
+ """Execute the doctor preflight command."""
14
+ fmt: str = getattr(args, "format", "json")
15
+ output = getattr(args, "output", None)
16
+ repo_root = Path(__file__).resolve().parents[2]
17
+ doctor = RepositoryDoctor(repo_root)
18
+ report = doctor.build_report()
19
+ output_path = Path(output) if output else None
20
+
21
+ if fmt == "json":
22
+ payload = report.to_dict()
23
+ text = json.dumps(payload, indent=2, ensure_ascii=False)
24
+ if output_path:
25
+ output_path.parent.mkdir(parents=True, exist_ok=True)
26
+ output_path.write_text(text + "\n", encoding="utf-8")
27
+ else:
28
+ print(text)
29
+ else:
30
+ markdown = report.to_markdown()
31
+ if output_path:
32
+ output_path.parent.mkdir(parents=True, exist_ok=True)
33
+ output_path.write_text(markdown, encoding="utf-8")
34
+ else:
35
+ print(markdown)
36
+
37
+ status_to_exit = {
38
+ "pass": ExitCode.OK.value,
39
+ "warning": ExitCode.OK.value,
40
+ "error": ExitCode.ERROR.value,
41
+ "blocked": ExitCode.BLOCKED.value,
42
+ "internal_error": ExitCode.INTERNAL_ERROR.value,
43
+ }
44
+ return status_to_exit.get(report.status, ExitCode.INTERNAL_ERROR.value)
@@ -0,0 +1,85 @@
1
+ """CLI commands for read-only repository inventory navigation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ from core_runtime.tooling.diagnostics import ExitCode
9
+ from core_runtime.tooling.repository_inventory import RepositoryInventory
10
+
11
+
12
+ _KIND_ALIASES = {
13
+ "schema": "schemas",
14
+ "schemas": "schemas",
15
+ "contract": "contracts",
16
+ "contracts": "contracts",
17
+ "adapter": "adapters",
18
+ "adapters": "adapters",
19
+ "domain": "domains",
20
+ "domains": "domains",
21
+ }
22
+
23
+
24
+ def _normalize_kind(kind: str | None) -> str | None:
25
+ if kind is None:
26
+ return None
27
+ return _KIND_ALIASES.get(kind.lower())
28
+
29
+
30
+ def _emit_report(report, fmt: str, output) -> None:
31
+ payload = report.to_dict()
32
+ if fmt == "json":
33
+ text = json.dumps(payload, indent=2, ensure_ascii=False)
34
+ else:
35
+ text = report.to_markdown()
36
+ if output:
37
+ output_path = Path(output)
38
+ output_path.parent.mkdir(parents=True, exist_ok=True)
39
+ output_path.write_text(text + ("\n" if fmt == "json" else ""), encoding="utf-8")
40
+ else:
41
+ print(text)
42
+
43
+
44
+ def cmd_list(args: object) -> int:
45
+ """List repository inventory items by kind."""
46
+ raw_kind = getattr(args, "kind", None)
47
+ kind = _normalize_kind(raw_kind)
48
+ fmt: str = getattr(args, "format", "json")
49
+ output = getattr(args, "output", None)
50
+ repo_root = Path(__file__).resolve().parents[2]
51
+ inventory = RepositoryInventory(repo_root)
52
+
53
+ if raw_kind is None:
54
+ kind = "schemas"
55
+ elif kind is None:
56
+ kind = str(raw_kind)
57
+
58
+ report = inventory.build_list_report(kind)
59
+ _emit_report(report, fmt, output)
60
+ return ExitCode.BLOCKED.value if report.status == "blocked" else ExitCode.OK.value
61
+
62
+
63
+ def cmd_info(args: object) -> int:
64
+ """Show inventory summary or item details."""
65
+ raw_kind = getattr(args, "kind", None)
66
+ kind = _normalize_kind(raw_kind)
67
+ name = getattr(args, "name", None)
68
+ fmt: str = getattr(args, "format", "json")
69
+ output = getattr(args, "output", None)
70
+ repo_root = Path(__file__).resolve().parents[2]
71
+ inventory = RepositoryInventory(repo_root)
72
+
73
+ if raw_kind is not None and kind is None:
74
+ kind = str(raw_kind)
75
+
76
+ report = inventory.build_info_report(kind=kind, name=name)
77
+ _emit_report(report, fmt, output)
78
+
79
+ return {
80
+ "pass": ExitCode.OK.value,
81
+ "warning": ExitCode.OK.value,
82
+ "error": ExitCode.ERROR.value,
83
+ "blocked": ExitCode.BLOCKED.value,
84
+ "internal_error": ExitCode.INTERNAL_ERROR.value,
85
+ }.get(report.status, ExitCode.INTERNAL_ERROR.value)
@@ -0,0 +1,8 @@
1
+ """CLI command for lint — thin import wrapper."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from core_runtime.cli.main import cmd_lint
6
+
7
+ __all__ = ["cmd_lint"]
8
+