huitzo-cli 0.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 (41) hide show
  1. huitzo_cli/__init__.py +14 -0
  2. huitzo_cli/auth.py +146 -0
  3. huitzo_cli/commands/__init__.py +1 -0
  4. huitzo_cli/commands/auth.py +61 -0
  5. huitzo_cli/commands/build.py +81 -0
  6. huitzo_cli/commands/config_cmd.py +98 -0
  7. huitzo_cli/commands/dashboard.py +145 -0
  8. huitzo_cli/commands/dev.py +113 -0
  9. huitzo_cli/commands/init.py +142 -0
  10. huitzo_cli/commands/registry.py +105 -0
  11. huitzo_cli/commands/secrets.py +197 -0
  12. huitzo_cli/commands/test.py +56 -0
  13. huitzo_cli/commands/validate.py +64 -0
  14. huitzo_cli/config.py +219 -0
  15. huitzo_cli/docs_server/__init__.py +1 -0
  16. huitzo_cli/docs_server/server.py +128 -0
  17. huitzo_cli/main.py +72 -0
  18. huitzo_cli/sandbox/__init__.py +1 -0
  19. huitzo_cli/sandbox/proxy.py +75 -0
  20. huitzo_cli/templates/dashboard/App.css.j2 +19 -0
  21. huitzo_cli/templates/dashboard/App.tsx.j2 +21 -0
  22. huitzo_cli/templates/dashboard/index.css.j2 +58 -0
  23. huitzo_cli/templates/dashboard/index.html.j2 +13 -0
  24. huitzo_cli/templates/dashboard/main.tsx.j2 +10 -0
  25. huitzo_cli/templates/dashboard/package.json.j2 +19 -0
  26. huitzo_cli/templates/dashboard/tsconfig.json.j2 +25 -0
  27. huitzo_cli/templates/dashboard/tsconfig.node.json.j2 +10 -0
  28. huitzo_cli/templates/dashboard/vite.config.ts.j2 +15 -0
  29. huitzo_cli/templates/pack/.gitignore.j2 +13 -0
  30. huitzo_cli/templates/pack/README.md.j2 +52 -0
  31. huitzo_cli/templates/pack/__init__.py.j2 +3 -0
  32. huitzo_cli/templates/pack/hello.py.j2 +20 -0
  33. huitzo_cli/templates/pack/huitzo.yaml.j2 +11 -0
  34. huitzo_cli/templates/pack/pyproject.toml.j2 +31 -0
  35. huitzo_cli/templates/pack/test_hello.py.j2 +22 -0
  36. huitzo_cli/validator.py +133 -0
  37. huitzo_cli/version.py +14 -0
  38. huitzo_cli-0.0.0.dist-info/METADATA +19 -0
  39. huitzo_cli-0.0.0.dist-info/RECORD +41 -0
  40. huitzo_cli-0.0.0.dist-info/WHEEL +4 -0
  41. huitzo_cli-0.0.0.dist-info/entry_points.txt +2 -0
