Skip to content

docs: add CI/CD and Dependabot sections to both READMEs #54

docs: add CI/CD and Dependabot sections to both READMEs

docs: add CI/CD and Dependabot sections to both READMEs #54

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
shell: bash
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.12", "3.13"]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ matrix.python-version }}
- name: Run tests
run: python3 tests/run_tests.py
test-cli:
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
shell: bash
strategy:
fail-fast: false
matrix:
node-version: ["20", "22", "23"]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: ${{ matrix.node-version }}
- name: Run CLI tests
run: node --test tests/cli.test.mjs tests/tui.test.mjs tests/lock.test.mjs
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: "22"
- name: Check Python syntax
run: |
for f in scripts/sync-agents.py scripts/sync_common.py scripts/update-manifest.py scripts/sync-skills.py; do
python3 -c "import ast; ast.parse(open('$f').read())"
done
- name: Check Node.js syntax
run: node --check bin/cli.mjs src/*.mjs src/tui/*.mjs
- name: Install shellcheck
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends shellcheck
- name: Lint install.sh
run: shellcheck install.sh
- name: Validate agent YAML frontmatter
run: |
python3 -c "
import sys, os, re
errors = []
agents_dir = 'agents'
for root, dirs, files in os.walk(agents_dir):
for fname in files:
if not fname.endswith('.md'):
continue
path = os.path.join(root, fname)
with open(path, 'r', encoding='utf-8') as f:
content = f.read().strip()
if not content.startswith('---'):
errors.append(f'{path}: missing YAML frontmatter (no opening ---)')
continue
end = content.find('\n---', 3)
if end == -1:
errors.append(f'{path}: missing closing --- in frontmatter')
continue
frontmatter = content[3:end].strip()
if not frontmatter:
errors.append(f'{path}: empty frontmatter')
continue
# Check that each non-indented line looks like a valid key: value
for i, line in enumerate(frontmatter.split('\n'), 1):
if not line.strip():
continue
if line.startswith(' ') or line.startswith('\t'):
continue # indented = nested value, skip
if not re.match(r'^[\w][\w-]*\s*:', line):
errors.append(f'{path}: line {i}: invalid frontmatter key: {line!r}')
print(f' OK: {path}')
if errors:
print('\nFrontmatter validation errors:', file=sys.stderr)
for e in errors:
print(f' {e}', file=sys.stderr)
sys.exit(1)
else:
print(f'\nAll agent files have valid YAML frontmatter.')
"
- name: Validate manifest.json
run: python3 -c "import json; json.load(open('manifest.json'))" && echo "manifest.json is valid JSON"
validate-agents:
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Verify sync-agents --list
run: |
output=$(python3 scripts/sync-agents.py --list 2>&1)
echo "$output"
echo "$output" | grep -q "Listing.*agents"
echo "sync-agents --list succeeded."
- name: Verify sync-agents --list --tier=extended
run: |
output=$(python3 scripts/sync-agents.py --list --tier=extended 2>&1)
echo "$output"
count=$(echo "$output" | grep -oP 'Listing \K[0-9]+')
echo "Extended tier agent count: $count"
if [ -z "$count" ] || [ "$count" -lt 1 ]; then
echo "ERROR: Expected at least 1 agent in extended tier, got: $count"
exit 1
fi
echo "sync-agents --list --tier=extended succeeded with $count agents."
- name: "Check for deprecated tools: field"
run: |
found=0
while IFS= read -r -d '' f; do
# Extract frontmatter only (between first --- and second ---)
frontmatter=$(sed -n '1,/^---$/!d; /^---$/d; p' "$f" | head -50)
if echo "$frontmatter" | grep -qP '^tools\s*:'; then
echo "ERROR: $f contains deprecated 'tools:' field (use 'permission:' instead)"
found=1
fi
done < <(find agents -name '*.md' -print0)
if [ "$found" -eq 1 ]; then
exit 1
fi
echo "No deprecated tools: field found."
- name: "Check for 'Specifically:.' artifact"
run: |
found=0
while IFS= read -r -d '' f; do
if grep -q 'Specifically:\.' "$f"; then
echo "ERROR: $f contains the artifact 'Specifically:.'"
found=1
fi
done < <(find agents -name '*.md' -print0)
if [ "$found" -eq 1 ]; then
exit 1
fi
echo "No 'Specifically:.' artifacts found."
- name: Validate manifest.json against actual files
run: |
python3 -c "
import json, sys, os
with open('manifest.json', 'r') as f:
manifest = json.load(f)
errors = []
base = manifest.get('source_path') or manifest.get('base_path', 'agents')
for agent in manifest.get('agents', []):
path = agent.get('path', '')
md_file = os.path.join(base, path + '.md')
if not os.path.isfile(md_file):
errors.append(f'Missing file for agent \"{agent[\"name\"]}\": {md_file}')
if errors:
for e in errors:
print(f'ERROR: {e}', file=sys.stderr)
sys.exit(1)
else:
print(f'All {len(manifest[\"agents\"])} agents in manifest.json have matching files.')
"