docs(410): a packaging contract, so a second client is a manifest and an adapter (#4032)
CI & Build / Python lint (push) Successful in 7s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 56s
CI & Build / Python tests (push) Successful in 1m26s
CI & Build / Build & push image (push) Successful in 16s
CI & Build / Python lint (push) Successful in 7s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 56s
CI & Build / Python tests (push) Successful in 1m26s
CI & Build / Build & push image (push) Successful in 16s
Step 5 of milestone 410 "One owner per piece of guidance". The operator wants any attempt to package Scribe for another agent client to find the repo already in the right shape. plugin/PACKAGING.md (linked from the README) states: - what every client package shares: plugin/skills/ (verbatim), the /mcp endpoint and its in-band responses, the /api/plugin/* adapter endpoints (context, retrieve, prior-art, tool-rules, processes), and one fmcp_ key - what each client adds: a manifest; hooks limited to timing and transport; optional commands; adapter static text that never copies a skill or the index - the Claude Code adapter file by file, as the worked example - how Agent Plugins 1.0 clients (Codex, Cursor, Copilot/VS Code, Kiro, ChatGPT) and Gemini CLI would map, marked researched-not-tested (#4023) - four open questions for the second package: passing the key to the MCP server, whether two manifests can share one folder, hook parity, and where process skills go Hook audit: every hook prints only server-provided text, status or outage lines, the running version, or the compaction reload pointer. No guidance copies, so nothing moved. Guard: test_the_skills_reference_nothing_outside_their_folder fails on a relative path upward or a reference to plugin/, hooks/, commands/ or a manifest from inside a skill, with a companion test showing it can fail. Plugin version minted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -251,3 +251,29 @@ def test_the_client_guard_can_fail():
|
||||
assert client_specific_hits("keep a copy in CLAUDE.md") == ["claude", "claude.md"]
|
||||
assert client_specific_hits("edits made through Bash") == ["Bash"]
|
||||
assert client_specific_hits("edits made through a bash shell") == []
|
||||
|
||||
|
||||
# A skill is shipped verbatim inside every client's package, at whatever path
|
||||
# that client installs skills to. A reference from a skill to anything outside
|
||||
# its own folder — the adapter's hooks, a manifest, a relative path upward —
|
||||
# points at a file that exists in one package layout and nowhere else
|
||||
# (plugin/PACKAGING.md).
|
||||
OUTSIDE_THE_SKILL = re.compile(r"\.\./|plugin/|hooks/|commands/|\.claude-plugin|\bplugin\.json\b|\bhooks\.json\b")
|
||||
|
||||
|
||||
def test_the_skills_reference_nothing_outside_their_folder():
|
||||
offenders = {
|
||||
str(p.relative_to(ROOT)): sorted(set(OUTSIDE_THE_SKILL.findall(p.read_text())))
|
||||
for p in sorted((ROOT / "plugin/skills").glob("*/SKILL.md"))
|
||||
}
|
||||
offenders = {path: refs for path, refs in offenders.items() if refs}
|
||||
assert not offenders, (
|
||||
f"skills that reference files outside their own folder: {offenders}. A "
|
||||
f"skill ships verbatim in every client package; see plugin/PACKAGING.md."
|
||||
)
|
||||
|
||||
|
||||
def test_the_layout_guard_can_fail():
|
||||
assert OUTSIDE_THE_SKILL.findall("run ../hooks/sync.sh") == ["../", "hooks/"]
|
||||
assert OUTSIDE_THE_SKILL.findall("see plugin.json") == ["plugin.json"]
|
||||
assert OUTSIDE_THE_SKILL.findall("record the plugin's behaviour") == []
|
||||
|
||||
Reference in New Issue
Block a user