huitzo_cli/__init__.py ADDED
@@ -0,0 +1,14 @@
1
+ """
2
+ Module: huitzo_cli
3
+ Description: CLI for developing Intelligence Packs on Huitzo
4
+
5
+ Implements:
6
+ - docs/cli/reference.md
7
+ - docs/guides/developer-environment.md
8
+
9
+ See Also:
10
+ - docs/architecture/overview.md (for platform architecture)
11
+ - docs/sdk/commands.md (for Intelligence Pack command patterns)
12
+ """
13
+
14
+ __version__ = "0.0.0"
huitzo_cli/auth.py ADDED
@@ -0,0 +1,146 @@
1
+ """
2
+ Module: auth
3
+ Description: Authentication client and token storage
4
+
5
+ Implements:
6
+ - docs/cli/reference.md#huitzo-login
7
+ - docs/guides/developer-environment.md#authentication
8
+
9
+ See Also:
10
+ - docs/reference/secrets.md (for secure storage patterns)
11
+ """
12
+
13
+ import os
14
+ from pathlib import Path
15
+ from typing import Any, Optional
16
+
17
+ from huitzo_cli.config import ConfigManager
18
+
19
+
20
+ class TokenStore:
21
+ """Manages authentication tokens securely."""
22
+
23
+ def __init__(self) -> None:
24
+ """Initialize token store."""
25
+ self.token_path = self._get_token_path()
26
+
27
+ @staticmethod
28
+ def _get_token_path() -> Path:
29
+ """Get platform-specific token file path."""
30
+ if os.name == "nt": # Windows
31
+ base = Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming"))
32
+ return base / "huitzo" / "token"
33
+ else: # Unix-like
34
+ return Path.home() / ".huitzo" / "token"
35
+
36
+ def save(self, token: str) -> None:
37
+ """Save token to secure storage.
38
+
39
+ Args:
40
+ token: Authentication token to save
41
+ """
42
+ self.token_path.parent.mkdir(parents=True, exist_ok=True)
43
+
44
+ # Save token
45
+ with open(self.token_path, "w") as f:
46
+ f.write(token)
47
+
48
+ # Set restrictive permissions (Unix-like only)
49
+ if os.name != "nt":
50
+ self.token_path.chmod(0o600)
51
+ self.token_path.parent.chmod(0o700)
52
+
53
+ def load(self) -> Optional[str]:
54
+ """Load token from secure storage.
55
+
56
+ Returns:
57
+ Token if it exists, None otherwise
58
+ """
59
+ if not self.token_path.exists():
60
+ return None
61
+
62
+ try:
63
+ with open(self.token_path) as f:
64
+ return f.read().strip()
65
+ except OSError:
66
+ return None
67
+
68
+ def delete(self) -> None:
69
+ """Delete stored token."""
70
+ if self.token_path.exists():
71
+ try:
72
+ self.token_path.unlink()
73
+ except OSError:
74
+ pass
75
+
76
+
77
+ class AuthClient:
78
+ """Client for handling authentication with Huitzo Cloud.
79
+
80
+ Note: Huitzo Cloud is not yet available. This is a placeholder implementation.
81
+ """
82
+
83
+ def __init__(self, config_manager: ConfigManager) -> None:
84
+ """Initialize auth client.
85
+
86
+ Args:
87
+ config_manager: Configuration manager instance
88
+ """
89
+ self.config = config_manager
90
+ self.token_store = TokenStore()
91
+
92
+ def login(self, token: Optional[str] = None) -> None:
93
+ """Login to Huitzo Cloud.
94
+
95
+ Args:
96
+ token: Optional token for testing. If None, will attempt cloud auth (not yet available).
97
+ """
98
+ if token:
99
+ # Save token for testing
100
+ self.token_store.save(token)
101
+ self.config.set("auth.token", token, scope="user")
102
+ else:
103
+ # Cloud authentication would happen here
104
+ raise RuntimeError("Huitzo Cloud is not yet available")
105
+
106
+ def logout(self) -> None:
107
+ """Logout and remove stored token."""
108
+ self.token_store.delete()
109
+ self.config.set("auth.token", None, scope="user")
110
+
111
+ def get_token(self) -> Optional[str]:
112
+ """Get current authentication token.
113
+
114
+ Returns:
115
+ Token if authenticated, None otherwise
116
+ """
117
+ # Try config first
118
+ config_token = self.config.get("auth.token")
119
+ if config_token is not None and isinstance(config_token, str):
120
+ return str(config_token)
121
+
122
+ # Try token file
123
+ token = self.token_store.load()
124
+ if token:
125
+ return token
126
+
127
+ return None
128
+
129
+ def is_authenticated(self) -> bool:
130
+ """Check if user is authenticated.
131
+
132
+ Returns:
133
+ True if authenticated, False otherwise
134
+ """
135
+ return self.get_token() is not None
136
+
137
+ def get_current_user(self) -> Optional[dict[str, Any]]:
138
+ """Get current authenticated user info.
139
+
140
+ Note: Returns None until cloud is available.
141
+
142
+ Returns:
143
+ User info dict if authenticated, None otherwise
144
+ """
145
+ # Cloud API would be called here
146
+ return None
@@ -0,0 +1 @@
1
+ """CLI commands for Huitzo."""
@@ -0,0 +1,61 @@
1
+ """
2
+ Module: auth_command
3
+ Description: Login and logout commands
4
+
5
+ Implements:
6
+ - docs/cli/reference.md#huitzo-login
7
+ - docs/guides/developer-environment.md#authentication
8
+ """
9
+
10
+ from typing import Optional
11
+
12
+ import typer
13
+ from rich.console import Console
14
+
15
+ from huitzo_cli.auth import AuthClient
16
+ from huitzo_cli.config import ConfigManager
17
+
18
+ console = Console()
19
+ auth_app = typer.Typer(help="Authentication commands", invoke_without_command=True)
20
+
21
+
22
+ @auth_app.command()
23
+ def login(
24
+ token: Optional[str] = typer.Option(
25
+ None,
26
+ "--token",
27
+ help="Authentication token (for testing only)",
28
+ ),
29
+ ) -> None:
30
+ """Login to Huitzo Cloud.
31
+
32
+ Note: Huitzo Cloud is not yet available. Use --token for testing.
33
+ """
34
+ config = ConfigManager()
35
+ auth_client = AuthClient(config)
36
+
37
+ if not token:
38
+ console.print("[yellow]ℹ️ Huitzo Cloud is not yet available[/yellow]")
39
+ console.print("Use [bold]--token <value>[/bold] for testing")
40
+ raise typer.Exit(1)
41
+
42
+ try:
43
+ auth_client.login(token=token)
44
+ console.print("[green]✅ Logged in successfully[/green]")
45
+ except Exception as e:
46
+ console.print(f"[red]❌ Login failed: {e}[/red]")
47
+ raise typer.Exit(1)
48
+
49
+
50
+ @auth_app.command()
51
+ def logout() -> None:
52
+ """Logout from Huitzo Cloud."""
53
+ config = ConfigManager()
54
+ auth_client = AuthClient(config)
55
+
56
+ try:
57
+ auth_client.logout()
58
+ console.print("[green]✅ Logged out successfully[/green]")
59
+ except Exception as e:
60
+ console.print(f"[red]❌ Logout failed: {e}[/red]")
61
+ raise typer.Exit(1)
@@ -0,0 +1,81 @@
1
+ """
2
+ Module: build_command
3
+ Description: Pack building command
4
+
5
+ Implements:
6
+ - docs/cli/reference.md#huitzo-build
7
+ """
8
+
9
+ import subprocess
10
+ from pathlib import Path
11
+ from typing import Optional
12
+
13
+ import typer
14
+ from rich.console import Console
15
+
16
+ from huitzo_cli.validator import PackValidator
17
+
18
+ console = Console()
19
+ build_app = typer.Typer(help="Build pack")
20
+
21
+
22
+ @build_app.command()
23
+ def build(
24
+ output: Optional[Path] = typer.Option(None, "--output", "-o", help="Output directory"),
25
+ format_type: str = typer.Option(
26
+ "wheel", "--format", "-f", help="Build format (wheel or sdist)"
27
+ ),
28
+ sign: bool = typer.Option(False, "--sign", help="Sign the build"),
29
+ ) -> None:
30
+ """Build Intelligence Pack for distribution.
31
+
32
+ Validates pack first, then builds wheel or sdist using hatchling.
33
+
34
+ Options:
35
+ - --output: Output directory (default: dist/)
36
+ - --format: Build format (wheel or sdist)
37
+ - --sign: Sign the build artifacts (requires GPG)
38
+ """
39
+ # Validate pack first
40
+ validator = PackValidator()
41
+ result = validator.validate()
42
+
43
+ if not result.valid:
44
+ console.print("[red]❌ Pack validation failed[/red]")
45
+ for error in result.errors:
46
+ console.print(f" • {error}")
47
+ raise typer.Exit(1)
48
+
49
+ # Set output directory
50
+ output_dir = output or Path("dist")
51
+ output_dir.mkdir(parents=True, exist_ok=True)
52
+
53
+ console.print("[bold]Building pack...[/bold]")
54
+
55
+ cmd = ["uv", "build"]
56
+
57
+ if format_type == "wheel":
58
+ cmd.append("--wheel")
59
+ elif format_type == "sdist":
60
+ cmd.append("--sdist")
61
+ else:
62
+ console.print(f"[red]❌ Unknown format: {format_type}[/red]")
63
+ raise typer.Exit(1)
64
+
65
+ if output:
66
+ cmd.extend(["-o", str(output_dir)])
67
+
68
+ try:
69
+ build_result = subprocess.run(cmd, check=False)
70
+ if build_result.returncode != 0:
71
+ raise typer.Exit(build_result.returncode)
72
+
73
+ console.print("[green]✅ Build successful[/green]")
74
+ console.print(f"Output: {output_dir}")
75
+
76
+ if sign:
77
+ console.print("[yellow]⚠️ Code signing not yet implemented[/yellow]")
78
+
79
+ except FileNotFoundError:
80
+ console.print("[red]❌ uv not found. Install from: https://docs.astral.sh/uv/[/red]")
81
+ raise typer.Exit(1)
@@ -0,0 +1,98 @@
1
+ """
2
+ Module: config_cmd
3
+ Description: Configuration management commands
4
+
5
+ Implements:
6
+ - docs/cli/reference.md#huitzo-config
7
+ """
8
+
9
+ from typing import Any
10
+
11
+ import typer
12
+ from rich.console import Console
13
+ from rich.table import Table
14
+
15
+ from huitzo_cli.config import ConfigManager
16
+
17
+ console = Console()
18
+ config_app = typer.Typer(help="Manage configuration")
19
+
20
+
21
+ @config_app.command()
22
+ def get(key: str) -> None:
23
+ """Get configuration value.
24
+
25
+ Args:
26
+ key: Configuration key (e.g., 'api_url' or 'auth.token')
27
+ """
28
+ config = ConfigManager()
29
+ value = config.get(key)
30
+
31
+ if value is None:
32
+ console.print(f"[yellow]⚠️ {key} not set[/yellow]")
33
+ raise typer.Exit(1)
34
+
35
+ console.print(f"{key} = {value}")
36
+
37
+
38
+ @config_app.command()
39
+ def set(
40
+ key: str,
41
+ value: str,
42
+ scope: str = typer.Option("user", "--scope", help="Config scope (user or project)"),
43
+ ) -> None:
44
+ """Set configuration value.
45
+
46
+ Args:
47
+ key: Configuration key (e.g., 'auth.token')
48
+ value: Value to set
49
+ """
50
+ config = ConfigManager()
51
+
52
+ try:
53
+ config.set(key, value, scope=scope)
54
+ console.print(f"[green]✅ {key} set to {value}[/green]")
55
+ except Exception as e:
56
+ console.print(f"[red]❌ Failed to set {key}: {e}[/red]")
57
+ raise typer.Exit(1)
58
+
59
+
60
+ @config_app.command()
61
+ def list_config() -> None:
62
+ """List all configuration values."""
63
+ config = ConfigManager()
64
+ config_dict = config.to_dict()
65
+
66
+ table = Table(title="Configuration")
67
+ table.add_column("Key", style="cyan")
68
+ table.add_column("Value", style="green")
69
+
70
+ def flatten_dict(d: dict[str, Any], parent_key: str = "") -> list[tuple[str, str]]:
71
+ items = []
72
+ for k, v in d.items():
73
+ new_key = f"{parent_key}.{k}" if parent_key else k
74
+ if isinstance(v, dict):
75
+ items.extend(flatten_dict(v, new_key))
76
+ else:
77
+ items.append((new_key, str(v)))
78
+ return items
79
+
80
+ for key, value in flatten_dict(config_dict):
81
+ table.add_row(key, value)
82
+
83
+ console.print(table)
84
+
85
+
86
+ @config_app.command()
87
+ def path(
88
+ scope: str = typer.Option("user", "--scope", help="Config scope (user or project)"),
89
+ ) -> None:
90
+ """Show configuration file path.
91
+
92
+ Args:
93
+ scope: Config scope (user or project)
94
+ """
95
+ config = ConfigManager()
96
+ config_path = config.get_config_path(scope=scope)
97
+ console.print(f"[cyan]{scope.capitalize()} config:[/cyan] {config_path}")
98
+ console.print(f"[yellow]Exists:[/yellow] {config_path.exists()}")
@@ -0,0 +1,145 @@
1
+ """
2
+ Module: dashboard_command
3
+ Description: Dashboard management commands
4
+
5
+ Implements:
6
+ - docs/cli/reference.md#dashboard-commands
7
+ """
8
+
9
+ import subprocess
10
+ from pathlib import Path
11
+ from typing import Optional
12
+
13
+ import typer
14
+ from jinja2 import Environment, PackageLoader
15
+ from rich.console import Console
16
+
17
+ console = Console()
18
+ dashboard_app = typer.Typer(help="Dashboard operations")
19
+
20
+
21
+ @dashboard_app.command()
22
+ def new(
23
+ name: Optional[str] = typer.Argument(None, help="Dashboard name"),
24
+ author: Optional[str] = typer.Option(None, "--author", help="Author name"),
25
+ ) -> None:
26
+ """Create a new dashboard project.
27
+
28
+ Scaffolds a React + TypeScript dashboard using Vite.
29
+ """
30
+ if not name:
31
+ name = typer.prompt("Dashboard name")
32
+
33
+ project_root = Path.cwd() / name
34
+
35
+ if project_root.exists():
36
+ console.print(f"[red]❌ Directory '{name}' already exists[/red]")
37
+ raise typer.Exit(1)
38
+
39
+ project_root.mkdir(parents=True, exist_ok=True)
40
+ src_dir = project_root / "src"
41
+ src_dir.mkdir(parents=True, exist_ok=True)
42
+
43
+ # Set up Jinja2
44
+ env = Environment(loader=PackageLoader("huitzo_cli", "templates/dashboard"))
45
+
46
+ context = {"dashboard_name": name}
47
+
48
+ # Files to create
49
+ files = {
50
+ "package.json.j2": project_root / "package.json",
51
+ "vite.config.ts.j2": project_root / "vite.config.ts",
52
+ "index.html.j2": project_root / "index.html",
53
+ "tsconfig.json.j2": project_root / "tsconfig.json",
54
+ "tsconfig.node.json.j2": project_root / "tsconfig.node.json",
55
+ "main.tsx.j2": src_dir / "main.tsx",
56
+ "App.tsx.j2": src_dir / "App.tsx",
57
+ "index.css.j2": src_dir / "index.css",
58
+ "App.css.j2": src_dir / "App.css",
59
+ }
60
+
61
+ for template_name, target_path in files.items():
62
+ template = env.get_template(template_name)
63
+ content = template.render(**context)
64
+ target_path.write_text(content)
65
+
66
+ # Create .gitignore
67
+ (project_root / ".gitignore").write_text("node_modules/\n.env.local\ndist/\n.DS_Store\n")
68
+
69
+ console.print(f"\n[green]✅ Dashboard '{name}' created![/green]")
70
+ console.print("\n[bold]Next steps:[/bold]")
71
+ console.print(f" cd {name}")
72
+ console.print(" npm install")
73
+ console.print(" npm run dev")
74
+
75
+
76
+ @dashboard_app.command()
77
+ def dev() -> None:
78
+ """Start dashboard development server.
79
+
80
+ Starts Vite dev server with hot module replacement.
81
+ """
82
+ console.print("[bold]Starting dashboard dev server...[/bold]")
83
+
84
+ try:
85
+ subprocess.run(["npm", "run", "dev"], check=False)
86
+ except FileNotFoundError:
87
+ console.print("[red]❌ npm not found. Install Node.js from nodejs.org[/red]")
88
+ raise typer.Exit(1)
89
+
90
+
91
+ @dashboard_app.command()
92
+ def build() -> None:
93
+ """Build dashboard for production.
94
+
95
+ Creates optimized production build in dist/ directory.
96
+ """
97
+ console.print("[bold]Building dashboard for production...[/bold]")
98
+
99
+ try:
100
+ result = subprocess.run(["npm", "run", "build"], check=False)
101
+ if result.returncode == 0:
102
+ console.print("[green]✅ Build successful. Output in dist/[/green]")
103
+ else:
104
+ raise typer.Exit(result.returncode)
105
+ except FileNotFoundError:
106
+ console.print("[red]❌ npm not found[/red]")
107
+ raise typer.Exit(1)
108
+
109
+
110
+ @dashboard_app.command()
111
+ def publish() -> None:
112
+ """Publish dashboard to Huitzo Hub.
113
+
114
+ Note: Requires Huitzo Cloud (not yet available).
115
+ """
116
+ console.print("[yellow]ℹ️ Huitzo Hub is not yet available[/yellow]")
117
+ console.print("This feature is coming soon!")
118
+ raise typer.Exit(1)
119
+
120
+
121
+ @dashboard_app.command()
122
+ def grant(
123
+ user: str = typer.Option(..., "--user", help="User email to grant access"),
124
+ ) -> None:
125
+ """Grant dashboard access to another user.
126
+
127
+ Note: Requires Huitzo Cloud (not yet available).
128
+
129
+ Args:
130
+ user: Email of user to grant access
131
+ """
132
+ console.print("[yellow]ℹ️ Huitzo Hub is not yet available[/yellow]")
133
+ console.print("This feature is coming soon!")
134
+ raise typer.Exit(1)
135
+
136
+
137
+ @dashboard_app.command()
138
+ def share() -> None:
139
+ """Share dashboard.
140
+
141
+ Note: Requires Huitzo Cloud (not yet available).
142
+ """
143
+ console.print("[yellow]ℹ️ Huitzo Hub is not yet available[/yellow]")
144
+ console.print("This feature is coming soon!")
145
+ raise typer.Exit(1)
@@ -0,0 +1,113 @@
1
+ """
2
+ Module: dev_command
3
+ Description: Development session command (STUBBED - cloud not yet available)
4
+
5
+ Implements:
6
+ - docs/cli/reference.md#huitzo-dev
7
+ - docs/guides/developer-environment.md#development-session-flow
8
+ """
9
+
10
+ import typer
11
+ from rich.console import Console
12
+
13
+ from huitzo_cli.validator import PackValidator
14
+
15
+ console = Console()
16
+ dev_app = typer.Typer(help="Start development session")
17
+
18
+
19
+ @dev_app.command()
20
+ def dev(
21
+ docs: bool = typer.Option(True, "--docs", help="Start docs server"),
22
+ port: int = typer.Option(8080, "--port", help="Proxy port"),
23
+ persist: bool = typer.Option(False, "--persist", help="Keep sandbox after exit"),
24
+ ) -> None:
25
+ """Start pack development session.
26
+
27
+ Note: Huitzo Cloud is not yet available. This command shows what will happen
28
+ when the cloud platform is ready.
29
+
30
+ When cloud becomes available, this will:
31
+ 1. Validate your pack
32
+ 2. Upload to cloud sandbox
33
+ 3. Start local proxy at localhost:8080
34
+ 4. Watch files for changes and auto-reload
35
+ 5. Serve docs at localhost:8124
36
+
37
+ Args:
38
+ docs: Start local docs server (always available)
39
+ port: Local proxy port
40
+ persist: Keep sandbox after exiting (cloud only)
41
+ """
42
+ # Validate pack
43
+ console.print("[bold]Validating pack...[/bold]")
44
+ validator = PackValidator()
45
+ result = validator.validate()
46
+
47
+ if not result.valid:
48
+ console.print("[red]❌ Pack validation failed[/red]")
49
+ for error in result.errors:
50
+ console.print(f" • {error}")
51
+ raise typer.Exit(1)
52
+
53
+ console.print("[green]✅ Pack validation passed[/green]")
54
+
55
+ # Show cloud status
56
+ console.print()
57
+ console.print("[yellow]ℹ️ Huitzo Cloud is not yet available[/yellow]")
58
+ console.print()
59
+ console.print("[bold]What will happen when cloud is ready:[/bold]")
60
+ console.print(" 1. Upload pack to cloud sandbox")
61
+ console.print(f" 2. Start local proxy at http://localhost:{port}")
62
+ console.print(" 3. Watch files for changes and auto-reload")
63
+ if docs:
64
+ console.print(" 4. Serve docs at http://localhost:8124")
65
+ console.print()
66
+
67
+ # Show local alternatives
68
+ console.print("[bold]For local development now:[/bold]")
69
+ console.print(" • huitzo validate - Validate pack structure")
70
+ console.print(" • huitzo test - Run tests")
71
+ console.print(" • huitzo build - Build wheel")
72
+ console.print()
73
+ console.print("To test commands locally, see SDK documentation:")
74
+ console.print(" • docs/sdk/commands.md")
75
+ console.print(" • docs/sdk/context.md")
76
+
77
+ if docs:
78
+ console.print()
79
+ console.print("[bold]Starting documentation server...[/bold]")
80
+ try:
81
+ from huitzo_cli.docs_server.server import DocsServer
82
+
83
+ docs_server = DocsServer(port=8124)
84
+ docs_server.start()
85
+ console.print("[green]✅ Docs server running at http://localhost:8124[/green]")
86
+ console.print()
87
+ console.print("Press Ctrl+C to exit...")
88
+
89
+ try:
90
+ import signal
91
+
92
+ def signal_handler(sig, frame): # type: ignore
93
+ console.print("\n[yellow]Shutting down...[/yellow]")
94
+ docs_server.stop()
95
+ raise typer.Exit(0)
96
+
97
+ signal.signal(signal.SIGINT, signal_handler)
98
+
99
+ # Keep server running
100
+ while docs_server.is_running():
101
+ import time
102
+
103
+ time.sleep(1)
104
+
105
+ except KeyboardInterrupt:
106
+ docs_server.stop()
107
+
108
+ except Exception as e:
109
+ console.print(f"[yellow]⚠️ Could not start docs server: {e}[/yellow]")
110
+ console.print("Continuing without docs server...")
111
+
112
+ console.print()
113
+ console.print("[green]Development session ready[/green]")