core-cli 1.0.0__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_cli/__init__.py +3 -0
- core_cli/main.py +33 -0
- core_cli/resources/__init__.py +3 -0
- core_cli/resources/code/__init__.py +22 -0
- core_cli/resources/code/actions.py +54 -0
- core_cli/resources/code/audit_duplicates.py +37 -0
- core_cli/resources/code/bridges.py +90 -0
- core_cli/resources/code/check_imports.py +75 -0
- core_cli/resources/code/check_ui.py +61 -0
- core_cli/resources/code/docstrings.py +44 -0
- core_cli/resources/code/fix_atomic.py +57 -0
- core_cli/resources/code/format.py +62 -0
- core_cli/resources/code/hub.py +10 -0
- core_cli/resources/code/integrity.py +75 -0
- core_cli/resources/code/lint.py +37 -0
- core_cli/resources/code/logging.py +55 -0
- core_cli/resources/code/test.py +32 -0
- core_cli/resources/lane/__init__.py +26 -0
- core_cli/resources/lane/claim.py +48 -0
- core_cli/resources/lane/list.py +50 -0
- core_cli/resources/lane/next.py +82 -0
- core_cli/resources/lane/propose.py +119 -0
- core_cli/resources/project/__init__.py +21 -0
- core_cli/resources/project/docs.py +41 -0
- core_cli/resources/project/onboard.py +110 -0
- core_cli/resources/project/scout.py +288 -0
- core_cli/resources/proposals/__init__.py +27 -0
- core_cli/resources/proposals/create.py +99 -0
- core_cli/resources/proposals/integrate.py +57 -0
- core_cli/resources/proposals/list.py +115 -0
- core_cli/resources/proposals/manage.py +183 -0
- core_cli/resources/secrets/__init__.py +10 -0
- core_cli/resources/secrets/hub.py +9 -0
- core_cli/resources/secrets/manage.py +160 -0
- core_cli/resources/symbols/__init__.py +14 -0
- core_cli/resources/symbols/audit.py +56 -0
- core_cli/resources/symbols/fix_ids.py +39 -0
- core_cli/resources/symbols/hub.py +11 -0
- core_cli/resources/symbols/resolve_duplicates.py +37 -0
- core_cli/resources/symbols/sync.py +39 -0
- core_cli/resources/vectors/__init__.py +15 -0
- core_cli/resources/vectors/hub.py +9 -0
- core_cli/resources/vectors/query.py +66 -0
- core_cli/resources/vectors/rebuild.py +71 -0
- core_cli/resources/vectors/status.py +40 -0
- core_cli/resources/vectors/sync.py +50 -0
- core_cli/resources/vectors/sync_code.py +58 -0
- core_cli-1.0.0.dist-info/METADATA +72 -0
- core_cli-1.0.0.dist-info/RECORD +52 -0
- core_cli-1.0.0.dist-info/WHEEL +4 -0
- core_cli-1.0.0.dist-info/entry_points.txt +2 -0
- core_cli-1.0.0.dist-info/licenses/LICENSE +21 -0
core_cli/__init__.py
ADDED
core_cli/main.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""core-cli entry point — consumer governance CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from core_cli.resources.code import app as code_app
|
|
8
|
+
from core_cli.resources.lane import app as lane_app
|
|
9
|
+
from core_cli.resources.project import app as project_app
|
|
10
|
+
from core_cli.resources.proposals import app as proposals_app
|
|
11
|
+
from core_cli.resources.secrets import app as secrets_app
|
|
12
|
+
from core_cli.resources.symbols import app as symbols_app
|
|
13
|
+
from core_cli.resources.vectors import app as vectors_app
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# ID: 92ffef66-e5d7-48ae-929e-a82f136a9719
|
|
17
|
+
app = typer.Typer(
|
|
18
|
+
name="core",
|
|
19
|
+
help="CORE consumer governance CLI — govern your project against CORE constitutional rules.",
|
|
20
|
+
no_args_is_help=True,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
app.add_typer(lane_app, name="lane")
|
|
24
|
+
app.add_typer(proposals_app, name="proposals")
|
|
25
|
+
app.add_typer(secrets_app, name="secrets")
|
|
26
|
+
app.add_typer(code_app, name="code")
|
|
27
|
+
app.add_typer(symbols_app, name="symbols")
|
|
28
|
+
app.add_typer(vectors_app, name="vectors")
|
|
29
|
+
app.add_typer(project_app, name="project")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
app()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Consumer codebase commands — quality, style, and verification over HTTP."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from . import ( # noqa: F401
|
|
6
|
+
actions,
|
|
7
|
+
audit_duplicates,
|
|
8
|
+
bridges,
|
|
9
|
+
check_imports,
|
|
10
|
+
check_ui,
|
|
11
|
+
docstrings,
|
|
12
|
+
fix_atomic,
|
|
13
|
+
format,
|
|
14
|
+
integrity,
|
|
15
|
+
lint,
|
|
16
|
+
logging,
|
|
17
|
+
test,
|
|
18
|
+
)
|
|
19
|
+
from .hub import app
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = ["app"]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# src/cli/resources/code/actions.py
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
|
|
7
|
+
from api.cli import CoreApiClient
|
|
8
|
+
from cli.utils import core_command
|
|
9
|
+
from shared.cli.command_meta import (
|
|
10
|
+
CommandBehavior,
|
|
11
|
+
CommandExposure,
|
|
12
|
+
CommandLayer,
|
|
13
|
+
command_meta,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from .hub import app
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
console = Console()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@app.command("actions")
|
|
24
|
+
@command_meta(
|
|
25
|
+
canonical_name="code.actions",
|
|
26
|
+
behavior=CommandBehavior.READ,
|
|
27
|
+
layer=CommandLayer.BODY,
|
|
28
|
+
exposure=CommandExposure.GOVERNOR_ONLY,
|
|
29
|
+
summary="List all registered Atomic Actions showing their IDs, categories, impact levels, and descriptions.",
|
|
30
|
+
dangerous=False,
|
|
31
|
+
)
|
|
32
|
+
@core_command(dangerous=False, requires_context=False)
|
|
33
|
+
# ID: 4f8e13a3-f017-4d0f-a6ef-340f81d05341
|
|
34
|
+
async def list_actions_cmd() -> None:
|
|
35
|
+
"""
|
|
36
|
+
List all registered Atomic Actions (Body Capabilities).
|
|
37
|
+
Shows IDs, categories, and impact levels for autonomous building blocks.
|
|
38
|
+
"""
|
|
39
|
+
client = CoreApiClient()
|
|
40
|
+
payload = await client.list_actions()
|
|
41
|
+
actions = payload.get("actions", [])
|
|
42
|
+
table = Table(title="Registered Atomic Actions", header_style="bold green")
|
|
43
|
+
table.add_column("Action ID", style="cyan")
|
|
44
|
+
table.add_column("Category", style="blue")
|
|
45
|
+
table.add_column("Impact", style="magenta")
|
|
46
|
+
table.add_column("Description")
|
|
47
|
+
for action in sorted(actions, key=lambda a: a.get("action_id", "")):
|
|
48
|
+
table.add_row(
|
|
49
|
+
action.get("action_id", ""),
|
|
50
|
+
action.get("category", ""),
|
|
51
|
+
action.get("impact_level", ""),
|
|
52
|
+
action.get("description", ""),
|
|
53
|
+
)
|
|
54
|
+
console.print(table)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# src/core_cli/resources/code/audit_duplicates.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
|
|
7
|
+
from api.cli import CoreApiClient
|
|
8
|
+
from cli.utils import core_command
|
|
9
|
+
|
|
10
|
+
from .hub import app
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@app.command("audit-duplicates")
|
|
17
|
+
@core_command(dangerous=False, requires_context=False)
|
|
18
|
+
# ID: 2ad098e5-aa8a-4e12-89e4-be20d2e12e03
|
|
19
|
+
async def audit_duplicates_cmd(
|
|
20
|
+
threshold: float = typer.Option(0.96, help="Similarity threshold (0.0-1.0)."),
|
|
21
|
+
) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Perform a semantic scan to find duplicate logic across the codebase.
|
|
24
|
+
Helps enforce the 'dry_by_design' constitutional principle.
|
|
25
|
+
"""
|
|
26
|
+
client = CoreApiClient()
|
|
27
|
+
result = await client.inspect.analysis_duplicates(threshold=threshold)
|
|
28
|
+
if result.get("ok"):
|
|
29
|
+
console.print(
|
|
30
|
+
f"[green]✓ Duplicates scan complete (threshold={threshold}).[/green]"
|
|
31
|
+
)
|
|
32
|
+
note = result.get("note", "")
|
|
33
|
+
if note:
|
|
34
|
+
console.print(f"[dim]{note}[/dim]")
|
|
35
|
+
else:
|
|
36
|
+
console.print(f"[red]✗ Duplicates scan failed: {result.get('error')}[/red]")
|
|
37
|
+
raise typer.Exit(1)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# src/core_cli/resources/code/bridges.py
|
|
2
|
+
"""CLI command: list declared architecture bridge points (issue #617)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
from api.cli import CoreApiClient
|
|
11
|
+
from cli.utils import core_command
|
|
12
|
+
|
|
13
|
+
from .hub import app
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@app.command("bridges")
|
|
20
|
+
@core_command(dangerous=False, requires_context=False)
|
|
21
|
+
# ID: 5b2e91f3-a4c8-4d7e-b6f0-8c1a9d2e3f04
|
|
22
|
+
async def list_bridges_cmd(
|
|
23
|
+
consuming: str | None = typer.Option(
|
|
24
|
+
None,
|
|
25
|
+
"--consuming",
|
|
26
|
+
"-c",
|
|
27
|
+
help="Filter by consuming type (e.g. AuditFinding, Proposal).",
|
|
28
|
+
),
|
|
29
|
+
) -> None:
|
|
30
|
+
"""
|
|
31
|
+
List declared architecture bridge points.
|
|
32
|
+
|
|
33
|
+
Each bridge is a constitutionally-declared data-flow crossing where
|
|
34
|
+
information moves between CORE layers. Use --consuming to find bridges
|
|
35
|
+
that consume a specific data type.
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
core code bridges --consuming AuditFinding
|
|
39
|
+
"""
|
|
40
|
+
client = CoreApiClient()
|
|
41
|
+
result = await client.inspect.analysis_bridges(consuming=consuming)
|
|
42
|
+
|
|
43
|
+
if not result.get("available"):
|
|
44
|
+
console.print(f"[red]✗ Bridges unavailable: {result.get('error')}[/red]")
|
|
45
|
+
raise typer.Exit(1)
|
|
46
|
+
|
|
47
|
+
bridges = result.get("bridges", [])
|
|
48
|
+
if not bridges:
|
|
49
|
+
if consuming:
|
|
50
|
+
console.print(
|
|
51
|
+
f"[yellow]No bridges found consuming type '{consuming}'.[/yellow]"
|
|
52
|
+
)
|
|
53
|
+
else:
|
|
54
|
+
console.print("[yellow]No bridge declarations found.[/yellow]")
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
title = (
|
|
58
|
+
f"Architecture Bridges (consuming '{consuming}')"
|
|
59
|
+
if consuming
|
|
60
|
+
else "Architecture Bridges"
|
|
61
|
+
)
|
|
62
|
+
table = Table(title=title, header_style="bold cyan", show_lines=True)
|
|
63
|
+
table.add_column("ID", style="cyan", no_wrap=True)
|
|
64
|
+
table.add_column("Bridge Class", style="bold")
|
|
65
|
+
table.add_column("Layer", style="blue")
|
|
66
|
+
table.add_column("Source", style="dim")
|
|
67
|
+
table.add_column("Sink Target", style="green")
|
|
68
|
+
table.add_column("Attribution", style="magenta")
|
|
69
|
+
table.add_column("ADRs", style="dim")
|
|
70
|
+
|
|
71
|
+
for bridge in bridges:
|
|
72
|
+
source = bridge.get("source_layer") or (
|
|
73
|
+
bridge.get("source_context", "")[:40]
|
|
74
|
+
)
|
|
75
|
+
attr_mechanism = bridge.get("attribution_mechanism", "")
|
|
76
|
+
attr_field = bridge.get("attribution_field")
|
|
77
|
+
attribution = (
|
|
78
|
+
f"{attr_mechanism} → {attr_field}" if attr_field else attr_mechanism
|
|
79
|
+
)
|
|
80
|
+
table.add_row(
|
|
81
|
+
bridge.get("id", ""),
|
|
82
|
+
bridge.get("bridge_class", ""),
|
|
83
|
+
bridge.get("bridge_layer", ""),
|
|
84
|
+
source,
|
|
85
|
+
bridge.get("sink_target", ""),
|
|
86
|
+
attribution,
|
|
87
|
+
", ".join(bridge.get("authority_adrs", [])),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
console.print(table)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# src/cli/resources/code/check_imports.py
|
|
2
|
+
"""
|
|
3
|
+
Import integrity check command.
|
|
4
|
+
|
|
5
|
+
Routes through POST /v1/quality/imports for constitutional compliance.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.table import Table
|
|
15
|
+
|
|
16
|
+
from api.cli import CoreApiClient
|
|
17
|
+
from cli.utils import core_command
|
|
18
|
+
|
|
19
|
+
from .hub import app
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
console = Console()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@app.command("check-imports")
|
|
27
|
+
@core_command(dangerous=False, requires_context=False)
|
|
28
|
+
# ID: 53db749b-a5cf-44e4-8e7f-47e6499aec0b
|
|
29
|
+
async def check_imports_cmd(ctx: typer.Context) -> None:
|
|
30
|
+
"""
|
|
31
|
+
Verify all import statements resolve to existing modules.
|
|
32
|
+
|
|
33
|
+
Checks for:
|
|
34
|
+
- F821: References to undefined names (moved/deleted modules)
|
|
35
|
+
- F401: Stale imports left after refactoring
|
|
36
|
+
|
|
37
|
+
Enforces:
|
|
38
|
+
- code.imports.must_resolve
|
|
39
|
+
- code.imports.no_stale_namespace
|
|
40
|
+
"""
|
|
41
|
+
console.rule("[bold cyan]Import Integrity Check[/bold cyan]")
|
|
42
|
+
console.print("[dim]Scanning src/ for unresolvable imports (F821, F401)...[/dim]\n")
|
|
43
|
+
client = CoreApiClient()
|
|
44
|
+
result = await client.quality_imports()
|
|
45
|
+
if result.get("status") == "ok":
|
|
46
|
+
console.print("[bold green]✅ All imports resolve cleanly.[/bold green]")
|
|
47
|
+
return
|
|
48
|
+
violations = result.get("violations", [])
|
|
49
|
+
violation_count = len(violations)
|
|
50
|
+
console.print(
|
|
51
|
+
f"[bold red]❌ {violation_count} unresolvable import(s) found.[/bold red]\n"
|
|
52
|
+
)
|
|
53
|
+
if violations:
|
|
54
|
+
table = Table(
|
|
55
|
+
title="Import Violations", show_header=True, header_style="bold red"
|
|
56
|
+
)
|
|
57
|
+
table.add_column("File", style="cyan", overflow="fold")
|
|
58
|
+
table.add_column("Line", style="yellow", justify="right", width=6)
|
|
59
|
+
table.add_column("Rule", style="magenta", width=6)
|
|
60
|
+
table.add_column("Message", style="white", overflow="fold")
|
|
61
|
+
for v in violations:
|
|
62
|
+
table.add_row(
|
|
63
|
+
v.get("file", ""),
|
|
64
|
+
str(v.get("line", "")),
|
|
65
|
+
v.get("rule", ""),
|
|
66
|
+
v.get("message", ""),
|
|
67
|
+
)
|
|
68
|
+
console.print(table)
|
|
69
|
+
console.print(
|
|
70
|
+
"\n[dim]Fix: Update stale import paths or remove unused imports.[/dim]"
|
|
71
|
+
)
|
|
72
|
+
console.print(
|
|
73
|
+
"[dim]Rule: code.imports.must_resolve (.intent/rules/code/imports.json)[/dim]"
|
|
74
|
+
)
|
|
75
|
+
raise typer.Exit(code=1)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# src/core_cli/resources/code/check_ui.py
|
|
2
|
+
"""
|
|
3
|
+
Code UI Compliance Action.
|
|
4
|
+
Ensures Body-layer modules are HEADLESS (no print, rich, or direct os.environ).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
|
|
12
|
+
from api.cli import CoreApiClient
|
|
13
|
+
from cli.utils import core_command
|
|
14
|
+
|
|
15
|
+
from .hub import app
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@app.command("check-ui")
|
|
22
|
+
@core_command(dangerous=True, requires_context=False)
|
|
23
|
+
# ID: 662d6bed-c6fc-4120-ae34-d9063f703994
|
|
24
|
+
async def check_ui_cmd(
|
|
25
|
+
write: bool = typer.Option(
|
|
26
|
+
False, "--write", help="Use LLM to autonomously fix UI contract violations."
|
|
27
|
+
),
|
|
28
|
+
) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Check and repair Body-layer UI contract violations.
|
|
31
|
+
|
|
32
|
+
Validates that logic in features/ and services/ does not use:
|
|
33
|
+
- print() or input()
|
|
34
|
+
- rich.console or formatting
|
|
35
|
+
- os.environ (must use shared.config.settings)
|
|
36
|
+
|
|
37
|
+
If --write is used, CORE invokes an AI specialist to refactor the
|
|
38
|
+
violating modules into a headless state.
|
|
39
|
+
"""
|
|
40
|
+
client = CoreApiClient()
|
|
41
|
+
if not write:
|
|
42
|
+
console.print("[bold cyan]🔍 Checking Body UI Contracts...[/bold cyan]")
|
|
43
|
+
result = await client.quality_body_ui()
|
|
44
|
+
if result.get("status") == "ok":
|
|
45
|
+
console.print("[green]✓ Body UI contracts clean.[/green]")
|
|
46
|
+
return
|
|
47
|
+
violations = result.get("violations", [])
|
|
48
|
+
console.print(f"\n[red]❌ Found {len(violations)} contract violations.[/red]")
|
|
49
|
+
console.print("[yellow]💡 Run with '--write' to auto-fix via LLM.[/yellow]")
|
|
50
|
+
raise typer.Exit(1)
|
|
51
|
+
console.print("[bold cyan]🔧 Refactoring UI leaks out of Body layer...[/bold cyan]")
|
|
52
|
+
initial = await client.run_fix("fix.body_ui", write=True)
|
|
53
|
+
run_id = initial.get("run_id")
|
|
54
|
+
if not run_id:
|
|
55
|
+
console.print(f"[red]fix.body_ui failed to dispatch: {initial}[/red]")
|
|
56
|
+
raise typer.Exit(1)
|
|
57
|
+
final = await client._poll_run(run_id)
|
|
58
|
+
if final.get("status") != "completed":
|
|
59
|
+
console.print(f"[red]fix.body_ui failed: {final.get('error') or final}[/red]")
|
|
60
|
+
raise typer.Exit(1)
|
|
61
|
+
console.print("[green]✓ fix.body_ui completed.[/green]")
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# src/core_cli/resources/code/docstrings.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
|
|
7
|
+
from api.cli import CoreApiClient
|
|
8
|
+
from cli.utils import core_command
|
|
9
|
+
|
|
10
|
+
from .hub import app
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@app.command("docstrings")
|
|
17
|
+
@core_command(dangerous=True, requires_context=False)
|
|
18
|
+
# ID: 9f0d0239-d29d-4dff-8c32-3fdecaa809e9
|
|
19
|
+
async def fix_docstrings_command(
|
|
20
|
+
write: bool = typer.Option(False, "--write", help="Apply changes."),
|
|
21
|
+
limit: int = typer.Option(3, "--limit", help="Symbols to process."),
|
|
22
|
+
file: str | None = typer.Option(
|
|
23
|
+
None, "--file", help="Target a single file (repo-relative)."
|
|
24
|
+
),
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Autonomously generate and inject missing docstrings using AI."""
|
|
27
|
+
client = CoreApiClient()
|
|
28
|
+
initial = await client.run_fix(
|
|
29
|
+
"fix.docstrings",
|
|
30
|
+
target_files=[file] if file else None,
|
|
31
|
+
write=write,
|
|
32
|
+
params={"limit": limit},
|
|
33
|
+
)
|
|
34
|
+
run_id = initial.get("run_id")
|
|
35
|
+
if not run_id:
|
|
36
|
+
console.print(f"[red]fix.docstrings failed to dispatch: {initial}[/red]")
|
|
37
|
+
raise typer.Exit(1)
|
|
38
|
+
final = await client._poll_run(run_id)
|
|
39
|
+
if final.get("status") != "completed":
|
|
40
|
+
console.print(
|
|
41
|
+
f"[red]fix.docstrings failed: {final.get('error') or final}[/red]"
|
|
42
|
+
)
|
|
43
|
+
raise typer.Exit(1)
|
|
44
|
+
console.print("[green]✓ fix.docstrings completed.[/green]")
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# src/cli/resources/code/fix_atomic.py
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
|
|
7
|
+
from api.cli import CoreApiClient
|
|
8
|
+
from cli.utils import core_command
|
|
9
|
+
from shared.cli.command_meta import (
|
|
10
|
+
CommandBehavior,
|
|
11
|
+
CommandExposure,
|
|
12
|
+
CommandLayer,
|
|
13
|
+
command_meta,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from .hub import app
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
console = Console()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@app.command("fix-atomic")
|
|
24
|
+
@command_meta(
|
|
25
|
+
canonical_name="code.fix-atomic",
|
|
26
|
+
behavior=CommandBehavior.MUTATE,
|
|
27
|
+
layer=CommandLayer.BODY,
|
|
28
|
+
exposure=CommandExposure.GOVERNOR_ONLY,
|
|
29
|
+
summary="Heal violations in the Atomic Action pattern.",
|
|
30
|
+
dangerous=True,
|
|
31
|
+
)
|
|
32
|
+
@core_command(dangerous=True, requires_context=False)
|
|
33
|
+
# ID: 1a0797b9-e800-4783-9305-158b5f9247af
|
|
34
|
+
async def fix_atomic_cmd(
|
|
35
|
+
ctx: typer.Context,
|
|
36
|
+
write: bool = typer.Option(
|
|
37
|
+
False, "--write", help="Apply fixes to atomic action patterns."
|
|
38
|
+
),
|
|
39
|
+
) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Heal violations in the Atomic Action pattern.
|
|
42
|
+
Ensures @atomic_action decorators and ActionResult returns are present.
|
|
43
|
+
"""
|
|
44
|
+
client = CoreApiClient()
|
|
45
|
+
initial = await client.run_fix("fix.atomic_actions", write=write)
|
|
46
|
+
run_id = initial.get("run_id")
|
|
47
|
+
if not run_id:
|
|
48
|
+
console.print(f"[red]fix.atomic_actions failed to dispatch: {initial}[/red]")
|
|
49
|
+
raise typer.Exit(1)
|
|
50
|
+
final = await client._poll_run(run_id)
|
|
51
|
+
if final.get("status") == "completed":
|
|
52
|
+
console.print("[green]✓ fix.atomic_actions completed.[/green]")
|
|
53
|
+
return
|
|
54
|
+
console.print(
|
|
55
|
+
f"[red]fix.atomic_actions failed: {final.get('error') or final}[/red]"
|
|
56
|
+
)
|
|
57
|
+
raise typer.Exit(1)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# src/core_cli/resources/code/format.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
|
|
9
|
+
from api.cli import CoreApiClient
|
|
10
|
+
from cli.utils import core_command
|
|
11
|
+
|
|
12
|
+
from .hub import app
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def _run_and_poll(fix_id: str, *, write: bool) -> None:
|
|
20
|
+
client = CoreApiClient()
|
|
21
|
+
initial = await client.run_fix(fix_id, write=write)
|
|
22
|
+
run_id = initial.get("run_id")
|
|
23
|
+
if not run_id:
|
|
24
|
+
console.print(f"[red]{fix_id} failed to dispatch: {initial}[/red]")
|
|
25
|
+
raise typer.Exit(1)
|
|
26
|
+
final = await client._poll_run(run_id)
|
|
27
|
+
if final.get("status") != "completed":
|
|
28
|
+
console.print(f"[red]{fix_id} failed: {final.get('error') or final}[/red]")
|
|
29
|
+
raise typer.Exit(1)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@app.command("format")
|
|
33
|
+
@core_command(dangerous=True, requires_context=False)
|
|
34
|
+
# ID: 3dfcec28-34fd-44d4-9aec-bcbf29eb6d76
|
|
35
|
+
async def format_command(
|
|
36
|
+
write: bool = typer.Option(
|
|
37
|
+
False, "--write", help="Apply formatting changes to disk."
|
|
38
|
+
),
|
|
39
|
+
) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Format code using Black and Ruff.
|
|
42
|
+
|
|
43
|
+
Defaults to dry-run mode. Use --write to apply changes.
|
|
44
|
+
"""
|
|
45
|
+
mode = "Applying" if write else "Checking"
|
|
46
|
+
console.print(f"[bold cyan]✨ {mode} code formatting (Black + Ruff)...[/bold cyan]")
|
|
47
|
+
await _run_and_poll("fix.format", write=write)
|
|
48
|
+
if not write:
|
|
49
|
+
console.print("[yellow]💡 Run with --write to apply these changes.[/yellow]")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@app.command("format-imports")
|
|
53
|
+
@core_command(dangerous=True, requires_context=False)
|
|
54
|
+
# ID: 582d69b9-6f50-411a-a950-37e66ce2e07b
|
|
55
|
+
async def format_imports_cmd(
|
|
56
|
+
write: bool = typer.Option(False, "--write", help="Apply import sorting to disk."),
|
|
57
|
+
) -> None:
|
|
58
|
+
"""
|
|
59
|
+
Sort and group Python imports according to PEP 8 / Constitutional standards.
|
|
60
|
+
Uses Ruff's import sorter (I) rules.
|
|
61
|
+
"""
|
|
62
|
+
await _run_and_poll("fix.imports", write=write)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# src/cli/resources/code/integrity.py
|
|
2
|
+
"""
|
|
3
|
+
Integrity CLI Commands - Phase 2 Hardening.
|
|
4
|
+
Allows the operator to baseline and verify the codebase state.
|
|
5
|
+
|
|
6
|
+
Thin clients over POST /v1/integrity/{baseline,verify}. All execution
|
|
7
|
+
moves server-side; this module only dispatches and renders.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
|
|
15
|
+
from api.cli import CoreApiClient
|
|
16
|
+
from cli.utils import core_command
|
|
17
|
+
|
|
18
|
+
from .hub import app
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
console = Console()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@app.command("baseline")
|
|
25
|
+
@core_command(dangerous=False)
|
|
26
|
+
# ID: d76fd565-17ef-4f35-9138-9530efc28324
|
|
27
|
+
async def code_baseline_cmd(
|
|
28
|
+
ctx: typer.Context,
|
|
29
|
+
label: str = typer.Option(
|
|
30
|
+
"default", "--label", "-l", help="Label for this baseline."
|
|
31
|
+
),
|
|
32
|
+
) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Create a secure checksum baseline of the current 'src/' directory.
|
|
35
|
+
Run this before starting autonomous tasks.
|
|
36
|
+
"""
|
|
37
|
+
_ = ctx
|
|
38
|
+
client = CoreApiClient()
|
|
39
|
+
console.print(f"[bold cyan]Creating integrity baseline: {label}...[/bold cyan]")
|
|
40
|
+
result = await client.baseline(label=label)
|
|
41
|
+
path = result.get("path")
|
|
42
|
+
files_hashed = result.get("files_hashed", 0)
|
|
43
|
+
console.print(
|
|
44
|
+
f"[bold green]Baseline stored at: {path} ({files_hashed} files)[/bold green]"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@app.command("verify")
|
|
49
|
+
@core_command(dangerous=False)
|
|
50
|
+
# ID: 56975e9b-9040-4c0e-bdc4-76b7c68c5abd
|
|
51
|
+
async def code_verify_cmd(
|
|
52
|
+
ctx: typer.Context,
|
|
53
|
+
label: str = typer.Option(
|
|
54
|
+
"default", "--label", "-l", help="Baseline label to verify against."
|
|
55
|
+
),
|
|
56
|
+
) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Verify the current codebase against a previously created baseline.
|
|
59
|
+
Detects any unauthorized MODIFICATIONS, DELETIONS, or NEW files.
|
|
60
|
+
"""
|
|
61
|
+
_ = ctx
|
|
62
|
+
client = CoreApiClient()
|
|
63
|
+
console.print(
|
|
64
|
+
f"[bold cyan]Verifying code integrity against baseline: {label}...[/bold cyan]"
|
|
65
|
+
)
|
|
66
|
+
result = await client.verify(label=label)
|
|
67
|
+
if result.get("ok"):
|
|
68
|
+
console.print(
|
|
69
|
+
"[bold green]Integrity verified: no unauthorized changes detected.[/bold green]"
|
|
70
|
+
)
|
|
71
|
+
else:
|
|
72
|
+
console.print("[bold red]Integrity violation found.[/bold red]")
|
|
73
|
+
for error in result.get("errors", []):
|
|
74
|
+
console.print(f" [yellow]-[/yellow] {error}")
|
|
75
|
+
raise typer.Exit(code=1)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# src/cli/resources/code/lint.py
|
|
2
|
+
import typer
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
|
|
5
|
+
from api.cli import CoreApiClient
|
|
6
|
+
from cli.utils import core_command
|
|
7
|
+
|
|
8
|
+
from .hub import app
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
console = Console()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.command("lint")
|
|
15
|
+
@core_command(dangerous=False, requires_context=False)
|
|
16
|
+
# ID: 044f8edf-3262-4e8b-b394-fd76bdd74136
|
|
17
|
+
async def lint_command() -> None:
|
|
18
|
+
"""Check code quality using Black and Ruff (Read-Only)."""
|
|
19
|
+
console.print("[bold cyan]🔎 Linting codebase...[/bold cyan]")
|
|
20
|
+
client = CoreApiClient()
|
|
21
|
+
result = await client.lint()
|
|
22
|
+
|
|
23
|
+
if result.get("error"):
|
|
24
|
+
console.print(f"[red]Lint failed: {result['error']}[/red]")
|
|
25
|
+
raise typer.Exit(1)
|
|
26
|
+
|
|
27
|
+
for tool_name, tool_result in result.get("tools", {}).items():
|
|
28
|
+
rc = tool_result["returncode"]
|
|
29
|
+
marker = "[green]✓[/green]" if rc == 0 else "[red]✗[/red]"
|
|
30
|
+
console.print(f"{marker} {tool_name} (returncode={rc})")
|
|
31
|
+
if tool_result.get("stdout"):
|
|
32
|
+
console.print(tool_result["stdout"], highlight=False, end="")
|
|
33
|
+
if tool_result.get("stderr"):
|
|
34
|
+
console.print(tool_result["stderr"], highlight=False, end="")
|
|
35
|
+
|
|
36
|
+
if not result.get("ok"):
|
|
37
|
+
raise typer.Exit(1)
|