feat(chat): --cwd 支持可选值,缺省为当前目录
使 --cwd 选项支持无值调用: - --cwd 单独使用 → 默认为当前目录 "." - --cwd /path → 使用指定路径 - --cwd=. / -C. → 等号或短选项语法 修复:--cwd 后接其他 flag 时被误认为值的歧义问题。
This commit is contained in:
@@ -12,21 +12,60 @@ from rich.console import Console
|
|||||||
from myclaude.project_root import get_myclaude_project_root
|
from myclaude.project_root import get_myclaude_project_root
|
||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
|
stderr_console = Console(stderr=True)
|
||||||
|
|
||||||
|
|
||||||
def _resolve_chat_cwd() -> Path:
|
def _resolve_chat_cwd() -> Path:
|
||||||
root = get_myclaude_project_root()
|
root = get_myclaude_project_root()
|
||||||
fallback = Path.home() / ".myclaude"
|
fallback = Path.home() / ".myclaude"
|
||||||
if root == fallback:
|
if root == fallback:
|
||||||
console.print(
|
stderr_console.print(
|
||||||
"[red]Cannot resolve myclaude project root.[/red] Run from the repo or set "
|
"[red]Cannot resolve myclaude project root.[/red] Run from the repo or set "
|
||||||
"[cyan]MYCLAUDE_PROJECT_ROOT[/cyan].",
|
"[cyan]MYCLAUDE_PROJECT_ROOT[/cyan].",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_cwd_from_args(args: list[str]) -> tuple[str | None, list[str]]:
|
||||||
|
"""
|
||||||
|
Parse --cwd with optional value from args.
|
||||||
|
|
||||||
|
Returns (cwd_value, remaining_args).
|
||||||
|
--cwd alone -> cwd_value='.'
|
||||||
|
--cwd /path -> cwd_value='/path'
|
||||||
|
--cwd . -> cwd_value='.'
|
||||||
|
"""
|
||||||
|
cwd_value: str | None = None
|
||||||
|
remaining: list[str] = []
|
||||||
|
skip_next = False
|
||||||
|
|
||||||
|
for i, arg in enumerate(args):
|
||||||
|
if skip_next:
|
||||||
|
skip_next = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
if arg in ("--cwd", "-C"):
|
||||||
|
# Check if next arg exists and is not a flag
|
||||||
|
if i + 1 < len(args) and not args[i + 1].startswith("-"):
|
||||||
|
cwd_value = args[i + 1]
|
||||||
|
skip_next = True
|
||||||
|
else:
|
||||||
|
# --cwd alone, default to current directory
|
||||||
|
cwd_value = "."
|
||||||
|
elif arg.startswith("--cwd="):
|
||||||
|
cwd_value = arg[6:]
|
||||||
|
elif arg.startswith("-C="):
|
||||||
|
cwd_value = arg[3:]
|
||||||
|
elif arg.startswith("-C") and len(arg) > 2:
|
||||||
|
# -C/path or -C.
|
||||||
|
cwd_value = arg[2:]
|
||||||
|
else:
|
||||||
|
remaining.append(arg)
|
||||||
|
|
||||||
|
return cwd_value, remaining
|
||||||
|
|
||||||
|
|
||||||
@click.command(
|
@click.command(
|
||||||
"chat",
|
"chat",
|
||||||
context_settings={
|
context_settings={
|
||||||
@@ -40,23 +79,47 @@ def _resolve_chat_cwd() -> Path:
|
|||||||
default=False,
|
default=False,
|
||||||
help="Forward to claude: bypass permission checks (sandbox / isolated use only).",
|
help="Forward to claude: bypass permission checks (sandbox / isolated use only).",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"--cwd",
|
||||||
|
"-C",
|
||||||
|
multiple=True,
|
||||||
|
metavar="PATH",
|
||||||
|
help="Working directory for claude (default: myclaude project root; '--cwd' uses current dir; '--cwd /path' uses specific path).",
|
||||||
|
)
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def chat_cmd(ctx: click.Context, dangerously_skip_permissions: bool) -> None:
|
def chat_cmd(ctx: click.Context, dangerously_skip_permissions: bool, cwd: tuple[str, ...]) -> None:
|
||||||
"""Run `claude` in the myclaude repo so CLAUDE.md and .claude/ apply; extra args pass through."""
|
"""Run `claude` in the myclaude repo so CLAUDE.md and .claude/ apply; extra args pass through.
|
||||||
|
|
||||||
|
\b
|
||||||
|
Working directory options:
|
||||||
|
--cwd, -C Use current directory as working directory
|
||||||
|
--cwd /path, -C/path Use specified path as working directory
|
||||||
|
(none) Use myclaude project root (default)
|
||||||
|
"""
|
||||||
binary = os.environ.get("CLAUDE_BIN") or shutil.which("claude")
|
binary = os.environ.get("CLAUDE_BIN") or shutil.which("claude")
|
||||||
if not binary:
|
if not binary:
|
||||||
console.print(
|
stderr_console.print(
|
||||||
"[red]claude CLI not found in PATH.[/red] Install Claude Code or set "
|
"[red]claude CLI not found in PATH.[/red] Install Claude Code or set "
|
||||||
"[cyan]CLAUDE_BIN[/cyan].",
|
"[cyan]CLAUDE_BIN[/cyan].",
|
||||||
file=sys.stderr,
|
|
||||||
)
|
)
|
||||||
raise SystemExit(127)
|
raise SystemExit(127)
|
||||||
|
|
||||||
cwd = _resolve_chat_cwd()
|
# Parse --cwd from raw args since Click doesn't support optional values natively
|
||||||
|
# (We ignore the 'cwd' parameter from Click and parse manually from ctx.args)
|
||||||
|
cwd_value, remaining_args = _parse_cwd_from_args(ctx.args)
|
||||||
|
|
||||||
|
if cwd_value is None:
|
||||||
|
chat_cwd = _resolve_chat_cwd()
|
||||||
|
else:
|
||||||
|
chat_cwd = Path(cwd_value).resolve()
|
||||||
|
if not chat_cwd.is_dir():
|
||||||
|
stderr_console.print(f"[red]Not a directory:[/red] {cwd_value}")
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
cmd: list[str] = [binary]
|
cmd: list[str] = [binary]
|
||||||
if dangerously_skip_permissions:
|
if dangerously_skip_permissions:
|
||||||
cmd.append("--dangerously-skip-permissions")
|
cmd.append("--dangerously-skip-permissions")
|
||||||
cmd.extend(ctx.args)
|
cmd.extend(remaining_args)
|
||||||
|
|
||||||
proc = subprocess.run(cmd, cwd=str(cwd), check=False)
|
proc = subprocess.run(cmd, cwd=str(chat_cwd), check=False)
|
||||||
raise SystemExit(proc.returncode)
|
raise SystemExit(proc.returncode)
|
||||||
|
|||||||
Reference in New Issue
Block a user