feat(ship-it): add tea-ci-watch and background CI verification phases #12
No files matched your search
@@ -359,6 +359,7 @@ generator. A plugin can bundle any subset of:
|
||||
plugins/<name>/
|
||||
├── plugin.json # required: name, description, version, author
|
||||
├── skills/<skill>/SKILL.md # 0+ skills (YAML frontmatter: name, description, version, author)
|
||||
├── skills/<skill>/scripts/ # optional: helper scripts, shipped with the skill
|
||||
├── hooks.json # optional: lifecycle hooks, Claude-shaped event → matcher groups
|
||||
├── mcp.json # optional: {"mcpServers": {...}}
|
||||
├── rules/AGENTS.md # optional: agy-only, always-on project rules
|
||||
@@ -366,6 +367,10 @@ plugins/<name>/
|
||||
└── agents/*.md # optional: Claude Code-only subagents
|
||||
```
|
||||
|
||||
Anything else at the plugin root fails validation, since the generator would
|
||||
not ship it. Each skill directory is copied whole, so a skill reaches its
|
||||
helper scripts by a path relative to its own `SKILL.md`.
|
||||
|
||||
`hooks.json` and `mcp.json` are translated per target rather than copied
|
||||
verbatim where the two tools' schemas diverge:
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ Execute the following phases sequentially. Do not proceed to the next phase unle
|
||||
- Wait for CI on the PR head commit with ONE blocking watcher. Never poll with `sleep` or repeated status checks.
|
||||
- Run the watcher as a background command (Claude Code: Bash `run_in_background: true`); you are re-invoked when it exits. Without background support, run it in the foreground with a tool timeout longer than the watcher's.
|
||||
- Derive `<owner>/<repo>` and the platform from `git remote get-url origin`.
|
||||
- Gitea:
|
||||
- Gitea: run the bundled watcher, `scripts/tea-ci-watch.sh` in this skill's directory (the directory holding this SKILL.md). Use its absolute path; it is not on `PATH`. Requires `tea` (logged in) and `jq`.
|
||||
```bash
|
||||
tea-ci-watch "<owner>/<repo>" "$(git rev-parse HEAD)"
|
||||
bash "<skill-dir>/scripts/tea-ci-watch.sh" "<owner>/<repo>" "$(git rev-parse HEAD)"
|
||||
```
|
||||
| Exit | Meaning | Action |
|
||||
|------|---------|--------|
|
||||
|
||||
File renamed without changes.
@@ -7,7 +7,9 @@ A "plugin" is a directory under `plugins/<name>/` that may bundle any mix of:
|
||||
optional targets: ["claude-code", "agy", ...] — restricts the
|
||||
WHOLE plugin, including its hooks/mcp/rules/commands/agents, to
|
||||
only the listed targets; default is every target in manifest.yaml)
|
||||
- skills/<name>/SKILL.md (0+ skills; a skill's own frontmatter may also declare
|
||||
- skills/<name>/SKILL.md (0+ skills; the whole skill directory is shipped verbatim,
|
||||
so helper scripts belong in skills/<name>/scripts/; a
|
||||
skill's own frontmatter may also declare
|
||||
`targets:` to further restrict just that one skill, independent
|
||||
of its sibling skills — narrowed to, never wider than, the
|
||||
plugin's own `targets`)
|
||||
@@ -76,6 +78,9 @@ REQUIRED_SKILL_FIELDS = ("name", "description")
|
||||
AGY_GROUPED_EVENTS = ("PreToolUse", "PostToolUse")
|
||||
AGY_FLAT_EVENTS = ("PreInvocation", "PostInvocation", "Stop")
|
||||
|
||||
# Everything else at a plugin's root would be silently dropped from dist/, so reject it.
|
||||
PLUGIN_ENTRIES = {"plugin.json", "skills", "hooks.json", "mcp.json", "rules", "commands", "agents"}
|
||||
|
||||
|
||||
class ValidationError(Exception):
|
||||
pass
|
||||
@@ -197,6 +202,12 @@ def discover_plugins(merged_root: Path, valid_targets: list[str]) -> list[dict]:
|
||||
raise ValidationError(
|
||||
f"{manifest_path}: name '{meta.get('name')}' does not match directory name '{plugin_dir.name}'"
|
||||
)
|
||||
unknown = sorted(e.name for e in plugin_dir.iterdir() if e.name not in PLUGIN_ENTRIES)
|
||||
if unknown:
|
||||
raise ValidationError(
|
||||
f"plugins/{plugin_dir.name}: unsupported entries {unknown} would not be shipped; "
|
||||
"put helper files inside skills/<skill>/ instead"
|
||||
)
|
||||
for field in REQUIRED_PLUGIN_FIELDS:
|
||||
if not meta.get(field):
|
||||
raise ValidationError(f"{manifest_path}: missing required field '{field}'")
|
||||
|
||||
Reference in new issue
Block a user