refactor: rename source dir to bin, reorganize templates, remove personal info
- Rename myclaude/ source directory → bin/ - Update all Python imports (myclaude.* → bin.*) and mock patch paths - Update pyproject.toml: entry point and packages config - Move workspace_template/ → templates/workspace/ for future extensibility - Migrate .claude/skills/ to global ~/.claude/skills/ and remove local copies - Remove SessionStart hook (sync-global.sh) from settings.local.json - Remove unrelated script rename_funcs_to_snake.py - Remove personal info (name, org, paths) from CLAUDE.md, README.md, settings - Update .gitignore with standard Python/runtime exclusions - Clean .DS_Store, __pycache__, .mypy_cache, .pytest_cache, .ruff_cache, .playwright-mcp runtime files
This commit is contained in:
@@ -0,0 +1 @@
|
||||
"""Myclaude CLI package."""
|
||||
@@ -0,0 +1,3 @@
|
||||
from bin.cli import main
|
||||
|
||||
main()
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
"""Myclaude CLI entrypoint."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from importlib.metadata import PackageNotFoundError, version
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
from rich.console import Console
|
||||
|
||||
from bin.commands import update_cmd, upgrade_cmd
|
||||
from bin.project_root import get_workspace_root
|
||||
|
||||
stderr_console = Console(stderr=True)
|
||||
|
||||
|
||||
def _package_version() -> str:
|
||||
try:
|
||||
return version("myclaude")
|
||||
except PackageNotFoundError:
|
||||
return "0.0.0"
|
||||
|
||||
|
||||
def _resolve_chat_cwd() -> Path:
|
||||
return get_workspace_root()
|
||||
|
||||
|
||||
@click.group(
|
||||
invoke_without_command=True,
|
||||
context_settings={
|
||||
"ignore_unknown_options": True,
|
||||
"allow_extra_args": True,
|
||||
},
|
||||
)
|
||||
@click.version_option(version=_package_version())
|
||||
@click.option(
|
||||
"--cwd",
|
||||
"-C",
|
||||
is_flag=False,
|
||||
flag_value=".",
|
||||
default=None,
|
||||
type=click.Path(dir_okay=True, file_okay=False),
|
||||
help="Use specified path as working directory (default: workspace/).",
|
||||
)
|
||||
@click.option(
|
||||
"--dangerously-skip-permissions",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Forward to claude: bypass all permission checks.",
|
||||
)
|
||||
@click.pass_context
|
||||
def cli(
|
||||
ctx: click.Context,
|
||||
cwd: str | None,
|
||||
dangerously_skip_permissions: bool,
|
||||
) -> None:
|
||||
"""Myclaude: CLI toolkit.
|
||||
|
||||
Run without subcommands to start Claude Code in workspace/.
|
||||
Extra arguments pass through to claude.
|
||||
"""
|
||||
if ctx.invoked_subcommand is not None:
|
||||
return
|
||||
|
||||
binary = os.environ.get("CLAUDE_BIN") or shutil.which("claude")
|
||||
if not binary:
|
||||
stderr_console.print(
|
||||
"[red]claude CLI not found in PATH.[/red] Install Claude Code or set "
|
||||
"[cyan]CLAUDE_BIN[/cyan].",
|
||||
)
|
||||
raise SystemExit(127)
|
||||
|
||||
if cwd is None:
|
||||
chat_cwd = _resolve_chat_cwd()
|
||||
else:
|
||||
chat_cwd = Path(cwd).resolve()
|
||||
if not chat_cwd.is_dir():
|
||||
stderr_console.print(f"[red]Not a directory:[/red] {cwd}")
|
||||
raise SystemExit(1)
|
||||
|
||||
cmd: list[str] = [binary]
|
||||
if dangerously_skip_permissions:
|
||||
cmd.append("--dangerously-skip-permissions")
|
||||
cmd.extend(ctx.args)
|
||||
|
||||
proc = subprocess.run(cmd, cwd=str(chat_cwd), check=False)
|
||||
raise SystemExit(proc.returncode)
|
||||
|
||||
|
||||
cli.add_command(update_cmd)
|
||||
cli.add_command(upgrade_cmd, name="upgrade")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
cli()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,8 @@
|
||||
"""CLI subcommands."""
|
||||
|
||||
from .update import update_cmd
|
||||
|
||||
# alias: upgrade is the same as update
|
||||
upgrade_cmd = update_cmd
|
||||
|
||||
__all__ = ["update_cmd", "upgrade_cmd"]
|
||||
@@ -0,0 +1,54 @@
|
||||
"""Reinstall myclaude from the local repo."""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
from rich.console import Console
|
||||
|
||||
from bin.project_root import get_myclaude_project_root
|
||||
|
||||
console = Console()
|
||||
stderr_console = Console(stderr=True)
|
||||
|
||||
|
||||
def _run_make_install(root: Path) -> int:
|
||||
return subprocess.run(
|
||||
["make", "install"],
|
||||
cwd=str(root),
|
||||
check=False,
|
||||
).returncode
|
||||
|
||||
|
||||
def _run_pip_editable(root: Path) -> int:
|
||||
cmd = [sys.executable, "-m", "pip", "install", "-e", str(root)]
|
||||
return subprocess.run(cmd, cwd=str(root), check=False).returncode
|
||||
|
||||
|
||||
@click.command("update")
|
||||
def update_cmd() -> None:
|
||||
"""Reinstall myclaude from this repository (make install, or pip install -e)."""
|
||||
root = get_myclaude_project_root()
|
||||
fallback = Path.home() / ".myclaude"
|
||||
if root == fallback:
|
||||
stderr_console.print(
|
||||
"[red]Not inside myclaude repo.[/red] Set [cyan]MYCLAUDE_PROJECT_ROOT[/cyan] "
|
||||
"or run from the clone.",
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
console.print("[bold cyan]Updating myclaude…[/bold cyan]")
|
||||
makefile = root / "Makefile"
|
||||
if makefile.is_file():
|
||||
rc = _run_make_install(root)
|
||||
if rc != 0:
|
||||
console.print("[yellow]make install failed, trying pip -e…[/yellow]")
|
||||
rc = _run_pip_editable(root)
|
||||
else:
|
||||
rc = _run_pip_editable(root)
|
||||
|
||||
if rc != 0:
|
||||
stderr_console.print("[red]Update failed.[/red]")
|
||||
raise SystemExit(rc)
|
||||
console.print("[green]myclaude updated.[/green]")
|
||||
@@ -0,0 +1,78 @@
|
||||
"""Resolve myclaude repository and workspace roots."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _pyproject_names_myclaude(path: Path) -> bool:
|
||||
try:
|
||||
text = path.read_text(encoding="utf-8", errors="ignore")
|
||||
except OSError:
|
||||
return False
|
||||
return 'name = "myclaude"' in text or "name = 'myclaude'" in text
|
||||
|
||||
|
||||
def _walk_up_for_pyproject(start: Path) -> Path | None:
|
||||
p = start.resolve()
|
||||
for _ in range(16):
|
||||
candidate = p / "pyproject.toml"
|
||||
if candidate.is_file() and _pyproject_names_myclaude(candidate):
|
||||
return p
|
||||
parent = p.parent
|
||||
if parent == p:
|
||||
break
|
||||
p = parent
|
||||
return None
|
||||
|
||||
|
||||
def get_myclaude_project_root() -> Path:
|
||||
"""
|
||||
Root of the myclaude repo (contains Makefile + pyproject).
|
||||
|
||||
Order: MYCLAUDE_PROJECT_ROOT > walk from cwd > package source tree > ~/.myclaude
|
||||
"""
|
||||
env_root = os.environ.get("MYCLAUDE_PROJECT_ROOT")
|
||||
if env_root:
|
||||
return Path(env_root).resolve()
|
||||
|
||||
try:
|
||||
cwd = Path.cwd()
|
||||
except (OSError, PermissionError):
|
||||
cwd = None
|
||||
if cwd is not None:
|
||||
found = _walk_up_for_pyproject(cwd)
|
||||
if found is not None:
|
||||
return found
|
||||
|
||||
here = Path(__file__).resolve().parent
|
||||
for _ in range(8):
|
||||
pyproject = here / "pyproject.toml"
|
||||
if pyproject.is_file() and _pyproject_names_myclaude(pyproject):
|
||||
return here
|
||||
if here.parent == here:
|
||||
break
|
||||
here = here.parent
|
||||
|
||||
return Path.home() / ".myclaude"
|
||||
|
||||
|
||||
def get_workspace_root() -> Path:
|
||||
"""
|
||||
Root of the myclaude workspace directory.
|
||||
|
||||
Order: MYCLAUDE_WORKSPACE_ROOT > project_root/workspace/ > ~/workspace
|
||||
Creates the directory if it does not exist.
|
||||
"""
|
||||
env_root = os.environ.get("MYCLAUDE_WORKSPACE_ROOT")
|
||||
if env_root:
|
||||
root = Path(env_root).expanduser().resolve()
|
||||
else:
|
||||
project_root = get_myclaude_project_root()
|
||||
project_workspace = project_root / "workspace"
|
||||
if project_workspace.is_dir():
|
||||
root = project_workspace
|
||||
else:
|
||||
root = Path.home() / "workspace"
|
||||
|
||||
root.mkdir(parents=True, exist_ok=True)
|
||||
return root
|
||||
Reference in New Issue
Block a user