Skip to content
Closed
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
29 changes: 29 additions & 0 deletions .git-hooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/sh
# .git-hooks/commit-msg
#
# Enforces that commit messages are non-empty and not the default editor template.
#
# Pattern encoded: agents and bots must always pass -m to `git commit`. Without -m,
# git opens $EDITOR and hangs in non-interactive contexts. This hook is the last line
# of defense: if something slips through and produces an empty or template-only message,
# the commit is rejected rather than creating a garbage commit object.
#
# Install: run .git-hooks/install.sh once after cloning.
# chmod +x .git-hooks/commit-msg (already set if you used install.sh)

COMMIT_MSG_FILE="$1"

if [ ! -f "$COMMIT_MSG_FILE" ]; then
echo "commit-msg: commit message file not found: $COMMIT_MSG_FILE" >&2
exit 1
fi

# Strip comment lines (lines starting with #) and whitespace-only lines.
MSG=$(grep -v '^#' "$COMMIT_MSG_FILE" | tr -d '[:space:]')

if [ -z "$MSG" ]; then
echo "commit-msg: commit message is empty. Use 'git commit -m \"your message\"'." >&2
exit 1
fi

exit 0
27 changes: 27 additions & 0 deletions .git-hooks/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/sh
# .git-hooks/install.sh
#
# Symlinks the tracked hooks in .git-hooks/ into .git/hooks/ so git picks them up.
# Run once after cloning: sh .git-hooks/install.sh
#
# Why symlinks: .git/hooks/ is not tracked by git. Storing hooks in .git-hooks/
# keeps them in the repo and version-controlled. The symlink is the bridge.

set -e

# Resolve repo root (one level up from this script's directory)
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
HOOKS_SRC="$REPO_ROOT/.git-hooks"
HOOKS_DST="$REPO_ROOT/.git/hooks"

if [ ! -d "$HOOKS_DST" ]; then
echo "Error: $HOOKS_DST does not exist. Are you in a git repository?" >&2
exit 1
fi

# Install commit-msg hook
ln -sf "$HOOKS_SRC/commit-msg" "$HOOKS_DST/commit-msg"
chmod +x "$HOOKS_SRC/commit-msg"
echo "Installed: .git/hooks/commit-msg -> .git-hooks/commit-msg"

echo "Done. Git hooks are active."
154 changes: 154 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
name: Checks

# Lightweight structural checks that enforce project invariants which can't
# be caught by code review alone: zero dependencies, a valid CHANGELOG format,
# and no permission declarations in agent prompt files.
# These run on every push and PR — they're fast (no install step needed).

on:
push:
pull_request:

permissions: {}

jobs:
zero-deps:
name: Zero dependencies check
runs-on: ubuntu-latest
# Pattern enforced: the only allowed runtime dependency is @opencode-ai/plugin.
# Any other entry in dependencies is a violation. devDependencies must remain empty.
steps:
- uses: actions/checkout@v4

- name: Assert dependencies contains only @opencode-ai/plugin and devDependencies is empty
run: |
node -e "
const pkg = JSON.parse(require('fs').readFileSync('package.json', 'utf8'));
const deps = pkg.dependencies ?? {};
const devDeps = pkg.devDependencies ?? {};
if (!deps['@opencode-ai/plugin']) {
console.error('Error: @opencode-ai/plugin must be present in dependencies.');
process.exit(1);
}
const extraDeps = Object.keys(deps).filter(k => k !== '@opencode-ai/plugin');
if (extraDeps.length > 0) {
console.error('Error: dependencies may only contain @opencode-ai/plugin. Found unexpected: ' + extraDeps.join(', '));
process.exit(1);
}
if (Object.keys(devDeps).length > 0) {
console.error('Error: devDependencies must be empty. Found: ' + Object.keys(devDeps).join(', '));
process.exit(1);
}
console.log('OK: dependency constraints confirmed');
"

changelog-unreleased:
name: CHANGELOG Unreleased section check
runs-on: ubuntu-latest
# Pattern enforced: CHANGELOG.md must always have a ## [Unreleased] section.
# Without it, the release process breaks — the workflow that cuts a release reads
# the Unreleased block to populate the GitHub release notes. A missing section
# also signals that someone may have committed user-visible changes without
# documenting them (see docs/guiding-principles.md — CHANGELOG entries target users).
steps:
- uses: actions/checkout@v4

- name: Assert ## [Unreleased] section exists in CHANGELOG.md
run: |
node -e "
const fs = require('fs');
const changelog = fs.readFileSync('CHANGELOG.md', 'utf8');
if (!changelog.includes('## [Unreleased]')) {
console.error('Error: CHANGELOG.md must contain a ## [Unreleased] section.');
console.error('Add one before the first versioned section.');
process.exit(1);
}
console.log('OK: ## [Unreleased] section found');
"

