Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

Follow `AGENTS.md` as the repository-wide engineering contract.

Keep fstack portable, dependency-light, and simple. Preserve the distinction between interactive skills and the continuous `fstack-run` cloud workflow. Inspect source truth before editing, make the smallest complete change, run `sh scripts/validate.sh`, inspect the final diff, and deliver changes on a task branch through a pull request.
Keep fstack portable, dependency-light, and simple. Preserve the distinction between interactive skills and the continuous `fstack-run` cloud workflow. Inspect source truth before editing, make the smallest complete change, run `sh scripts/test-validate.sh` and `sh scripts/validate.sh`, inspect the final diff, and deliver changes on a task branch through a pull request.

Never overwrite unrelated work, commit secrets, force-push, or claim unobserved test results.
2 changes: 2 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Observed results:
## Skill and documentation checks

- [ ] `sh -n scripts/validate.sh`
- [ ] `sh -n scripts/test-validate.sh`
- [ ] `sh scripts/test-validate.sh`
- [ ] `sh scripts/validate.sh`
- [ ] `git diff --check` against the base branch
- [ ] New or changed skills are documented in `README.md`
Expand Down
21 changes: 17 additions & 4 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,27 @@ jobs:
with:
fetch-depth: 0

- name: Check validator syntax
run: sh -n scripts/validate.sh
- name: Check shell syntax
run: |
sh -n scripts/validate.sh
sh -n scripts/test-validate.sh

- name: Test validator
run: sh scripts/test-validate.sh

- name: Validate skill collection
run: sh scripts/validate.sh

- name: Check changed text for whitespace errors
shell: sh
env:
BEFORE_SHA: ${{ github.event.before }}
run: |
base=${GITHUB_BASE_REF:-main}
git diff --check "origin/$base...HEAD"
if [ -n "${GITHUB_BASE_REF:-}" ]; then
git diff --check "origin/$GITHUB_BASE_REF...HEAD"
elif [ -n "${BEFORE_SHA:-}" ] && \
[ "$BEFORE_SHA" != '0000000000000000000000000000000000000000' ]; then
git diff --check "$BEFORE_SHA...HEAD"
else
git show --check --format= HEAD

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check the whole branch on its first push

When a new feat/** or fix/** branch is pushed, GitHub sets before to the all-zero SHA and leaves GITHUB_BASE_REF empty, so this fallback runs. The git show [<options>] <object>... command receives only HEAD, meaning whitespace errors in earlier commits from the same initial push are skipped; for example, a two-commit branch whose first commit adds trailing whitespace passes when its tip is clean. Compare the new branch with its base branch instead so the repository's required git diff --check gate covers the complete change.

AGENTS.md reference: AGENTS.md:L41-L49

Useful? React with 👍 / 👎.

fi
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ Run all applicable checks. For this repository, the minimum gate is:

```sh
sh -n scripts/validate.sh
sh -n scripts/test-validate.sh
sh scripts/test-validate.sh
sh scripts/validate.sh
git diff --check
```
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,15 @@ This fork includes:
- `.github/workflows/validate.yml` — automatic skill validation;
- `.github/pull_request_template.md` — evidence-focused delivery checklist;
- `scripts/validate.sh` — dependency-free frontmatter, naming, size, duplication, and README checks;
- `scripts/test-validate.sh` — regression tests for validator behavior and path safety;
- `docs/CLOUD_AGENTS.md` — complete operator guide.

Validate locally with:

```sh
sh -n scripts/validate.sh
sh -n scripts/test-validate.sh
sh scripts/test-validate.sh
sh scripts/validate.sh
git diff --check
```
Expand Down
2 changes: 2 additions & 0 deletions docs/CLOUD_AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ For this skills repository, run:

```sh
sh -n scripts/validate.sh
sh -n scripts/test-validate.sh
sh scripts/test-validate.sh
sh scripts/validate.sh
git diff --check
```
Expand Down
70 changes: 70 additions & 0 deletions scripts/test-validate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/sh

set -eu

root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
tmp_root="${TMPDIR:-/tmp}/fstack-validator-tests.$$"
fixture="$tmp_root/fstack fixture"

cleanup() {
rm -rf "$tmp_root"
}

trap cleanup EXIT HUP INT TERM
umask 077
mkdir -p "$fixture/scripts" "$fixture/skills/fstack" "$fixture/skills/fstack-run"
cp "$root/scripts/validate.sh" "$fixture/scripts/validate.sh"

write_valid_fixture() {
cat > "$fixture/README.md" <<'MARKDOWN'
`/fstack`
`/fstack-run`
MARKDOWN

cat > "$fixture/skills/fstack/SKILL.md" <<'MARKDOWN'
---
name: fstack
description: Front door.
---
MARKDOWN

cat > "$fixture/skills/fstack-run/SKILL.md" <<'MARKDOWN'
---
name: fstack-run
description: Continuous runner.
---
MARKDOWN
}

expect_failure() {
expected=$1
if sh "$fixture/scripts/validate.sh" > "$tmp_root/stdout" 2> "$tmp_root/stderr"; then
printf 'ERROR: validator unexpectedly passed: %s\n' "$expected" >&2
exit 1
fi
if ! grep -Fq "$expected" "$tmp_root/stderr"; then
printf 'ERROR: validator failed without expected message: %s\n' "$expected" >&2
cat "$tmp_root/stderr" >&2
exit 1
fi
}

write_valid_fixture
output=$(sh "$fixture/scripts/validate.sh")
if [ "$output" != 'Validated 2 skills.' ]; then
printf 'ERROR: unexpected validator output: %s\n' "$output" >&2
exit 1
fi

sed 's/name: fstack-run/name: wrong-name/' \
"$fixture/skills/fstack-run/SKILL.md" > "$tmp_root/invalid-skill"
mv "$tmp_root/invalid-skill" "$fixture/skills/fstack-run/SKILL.md"
expect_failure 'declares name wrong-name but directory is fstack-run.'

write_valid_fixture
cat > "$fixture/README.md" <<'MARKDOWN'
`/fstack`
MARKDOWN
expect_failure 'README.md does not document /fstack-run.'

printf '%s\n' 'Validator tests passed.'
22 changes: 14 additions & 8 deletions scripts/validate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@ set -eu

root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
names_file="${TMPDIR:-/tmp}/fstack-skill-names.$$"
trap 'rm -f "$names_file"' EXIT HUP INT TERM
: > "$names_file"
files_file="${TMPDIR:-/tmp}/fstack-skill-files.$$"

failures=0
count=0
files=$(find "$root/skills" -type f -name SKILL.md -print | LC_ALL=C sort)
cleanup() {
rm -f "$names_file" "$files_file"
}

trap cleanup EXIT HUP INT TERM
: > "$names_file"
find "$root/skills" -type f -name SKILL.md -print | LC_ALL=C sort > "$files_file"

if [ -z "$files" ]; then
if [ ! -s "$files_file" ]; then
printf '%s\n' 'ERROR: no skills/*/SKILL.md files found.' >&2
exit 1
fi

for file in $files; do
failures=0
count=0

while IFS= read -r file; do
count=$((count + 1))
relative=${file#"$root/"}
directory=$(basename "$(dirname "$file")")
Expand Down Expand Up @@ -87,7 +93,7 @@ for file in $files; do
failures=$((failures + 1))
fi
fi
done
done < "$files_file"

if [ "$failures" -ne 0 ]; then
printf 'Validation failed: %s problem(s) across %s skill(s).\n' "$failures" "$count" >&2
Expand Down