feat(kyberforge): retarget forge skills to author/audit APM content #93

Merged
Defame1297 merged 14 commits from feat/89-apm-native-authoring into main 2026-08-12 11:48:50 +00:00
2 changed files with 31 additions and 1 deletions
Showing only changes of commit ffebdc6584 - Show all commits

View File

@@ -147,16 +147,22 @@ def find_apm_package_root(apm_yml_path):
return False
def detect_scope(start_dir):
Review

Misclassifies legacy plugin.json-only plugins. detect_scope() no longer recognizes plugin.json (only apm.yml), so existing plugin.json-only plugins are misclassified as project scope with the wrong counterpart path.

Reproduced: validate.sh plugins/kyberforge/agents/apm-orchestrate.md — a real, correctly-paired agent in this very plugin — fails with FAIL counterpart file not found: .github/agents/apm-orchestrate.agent.md, even though the real counterpart sits right next to it.

**Misclassifies legacy plugin.json-only plugins.** `detect_scope()` no longer recognizes `plugin.json` (only `apm.yml`), so existing plugin.json-only plugins are misclassified as project scope with the wrong counterpart path. Reproduced: `validate.sh plugins/kyberforge/agents/apm-orchestrate.md` — a real, correctly-paired agent in this very plugin — fails with `FAIL counterpart file not found: .github/agents/apm-orchestrate.agent.md`, even though the real counterpart sits right next to it.
home = os.path.expanduser('~')
current = os.path.abspath(start_dir)
while True:
apm_yml = os.path.join(current, 'apm.yml')
if os.path.isfile(apm_yml) and find_apm_package_root(apm_yml):
return 'plugin', current
# $HOME is the user-scope boundary — checked before the .git test
# below, so a dotfiles-managed $HOME (yadm, chezmoi bare-repo, etc.)
# can't shadow user scope by being its own .git repo.
if current == home:
return 'user', home
if os.path.isdir(os.path.join(current, '.git')):

.git-as-file (worktrees) not recognized as a boundary. The project-boundary check uses isdir()/[[ -d ]] in all four rewritten walk-up implementations, which misses git worktrees where .git is a regular file (gitdir: ...), not a directory.

In a git worktree add checkout, none of the four walk-ups (validate.sh, validate-provenance.sh, new-agent.sh, new-skill.sh) recognize .git as a project boundary, so the walk continues past the intended project root for any agent/skill work done inside a worktree.

**`.git`-as-file (worktrees) not recognized as a boundary.** The project-boundary check uses `isdir()`/`[[ -d ]]` in all four rewritten walk-up implementations, which misses git worktrees where `.git` is a regular file (`gitdir: ...`), not a directory. In a `git worktree add` checkout, none of the four walk-ups (`validate.sh`, `validate-provenance.sh`, `new-agent.sh`, `new-skill.sh`) recognize `.git` as a project boundary, so the walk continues past the intended project root for any agent/skill work done inside a worktree.
return 'project', current
parent = os.path.dirname(current)
if parent == current:

Root-fallback scope disagrees with new-agent.sh. detect_scope()'s filesystem-root fallback returns 'user' scope (pinned to real $HOME), while new-agent.sh's equivalent fallback returns 'project' scope rooted at the given path — the two scripts disagree on any directory outside $HOME with no .git/apm.yml above it.

Reproduced: new-agent.sh test-agent /tmp/scratch (outside $HOME, no .git anywhere above it) correctly creates a project-scope pair. Running validate.sh on the created file then falls back to user scope and looks for the counterpart at $HOME/.copilot/agents/test-agent.agent.md, failing with FAIL counterpart file not found even though the valid pair sits right there.

**Root-fallback scope disagrees with new-agent.sh.** `detect_scope()`'s filesystem-root fallback returns 'user' scope (pinned to real `$HOME`), while `new-agent.sh`'s equivalent fallback returns 'project' scope rooted at the given path — the two scripts disagree on any directory outside `$HOME` with no `.git`/`apm.yml` above it. Reproduced: `new-agent.sh test-agent /tmp/scratch` (outside `$HOME`, no `.git` anywhere above it) correctly creates a project-scope pair. Running `validate.sh` on the created file then falls back to user scope and looks for the counterpart at `$HOME/.copilot/agents/test-agent.agent.md`, failing with `FAIL counterpart file not found` even though the valid pair sits right there.
return 'user', os.path.expanduser('~')
return 'user', home
current = parent
agent_dir = os.path.dirname(agent_file)

View File

@@ -68,6 +68,30 @@ EOF
refute_output --partial "FAIL"
}
@test "user scope: \$HOME being a dotfiles .git repo does not shadow user scope" {
local fake_home="$TMPDIR/fakehome"
mkdir -p "$fake_home/.git" "$fake_home/.claude/agents" "$fake_home/.copilot/agents"
cat > "$fake_home/.claude/agents/my-agent.md" <<EOF
---
name: my-agent
description: A valid agent description.
---
You are a test agent. When invoked, do the thing.
EOF
cat > "$fake_home/.copilot/agents/my-agent.agent.md" <<EOF
---
name: my-agent
description: A valid agent description.
---
You are a test agent. When invoked, do the thing.
EOF
run env HOME="$fake_home" bash "$SCRIPT" "$fake_home/.claude/agents/my-agent.md"
assert_success
refute_output --partial "FAIL"
}
@test "--help exits 0 and shows Usage:" {
run bash "$SCRIPT" --help
assert_success