跳转至

ztxexp.vibe

ztxexp.vibe

Agent integration helpers for ztxexp CLI.

This module provides reusable functions used by ztxexp init-vibe style commands. It is intentionally independent from argument parsing so it can be tested directly.

DEFAULT_AGENTS_FILES module-attribute

DEFAULT_AGENTS_FILES = ('AGENTS.md', 'agents.md', 'agents.MD')

END_MARKER module-attribute

END_MARKER = '<!-- ztxexp:vibe:end -->'

LanguageType module-attribute

LanguageType = Literal['bilingual', 'zh', 'en']

ProfileType module-attribute

ProfileType = Literal['webcoding', 'codex', 'cursor', 'cline', 'copilot']

START_MARKER module-attribute

START_MARKER = '<!-- ztxexp:vibe:start -->'

SUPPORTED_LANGUAGES module-attribute

SUPPORTED_LANGUAGES = ('bilingual', 'zh', 'en')

SUPPORTED_PROFILES module-attribute

SUPPORTED_PROFILES = ('webcoding', 'codex', 'cursor', 'cline', 'copilot')

VibeOperationResult dataclass

Result of a managed block operation.

源代码位于: ztxexp/vibe.py
@dataclass(slots=True)
class VibeOperationResult:
    """Result of a managed block operation."""

    target_file: Path
    action: str
    changed: bool
    old_content: str
    new_content: str

    def diff_text(self) -> str:
        """Build a unified diff for preview output."""
        before = self.old_content.splitlines(keepends=True)
        after = self.new_content.splitlines(keepends=True)
        if before == after:
            return ""
        diff = difflib.unified_diff(
            before,
            after,
            fromfile=f"{self.target_file} (before)",
            tofile=f"{self.target_file} (after)",
        )
        return "".join(diff)

action instance-attribute

action: str

changed instance-attribute

changed: bool

new_content instance-attribute

new_content: str

old_content instance-attribute

old_content: str

target_file instance-attribute

target_file: Path

__init__

__init__(target_file: Path, action: str, changed: bool, old_content: str, new_content: str) -> None

diff_text

diff_text() -> str

Build a unified diff for preview output.

源代码位于: ztxexp/vibe.py
def diff_text(self) -> str:
    """Build a unified diff for preview output."""
    before = self.old_content.splitlines(keepends=True)
    after = self.new_content.splitlines(keepends=True)
    if before == after:
        return ""
    diff = difflib.unified_diff(
        before,
        after,
        fromfile=f"{self.target_file} (before)",
        tofile=f"{self.target_file} (after)",
    )
    return "".join(diff)

init_vibe

init_vibe(project_root: str | Path | None = None, agents_file: str | Path | None = None, profile: ProfileType = 'webcoding', language: LanguageType = 'bilingual', dry_run: bool = False) -> VibeOperationResult

Create or update managed vibe block in target project.

源代码位于: ztxexp/vibe.py
def init_vibe(
    project_root: str | Path | None = None,
    agents_file: str | Path | None = None,
    profile: ProfileType = "webcoding",
    language: LanguageType = "bilingual",
    dry_run: bool = False,
) -> VibeOperationResult:
    """Create or update managed vibe block in target project."""
    root = resolve_project_root(project_root)
    target = resolve_agents_file(root, agents_file)
    file_exists = target.exists()
    old_content = target.read_text(encoding="utf-8") if file_exists else ""
    block = render_vibe_block(profile=profile, language=language)
    new_content, action = _upsert_managed_block(old_content, block, file_exists=file_exists)
    changed = old_content != new_content

    if changed and not dry_run:
        target.parent.mkdir(parents=True, exist_ok=True)
        target.write_text(new_content, encoding="utf-8")
    if not changed:
        action = "unchanged"

    return VibeOperationResult(
        target_file=target,
        action=action,
        changed=changed,
        old_content=old_content,
        new_content=new_content,
    )

remove_vibe

remove_vibe(project_root: str | Path | None = None, agents_file: str | Path | None = None, dry_run: bool = False) -> VibeOperationResult

Remove managed vibe block from target project.

源代码位于: ztxexp/vibe.py
def remove_vibe(
    project_root: str | Path | None = None,
    agents_file: str | Path | None = None,
    dry_run: bool = False,
) -> VibeOperationResult:
    """Remove managed vibe block from target project."""
    root = resolve_project_root(project_root)
    target = resolve_agents_file(root, agents_file)
    if not target.exists():
        return VibeOperationResult(
            target_file=target,
            action="not_found",
            changed=False,
            old_content="",
            new_content="",
        )

    old_content = target.read_text(encoding="utf-8")
    new_content, removed = _remove_managed_block(old_content)
    changed = removed and new_content != old_content

    if changed and not dry_run:
        target.write_text(new_content, encoding="utf-8")

    action = "removed" if removed else "no_block"
    return VibeOperationResult(
        target_file=target,
        action=action,
        changed=changed,
        old_content=old_content,
        new_content=new_content,
    )

