Skip to content

Commit e5ee1ed

Browse files
rysweetclaude
andcommitted
Revert plugin architecture commits that broke main
This reverts commits: - 725d99f "fix: Actually remove duplicate PluginManager import" - a2f6702 "fix: Remove duplicate PluginManager import (for real)" - 49c4302 "fix: Restore plugin architecture from febfce8" These commits were accidentally pushed directly to main instead of going through a PR on feat/issue-1948-plugin-architecture branch. The plugin architecture work added imports of a non-existent plugin_manager module, causing runtime failures: ``` ModuleNotFoundError: No module named 'amplihack.plugin_manager' ``` This revert restores main to a working state. Plugin architecture work should continue on feat/issue-1948-plugin-architecture with a proper PR review process. Related: #1948 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 7eb635d commit e5ee1ed

File tree

1 file changed

+56
-206
lines changed

1 file changed

+56
-206
lines changed

src/amplihack/cli.py

Lines changed: 56 additions & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from . import copytree_manifest
1010
from .docker import DockerManager
1111
from .launcher import ClaudeLauncher
12-
from .plugin_manager import PluginManager
1312
from .proxy import ProxyConfig, ProxyManager
1413
from .utils import is_uvx_deployment
1514

@@ -75,7 +74,7 @@ def launch_command(args: argparse.Namespace, claude_args: list[str] | None = Non
7574

7675
return docker_manager.run_command(docker_args)
7776

78-
# In UVX mode, Claude uses --add-dir for both project directory and plugin directory
77+
# In UVX mode, Claude now runs from the current directory so no --add-dir needed
7978

8079
proxy_manager = None
8180
system_prompt_path = None
@@ -410,34 +409,6 @@ def create_parser() -> argparse.ArgumentParser:
410409
local_install_parser = subparsers.add_parser("_local_install", help=argparse.SUPPRESS)
411410
local_install_parser.add_argument("repo_root", help="Repository root directory")
412411

413-
# Plugin management commands
414-
plugin_parser = subparsers.add_parser("plugin", help="Plugin management commands")
415-
plugin_subparsers = plugin_parser.add_subparsers(
416-
dest="plugin_command", help="Plugin subcommands"
417-
)
418-
419-
# Link plugin command
420-
link_parser = plugin_subparsers.add_parser(
421-
"link", help="Link installed plugin to Claude Code settings"
422-
)
423-
link_parser.add_argument(
424-
"plugin_name",
425-
nargs="?",
426-
default="amplihack",
427-
help="Plugin name to link (default: amplihack)",
428-
)
429-
430-
# Verify plugin command
431-
verify_parser = plugin_subparsers.add_parser(
432-
"verify", help="Verify plugin installation and discoverability"
433-
)
434-
verify_parser.add_argument(
435-
"plugin_name",
436-
nargs="?",
437-
default="amplihack",
438-
help="Plugin name to verify (default: amplihack)",
439-
)
440-
441412
# Memory tree visualization command
442413
memory_parser = subparsers.add_parser("memory", help="Memory system commands")
443414
memory_subparsers = memory_parser.add_subparsers(
@@ -528,71 +499,66 @@ def main(argv: list[str] | None = None) -> int:
528499
print(" Commit your changes and try again\n")
529500
sys.exit(0)
530501

502+
temp_claude_dir = str(copy_strategy.target_dir)
503+
531504
# Store original_cwd for auto mode (always set, regardless of conflicts)
532505
os.environ["AMPLIHACK_ORIGINAL_CWD"] = original_cwd
533506

534507
if os.environ.get("AMPLIHACK_DEBUG", "").lower() == "true":
535-
print(f"UVX mode: Using plugin architecture")
508+
print(f"UVX mode: Staging Claude environment in current directory: {original_cwd}")
536509
print(f"Working directory remains: {original_cwd}")
537510

538-
# Setup plugin architecture
539-
# 1. Check if plugin already installed at ~/.amplihack/.claude/
540-
plugin_root = Path.home() / ".amplihack" / ".claude"
541-
plugin_manager = PluginManager(plugin_root=plugin_root.parent / "plugins")
542-
543-
# Check if local .claude/ exists (backward compatibility)
544-
local_claude_dir = Path(original_cwd) / ".claude"
545-
if local_claude_dir.exists() and (local_claude_dir / "settings.json").exists():
546-
if os.environ.get("AMPLIHACK_DEBUG", "").lower() == "true":
547-
print(f"Using existing local .claude/ directory at {local_claude_dir}")
548-
temp_claude_dir = str(local_claude_dir)
549-
else:
550-
# Use plugin architecture
551-
# 2. Install plugin if not already installed
552-
import amplihack
553-
amplihack_src = Path(amplihack.__file__).parent
554-
555-
# Check if plugin already installed
556-
if not plugin_root.exists() or not (plugin_root / "tools").exists():
557-
if os.environ.get("AMPLIHACK_DEBUG", "").lower() == "true":
558-
print(f"Installing plugin to {plugin_root}")
511+
# Stage framework files to the current directory's .claude directory
512+
# Find the amplihack package location
513+
# Find amplihack package location for .claude files
514+
import amplihack
559515

560-
# Create plugin directory
561-
plugin_root.mkdir(parents=True, exist_ok=True)
516+
amplihack_src = os.path.dirname(os.path.abspath(amplihack.__file__))
562517

563-
# Copy .claude contents to plugin root
564-
copied = copytree_manifest(str(amplihack_src), str(plugin_root), ".claude")
518+
# Copy .claude contents to temp .claude directory
519+
# Note: copytree_manifest copies TO the dst, not INTO dst/.claude
520+
copied = copytree_manifest(amplihack_src, temp_claude_dir, ".claude")
565521

566-
if not copied:
567-
print("\n❌ Failed to install plugin - cannot proceed")
568-
print(f" Package location: {amplihack_src}")
569-
sys.exit(1)
522+
# Bug #2 Fix: Detect empty copy results (Issue #1940)
523+
# When copytree_manifest returns empty list, no files were copied
524+
# This indicates a package installation problem - exit with clear error
525+
if not copied:
526+
print("\n❌ Failed to copy .claude files - cannot proceed")
527+
print(f" Package location: {amplihack_src}")
528+
print(f" Looking for .claude/ at: {amplihack_src}/.claude/")
529+
print(" This may indicate a package installation problem\n")
530+
sys.exit(1)
570531

532+
# Smart PROJECT.md initialization for UVX mode
533+
if copied:
534+
try:
535+
from .utils.project_initializer import InitMode, initialize_project_md
536+
537+
result = initialize_project_md(Path(original_cwd), mode=InitMode.FORCE)
538+
if result.success and result.action_taken.value in ["initialized", "regenerated"]:
539+
if os.environ.get("AMPLIHACK_DEBUG", "").lower() == "true":
540+
print(
541+
f"PROJECT.md {result.action_taken.value} for {Path(original_cwd).name}"
542+
)
543+
except Exception as e:
571544
if os.environ.get("AMPLIHACK_DEBUG", "").lower() == "true":
572-
print(f"Plugin installed successfully to {plugin_root}")
573-
else:
574-
if os.environ.get("AMPLIHACK_DEBUG", "").lower() == "true":
575-
print(f"Plugin already installed at {plugin_root}")
545+
print(f"Warning: PROJECT.md initialization failed: {e}")
576546

577-
# 3. Generate settings.json in project's .claude/ that references plugin
578-
temp_claude_dir = str(local_claude_dir)
579-
local_claude_dir.mkdir(parents=True, exist_ok=True)
580-
581-
settings_path = local_claude_dir / "settings.json"
547+
# Create settings.json with relative paths (Claude will resolve relative to CLAUDE_PROJECT_DIR)
548+
# When CLAUDE_PROJECT_DIR is set, Claude will use settings.json from that directory only
549+
if copied:
550+
settings_path = os.path.join(temp_claude_dir, "settings.json")
582551
import json
583552

584-
# Set CLAUDE_PLUGIN_ROOT for path resolution
585-
os.environ["CLAUDE_PLUGIN_ROOT"] = str(plugin_root)
586-
587-
# Create settings.json with plugin references
553+
# Create minimal settings.json with just amplihack hooks
588554
settings = {
589555
"hooks": {
590556
"SessionStart": [
591557
{
592558
"hooks": [
593559
{
594560
"type": "command",
595-
"command": "${CLAUDE_PLUGIN_ROOT}/tools/amplihack/hooks/session_start.py",
561+
"command": "$CLAUDE_PROJECT_DIR/.claude/tools/amplihack/hooks/session_start.py",
596562
"timeout": 10000,
597563
}
598564
]
@@ -603,7 +569,7 @@ def main(argv: list[str] | None = None) -> int:
603569
"hooks": [
604570
{
605571
"type": "command",
606-
"command": "${CLAUDE_PLUGIN_ROOT}/tools/amplihack/hooks/stop.py",
572+
"command": "$CLAUDE_PROJECT_DIR/.claude/tools/amplihack/hooks/stop.py",
607573
"timeout": 30000,
608574
}
609575
]
@@ -615,7 +581,7 @@ def main(argv: list[str] | None = None) -> int:
615581
"hooks": [
616582
{
617583
"type": "command",
618-
"command": "${CLAUDE_PLUGIN_ROOT}/tools/amplihack/hooks/post_tool_use.py",
584+
"command": "$CLAUDE_PROJECT_DIR/.claude/tools/amplihack/hooks/post_tool_use.py",
619585
}
620586
],
621587
}
@@ -625,7 +591,7 @@ def main(argv: list[str] | None = None) -> int:
625591
"hooks": [
626592
{
627593
"type": "command",
628-
"command": "${CLAUDE_PLUGIN_ROOT}/tools/amplihack/hooks/pre_compact.py",
594+
"command": "$CLAUDE_PROJECT_DIR/.claude/tools/amplihack/hooks/pre_compact.py",
629595
"timeout": 30000,
630596
}
631597
]
@@ -634,39 +600,26 @@ def main(argv: list[str] | None = None) -> int:
634600
}
635601
}
636602

603+
# Write settings.json
604+
os.makedirs(temp_claude_dir, exist_ok=True)
637605
with open(settings_path, "w") as f:
638606
json.dump(settings, f, indent=2)
639607

640608
if os.environ.get("AMPLIHACK_DEBUG", "").lower() == "true":
641-
print(f"Created settings.json at {settings_path}")
642-
print(f"Settings reference plugin at {plugin_root}")
643-
644-
# Smart PROJECT.md initialization for UVX mode
645-
try:
646-
from .utils.project_initializer import InitMode, initialize_project_md
647-
648-
result = initialize_project_md(Path(original_cwd), mode=InitMode.FORCE)
649-
if result.success and result.action_taken.value in ["initialized", "regenerated"]:
650-
if os.environ.get("AMPLIHACK_DEBUG", "").lower() == "true":
651-
print(
652-
f"PROJECT.md {result.action_taken.value} for {Path(original_cwd).name}"
653-
)
654-
except Exception as e:
655-
if os.environ.get("AMPLIHACK_DEBUG", "").lower() == "true":
656-
print(f"Warning: PROJECT.md initialization failed: {e}")
609+
print(f"UVX staging completed to {temp_claude_dir}")
610+
print("Created settings.json with relative hook paths")
657611

658612
args, claude_args = parse_args_with_passthrough(argv)
659613

660614
if not args.command:
661615
# If we have claude_args but no command, default to launching Claude directly
662616
if claude_args:
663-
# If in UVX mode, ensure we use --add-dir for BOTH the original directory AND plugin directory
617+
# If in UVX mode, ensure we use --add-dir for the ORIGINAL directory
664618
if is_uvx_deployment():
665619
# Get the original directory (before we changed to temp)
666620
original_cwd = os.environ.get("AMPLIHACK_ORIGINAL_CWD", os.getcwd())
667-
plugin_root = str(Path.home() / ".amplihack" / ".claude")
668621
if "--add-dir" not in claude_args:
669-
claude_args = ["--add-dir", original_cwd, "--add-dir", plugin_root] + claude_args
622+
claude_args = ["--add-dir", original_cwd] + claude_args
670623

671624
# Check if Docker should be used for direct launch
672625
if DockerManager.should_use_docker():
@@ -711,16 +664,15 @@ def main(argv: list[str] | None = None) -> int:
711664
if getattr(args, "append", None):
712665
return handle_append_instruction(args)
713666

714-
# If in UVX mode, ensure we use --add-dir for BOTH the original directory AND plugin directory
667+
# If in UVX mode, ensure we use --add-dir for the ORIGINAL directory
715668
if is_uvx_deployment():
716669
# Get the original directory (before we changed to temp)
717670
original_cwd = os.environ.get("AMPLIHACK_ORIGINAL_CWD", os.getcwd())
718-
plugin_root = str(Path.home() / ".amplihack" / ".claude")
719671
# Add --add-dir to claude_args if not already present
720672
if claude_args and "--add-dir" not in claude_args:
721-
claude_args = ["--add-dir", original_cwd, "--add-dir", plugin_root] + claude_args
673+
claude_args = ["--add-dir", original_cwd] + claude_args
722674
elif not claude_args:
723-
claude_args = ["--add-dir", original_cwd, "--add-dir", plugin_root]
675+
claude_args = ["--add-dir", original_cwd]
724676

725677
# Handle auto mode
726678
exit_code = handle_auto_mode("claude", args, claude_args)
@@ -737,11 +689,10 @@ def main(argv: list[str] | None = None) -> int:
737689
# Claude is an alias for launch
738690
if is_uvx_deployment():
739691
original_cwd = os.environ.get("AMPLIHACK_ORIGINAL_CWD", os.getcwd())
740-
plugin_root = str(Path.home() / ".amplihack" / ".claude")
741692
if claude_args and "--add-dir" not in claude_args:
742-
claude_args = ["--add-dir", original_cwd, "--add-dir", plugin_root] + claude_args
693+
claude_args = ["--add-dir", original_cwd] + claude_args
743694
elif not claude_args:
744-
claude_args = ["--add-dir", original_cwd, "--add-dir", plugin_root]
695+
claude_args = ["--add-dir", original_cwd]
745696

746697
# Handle auto mode
747698
exit_code = handle_auto_mode("claude", args, claude_args)
@@ -762,11 +713,10 @@ def main(argv: list[str] | None = None) -> int:
762713
# RustyClawd launcher setup (similar to claude command)
763714
if is_uvx_deployment():
764715
original_cwd = os.environ.get("AMPLIHACK_ORIGINAL_CWD", os.getcwd())
765-
plugin_root = str(Path.home() / ".amplihack" / ".claude")
766716
if claude_args and "--add-dir" not in claude_args:
767-
claude_args = ["--add-dir", original_cwd, "--add-dir", plugin_root] + claude_args
717+
claude_args = ["--add-dir", original_cwd] + claude_args
768718
elif not claude_args:
769-
claude_args = ["--add-dir", original_cwd, "--add-dir", plugin_root]
719+
claude_args = ["--add-dir", original_cwd]
770720

771721
# Handle auto mode
772722
exit_code = handle_auto_mode("claude", args, claude_args) # Reuse claude auto mode
@@ -835,106 +785,6 @@ def main(argv: list[str] | None = None) -> int:
835785
print_uvx_usage_instructions()
836786
return 0
837787

838-
elif args.command == "plugin":
839-
if args.plugin_command == "link":
840-
from .plugin_manager import PluginManager
841-
842-
plugin_name = args.plugin_name
843-
plugin_root = Path.home() / ".amplihack" / "plugins"
844-
plugin_path = plugin_root / plugin_name
845-
846-
if not plugin_path.exists():
847-
print(f"Error: Plugin not found at {plugin_path}")
848-
print(f"Install the plugin first with: amplihack install")
849-
return 1
850-
851-
# Create plugin manager and link plugin
852-
manager = PluginManager(plugin_root=plugin_root)
853-
if manager._register_plugin(plugin_name, plugin_path):
854-
print(f"{EMOJI['check']} Plugin linked successfully: {plugin_name}")
855-
print(f" Settings updated in: ~/.claude/settings.json")
856-
print(f" Plugin should now appear in /plugin command")
857-
return 0
858-
else:
859-
print(f"Error: Failed to link plugin: {plugin_name}")
860-
return 1
861-
862-
elif args.plugin_command == "verify":
863-
plugin_name = args.plugin_name
864-
plugin_root = Path.home() / ".amplihack" / "plugins"
865-
plugin_path = plugin_root / plugin_name
866-
settings_path = Path.home() / ".claude" / "settings.json"
867-
868-
print(f"Verifying plugin: {plugin_name}\n")
869-
870-
# Check 1: Plugin directory exists
871-
if plugin_path.exists():
872-
print(f"{EMOJI['check']} Plugin directory exists: {plugin_path}")
873-
else:
874-
print(f"❌ Plugin directory not found: {plugin_path}")
875-
print(f" Install with: amplihack install")
876-
return 1
877-
878-
# Check 2: Manifest exists
879-
manifest_path = plugin_path / "manifest.json"
880-
if manifest_path.exists():
881-
print(f"{EMOJI['check']} Manifest file exists")
882-
try:
883-
import json
884-
manifest = json.loads(manifest_path.read_text())
885-
print(f" Name: {manifest.get('name', 'N/A')}")
886-
print(f" Version: {manifest.get('version', 'N/A')}")
887-
except json.JSONDecodeError:
888-
print(f" ⚠️ Warning: Manifest is not valid JSON")
889-
else:
890-
print(f"❌ Manifest file not found: {manifest_path}")
891-
892-
# Check 3: Settings.json exists and contains plugin
893-
if settings_path.exists():
894-
print(f"{EMOJI['check']} Settings file exists: {settings_path}")
895-
try:
896-
import json
897-
settings = json.loads(settings_path.read_text())
898-
899-
if 'enabledPlugins' in settings:
900-
if plugin_name in settings['enabledPlugins']:
901-
print(f"{EMOJI['check']} Plugin is registered in enabledPlugins array")
902-
else:
903-
print(f"❌ Plugin NOT in enabledPlugins array")
904-
print(f" Fix with: amplihack plugin link {plugin_name}")
905-
return 1
906-
else:
907-
print(f"❌ No enabledPlugins array in settings")
908-
print(f" Fix with: amplihack plugin link {plugin_name}")
909-
return 1
910-
except json.JSONDecodeError:
911-
print(f" ⚠️ Warning: Settings file is not valid JSON")
912-
else:
913-
print(f"❌ Settings file not found: {settings_path}")
914-
print(f" Fix with: amplihack plugin link {plugin_name}")
915-
return 1
916-
917-
# Check 4: CLAUDE_PLUGIN_ROOT environment variable
918-
plugin_root_env = os.environ.get("CLAUDE_PLUGIN_ROOT")
919-
expected_root = str(Path.home() / ".amplihack" / ".claude")
920-
if plugin_root_env:
921-
if plugin_root_env == expected_root:
922-
print(f"{EMOJI['check']} CLAUDE_PLUGIN_ROOT is set correctly")
923-
else:
924-
print(f"⚠️ CLAUDE_PLUGIN_ROOT mismatch:")
925-
print(f" Current: {plugin_root_env}")
926-
print(f" Expected: {expected_root}")
927-
else:
928-
print(f"⚠️ CLAUDE_PLUGIN_ROOT not set (will be set on launch)")
929-
930-
print(f"\n{EMOJI['check']} Plugin verification complete!")
931-
print(f" Plugin should be discoverable via /plugin command in Claude Code")
932-
return 0
933-
934-
else:
935-
create_parser().print_help()
936-
return 1
937-
938788
elif args.command == "memory":
939789
if args.memory_command == "tree":
940790
from .memory.cli_visualize import visualize_memory_tree

0 commit comments

Comments
 (0)