Ctrl K
ComunidadGuías…

butler/gateway/owner_delegate_shortcuts.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Owner shortcuts: /改 → dev delegate NL, /转交CC → CC handoff package (PROD-P4-B)."""

from __future__ import annotations

from pathlib import Path
from typing import Any, cast


def parse_edit_command_arg(arg: str) -> tuple[str, str]:
    """Split ``/改 <path> <goal>`` — first token path, remainder goal."""
    body = str(arg or "").strip()
    if not body:
        return "", ""
    parts = body.split(maxsplit=1)
    path = parts[0].strip()
    goal = parts[1].strip() if len(parts) > 1 else "按说明修改"
    return path, goal


def build_dev_delegate_prompt(
    path: str,
    goal: str,
    *,
    project_name: str = "",
) -> str:
    """Standardized dev delegate phrase for Lead (role=dev, scoped)."""
    path = str(path or "").strip() or "docs/"
    goal = str(goal or "").strip() or "按说明修改"
    proj = f"当前项目 {project_name}。" if project_name else ""
    return (
        f"请委派开发代理(role=dev,禁止 run_workflow)。{proj}"
        f"限定范围:{path}。目标:{goal}。"
        "改完后 read_file 确认;不要改范围外文件;不要开 terminal 除非 VERIFY 需要。"
    )


def try_expand_owner_edit_slash(text: str, *, project_name: str = "") -> str | None:
    """
    Expand ``/改 <path> <goal>`` to delegate NL before the agent loop.

    Returns ``None`` if not ``/改`` or missing args (slash handler shows usage).
    """
    raw = str(text or "").strip()
    if not raw.lower().startswith("/改"):
        return None
    arg = raw.split(maxsplit=1)[1].strip() if len(raw.split(maxsplit=1)) > 1 else ""
    if not arg:
        return None
    path, goal = parse_edit_command_arg(arg)
    if not path:
        return None
    return build_dev_delegate_prompt(path, goal, project_name=project_name)


def format_edit_command_usage() -> str:
    return (
        "用法:/改 <路径或范围> <目标>\n"
        "例:/改 docs/foo.md 加一段说明\n"
        "例:/改 src/auth.py 修复登录校验\n"
        "将自动交给开发代理(限定文件范围)。"
    )


def _recent_read_paths(session_key: str, *, limit: int = 5) -> list[str]:
    from butler.gateway.owner_delegate_shortcuts_ops import recent_read_paths_safe

    return cast(list[str], recent_read_paths_safe(session_key, limit=limit))


def build_cc_handoff_package(
    scope: str,
    *,
    project_name: str = "",
    workspace: str | Path = "",
    session_key: str = "",
) -> str:
    """Markdown task pack for local Claude Code / Cursor (P4-04)."""
    scope = str(scope or "").strip() or "(请补充范围)"
    proj = str(project_name or "").strip() or "(未选项目)"
    ws = str(workspace or "").strip() or "—"
    reads = _recent_read_paths(session_key)

    lines = [
        "【CC 任务包】复制到本机 Claude Code / Cursor",
        "",
        "## 范围",
        scope,
        "",
        "## 项目",
        proj,
        "",
        "## 工作区",
        ws,
    ]
    if reads:
        lines.extend(["", "## 相关文件(本会话已读)", *[f"- `{p}`" for p in reads[:5]]])

    lines.extend(
        [
            "",
            "## 现状",
            "- 微信 Butler 负责派工与验收摘要;大改码/全库 refactor 建议本机 CC 执行",
            "- 完成后可将 PR 链接或变更摘要发回微信",
            "",
            "## Butler 侧下一步",
        ]
    )

    from butler.gateway.owner_delegate_shortcuts_ops import cc_bridge_enabled_safe

    cc_on = cc_bridge_enabled_safe()

    if cc_on:
        brief = scope.replace("\n", " ")[:160]
        lines.append(f"- 网关已开 CC 桥接:发 **/cc-bridge {brief}**")
    else:
        lines.append("- 本机执行;或设 `BUTLER_CC_BRIDGE=1` 后用 **/cc-bridge <摘要>**")

    lines.append("- 验收:改完后在微信发「交给开发代理:只读检查 …」或看委派 **验收卡**")
    return "\n".join(lines)


def resolve_project_context(orchestrator: Any, session_key: str) -> tuple[str, str]:
    """Return (display_name, workspace_path) for current project."""
    pm = getattr(orchestrator, "project_manager", None)
    if pm is None:
        return "", ""
    proj = pm.get_current(session_key=str(session_key or "").strip())
    if proj is None:
        return "", ""
    name = str(getattr(proj, "name", "") or "").strip()
    ws = str(getattr(proj, "workspace", "") or "").strip()
    return name, ws


__all__ = [
    "build_cc_handoff_package",
    "build_dev_delegate_prompt",
    "format_edit_command_usage",
    "parse_edit_command_arg",
    "resolve_project_context",
    "try_expand_owner_edit_slash",
]