render_vibe_block

render_vibe_block(profile: ProfileType = 'webcoding', language: LanguageType = 'bilingual') -> str

Render managed block content by profile and language.

源代码位于: ztxexp/vibe.py
def render_vibe_block(
    profile: ProfileType = "webcoding",
    language: LanguageType = "bilingual",
) -> str:
    """Render managed block content by profile and language."""
    if profile not in SUPPORTED_PROFILES:
        raise ValueError(f"unsupported profile: {profile}")
    if language not in SUPPORTED_LANGUAGES:
        raise ValueError(f"unsupported language: {language}")

    hints = _PROFILE_HINTS[profile]

    zh_section = f"""## ztxexp Agent 使用约定(受管区块)

- Profile: `{profile}`
- 说明:{hints["zh"]}
- 本项目已启用 `ztxexp`,请优先基于以下抽象完成实验代码:
  - `ExperimentPipeline`
  - `RunContext`
  - `ResultAnalyzer`
- 实验函数契约:`exp_fn(ctx: RunContext) -> dict | None`
- 成功判定:`run.json.status == "succeeded"`
- 常用命令:
  - `pip install -U ztxexp`
  - `python -m pytest`
  - `python -m ztxexp show-vibe`

最小代码示例:

```python
from ztxexp import ExperimentPipeline, RunContext

def exp_fn(ctx: RunContext):
    return {{"score": 1.0}}

summary = (
    ExperimentPipeline("./results")
    .grid({{"seed": [42]}})
    .run(exp_fn, mode="sequential")
)
print(summary)
```
"""

    en_section = f"""## ztxexp Agent Guidelines (Managed Block)

- Profile: `{profile}`
- Note: {hints["en"]}
- This project uses `ztxexp`. Prefer these abstractions in generated code:
  - `ExperimentPipeline`
  - `RunContext`
  - `ResultAnalyzer`
- Experiment contract: `exp_fn(ctx: RunContext) -> dict | None`
- Success criteria: `run.json.status == "succeeded"`
- Common commands:
  - `pip install -U ztxexp`
  - `python -m pytest`
  - `python -m ztxexp show-vibe`

Minimal example:

```python
from ztxexp import ExperimentPipeline, RunContext

def exp_fn(ctx: RunContext):
    return {{"score": 1.0}}

summary = (
    ExperimentPipeline("./results")
    .grid({{"seed": [42]}})
    .run(exp_fn, mode="sequential")
)
print(summary)
```
"""

    if language == "zh":
        payload = zh_section.strip()
    elif language == "en":
        payload = en_section.strip()
    else:
        payload = f"{zh_section.strip()}\n\n---\n\n{en_section.strip()}"

    return f"{START_MARKER}\n{payload}\n{END_MARKER}\n"

resolve_agents_file

resolve_agents_file(project_root: Path, agents_file: str | Path | None = None) -> Path

Resolve target agents file according to matching policy.

源代码位于: ztxexp/vibe.py
def resolve_agents_file(project_root: Path, agents_file: str | Path | None = None) -> Path:
    """Resolve target agents file according to matching policy."""
    if agents_file is not None:
        explicit_path = Path(agents_file).expanduser()
        if not explicit_path.is_absolute():
            explicit_path = project_root / explicit_path
        return explicit_path.resolve()

    for filename in DEFAULT_AGENTS_FILES:
        candidate = project_root / filename
        if candidate.exists():
            return candidate.resolve()
    return (project_root / DEFAULT_AGENTS_FILES[0]).resolve()

resolve_project_root

resolve_project_root(project_root: str | Path | None) -> Path

Resolve and validate project root directory.

源代码位于: ztxexp/vibe.py
def resolve_project_root(project_root: str | Path | None) -> Path:
    """Resolve and validate project root directory."""
    root = Path.cwd() if project_root is None else Path(project_root)
    root = root.expanduser().resolve()
    if root.exists() and not root.is_dir():
        raise ValueError(f"project root is not a directory: {root}")
    if not root.exists():
        raise ValueError(f"project root does not exist: {root}")
    return root

show_vibe

show_vibe(profile: ProfileType = 'webcoding', language: LanguageType = 'bilingual') -> str

Preview managed block content.

源代码位于: ztxexp/vibe.py
def show_vibe(profile: ProfileType = "webcoding", language: LanguageType = "bilingual") -> str:
    """Preview managed block content."""
    return render_vibe_block(profile=profile, language=language)