Initial commit: Heretek Actions — agent-facing GitHub Actions

Complete implementation of all 7 phases:

Phase 1: Foundation
- Repository directory structure
- .agent/schema.json — Standard JSON envelope schema
- .github/CODEOWNERS, LICENSE (MIT), .gitignore
- README.md shell

Phase 2: Agent-Facing Actions (5 composite actions)
- agent-context — Scans repo, detects stack, queries GitHub API
- pr-summary — Analyzes PR diff, categorizes files, links issues
- issue-triage — Classifies, labels, detects duplicates
- check-status — Watches CI checks, produces merged status
- review — Lightweight structural code analysis
- Shared agent-envelope.sh helper for standardized .agent/output.json

Phase 3: Pre-commit Hooks
- .pre-commit-config.yaml — Universal hook config
- pre-commit-ci.yml — CI workflow running pre-commit

Phase 4: CI Composite Actions
- rust-ci — fmt, clippy, test, coverage (opt), audit (opt)
- js-ci — lint, typecheck, test (npm/pnpm/yarn/bun)
- python-ci — lint (ruff), typecheck (mypy), test (pytest)

Phase 5: Universal CI Orchestrator
- check.yml — Auto-detects languages, routes to CI actions

Phase 6: Universal Release
- release action — Docker, Flatpak, npm, GitHub releases
- release.yml — workflow_call reusable workflow

Phase 7: Documentation
- Full README with quick start, per-action docs, agent integration guide

