mirror of
https://github.com/NCBM/plyngent.git
synced 2026-11-07 23:47:54 +08:00
cli/skills: open the skill directory instead of printing SKILL.md
This commit is contained in:
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
@@ -9,6 +10,7 @@ from plyngent.cli.app import main
|
||||
from plyngent.cli.editor import (
|
||||
ensure_config_file,
|
||||
get_editor,
|
||||
open_directory,
|
||||
open_in_editor,
|
||||
prompt_edit_config,
|
||||
resolve_config_path,
|
||||
@@ -70,7 +72,8 @@ def test_open_in_editor_system_fallback(
|
||||
path = tmp_path / "x.toml"
|
||||
opened: list[str] = []
|
||||
|
||||
def fake_system(p: Path) -> None:
|
||||
def fake_system(p: Path, *, hint: str = "") -> None:
|
||||
assert hint
|
||||
opened.append(str(p))
|
||||
|
||||
monkeypatch.setattr("plyngent.cli.editor._open_with_system_default", fake_system)
|
||||
@@ -80,6 +83,43 @@ def test_open_in_editor_system_fallback(
|
||||
assert path.is_file()
|
||||
|
||||
|
||||
def test_open_directory_uses_the_system_opener_even_with_an_editor(
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
opened: list[Path] = []
|
||||
|
||||
def fake_system(p: Path, *, hint: str = "") -> None:
|
||||
assert hint
|
||||
opened.append(p)
|
||||
|
||||
# A folder is not an editor target: $EDITOR must not be consulted.
|
||||
monkeypatch.setenv("EDITOR", "codium --wait")
|
||||
monkeypatch.setattr("plyngent.cli.editor._open_with_system_default", fake_system)
|
||||
directory = tmp_path / "acme-deploy"
|
||||
directory.mkdir()
|
||||
|
||||
open_directory(directory)
|
||||
|
||||
assert opened == [directory]
|
||||
|
||||
|
||||
def test_open_directory_names_the_printed_path_when_the_opener_is_missing(
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
def missing(*args: object, **kwargs: object) -> object:
|
||||
del args, kwargs
|
||||
msg = "xdg-open"
|
||||
raise FileNotFoundError(msg)
|
||||
|
||||
monkeypatch.setattr("sys.platform", "linux")
|
||||
monkeypatch.setattr("plyngent.cli.editor.subprocess.run", missing)
|
||||
|
||||
with pytest.raises(click.ClickException, match=r"xdg-open not found; open the printed path"):
|
||||
open_directory(tmp_path)
|
||||
|
||||
|
||||
def test_open_in_editor_no_fallback_requires_editor(
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
@@ -227,7 +267,7 @@ def test_config_edit_system_fallback_message(
|
||||
monkeypatch.delenv("VISUAL", raising=False)
|
||||
monkeypatch.setattr(
|
||||
"plyngent.cli.editor._open_with_system_default",
|
||||
lambda p: None,
|
||||
lambda p, **kwargs: None,
|
||||
)
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["config", "edit", "--config", str(path)])
|
||||
|
||||
@@ -99,7 +99,7 @@ async def test_skills_are_discovered_from_a_configured_root(tmp_path: Path) -> N
|
||||
await state.memory.close()
|
||||
|
||||
|
||||
async def test_slash_skills_lists_search_and_reads(
|
||||
async def test_slash_skills_lists_and_searches(
|
||||
skills_state: ReplState,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
) -> None:
|
||||
@@ -112,11 +112,53 @@ async def test_slash_skills_lists_search_and_reads(
|
||||
assert await handle_slash(skills_state, "/skills search pdftotext") is True
|
||||
assert "acme-deploy/SKILL.md:" in capsys.readouterr().out
|
||||
|
||||
assert await handle_slash(skills_state, "/skills read acme-deploy") is True
|
||||
assert "description: Deploy the acme stack" in capsys.readouterr().out
|
||||
|
||||
assert await handle_slash(skills_state, "/skills read acme-deploy --file SKILL.md") is True
|
||||
assert "acme-deploy" in capsys.readouterr().out
|
||||
async def test_slash_skills_open_reveals_the_skill_directory(
|
||||
skills_state: ReplState,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
opened: list[Path] = []
|
||||
monkeypatch.setattr("plyngent.cli.editor._open_with_system_default", lambda path, **kw: opened.append(path))
|
||||
|
||||
assert await handle_slash(skills_state, "/skills open acme-deploy") is True
|
||||
|
||||
assert skills_state.skills is not None
|
||||
skill = skills_state.skills.find("acme-deploy")
|
||||
assert skill is not None
|
||||
assert opened == [skill.path]
|
||||
assert f"opened {skill.path}" in capsys.readouterr().out
|
||||
|
||||
|
||||
async def test_slash_skills_open_falls_back_to_the_printed_path(
|
||||
skills_state: ReplState,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
import click
|
||||
|
||||
def no_opener(path: Path, **kwargs: object) -> None:
|
||||
del path, kwargs
|
||||
msg = "xdg-open not found"
|
||||
raise click.ClickException(msg)
|
||||
|
||||
monkeypatch.setattr("plyngent.cli.editor._open_with_system_default", no_opener)
|
||||
assert await handle_slash(skills_state, "/skills open acme-deploy") is True
|
||||
|
||||
assert skills_state.skills is not None
|
||||
skill = skills_state.skills.find("acme-deploy")
|
||||
assert skill is not None
|
||||
out = capsys.readouterr().out
|
||||
assert "error: xdg-open not found" in out
|
||||
assert f"skill directory: {skill.path}" in out
|
||||
|
||||
|
||||
async def test_slash_skills_open_reports_an_unknown_skill(
|
||||
skills_state: ReplState,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
) -> None:
|
||||
assert await handle_slash(skills_state, "/skills open nope") is True
|
||||
assert "unknown skill 'nope'" in capsys.readouterr().out
|
||||
|
||||
|
||||
async def test_slash_skills_reload_picks_up_a_new_skill(
|
||||
@@ -150,7 +192,7 @@ async def test_slash_skills_reports_a_disabled_feature(tmp_path: Path, capsys: p
|
||||
|
||||
|
||||
async def test_slash_skills_requires_a_target(skills_state: ReplState, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
for command in ("/skills search", "/skills read"):
|
||||
for command in ("/skills search", "/skills open"):
|
||||
assert await handle_slash(skills_state, command) is True
|
||||
assert "requires" in capsys.readouterr().err
|
||||
|
||||
|
||||
Reference in New Issue
Block a user