fips-agents-cli 0.1.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.
@@ -0,0 +1,5 @@
1
+ """FIPS Agents CLI - A tool for creating and managing FIPS-compliant AI agent projects."""
2
+
3
+ from fips_agents_cli.version import __version__
4
+
5
+ __all__ = ["__version__"]
@@ -0,0 +1,6 @@
1
+ """Allow execution via python -m fips_agents_cli."""
2
+
3
+ from fips_agents_cli.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
fips_agents_cli/cli.py ADDED
@@ -0,0 +1,34 @@
1
+ """Main CLI entry point for fips-agents-cli."""
2
+
3
+ import click
4
+ from rich.console import Console
5
+
6
+ from fips_agents_cli.commands.create import create
7
+ from fips_agents_cli.version import __version__
8
+
9
+ console = Console()
10
+
11
+
12
+ @click.group()
13
+ @click.version_option(version=__version__, prog_name="fips-agents")
14
+ @click.pass_context
15
+ def cli(ctx):
16
+ """
17
+ FIPS Agents CLI - A tool for creating and managing FIPS-compliant AI agent projects.
18
+
19
+ Use 'fips-agents COMMAND --help' for more information on a specific command.
20
+ """
21
+ ctx.ensure_object(dict)
22
+
23
+
24
+ # Register commands
25
+ cli.add_command(create)
26
+
27
+
28
+ def main():
29
+ """Main entry point for the CLI application."""
30
+ cli(obj={})
31
+
32
+
33
+ if __name__ == "__main__":
34
+ main()
@@ -0,0 +1 @@
1
+ """CLI commands for fips-agents-cli."""
@@ -0,0 +1,182 @@
1
+ """Create command for generating new projects from templates."""
2
+
3
+ import sys
4
+ from typing import Optional
5
+
6
+ import click
7
+ from rich.console import Console
8
+ from rich.panel import Panel
9
+ from rich.progress import Progress, SpinnerColumn, TextColumn
10
+
11
+ from fips_agents_cli.tools.filesystem import resolve_target_path, validate_target_directory
12
+ from fips_agents_cli.tools.git import clone_template, init_repository, is_git_installed
13
+ from fips_agents_cli.tools.project import (
14
+ cleanup_template_files,
15
+ to_module_name,
16
+ update_project_name,
17
+ validate_project_name,
18
+ )
19
+
20
+ console = Console()
21
+
22
+ # Template URL for MCP server projects
23
+ MCP_SERVER_TEMPLATE_URL = "https://github.com/rdwj/mcp-server-template"
24
+
25
+
26
+ @click.group()
27
+ def create():
28
+ """Create new projects from templates."""
29
+ pass
30
+
31
+
32
+ @create.command("mcp-server")
33
+ @click.argument("project_name")
34
+ @click.option(
35
+ "--target-dir",
36
+ "-t",
37
+ default=None,
38
+ help="Target directory for the project (default: current directory)",
39
+ )
40
+ @click.option(
41
+ "--no-git",
42
+ is_flag=True,
43
+ default=False,
44
+ help="Skip git repository initialization",
45
+ )
46
+ def mcp_server(project_name: str, target_dir: Optional[str], no_git: bool):
47
+ """
48
+ Create a new MCP server project from template.
49
+
50
+ PROJECT_NAME must start with a lowercase letter and contain only
51
+ lowercase letters, numbers, hyphens, and underscores.
52
+
53
+ Example:
54
+ fips-agents create mcp-server my-mcp-server
55
+ fips-agents create mcp-server my-mcp-server --target-dir ~/projects
56
+ """
57
+ try:
58
+ # Step 1: Validate project name
59
+ console.print("\n[bold cyan]Creating MCP Server Project[/bold cyan]\n")
60
+
61
+ is_valid, error_msg = validate_project_name(project_name)
62
+ if not is_valid:
63
+ console.print(f"[red]✗[/red] Invalid project name: {error_msg}")
64
+ sys.exit(1)
65
+
66
+ console.print(f"[green]✓[/green] Project name '{project_name}' is valid")
67
+
68
+ # Step 2: Resolve and validate target directory
69
+ target_path = resolve_target_path(project_name, target_dir)
70
+
71
+ is_valid, error_msg = validate_target_directory(target_path, allow_existing=False)
72
+ if not is_valid:
73
+ console.print(f"[red]✗[/red] {error_msg}")
74
+ console.print(
75
+ "\n[yellow]Hint:[/yellow] Choose a different name or remove the existing directory"
76
+ )
77
+ sys.exit(1)
78
+
79
+ console.print(f"[green]✓[/green] Target directory: {target_path}")
80
+
81
+ # Step 3: Check if git is installed
82
+ if not is_git_installed():
83
+ console.print(
84
+ "[yellow]⚠[/yellow] Git is not installed. This is required for cloning templates."
85
+ )
86
+ console.print("[yellow]Hint:[/yellow] Install git from https://git-scm.com/downloads")
87
+ sys.exit(1)
88
+
89
+ # Step 4: Clone template repository
90
+ with Progress(
91
+ SpinnerColumn(),
92
+ TextColumn("[progress.description]{task.description}"),
93
+ console=console,
94
+ ) as progress:
95
+ progress.add_task(description="Cloning template repository...", total=None)
96
+ try:
97
+ clone_template(MCP_SERVER_TEMPLATE_URL, target_path)
98
+ except Exception as e:
99
+ console.print("\n[red]✗[/red] Failed to clone template repository")
100
+ console.print(f"[red]Error:[/red] {e}")
101
+ console.print(
102
+ f"\n[yellow]Hint:[/yellow] Check your internet connection and verify "
103
+ f"the template URL is accessible:\n{MCP_SERVER_TEMPLATE_URL}"
104
+ )
105
+ sys.exit(1)
106
+
107
+ # Step 5: Customize project
108
+ with Progress(
109
+ SpinnerColumn(),
110
+ TextColumn("[progress.description]{task.description}"),
111
+ console=console,
112
+ ) as progress:
113
+ progress.add_task(description="Customizing project...", total=None)
114
+ try:
115
+ update_project_name(target_path, project_name)
116
+ cleanup_template_files(target_path)
117
+ except Exception as e:
118
+ console.print("\n[red]✗[/red] Failed to customize project")
119
+ console.print(f"[red]Error:[/red] {e}")
120
+ sys.exit(1)
121
+
122
+ # Step 6: Initialize git repository
123
+ if not no_git:
124
+ with Progress(
125
+ SpinnerColumn(),
126
+ TextColumn("[progress.description]{task.description}"),
127
+ console=console,
128
+ ) as progress:
129
+ progress.add_task(description="Initializing git repository...", total=None)
130
+ try:
131
+ init_repository(target_path, initial_commit=True)
132
+ except Exception as e:
133
+ console.print("\n[yellow]⚠[/yellow] Failed to initialize git repository")
134
+ console.print(f"[yellow]Warning:[/yellow] {e}")
135
+ console.print(
136
+ "[yellow]You can initialize git manually later with:[/yellow] git init"
137
+ )
138
+
139
+ # Step 7: Success message with next steps
140
+ module_name = to_module_name(project_name)
141
+
142
+ success_message = f"""
143
+ [bold green]✓ Successfully created MCP server project![/bold green]
144
+
145
+ [bold cyan]Project Details:[/bold cyan]
146
+ • Name: {project_name}
147
+ • Module: {module_name}
148
+ • Location: {target_path}
149
+
150
+ [bold cyan]Next Steps:[/bold cyan]
151
+
152
+ 1. Navigate to your project:
153
+ [dim]cd {target_path.name}[/dim]
154
+
155
+ 2. Create and activate a virtual environment:
156
+ [dim]python -m venv venv[/dim]
157
+ [dim]source venv/bin/activate[/dim] # On Windows: venv\\Scripts\\activate
158
+
159
+ 3. Install the project in editable mode:
160
+ [dim]pip install -e .[dev][/dim]
161
+
162
+ 4. Start developing your MCP server!
163
+ [dim]Edit src/{module_name}/ files to add your functionality[/dim]
164
+
165
+ 5. Run tests:
166
+ [dim]pytest[/dim]
167
+
168
+ [bold cyan]Documentation:[/bold cyan]
169
+ • Check the README.md in your project for detailed instructions
170
+ • MCP Protocol docs: https://modelcontextprotocol.io/
171
+
172
+ Happy coding! 🚀
173
+ """
174
+
175
+ console.print(Panel(success_message, border_style="green", padding=(1, 2)))
176
+
177
+ except KeyboardInterrupt:
178
+ console.print("\n[yellow]⚠[/yellow] Operation cancelled by user")
179
+ sys.exit(130)
180
+ except Exception as e:
181
+ console.print(f"\n[red]✗[/red] Unexpected error: {e}")
182
+ sys.exit(1)
@@ -0,0 +1 @@
1
+ """Tools and utilities for fips-agents-cli."""
@@ -0,0 +1,125 @@
1
+ """Filesystem utilities for project operations."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+ from rich.console import Console
7
+
8
+ console = Console()
9
+
10
+
11
+ def ensure_directory_exists(path: Path, create: bool = False) -> bool:
12
+ """
13
+ Check if a directory exists, optionally creating it.
14
+
15
+ Args:
16
+ path: The directory path to check
17
+ create: Whether to create the directory if it doesn't exist
18
+
19
+ Returns:
20
+ bool: True if directory exists (or was created), False otherwise
21
+ """
22
+ if path.exists():
23
+ return path.is_dir()
24
+
25
+ if create:
26
+ try:
27
+ path.mkdir(parents=True, exist_ok=True)
28
+ return True
29
+ except Exception as e:
30
+ console.print(f"[red]✗[/red] Failed to create directory {path}: {e}")
31
+ return False
32
+
33
+ return False
34
+
35
+
36
+ def check_directory_empty(path: Path) -> bool:
37
+ """
38
+ Check if a directory is empty.
39
+
40
+ Args:
41
+ path: The directory path to check
42
+
43
+ Returns:
44
+ bool: True if directory is empty, False otherwise
45
+ """
46
+ if not path.exists():
47
+ return True
48
+
49
+ if not path.is_dir():
50
+ return False
51
+
52
+ return not any(path.iterdir())
53
+
54
+
55
+ def validate_target_directory(
56
+ target_path: Path, allow_existing: bool = False
57
+ ) -> tuple[bool, Optional[str]]:
58
+ """
59
+ Validate that a target directory is suitable for project creation.
60
+
61
+ Args:
62
+ target_path: The target directory path
63
+ allow_existing: Whether to allow existing directories (must be empty)
64
+
65
+ Returns:
66
+ tuple: (is_valid, error_message)
67
+ is_valid is True if valid, False otherwise
68
+ error_message is None if valid, otherwise contains the error description
69
+ """
70
+ # Check if parent directory exists
71
+ parent = target_path.parent
72
+ if not parent.exists():
73
+ return False, f"Parent directory does not exist: {parent}"
74
+
75
+ # Check if target already exists
76
+ if target_path.exists():
77
+ if not allow_existing:
78
+ return False, f"Directory already exists: {target_path}"
79
+
80
+ if not target_path.is_dir():
81
+ return False, f"Path exists but is not a directory: {target_path}"
82
+
83
+ if not check_directory_empty(target_path):
84
+ return False, f"Directory is not empty: {target_path}"
85
+
86
+ return True, None
87
+
88
+
89
+ def resolve_target_path(project_name: str, target_dir: Optional[str] = None) -> Path:
90
+ """
91
+ Resolve the target path for project creation.
92
+
93
+ Args:
94
+ project_name: The name of the project
95
+ target_dir: Optional target directory (defaults to current directory)
96
+
97
+ Returns:
98
+ Path: The resolved absolute path for the project
99
+ """
100
+ if target_dir:
101
+ base = Path(target_dir).resolve()
102
+ else:
103
+ base = Path.cwd()
104
+
105
+ return base / project_name
106
+
107
+
108
+ def get_relative_path(path: Path, base: Optional[Path] = None) -> str:
109
+ """
110
+ Get a relative path string for display purposes.
111
+
112
+ Args:
113
+ path: The path to convert
114
+ base: Base path for relative calculation (defaults to current directory)
115
+
116
+ Returns:
117
+ str: Relative path string, or absolute if relative calculation fails
118
+ """
119
+ if base is None:
120
+ base = Path.cwd()
121
+
122
+ try:
123
+ return str(path.relative_to(base))
124
+ except ValueError:
125
+ return str(path)
@@ -0,0 +1,100 @@
1
+ """Git operations for cloning and initializing repositories."""
2
+
3
+ import shutil
4
+ from pathlib import Path
5
+
6
+ import git
7
+ from rich.console import Console
8
+
9
+ console = Console()
10
+
11
+
12
+ def clone_template(repo_url: str, target_path: Path, branch: str = "main") -> None:
13
+ """
14
+ Clone a git repository template to a target path.
15
+
16
+ Performs a shallow clone (depth=1) for faster cloning and removes the .git
17
+ directory to allow fresh initialization.
18
+
19
+ Args:
20
+ repo_url: The URL of the git repository to clone
21
+ target_path: The local path where the repository should be cloned
22
+ branch: The branch to clone (default: "main")
23
+
24
+ Raises:
25
+ git.GitCommandError: If the clone operation fails
26
+ OSError: If there are filesystem permission issues
27
+ """
28
+ try:
29
+ # Perform shallow clone for faster operation
30
+ console.print(f"[cyan]Cloning template from {repo_url}...[/cyan]")
31
+ git.Repo.clone_from(
32
+ repo_url,
33
+ str(target_path),
34
+ branch=branch,
35
+ depth=1,
36
+ single_branch=True,
37
+ )
38
+
39
+ # Remove .git directory to allow fresh initialization
40
+ git_dir = target_path / ".git"
41
+ if git_dir.exists():
42
+ shutil.rmtree(git_dir)
43
+ console.print("[green]✓[/green] Template cloned successfully")
44
+
45
+ except git.GitCommandError as e:
46
+ console.print(f"[red]✗[/red] Failed to clone template: {e}")
47
+ raise
48
+ except Exception as e:
49
+ console.print(f"[red]✗[/red] Unexpected error during clone: {e}")
50
+ raise
51
+
52
+
53
+ def init_repository(project_path: Path, initial_commit: bool = True) -> None:
54
+ """
55
+ Initialize a new git repository in the project directory.
56
+
57
+ Args:
58
+ project_path: Path to the project directory
59
+ initial_commit: Whether to create an initial commit (default: True)
60
+
61
+ Raises:
62
+ git.GitCommandError: If git operations fail
63
+ """
64
+ try:
65
+ console.print("[cyan]Initializing git repository...[/cyan]")
66
+
67
+ # Initialize repository
68
+ repo = git.Repo.init(str(project_path))
69
+
70
+ if initial_commit:
71
+ # Add all files
72
+ repo.index.add("*")
73
+
74
+ # Create initial commit
75
+ repo.index.commit("Initial commit from fips-agents-cli")
76
+
77
+ console.print("[green]✓[/green] Git repository initialized with initial commit")
78
+ else:
79
+ console.print("[green]✓[/green] Git repository initialized")
80
+
81
+ except git.GitCommandError as e:
82
+ console.print(f"[red]✗[/red] Failed to initialize git repository: {e}")
83
+ raise
84
+ except Exception as e:
85
+ console.print(f"[red]✗[/red] Unexpected error during git init: {e}")
86
+ raise
87
+
88
+
89
+ def is_git_installed() -> bool:
90
+ """
91
+ Check if git is installed and available in the system PATH.
92
+
93
+ Returns:
94
+ bool: True if git is installed, False otherwise
95
+ """
96
+ try:
97
+ git.Git().version()
98
+ return True
99
+ except (git.GitCommandNotFound, git.GitCommandError):
100
+ return False
@@ -0,0 +1,163 @@
1
+ """Project customization and configuration utilities."""
2
+
3
+ import re
4
+ import shutil
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ import tomlkit
9
+ from rich.console import Console
10
+
11
+ console = Console()
12
+
13
+
14
+ def validate_project_name(name: str) -> tuple[bool, Optional[str]]:
15
+ """
16
+ Validate project name according to Python package naming conventions.
17
+
18
+ Project names must:
19
+ - Start with a lowercase letter
20
+ - Contain only lowercase letters, numbers, hyphens, and underscores
21
+ - Not be empty
22
+
23
+ Args:
24
+ name: The project name to validate
25
+
26
+ Returns:
27
+ tuple: (is_valid, error_message)
28
+ is_valid is True if valid, False otherwise
29
+ error_message is None if valid, otherwise contains the error description
30
+ """
31
+ if not name:
32
+ return False, "Project name cannot be empty"
33
+
34
+ if not re.match(r"^[a-z][a-z0-9\-_]*$", name):
35
+ return False, (
36
+ "Project name must start with a lowercase letter and contain only "
37
+ "lowercase letters, numbers, hyphens, and underscores"
38
+ )
39
+
40
+ return True, None
41
+
42
+
43
+ def to_module_name(project_name: str) -> str:
44
+ """
45
+ Convert a project name to a valid Python module name.
46
+
47
+ Replaces hyphens with underscores to ensure compatibility with Python imports.
48
+
49
+ Args:
50
+ project_name: The project name (may contain hyphens)
51
+
52
+ Returns:
53
+ str: A valid Python module name (hyphens replaced with underscores)
54
+ """
55
+ return project_name.replace("-", "_")
56
+
57
+
58
+ def update_project_name(project_path: Path, new_name: str) -> None:
59
+ """
60
+ Update the project name in pyproject.toml and rename the source directory.
61
+
62
+ This function:
63
+ 1. Updates the 'name' field in pyproject.toml
64
+ 2. Updates the entry point script name if present
65
+ 3. Renames the source directory from the template name to the new module name
66
+
67
+ Args:
68
+ project_path: Path to the project root directory
69
+ new_name: The new project name (with hyphens allowed)
70
+
71
+ Raises:
72
+ FileNotFoundError: If pyproject.toml or source directory doesn't exist
73
+ ValueError: If pyproject.toml is malformed
74
+ """
75
+ try:
76
+ console.print(f"[cyan]Customizing project for '{new_name}'...[/cyan]")
77
+
78
+ pyproject_path = project_path / "pyproject.toml"
79
+
80
+ if not pyproject_path.exists():
81
+ raise FileNotFoundError(f"pyproject.toml not found at {pyproject_path}")
82
+
83
+ # Read and parse pyproject.toml
84
+ with open(pyproject_path) as f:
85
+ pyproject = tomlkit.parse(f.read())
86
+
87
+ # Get the old project name from pyproject.toml
88
+ old_name = pyproject.get("project", {}).get("name", "mcp-server-template")
89
+ old_module_name = to_module_name(old_name)
90
+ new_module_name = to_module_name(new_name)
91
+
92
+ # Update project name
93
+ if "project" in pyproject:
94
+ pyproject["project"]["name"] = new_name
95
+
96
+ # Update entry point script name if it exists
97
+ if "scripts" in pyproject["project"]:
98
+ # Find and update any entry points that reference the old name
99
+ scripts = pyproject["project"]["scripts"]
100
+ old_scripts = dict(scripts)
101
+
102
+ for script_name, script_path in old_scripts.items():
103
+ # Replace old module name with new module name in the path
104
+ new_script_path = script_path.replace(old_module_name, new_module_name)
105
+
106
+ # If the script name was based on the old project name, update it
107
+ if script_name == old_name or script_name == old_module_name:
108
+ del scripts[script_name]
109
+ scripts[new_name] = new_script_path
110
+ else:
111
+ scripts[script_name] = new_script_path
112
+
113
+ # Write updated pyproject.toml
114
+ with open(pyproject_path, "w") as f:
115
+ f.write(tomlkit.dumps(pyproject))
116
+
117
+ console.print("[green]✓[/green] Updated pyproject.toml")
118
+
119
+ # Rename source directory
120
+ src_dir = project_path / "src"
121
+ old_src_path = src_dir / old_module_name
122
+ new_src_path = src_dir / new_module_name
123
+
124
+ if old_src_path.exists() and old_src_path != new_src_path:
125
+ shutil.move(str(old_src_path), str(new_src_path))
126
+ console.print(f"[green]✓[/green] Renamed source directory to '{new_module_name}'")
127
+ elif not old_src_path.exists():
128
+ # Template might have a different structure, log a warning
129
+ console.print(
130
+ f"[yellow]⚠[/yellow] Source directory 'src/{old_module_name}' not found, "
131
+ "skipping rename"
132
+ )
133
+
134
+ console.print("[green]✓[/green] Project customization complete")
135
+
136
+ except FileNotFoundError as e:
137
+ console.print(f"[red]✗[/red] {e}")
138
+ raise
139
+ except Exception as e:
140
+ console.print(f"[red]✗[/red] Failed to customize project: {e}")
141
+ raise
142
+
143
+
144
+ def cleanup_template_files(project_path: Path) -> None:
145
+ """
146
+ Remove template-specific files that shouldn't be in the new project.
147
+
148
+ Args:
149
+ project_path: Path to the project root directory
150
+ """
151
+ # Files to remove (commonly found in templates)
152
+ files_to_remove = [
153
+ ".github/workflows/template-cleanup.yml", # Template-specific workflows
154
+ ]
155
+
156
+ for file_path in files_to_remove:
157
+ full_path = project_path / file_path
158
+ if full_path.exists():
159
+ try:
160
+ full_path.unlink()
161
+ console.print(f"[green]✓[/green] Removed template file: {file_path}")
162
+ except Exception as e:
163
+ console.print(f"[yellow]⚠[/yellow] Could not remove {file_path}: {e}")
@@ -0,0 +1,3 @@
1
+ """Version information for fips-agents-cli."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,317 @@
1
+ Metadata-Version: 2.4
2
+ Name: fips-agents-cli
3
+ Version: 0.1.0
4
+ Summary: CLI tool for creating and managing FIPS-compliant AI agent projects
5
+ Project-URL: Homepage, https://github.com/rdwj/fips-agents-cli
6
+ Project-URL: Repository, https://github.com/rdwj/fips-agents-cli
7
+ Project-URL: Issues, https://github.com/rdwj/fips-agents-cli/issues
8
+ Author-email: Wes Jackson <wjackson@redhat.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: agents,ai,cli,code-generator,fips,mcp,scaffolding
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Code Generators
21
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
22
+ Requires-Python: >=3.9
23
+ Requires-Dist: click>=8.1.0
24
+ Requires-Dist: gitpython>=3.1.0
25
+ Requires-Dist: rich>=13.0.0
26
+ Requires-Dist: tomlkit>=0.12.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: black>=23.0.0; extra == 'dev'
29
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
30
+ Requires-Dist: pytest>=7.4.0; extra == 'dev'
31
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # FIPS Agents CLI
35
+
36
+ A command-line tool for creating and managing FIPS-compliant AI agent projects, with a focus on MCP (Model Context Protocol) server development.
37
+
38
+ ## Features
39
+
40
+ - 🚀 Quick project scaffolding from templates
41
+ - 📦 MCP server project generation
42
+ - 🔧 Automatic project customization
43
+ - 🎨 Beautiful CLI output with Rich
44
+ - ✅ Git repository initialization
45
+ - 🧪 Comprehensive test coverage
46
+
47
+ ## Installation
48
+
49
+ ### Using pipx (Recommended)
50
+
51
+ ```bash
52
+ pipx install fips-agents-cli
53
+ ```
54
+
55
+ ### Using pip
56
+
57
+ ```bash
58
+ pip install fips-agents-cli
59
+ ```
60
+
61
+ ### From Source (Development)
62
+
63
+ ```bash
64
+ # Clone the repository
65
+ git clone https://github.com/rdwj/fips-agents-cli.git
66
+ cd fips-agents-cli
67
+
68
+ # Create virtual environment
69
+ python -m venv venv
70
+ source venv/bin/activate # On Windows: venv\Scripts\activate
71
+
72
+ # Install in editable mode with dev dependencies
73
+ pip install -e .[dev]
74
+ ```
75
+
76
+ ## Quick Start
77
+
78
+ ### Create a new MCP server project
79
+
80
+ ```bash
81
+ fips-agents create mcp-server my-awesome-server
82
+ ```
83
+
84
+ This will:
85
+ 1. Clone the MCP server template
86
+ 2. Customize the project with your chosen name
87
+ 3. Initialize a git repository
88
+ 4. Provide next steps for development
89
+
90
+ ### Specify a target directory
91
+
92
+ ```bash
93
+ fips-agents create mcp-server my-server --target-dir ~/projects
94
+ ```
95
+
96
+ ### Skip git initialization
97
+
98
+ ```bash
99
+ fips-agents create mcp-server my-server --no-git
100
+ ```
101
+
102
+ ## Usage
103
+
104
+ ### Basic Commands
105
+
106
+ ```bash
107
+ # Display version
108
+ fips-agents --version
109
+
110
+ # Get help
111
+ fips-agents --help
112
+ fips-agents create --help
113
+ fips-agents create mcp-server --help
114
+ ```
115
+
116
+ ### Create MCP Server
117
+
118
+ ```bash
119
+ fips-agents create mcp-server <project-name> [OPTIONS]
120
+ ```
121
+
122
+ **Arguments:**
123
+ - `project-name`: Name for your MCP server project (must start with lowercase letter, contain only lowercase letters, numbers, hyphens, and underscores)
124
+
125
+ **Options:**
126
+ - `--target-dir, -t PATH`: Target directory for the project (default: current directory)
127
+ - `--no-git`: Skip git repository initialization
128
+ - `--help`: Show help message
129
+
130
+ **Examples:**
131
+
132
+ ```bash
133
+ # Create in current directory
134
+ fips-agents create mcp-server my-mcp-server
135
+
136
+ # Create in specific directory
137
+ fips-agents create mcp-server my-server -t ~/projects
138
+
139
+ # Create without git initialization
140
+ fips-agents create mcp-server my-server --no-git
141
+ ```
142
+
143
+ ## Project Name Requirements
144
+
145
+ Project names must follow these rules:
146
+ - Start with a lowercase letter
147
+ - Contain only lowercase letters, numbers, hyphens (`-`), and underscores (`_`)
148
+ - Not be empty
149
+
150
+ **Valid examples:** `my-server`, `test_mcp`, `server123`, `my-awesome-mcp-server`
151
+
152
+ **Invalid examples:** `MyServer` (uppercase), `123server` (starts with number), `my@server` (special characters)
153
+
154
+ ## After Creating a Project
155
+
156
+ Once your project is created, follow these steps:
157
+
158
+ ```bash
159
+ # 1. Navigate to your project
160
+ cd my-mcp-server
161
+
162
+ # 2. Create and activate virtual environment
163
+ python -m venv venv
164
+ source venv/bin/activate # On Windows: venv\Scripts\activate
165
+
166
+ # 3. Install the project
167
+ pip install -e .[dev]
168
+
169
+ # 4. Run tests
170
+ pytest
171
+
172
+ # 5. Start developing!
173
+ # Edit src/my_mcp_server/ files to add your functionality
174
+ ```
175
+
176
+ ## Development
177
+
178
+ ### Setup Development Environment
179
+
180
+ ```bash
181
+ # Clone the repository
182
+ git clone https://github.com/rdwj/fips-agents-cli.git
183
+ cd fips-agents-cli
184
+
185
+ # Create virtual environment
186
+ python -m venv venv
187
+ source venv/bin/activate
188
+
189
+ # Install with dev dependencies
190
+ pip install -e .[dev]
191
+ ```
192
+
193
+ ### Running Tests
194
+
195
+ ```bash
196
+ # Run all tests
197
+ pytest
198
+
199
+ # Run with coverage
200
+ pytest --cov=fips_agents_cli --cov-report=html
201
+
202
+ # Run specific test file
203
+ pytest tests/test_create.py
204
+
205
+ # Run specific test
206
+ pytest tests/test_create.py::TestCreateMcpServer::test_successful_creation
207
+ ```
208
+
209
+ ### Code Quality
210
+
211
+ ```bash
212
+ # Format code with Black
213
+ black src tests
214
+
215
+ # Lint with Ruff
216
+ ruff check src tests
217
+
218
+ # Type checking (if using mypy)
219
+ mypy src
220
+ ```
221
+
222
+ ### Project Structure
223
+
224
+ ```
225
+ fips-agents-cli/
226
+ ├── pyproject.toml # Project configuration
227
+ ├── README.md # This file
228
+ ├── src/
229
+ │ └── fips_agents_cli/
230
+ │ ├── __init__.py # Package initialization
231
+ │ ├── __main__.py # Entry point for python -m
232
+ │ ├── cli.py # Main CLI application
233
+ │ ├── version.py # Version information
234
+ │ ├── commands/ # CLI command implementations
235
+ │ │ ├── __init__.py
236
+ │ │ └── create.py # Create command
237
+ │ └── tools/ # Utility modules
238
+ │ ├── __init__.py
239
+ │ ├── filesystem.py # Filesystem operations
240
+ │ ├── git.py # Git operations
241
+ │ └── project.py # Project customization
242
+ └── tests/ # Test suite
243
+ ├── __init__.py
244
+ ├── conftest.py # Pytest fixtures
245
+ ├── test_create.py # Create command tests
246
+ ├── test_filesystem.py # Filesystem utilities tests
247
+ └── test_project.py # Project utilities tests
248
+ ```
249
+
250
+ ## Requirements
251
+
252
+ - Python 3.9 or higher
253
+ - Git (for cloning templates and initializing repositories)
254
+
255
+ ## Dependencies
256
+
257
+ - **click** (>=8.1.0): Command-line interface creation
258
+ - **rich** (>=13.0.0): Terminal output formatting
259
+ - **gitpython** (>=3.1.0): Git operations
260
+ - **tomlkit** (>=0.12.0): TOML file manipulation
261
+
262
+ ## Contributing
263
+
264
+ Contributions are welcome! Please follow these steps:
265
+
266
+ 1. Fork the repository
267
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
268
+ 3. Make your changes
269
+ 4. Run tests (`pytest`)
270
+ 5. Format code (`black src tests`)
271
+ 6. Commit your changes (`git commit -m 'Add amazing feature'`)
272
+ 7. Push to the branch (`git push origin feature/amazing-feature`)
273
+ 8. Open a Pull Request
274
+
275
+ ## Troubleshooting
276
+
277
+ ### Git not found
278
+
279
+ If you see "Git is not installed" error:
280
+ - **macOS**: `brew install git`
281
+ - **Linux**: `sudo apt-get install git` or `sudo yum install git`
282
+ - **Windows**: Download from https://git-scm.com/downloads
283
+
284
+ ### Directory already exists
285
+
286
+ If you see "Directory already exists" error:
287
+ - Choose a different project name
288
+ - Remove the existing directory: `rm -rf project-name`
289
+ - Use a different target directory with `--target-dir`
290
+
291
+ ### Template clone fails
292
+
293
+ If template cloning fails:
294
+ - Check your internet connection
295
+ - Verify the template repository is accessible: https://github.com/rdwj/mcp-server-template
296
+ - Try again later if GitHub is experiencing issues
297
+
298
+ ## License
299
+
300
+ MIT License - see LICENSE file for details
301
+
302
+ ## Links
303
+
304
+ - **Repository**: https://github.com/rdwj/fips-agents-cli
305
+ - **Issues**: https://github.com/rdwj/fips-agents-cli/issues
306
+ - **MCP Protocol**: https://modelcontextprotocol.io/
307
+
308
+ ## Changelog
309
+
310
+ ### Version 0.1.0 (MVP)
311
+
312
+ - Initial release
313
+ - `fips-agents create mcp-server` command
314
+ - Template cloning and customization
315
+ - Git repository initialization
316
+ - Comprehensive test suite
317
+ - Beautiful CLI output with Rich
@@ -0,0 +1,15 @@
1
+ fips_agents_cli/__init__.py,sha256=Jfo6y6T4HIQ-BeeF89w7F-F4BED63KyCIc1yoFGn9OM,167
2
+ fips_agents_cli/__main__.py,sha256=rUeQY3jrV6hQVAI2IE0qZCcUnvXDMj5LiCjhlXsc9PQ,130
3
+ fips_agents_cli/cli.py,sha256=e2QGiAH73pHAjpdf7JdovyNZtc2nyb83LaVaLbLqS5o,718
4
+ fips_agents_cli/version.py,sha256=EeROn-Cy9mXSPq-OrZ--hVR0oQvwMNqIyAgy4w-YowU,70
5
+ fips_agents_cli/commands/__init__.py,sha256=AGxi2-Oc-gKE3sR9F39nIxwnzz-4bcfkkJzaP1qhMvU,40
6
+ fips_agents_cli/commands/create.py,sha256=GvGm8VgsIfqZV_yIXXYzeV-nasLIwuD85_l831YjAho,6403
7
+ fips_agents_cli/tools/__init__.py,sha256=ah4OrYFuyQavuhwguFwFS0o-7ZLGIm5ZWWQK5ZTZZSs,47
8
+ fips_agents_cli/tools/filesystem.py,sha256=G9wzHrgQpoMw3z5EslsyEL91VLlUT6Cf5Rt80DtkwOw,3313
9
+ fips_agents_cli/tools/git.py,sha256=-z2EO7vNZhN6Vuzh34ZbqNteZE4vNnLG8oysgzhmypk,3042
10
+ fips_agents_cli/tools/project.py,sha256=d_qIhsEzPOM55ONSLzheTozXuyuiEEf9OG4hVXISAbk,5730
11
+ fips_agents_cli-0.1.0.dist-info/METADATA,sha256=j3-wAEIftpKnjwj4BVKykYPQh4eDXGva_RNIKh55Qiw,8220
12
+ fips_agents_cli-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
+ fips_agents_cli-0.1.0.dist-info/entry_points.txt,sha256=srO4LAGNp6zcB9zuPW1toLGPyLbcsad9YWsfNxgz20s,57
14
+ fips_agents_cli-0.1.0.dist-info/licenses/LICENSE,sha256=dQJIqi2t9SinZu0yALTYJ8juzosu29KPbjU8WhyboRc,1068
15
+ fips_agents_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ fips-agents = fips_agents_cli.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Wes Jackson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.