Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,20 @@ parallel-cli
│ ├── cancel # Cancel a monitor (irreversible)
│ ├── events # List events for a monitor
│ └── trigger # Trigger an immediate one-off run
├── skills # Install, update, and manage Parallel agent skills
│ ├── install # Install or update managed Parallel skills
│ ├── list # List available Parallel skills
│ ├── reinstall # Cleanly reinstall managed Parallel skills
│ └── uninstall # Remove only CLI-managed Parallel skills
└── memory # Saved Task, Monitor, and FindAll entries
├── retrieve # Search Memory or list recent entries
├── evict # Remove one entry from Memory
└── clear # Permanently clear selected Memory
```

Run `parallel-cli skills install` to install or update agent skills. When using
`--skill`, other managed skills are removed; unmanaged skills are preserved.

## Quick Start

### 1. Authenticate
Expand Down
12 changes: 7 additions & 5 deletions parallel_web_tools/cli/skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ def create_skills_group(

@click.group(name="skills")
def skills() -> None:
"""Install and manage Parallel agent skills.
"""Install, update, and manage Parallel agent skills.

Run parallel-cli skills install to install or update managed skills.

Downloads come from skills.parallel.ai. Set PARALLEL_SKILLS_INDEX_URL to use a custom index.

Expand Down Expand Up @@ -93,14 +95,14 @@ def skills_list(output_json: bool) -> None:
"--skill",
"skill_names",
multiple=True,
help="Skill name to install (repeatable). Defaults to all. Skills not listed will be removed.",
help="Skill name to install (repeatable). Defaults to all. Other managed skills will be removed.",
)
@click.option("--json", "output_json", is_flag=True, help="Output as JSON")
def skills_install(project: bool, skill_names: tuple[str, ...], output_json: bool) -> None:
"""Install Parallel skills from skills.parallel.ai.
"""Install or update Parallel skills from skills.parallel.ai.

When --skill is provided, the managed install set is replaced with exactly
the listed skills.
the listed skills. Unmanaged skills are never overwritten or removed.
"""
from parallel_web_tools.core.skills import (
SkillsError,
Expand Down Expand Up @@ -178,7 +180,7 @@ def skills_uninstall(project: bool, output_json: bool) -> None:
"--skill",
"skill_names",
multiple=True,
help="Skill name to reinstall (repeatable). Defaults to all. Skills not listed will be removed.",
help="Skill name to reinstall (repeatable). Defaults to all. Other managed skills will be removed.",
)
@click.option("--json", "output_json", is_flag=True, help="Output as JSON")
def skills_reinstall(project: bool, skill_names: tuple[str, ...], output_json: bool) -> None:
Expand Down
15 changes: 12 additions & 3 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2432,18 +2432,27 @@ def test_enrich_run_wait_output_file(self, runner, tmp_path):


class TestSkillsCommands:
def test_skills_help_mentions_cdn_and_replacement_behavior(self, runner):
def test_skills_help_mentions_updates_cdn_and_managed_replacement(self, runner):
result = runner.invoke(main, ["skills", "--help"])

assert result.exit_code == 0
help_text = " ".join(result.output.split())
assert "parallel-cli skills install to install or update managed skills" in help_text
assert "skills.parallel.ai" in result.output
assert "PARALLEL_SKILLS_INDEX_URL" in result.output

result = runner.invoke(main, ["skills", "install", "--help"])

assert result.exit_code == 0
assert "Skills not" in result.output
assert "listed will be removed" in result.output
help_text = " ".join(result.output.split())
assert "Install or update Parallel skills" in help_text
assert "Unmanaged skills are never overwritten or removed" in help_text
assert "Other managed skills will be removed" in help_text

result = runner.invoke(main, ["skills", "reinstall", "--help"])

assert result.exit_code == 0
assert "Other managed skills will be removed" in " ".join(result.output.split())

def test_skills_list_json(self, runner):
with (
Expand Down
Loading