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.
- core_runtime/__init__.py +10 -0
- core_runtime/__version__.py +17 -0
- core_runtime/cli/__init__.py +7 -0
- core_runtime/cli/__main__.py +22 -0
- core_runtime/cli/bump_version.py +176 -0
- core_runtime/cli/contract_preflight.py +69 -0
- core_runtime/cli/create_domain.py +66 -0
- core_runtime/cli/doctor.py +44 -0
- core_runtime/cli/inventory.py +85 -0
- core_runtime/cli/lint.py +8 -0
- core_runtime/cli/main.py +579 -0
- core_runtime/cli/release_check.py +65 -0
- core_runtime/cli/repair_artifact_paths.py +48 -0
- core_runtime/cli/sync_template.py +67 -0
- core_runtime/cli/validate.py +73 -0
- core_runtime/core/__init__.py +34 -0
- core_runtime/core/audit_event.py +760 -0
- core_runtime/core/audit_trail_index.py +100 -0
- core_runtime/core/canonicalization.py +55 -0
- core_runtime/core/contract_evaluator.py +945 -0
- core_runtime/core/contract_executability.py +307 -0
- core_runtime/core/contract_loader.py +57 -0
- core_runtime/core/contract_probes.py +630 -0
- core_runtime/core/contract_program.py +131 -0
- core_runtime/core/contract_program_registry.py +104 -0
- core_runtime/core/contract_program_v2.py +126 -0
- core_runtime/core/dsk_v3.py +142 -0
- core_runtime/core/explainability.py +2115 -0
- core_runtime/core/numeric_normalization.py +120 -0
- core_runtime/core/rule_anchor.py +1388 -0
- core_runtime/core/schema_fingerprint.py +69 -0
- core_runtime/core/sensor_evidence.py +548 -0
- core_runtime/data/contracts/CoreAnchor.sol +109 -0
- core_runtime/data/contracts/CoreRuleAnchor.abi.json +111 -0
- core_runtime/data/contracts/CoreRuleAnchor.bin +1 -0
- core_runtime/data/contracts/CoreRuleAnchor.build.json +20 -0
- core_runtime/data/contracts/CoreRuleAnchor.runtime.bin +1 -0
- core_runtime/data/contracts/CoreRuleAnchor.sol +74 -0
- core_runtime/data/package_data_manifest.v1.json +173 -0
- core_runtime/data/schemas/core/causal_trace.v1.json +141 -0
- core_runtime/data/schemas/core/context_gate.v1.json +38 -0
- core_runtime/data/schemas/core/context_threshold.v1.json +46 -0
- core_runtime/data/schemas/core/contract_program.v1.json +186 -0
- core_runtime/data/schemas/core/contract_program.v2.json +187 -0
- core_runtime/data/schemas/core/control_decision.v1.json +114 -0
- core_runtime/data/schemas/core/dsk.v3.json +105 -0
- core_runtime/data/schemas/core/effect_result.v1.json +39 -0
- core_runtime/data/schemas/core/entropy_signal.v1.json +108 -0
- core_runtime/data/schemas/core/execution_receipt.v1.json +93 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v1.json +87 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v2.json +72 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v3.json +38 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v4.json +72 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v5.json +38 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v6.json +116 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v7.json +37 -0
- core_runtime/data/schemas/core/frozen_release_manifest.v8.json +114 -0
- core_runtime/data/schemas/core/frozen_rule_set.v1.json +282 -0
- core_runtime/data/schemas/core/memory_artifact.v1.json +120 -0
- core_runtime/data/schemas/core/memory_generation_result.v1.json +37 -0
- core_runtime/data/schemas/core/operational_learning_event.v1.json +63 -0
- core_runtime/data/schemas/core/pattern_candidate.v1.json +114 -0
- core_runtime/data/schemas/core/physical_safety_assurance_case.v1.json +676 -0
- core_runtime/data/schemas/core/policy_lifecycle.v1.json +99 -0
- core_runtime/data/schemas/core/retention_manifest.v1.json +53 -0
- core_runtime/data/schemas/core/reversibility_policy.v1.json +107 -0
- core_runtime/data/schemas/core/rule_anchor_batch.v1.json +93 -0
- core_runtime/data/schemas/core/rule_anchor_chain_evidence.v1.json +56 -0
- core_runtime/data/schemas/core/rule_approval.v1.json +49 -0
- core_runtime/data/schemas/core/rule_approval_request.v1.json +42 -0
- core_runtime/data/schemas/core/state_transition.v1.json +115 -0
- core_runtime/data/schemas/core/task_closeout.v1.json +47 -0
- core_runtime/data/schemas/core/template_promotion_candidate.v1.json +83 -0
- core_runtime/data/schemas/core/unsigned_rule_anchor_deployment.v1.json +106 -0
- core_runtime/data/schemas/core/unsigned_rule_anchor_transaction.v1.json +116 -0
- core_runtime/tooling/__init__.py +48 -0
- core_runtime/tooling/bump_version.py +900 -0
- core_runtime/tooling/contract_preflight.py +293 -0
- core_runtime/tooling/create_domain.py +254 -0
- core_runtime/tooling/diagnostics.py +129 -0
- core_runtime/tooling/doctor.py +482 -0
- core_runtime/tooling/file_inventory.py +182 -0
- core_runtime/tooling/json_checks.py +100 -0
- core_runtime/tooling/release_check.py +1017 -0
- core_runtime/tooling/repair_artifact_paths.py +458 -0
- core_runtime/tooling/report_writer.py +176 -0
- core_runtime/tooling/repository_inventory.py +399 -0
- core_runtime/tooling/safety_checks.py +172 -0
- core_runtime/tooling/sync_template.py +303 -0
- core_runtime/tooling/validation.py +507 -0
- core_runtime/tooling/version_inventory.py +256 -0
- core_runtime_engine-11.5.1.dist-info/METADATA +35 -0
- core_runtime_engine-11.5.1.dist-info/RECORD +96 -0
- core_runtime_engine-11.5.1.dist-info/WHEEL +5 -0
- core_runtime_engine-11.5.1.dist-info/entry_points.txt +2 -0
- core_runtime_engine-11.5.1.dist-info/top_level.txt +1 -0
core_runtime/__init__.py
ADDED
|
@@ -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,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)
|