diff --git a/src/cmdforge/cli/__init__.py b/src/cmdforge/cli/__init__.py index 9779f00..3ebb9fe 100644 --- a/src/cmdforge/cli/__init__.py +++ b/src/cmdforge/cli/__init__.py @@ -12,7 +12,7 @@ from .tool_commands import ( from .provider_commands import cmd_providers from .registry_commands import cmd_registry from .collections_commands import cmd_collections -from .project_commands import cmd_deps, cmd_deps_tree, cmd_install_deps, cmd_add, cmd_init, cmd_lock, cmd_verify +from .project_commands import cmd_deps, cmd_deps_tree, cmd_install_deps, cmd_add, cmd_remove, cmd_init, cmd_lock, cmd_verify from .config_commands import cmd_config @@ -323,6 +323,11 @@ def main(): p_add.add_argument("--no-install", action="store_true", help="Don't install after adding") p_add.set_defaults(func=cmd_add) + # 'remove' command + p_remove = subparsers.add_parser("remove", help="Remove a tool from project dependencies") + p_remove.add_argument("tool", help="Tool to remove (owner/name or just name)") + p_remove.set_defaults(func=cmd_remove) + # 'init' command p_init = subparsers.add_parser("init", help="Initialize cmdforge.yaml") p_init.add_argument("-n", "--name", help="Project name") diff --git a/src/cmdforge/cli/project_commands.py b/src/cmdforge/cli/project_commands.py index e197dcf..eb876ee 100644 --- a/src/cmdforge/cli/project_commands.py +++ b/src/cmdforge/cli/project_commands.py @@ -416,6 +416,39 @@ def cmd_add(args): return 0 +def cmd_remove(args): + """Remove a tool from project dependencies.""" + tool_spec = args.tool + + # Find manifest + manifest_path = find_manifest() + if not manifest_path: + print("No cmdforge.yaml found in current project.") + print("Nothing to remove.") + return 1 + + manifest = load_manifest(manifest_path) + + # Parse tool spec to get the name + parsed = ToolSpec.parse(tool_spec) + tool_name = parsed.full_name + + # Remove from manifest + if manifest.remove_dependency(tool_name): + save_manifest(manifest, manifest_path) + print(f"Removed {tool_name} from {manifest_path.name}") + else: + # Try without owner prefix + if manifest.remove_dependency(parsed.name): + save_manifest(manifest, manifest_path) + print(f"Removed {parsed.name} from {manifest_path.name}") + else: + print(f"Dependency '{tool_name}' not found in {manifest_path.name}") + return 1 + + return 0 + + def cmd_init(args): """Initialize a new cmdforge.yaml.""" manifest_path = Path.cwd() / MANIFEST_FILENAME diff --git a/src/cmdforge/manifest.py b/src/cmdforge/manifest.py index 3447189..133e80e 100644 --- a/src/cmdforge/manifest.py +++ b/src/cmdforge/manifest.py @@ -141,6 +141,31 @@ class Manifest: self.dependencies.append(Dependency(name=name, version=version)) + def remove_dependency(self, name: str) -> bool: + """Remove a dependency by name. + + Args: + name: Tool name (can be qualified like 'official/tool' or just 'tool') + + Returns: + True if dependency was removed, False if not found + """ + # Try exact match first + for i, dep in enumerate(self.dependencies): + if dep.name == name: + self.dependencies.pop(i) + return True + + # Try matching just the tool name part (without owner) + short_name = name.split("/")[-1] if "/" in name else name + for i, dep in enumerate(self.dependencies): + dep_short = dep.name.split("/")[-1] if "/" in dep.name else dep.name + if dep_short == short_name: + self.dependencies.pop(i) + return True + + return False + def find_manifest(start_dir: Optional[Path] = None) -> Optional[Path]: """