Restructure as a self-contained Agent Skills package (fix Codex install)
Move the skill into skills/watch/ so SKILL.md and its scripts/ runtime are
siblings inside one folder. `npx skills add` (Codex/Cursor/Copilot/agents) now
copies a working skill as a unit; previously it grabbed the root SKILL.md alone
and left scripts/ behind, so the skill was dead on arrival on every non-Claude
host. Mirrors the layout last30days-skill adopted for the same reason.
- skills/watch/{SKILL.md,scripts/}: self-contained skill folder
- SKILL.md: resolve a harness-agnostic $SKILL_DIR (the dir it was Read from)
instead of the Claude-Code-only ${CLAUDE_SKILL_DIR}; guard + 19 call sites
- drop commands/watch.md: /watch derives from frontmatter (name + user-invocable)
- .codex-plugin/plugin.json: full manifest with "skills": "./skills/" + interface
- add .agents/plugins/marketplace.json, AGENTS.md, CLAUDE.md, .skillignore
- build-skill.sh: archive the skills/watch subtree (one SKILL.md, no zip -d)
- fix paths in tests, hooks hint, .gitattributes, release.yml
- relocate dev-sync.sh to repo root and fix REPO_ROOT
- README: content-ideas structure, npx skills install, star history
Verified: 37/37 tests pass; npx skills add bundles the full scripts/ runtime;
manifests valid; versions synced at 0.1.3.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
"""setup.py --json surfaces the resolved watch detail."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SETUP = Path(__file__).resolve().parent.parent / "skills" / "watch" / "scripts" / "setup.py"
|
||||
|
||||
|
||||
def _run(args, *, home=None, extra_env=None):
|
||||
env = dict(os.environ)
|
||||
env.pop("WATCH_DETAIL", None)
|
||||
# Don't let a real key in the developer's shell env leak into the test.
|
||||
env.pop("GROQ_API_KEY", None)
|
||||
env.pop("OPENAI_API_KEY", None)
|
||||
env.pop("SETUP_COMPLETE", None)
|
||||
if home is not None:
|
||||
env["HOME"] = str(home)
|
||||
env["USERPROFILE"] = str(home) # Windows
|
||||
if extra_env:
|
||||
env.update(extra_env)
|
||||
return subprocess.run(
|
||||
[sys.executable, str(SETUP), *args],
|
||||
capture_output=True, text=True, env=env,
|
||||
)
|
||||
|
||||
|
||||
def _write_env(home: Path, body: str) -> None:
|
||||
cfg = home / ".config" / "watch"
|
||||
cfg.mkdir(parents=True, exist_ok=True)
|
||||
f = cfg / ".env"
|
||||
f.write_text(body, encoding="utf-8")
|
||||
f.chmod(0o600)
|
||||
|
||||
|
||||
def test_json_reports_watch_detail():
|
||||
proc = _run(["--json"])
|
||||
assert proc.returncode == 0, proc.stderr
|
||||
data = json.loads(proc.stdout)
|
||||
assert data["watch_detail"] == "balanced"
|
||||
|
||||
|
||||
def test_keyless_completed_setup_proceeds_silently(tmp_path):
|
||||
"""A user who finished setup without a key must NOT be nagged forever."""
|
||||
_write_env(tmp_path, "GROQ_API_KEY=\nOPENAI_API_KEY=\nSETUP_COMPLETE=true\n")
|
||||
chk = _run(["--check"], home=tmp_path)
|
||||
assert chk.returncode == 0, f"keyless-complete should pass --check; got {chk.returncode}: {chk.stderr}"
|
||||
assert chk.stdout == "" and chk.stderr == ""
|
||||
|
||||
js = json.loads(_run(["--json"], home=tmp_path).stdout)
|
||||
assert js["can_proceed"] is True
|
||||
assert js["first_run"] is False
|
||||
assert js["setup_complete"] is True
|
||||
# status still encourages a key even though we can proceed
|
||||
assert js["status"] == "needs_key"
|
||||
|
||||
|
||||
def test_keyless_first_run_is_encouraged(tmp_path):
|
||||
"""Genuine first run with no key: --check reports exit 3 (encourage a key)."""
|
||||
_write_env(tmp_path, "GROQ_API_KEY=\nOPENAI_API_KEY=\n")
|
||||
chk = _run(["--check"], home=tmp_path)
|
||||
assert chk.returncode == 3, chk.stderr
|
||||
|
||||
js = json.loads(_run(["--json"], home=tmp_path).stdout)
|
||||
assert js["can_proceed"] is False
|
||||
assert js["first_run"] is True
|
||||
|
||||
|
||||
def test_key_present_is_ready(tmp_path):
|
||||
_write_env(tmp_path, "GROQ_API_KEY=sk-test-abc\n")
|
||||
chk = _run(["--check"], home=tmp_path)
|
||||
assert chk.returncode == 0, chk.stderr
|
||||
|
||||
js = json.loads(_run(["--json"], home=tmp_path).stdout)
|
||||
assert js["status"] == "ready"
|
||||
assert js["can_proceed"] is True
|
||||
assert js["whisper_backend"] == "groq"
|
||||
Reference in New Issue
Block a user