159 lines
5.5 KiB
Bash
Executable File
159 lines
5.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
REQUIRED_FILES=(
|
|
"docs/PI-RESEARCH.md"
|
|
"docs/PI.md"
|
|
"docs/PI-SUPERPOWERS.md"
|
|
"docs/PI-COMMON-REVIEWER.md"
|
|
"skills/atlassian/pi/SKILL.md"
|
|
"skills/create-plan/pi/SKILL.md"
|
|
"skills/create-plan/pi/templates/continuation-runbook.md"
|
|
"skills/create-plan/pi/templates/milestone-plan.md"
|
|
"skills/create-plan/pi/templates/story-tracker.md"
|
|
"skills/do-task/pi/SKILL.md"
|
|
"skills/do-task/pi/templates/task-plan.md"
|
|
"skills/implement-plan/pi/SKILL.md"
|
|
"skills/web-automation/pi/SKILL.md"
|
|
"skills/reviewer-runtime/pi/run-review.sh"
|
|
"skills/reviewer-runtime/pi/notify-telegram.sh"
|
|
"scripts/install-pi-package.sh"
|
|
"scripts/sync-pi-package-skills.sh"
|
|
"pi-package/skills/atlassian/SKILL.md"
|
|
"pi-package/skills/create-plan/SKILL.md"
|
|
"pi-package/skills/do-task/SKILL.md"
|
|
"pi-package/skills/implement-plan/SKILL.md"
|
|
"pi-package/skills/web-automation/SKILL.md"
|
|
"package.json"
|
|
)
|
|
|
|
# These required-file checks are intentionally hard failures: removing any
|
|
# required artifact should make this script exit non-zero immediately.
|
|
for file in "${REQUIRED_FILES[@]}"; do
|
|
test -f "$file"
|
|
done
|
|
|
|
test -x skills/reviewer-runtime/pi/run-review.sh
|
|
test -x skills/reviewer-runtime/pi/notify-telegram.sh
|
|
test -x scripts/install-pi-package.sh
|
|
test -x scripts/sync-pi-package-skills.sh
|
|
find skills/web-automation/pi/scripts -type f -print -quit | grep -q .
|
|
find skills/atlassian/pi/scripts -type f -print -quit | grep -q .
|
|
|
|
for file in skills/create-plan/pi/SKILL.md skills/do-task/pi/SKILL.md skills/implement-plan/pi/SKILL.md; do
|
|
grep -q 'docs/PI-SUPERPOWERS.md' "$file"
|
|
grep -q 'docs/PI-COMMON-REVIEWER.md' "$file"
|
|
grep -q 'Reviewer CLI: `codex`, `claude`, `cursor`, `opencode`, `pi`, or `skip`' "$file"
|
|
grep -q 'pi --no-session --no-skills --no-prompt-templates --no-extensions --no-context-files' "$file"
|
|
grep -q -- '--tools read,grep,find,ls -p' "$file"
|
|
grep -q 'pi --list-models \[search\]' "$file"
|
|
done
|
|
|
|
grep -q 'reviewer model is configured independently' docs/PI-COMMON-REVIEWER.md
|
|
grep -q 'provider-qualified model IDs' docs/PI-COMMON-REVIEWER.md
|
|
grep -q 'MUST NOT include `write`, `edit`, or `bash`' docs/PI-COMMON-REVIEWER.md
|
|
grep -q 'Reviewer CLI | codex \\| claude \\| cursor \\| opencode \\| pi' skills/do-task/pi/templates/task-plan.md
|
|
|
|
grep -q 'pi-package/skills/atlassian/scripts' skills/atlassian/pi/SKILL.md
|
|
grep -q 'pi-package/skills/web-automation/scripts' skills/web-automation/pi/SKILL.md
|
|
grep -q 'local checkout package install keeps the runtime in `pi-package/skills/<skill>/scripts`' docs/PI.md
|
|
|
|
grep -q 'install-pi-package.sh --global' docs/PI.md
|
|
grep -q 'install-pi-package.sh --local' docs/PI.md
|
|
grep -q 'install-pi-package.sh --global' README.md
|
|
grep -q 'install-pi-package.sh --local' README.md
|
|
|
|
for family in atlassian create-plan do-task implement-plan web-automation; do
|
|
source_dir="skills/${family}/pi"
|
|
source_skill_md="${source_dir}/SKILL.md"
|
|
skill_name=$(awk '/^name:/ { print $2; exit }' "$source_skill_md")
|
|
mirror_dir="pi-package/skills/${skill_name}"
|
|
mirror_skill_md="${mirror_dir}/SKILL.md"
|
|
|
|
test -d "$mirror_dir"
|
|
test -f "$mirror_skill_md"
|
|
test "$skill_name" = "$(basename "$mirror_dir")"
|
|
test "$skill_name" = "$(awk '/^name:/ { print $2; exit }' "$mirror_skill_md")"
|
|
|
|
diff -qr \
|
|
--exclude '.DS_Store' \
|
|
--exclude 'node_modules' \
|
|
"$source_dir" "$mirror_dir" >/dev/null
|
|
|
|
while IFS= read -r -d '' source_path; do
|
|
rel_path=${source_path#"$source_dir"/}
|
|
mirror_path="${mirror_dir}/${rel_path}"
|
|
test -e "$mirror_path"
|
|
test "$(stat -f '%Lp' "$source_path")" = "$(stat -f '%Lp' "$mirror_path")"
|
|
done < <(find "$source_dir" \
|
|
\( -name '.DS_Store' -o -name 'node_modules' \) -prune -o \
|
|
-mindepth 1 -print0)
|
|
done
|
|
|
|
! grep -nE 'update_plan|plan mode|sub-agent|subagents' \
|
|
skills/create-plan/pi/SKILL.md \
|
|
skills/do-task/pi/SKILL.md \
|
|
skills/implement-plan/pi/SKILL.md
|
|
|
|
node <<'EOF'
|
|
const fs = require("fs");
|
|
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
|
|
|
|
if (!pkg.pi || !Array.isArray(pkg.pi.skills) || pkg.pi.skills.length !== 5) {
|
|
console.error("package.json must define pi.skills with exactly 5 entries");
|
|
process.exit(1);
|
|
}
|
|
|
|
const expectedSkills = [
|
|
"./pi-package/skills/atlassian",
|
|
"./pi-package/skills/create-plan",
|
|
"./pi-package/skills/do-task",
|
|
"./pi-package/skills/implement-plan",
|
|
"./pi-package/skills/web-automation",
|
|
];
|
|
|
|
if (JSON.stringify(pkg.pi.skills) !== JSON.stringify(expectedSkills)) {
|
|
console.error("package.json must point pi.skills at the pi-package mirror");
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!Array.isArray(pkg.files) || pkg.files.length === 0) {
|
|
console.error("package.json must define a non-empty files allowlist");
|
|
process.exit(1);
|
|
}
|
|
|
|
for (const requiredFile of [
|
|
"pi-package/skills",
|
|
"docs/PI-COMMON-REVIEWER.md",
|
|
"scripts/install-pi-package.sh",
|
|
"scripts/sync-pi-package-skills.sh",
|
|
]) {
|
|
if (!pkg.files.includes(requiredFile)) {
|
|
console.error(`package.json files must include ${requiredFile}`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
for (const forbiddenFile of [
|
|
"skills/atlassian/pi",
|
|
"skills/create-plan/pi",
|
|
"skills/do-task/pi",
|
|
"skills/implement-plan/pi",
|
|
"skills/web-automation/pi",
|
|
]) {
|
|
if (pkg.files.includes(forbiddenFile)) {
|
|
console.error(`package.json files must not include ${forbiddenFile}`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
if (!Array.isArray(pkg.keywords) || !pkg.keywords.includes("pi-package")) {
|
|
console.error("package.json must include the pi-package keyword");
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log("package metadata ok");
|
|
EOF
|
|
|
|
echo "pi resources verified"
|