no-permissions-in-agent-prompts:
name: No permissions section in agent prompts
runs-on: ubuntu-latest
# Pattern enforced: permissions are declared exclusively in index.js via SUBAGENT_DEFS.
# A ## Permissions section in an agents/*.md file is purely documentary and diverges
# from the real enforcement — it creates false documentation. Any such section is
# mechanically forbidden (see docs/guiding-principles.md).
steps:
- uses: actions/checkout@v4

- name: Assert no ## Permissions section in agents/*.md
run: |
if grep -l '^## Permissions$' agents/*.md 2>/dev/null; then
echo "Error: Found '## Permissions' section in one or more agent prompt files."
echo "Permissions must be declared exclusively in index.js via SUBAGENT_DEFS."
echo "Remove the ## Permissions section from the files listed above."
exit 1
fi
echo "OK: no ## Permissions section found in agents/*.md"

# Pattern enforced: product briefs written by the brainstorm agent must be structurally valid
# so downstream agents (Planning, Orion) can parse them reliably. A brief missing required
# sections or frontmatter is not machine-actionable. This check catches structural regressions
# without validating content quality.
brief-schema:
name: Product brief schema check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Assert all docs/briefs/*.md files are structurally valid
run: |
shopt -s nullglob
brief_files=(docs/briefs/*.md)
if [ ${#brief_files[@]} -eq 0 ]; then
echo "OK: no briefs found — nothing to check"
exit 0
fi

failed=0

for file in "${brief_files[@]}"; do
file_failed=0

# Check frontmatter block (file must start with ---)
if ! head -1 "$file" | tr -d '\r' | grep -q '^---$'; then
echo "FAIL [$file]: missing YAML frontmatter (file must start with ---)"
file_failed=1
else
# Check required frontmatter fields
for field in project: type: status: created: updated:; do
if ! awk '/^---$/{f++; if(f==2) exit; next} f==1{print}' "$file" | grep -qE "^${field}($|[[:space:]])"; then
echo "FAIL [$file]: missing frontmatter field '${field}'"
file_failed=1
fi
done

# Check for duplicate frontmatter keys
dupes=$(awk '/^---$/{f++; if(f==2) exit; next} f==1 && /^[^#[:space:]]/{print $1}' "$file" | sort | uniq -d)
if [ -n "$dupes" ]; then
echo "FAIL [$file]: duplicate frontmatter key(s): $dupes"
file_failed=1
fi
fi

# Check required section headings
for section in "## Problem" "## Vision" "## Users" "## Core Use Cases" "## Success Criteria" "## Scope"; do
if ! grep -q "^${section}$" "$file"; then
echo "FAIL [$file]: missing section '${section}'"
file_failed=1
fi
done

if [ "$file_failed" -eq 0 ]; then
echo "OK: $file"
else
failed=1
fi
done

if [ "$failed" -ne 0 ]; then
echo ""
echo "Error: one or more brief files failed the schema check (see above)."
exit 1
fi

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
node_modules/
.opencode/
!.opencode/agents/
!.opencode/tools/
40 changes: 40 additions & 0 deletions .opencode/agents/doc-inspector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name: doc-inspector
description: >
Visualise et inspecte le site de documentation team-lead-workflow en local.
Démarre/arrête le serveur Vite, prend des screenshots headless via Playwright,
navigue les pages et extrait le contenu HTML. À invoquer pour vérifier
visuellement la doc, détecter les problèmes de layout ou de contenu,
et reporter l'état réel du site rendu dans le browser.
---

You are **Doc Inspector**, a specialized agent for the `team-lead-workflow` documentation website.

## Your tools

- **`doc_dev_status`** — Check if the dev server is running and reachable. Always start here.
- **`doc_dev_start`** — Start the Vite dev server (default port 5173). Waits up to 15s for readiness.
- **`doc_dev_stop`** — Stop the running server. Call this when done unless instructed otherwise.
- **`doc_dev_logs`** — Read server logs. Use when startup fails or to debug issues.
- **`doc_screenshot`** — Take a headless screenshot of a URL. Saves to `.opencode/screenshots/`. Returns the file path.
- **`doc_navigate`** — Navigate to a URL and extract title, text content (up to 5000 chars), and links.
- **`doc_get_html`** — Get the outerHTML of a CSS selector on a page (up to 10000 chars).

## Standard workflow

1. `doc_dev_status` — check if already running
2. If not running → `doc_dev_start`
3. If startup fails → `doc_dev_logs` to diagnose
4. Navigate or screenshot the relevant pages
5. Report findings
6. `doc_dev_stop` when done (unless instructed to leave it running)

## Reporting

Always include in your response:
- Server status (was it already running, did you start it, any issues)
- For screenshots: the file path returned by the tool
- What you observed: page title, visible content, layout structure, any anomalies
- Specific findings relevant to the task (outdated content, broken layout, missing sections, etc.)

Be precise. If something looks wrong, say exactly what and where.
Loading
Loading