Co-Authored-By: Claude <[email protected]>
This commit is contained in:
John Smith
2026-07-30 10:58:06 -04:00
co-authored by Claude
commit c3633b2b14
33 changed files with 3796 additions and 0 deletions
+154
View File
@@ -0,0 +1,154 @@
name: "Python CI"
description: |
Composite action for Python projects: setup Python, cache, install deps,
lint (via ruff), typecheck (via mypy), and test (via pytest).
Outputs .agent/output.json.
author: "Heretek-AI"
inputs:
working-directory:
description: "Working directory"
default: "."
required: false
python-version:
description: "Python version"
default: "3.12"
required: false
package-manager:
description: "Package manager: pip, pipenv, poetry, uv, or auto (default)"
default: "auto"
required: false
lint-command:
description: "Override lint command"
default: ""
required: false
test-command:
description: "Override test command"
default: ""
required: false
lint-continue-on-error:
description: "Continue if lint fails"
default: "false"
required: false
test-continue-on-error:
description: "Continue if tests fail"
default: "false"
required: false
outputs:
agent_action:
description: "Always 'ci-python'"
value: "ci-python"
status:
description: "success | failure | partial"
value: ${{ steps.envelope.outputs.status }}
summary:
description: "CI summary"
value: ${{ steps.envelope.outputs.summary }}
runs:
using: "composite"
steps:
- name: "Detect Package Manager"
id: detect
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
PM="${{ inputs.package-manager }}"
if [ "$PM" = "auto" ]; then
if grep -q "\[tool.poetry\]" pyproject.toml 2>/dev/null; then PM="poetry"
elif [ -f "Pipfile" ]; then PM="pipenv"
elif [ -f "uv.lock" ]; then PM="uv"
else PM="pip"
fi
fi
echo "pm=${PM}" >> "$GITHUB_OUTPUT"
echo "manager: ${PM}"
- name: "Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
- name: "Cache Dependencies"
uses: actions/cache@v4
with:
path: |
${{ inputs.working-directory }}/.venv
${{ inputs.working-directory }}/__pypackages__
~/.cache/pip
~/.cache/pypoetry
key: ${{ runner.os }}-py${{ inputs.python-version }}-${{ hashFiles(format('{0}/{1}', inputs.working-directory, inputs.package-manager == 'poetry' && 'poetry.lock' || inputs.package-manager == 'pipenv' && 'Pipfile.lock' || 'pyproject.toml')) }}
- name: "Install Dependencies"
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
case "${{ steps.detect.outputs.pm }}" in
poetry)
pip install poetry
poetry install
;;
pipenv)
pip install pipenv
pipenv install --dev
;;
uv)
pip install uv
uv sync
;;
*)
pip install -e ".[dev]" 2>/dev/null || pip install -r requirements.txt 2>/dev/null || pip install -r requirements-dev.txt 2>/dev/null || true
pip install ruff pytest mypy
;;
esac
- name: "Lint (ruff)"
id: lint
continue-on-error: ${{ inputs.lint-continue-on-error == 'true' }}
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
CMD="${{ inputs.lint-command }}"
if [ -z "$CMD" ]; then
CMD="ruff check ."
fi
eval "$CMD" 2>&1 | tee .lint-output.txt
echo "exit_code=$?" >> $GITHUB_OUTPUT
- name: "TypeCheck (mypy)"
id: typecheck
continue-on-error: true
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
if [ -f "mypy.ini" ] || [ -f ".mypy.ini" ] || grep -q "\[tool.mypy" pyproject.toml 2>/dev/null; then
mypy . 2>&1 | tee .typecheck-output.txt
else
echo "No mypy config — skipping typecheck"
fi
echo "exit_code=$?" >> $GITHUB_OUTPUT
- name: "Test (pytest)"
id: test
continue-on-error: ${{ inputs.test-continue-on-error == 'true' }}
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
CMD="${{ inputs.test-command }}"
if [ -z "$CMD" ]; then
CMD="python -m pytest . --junitxml=test-results.xml -v"
fi
eval "$CMD" 2>&1 | tee .test-output.txt
echo "exit_code=$?" >> $GITHUB_OUTPUT
- name: "Generate Agent Envelope"
id: envelope
shell: bash
env:
LINT_EXIT: ${{ steps.lint.outputs.exit_code }}
TC_EXIT: ${{ steps.typecheck.outputs.exit_code }}
TEST_EXIT: ${{ steps.test.outputs.exit_code }}
run: |
${{ github.action_path }}/write-envelope.sh
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
set -euo pipefail
source "${{ github.action_path }}/../agent-envelope.sh"
echo "📝 Generating Python CI envelope..."
LINT_OK=1; [ "${LINT_EXIT:-0}" = "0" ] 2>/dev/null && LINT_OK=0 || true
TC_OK=1; [ "${TC_EXIT:-0}" = "0" ] 2>/dev/null && TC_OK=0 || true
TEST_OK=1; [ "${TEST_EXIT:-0}" = "0" ] 2>/dev/null && TEST_OK=0 || true
# Parse test output for pass/fail counts
TEST_PASS=0
TEST_FAIL=0
if [ -f .test-output.txt ]; then
grep -E 'passed|failed' .test-output.txt | tail -1 | while IFS= read -r line; do
if echo "$line" | grep -qP '\d+ passed'; then
TEST_PASS=$(echo "$line" | grep -oP '\d+(?= passed)' || echo 0)
fi
if echo "$line" | grep -qP '\d+ failed'; then
TEST_FAIL=$(echo "$line" | grep -oP '\d+(?= failed)' || echo 0)
fi
echo "TEST_PASS=${TEST_PASS}" >> "$GITHUB_ENV"
echo "TEST_FAIL=${TEST_FAIL}" >> "$GITHUB_ENV"
done
fi
ERRORS=0
[ "$LINT_OK" -eq 1 ] && ERRORS=$((ERRORS+1))
[ "$TC_OK" -eq 1 ] && ERRORS=$((ERRORS+1))
[ "$TEST_OK" -eq 1 ] && ERRORS=$((ERRORS+1))
if [ "$ERRORS" -gt 0 ]; then
STATUS="failure"
else
STATUS="success"
fi
SUMMARY="python-ci: lint=$([ "$LINT_OK" -eq 0 ] && echo '✓' || echo '✗') typecheck=$([ "$TC_OK" -eq 0 ] && echo '✓' || echo '✗') test=${TEST_PASS:-?}✓/${TEST_FAIL:-0}✗"
AGENT_OUTPUTS=$(cat <<OUTPUTS
{
"package_manager": "${{ steps.detect.outputs.pm }}",
"python_version": "${{ inputs.python-version }}",
"checks": {
"lint": {"passed": $([ "$LINT_OK" -eq 0 ] && echo 'true' || echo 'false')},
"typecheck": {"passed": $([ "$TC_OK" -eq 0 ] && echo 'true' || echo 'false')},
"test": {"passed": $([ "$TEST_OK" -eq 0 ] && echo 'true' || echo 'false'), "passed_count": ${TEST_PASS:-0}, "failed_count": ${TEST_FAIL:-0}}
}
}
OUTPUTS
export AGENT_OUTPUTS
write_envelope "ci-python" "$STATUS" "$SUMMARY"
echo "status=${STATUS}" >> "$GITHUB_OUTPUT"
echo "summary=${SUMMARY}" >> "$GITHUB_OUTPUT"