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.
Files changed (52) hide show
  1. core_cli/__init__.py +3 -0
  2. core_cli/main.py +33 -0
  3. core_cli/resources/__init__.py +3 -0
  4. core_cli/resources/code/__init__.py +22 -0
  5. core_cli/resources/code/actions.py +54 -0
  6. core_cli/resources/code/audit_duplicates.py +37 -0
  7. core_cli/resources/code/bridges.py +90 -0
  8. core_cli/resources/code/check_imports.py +75 -0
  9. core_cli/resources/code/check_ui.py +61 -0
  10. core_cli/resources/code/docstrings.py +44 -0
  11. core_cli/resources/code/fix_atomic.py +57 -0
  12. core_cli/resources/code/format.py +62 -0
  13. core_cli/resources/code/hub.py +10 -0
  14. core_cli/resources/code/integrity.py +75 -0
  15. core_cli/resources/code/lint.py +37 -0
  16. core_cli/resources/code/logging.py +55 -0
  17. core_cli/resources/code/test.py +32 -0
  18. core_cli/resources/lane/__init__.py +26 -0
  19. core_cli/resources/lane/claim.py +48 -0
  20. core_cli/resources/lane/list.py +50 -0
  21. core_cli/resources/lane/next.py +82 -0
  22. core_cli/resources/lane/propose.py +119 -0
  23. core_cli/resources/project/__init__.py +21 -0
  24. core_cli/resources/project/docs.py +41 -0
  25. core_cli/resources/project/onboard.py +110 -0
  26. core_cli/resources/project/scout.py +288 -0
  27. core_cli/resources/proposals/__init__.py +27 -0
  28. core_cli/resources/proposals/create.py +99 -0
  29. core_cli/resources/proposals/integrate.py +57 -0
  30. core_cli/resources/proposals/list.py +115 -0
  31. core_cli/resources/proposals/manage.py +183 -0
  32. core_cli/resources/secrets/__init__.py +10 -0
  33. core_cli/resources/secrets/hub.py +9 -0
  34. core_cli/resources/secrets/manage.py +160 -0
  35. core_cli/resources/symbols/__init__.py +14 -0
  36. core_cli/resources/symbols/audit.py +56 -0
  37. core_cli/resources/symbols/fix_ids.py +39 -0
  38. core_cli/resources/symbols/hub.py +11 -0
  39. core_cli/resources/symbols/resolve_duplicates.py +37 -0
  40. core_cli/resources/symbols/sync.py +39 -0
  41. core_cli/resources/vectors/__init__.py +15 -0
  42. core_cli/resources/vectors/hub.py +9 -0
  43. core_cli/resources/vectors/query.py +66 -0
  44. core_cli/resources/vectors/rebuild.py +71 -0
  45. core_cli/resources/vectors/status.py +40 -0
  46. core_cli/resources/vectors/sync.py +50 -0
  47. core_cli/resources/vectors/sync_code.py +58 -0
  48. core_cli-1.0.0.dist-info/METADATA +72 -0
  49. core_cli-1.0.0.dist-info/RECORD +52 -0
  50. core_cli-1.0.0.dist-info/WHEEL +4 -0
  51. core_cli-1.0.0.dist-info/entry_points.txt +2 -0
  52. core_cli-1.0.0.dist-info/licenses/LICENSE +21 -0
core_cli/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """core-cli — consumer governance CLI for CORE."""
2
+
3
+ from __future__ import annotations
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,3 @@
1
+ """Consumer resource command packages."""
2
+
3
+ from __future__ import annotations
@@ -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,10 @@
1
+ # src/cli/resources/code/hub.py
2
+ import typer
3
+
4
+
5
+ # This is the "Stable Hub" that everyone can import without circular risk
6
+ app = typer.Typer(
7
+ name="code",
8
+ help="Codebase quality, style, and verification operations.",
9
+ no_args_is_help=True,
10
+ )
@@ -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)