mirror of
https://github.com/msitarzewski/agency-agents.git
synced 2026-06-16 16:03:18 +03:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 715a80f79c | |||
| c5878a3bc2 |
@@ -0,0 +1,20 @@
|
||||
name: Check Divisions Consistency
|
||||
|
||||
# Runs on every PR (no path filter on purpose): a new division directory must
|
||||
# trip this check even when nobody touched divisions.json or the lint config.
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
check-divisions:
|
||||
name: divisions.json is the single source of truth
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Validate division set
|
||||
run: |
|
||||
chmod +x scripts/check-divisions.sh
|
||||
./scripts/check-divisions.sh
|
||||
@@ -9,6 +9,7 @@ on:
|
||||
- "finance/**"
|
||||
- "game-development/**"
|
||||
- "gis/**"
|
||||
- "integrations/**"
|
||||
- "marketing/**"
|
||||
- "paid-media/**"
|
||||
- "sales/**"
|
||||
@@ -19,6 +20,7 @@ on:
|
||||
- "support/**"
|
||||
- "spatial-computing/**"
|
||||
- "specialized/**"
|
||||
- "strategy/**"
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
@@ -33,9 +35,9 @@ jobs:
|
||||
id: changed
|
||||
run: |
|
||||
FILES=$(git diff --name-only --diff-filter=ACMR origin/${{ github.base_ref }}...HEAD -- \
|
||||
'academic/**/*.md' 'design/**/*.md' 'engineering/**/*.md' 'finance/**/*.md' 'game-development/**/*.md' 'gis/**/*.md' 'marketing/**/*.md' 'paid-media/**/*.md' 'sales/**/*.md' 'security/**/*.md' 'product/**/*.md' \
|
||||
'academic/**/*.md' 'design/**/*.md' 'engineering/**/*.md' 'finance/**/*.md' 'game-development/**/*.md' 'gis/**/*.md' 'integrations/**/*.md' 'marketing/**/*.md' 'paid-media/**/*.md' 'sales/**/*.md' 'security/**/*.md' 'product/**/*.md' \
|
||||
'project-management/**/*.md' 'testing/**/*.md' 'support/**/*.md' \
|
||||
'spatial-computing/**/*.md' 'specialized/**/*.md')
|
||||
'spatial-computing/**/*.md' 'specialized/**/*.md' 'strategy/**/*.md')
|
||||
{
|
||||
echo "files<<ENDOFLIST"
|
||||
echo "$FILES"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"_note": "Source of truth for the agent division set. Each division (a top-level agent directory) maps to a display label, a Lucide icon name (PascalCase), and a brand color (hex). Consumed by the Agency Agents app and any other catalog tooling. scripts/check-divisions.sh (CI: check-divisions.yml) fails the build if this list disagrees with the directories on disk, the AGENT_DIRS arrays in scripts/convert.sh and scripts/lint-agents.sh, or the path filters in lint-agents.yml. To add a division: create its directory, add an entry here, then run scripts/check-divisions.sh and update wherever it points.",
|
||||
"divisions": {
|
||||
"academic": { "label": "Academic", "icon": "GraduationCap", "color": "#8B5CF6" },
|
||||
"design": { "label": "Design", "icon": "PenTool", "color": "#EC4899" },
|
||||
"engineering": { "label": "Engineering", "icon": "Code", "color": "#3B82F6" },
|
||||
"finance": { "label": "Finance", "icon": "DollarSign", "color": "#22C55E" },
|
||||
"game-development": { "label": "Game Development", "icon": "Gamepad2", "color": "#A855F7" },
|
||||
"gis": { "label": "GIS", "icon": "Map", "color": "#14B8A6" },
|
||||
"integrations": { "label": "Integrations", "icon": "Workflow", "color": "#64748B" },
|
||||
"marketing": { "label": "Marketing", "icon": "Megaphone", "color": "#F97316" },
|
||||
"paid-media": { "label": "Paid Media", "icon": "Target", "color": "#EAB308" },
|
||||
"product": { "label": "Product", "icon": "Box", "color": "#D946EF" },
|
||||
"project-management": { "label": "Project Management", "icon": "ClipboardList", "color": "#0EA5E9" },
|
||||
"sales": { "label": "Sales", "icon": "TrendingUp", "color": "#10B981" },
|
||||
"security": { "label": "Security", "icon": "ShieldCheck", "color": "#EF4444" },
|
||||
"spatial-computing": { "label": "Spatial Computing", "icon": "Boxes", "color": "#06B6D4" },
|
||||
"specialized": { "label": "Specialized", "icon": "Sparkles", "color": "#6366F1" },
|
||||
"strategy": { "label": "Strategy", "icon": "Network", "color": "#F43F5E" },
|
||||
"support": { "label": "Support", "icon": "LifeBuoy", "color": "#84CC16" },
|
||||
"testing": { "label": "Testing", "icon": "FlaskConical", "color": "#F59E0B" }
|
||||
}
|
||||
}
|
||||
Executable
+113
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# check-divisions.sh — enforce a single source of truth for the division set.
|
||||
#
|
||||
# divisions.json (repo root) is canonical. This script fails if any of the
|
||||
# following disagree with it:
|
||||
# 1. The actual top-level agent directories on disk
|
||||
# 2. AGENT_DIRS in scripts/convert.sh
|
||||
# 3. AGENT_DIRS in scripts/lint-agents.sh
|
||||
# 4. The path filters in .github/workflows/lint-agents.yml
|
||||
# 5. Every divisions.json entry has label, icon, and color
|
||||
#
|
||||
# Add a division: create its directory, add an entry to divisions.json, then
|
||||
# this script tells you every other place that must be updated. No deps beyond
|
||||
# bash 3.2 + coreutils (no jq) so it runs the same on macOS and CI.
|
||||
#
|
||||
# Usage: ./scripts/check-divisions.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
JSON="divisions.json"
|
||||
|
||||
# Top-level directories that are NOT divisions. Everything else at the repo
|
||||
# root that is a directory is treated as a division (so a new division dir is
|
||||
# caught even if nobody remembered to register it).
|
||||
NON_DIVISION_DIRS=(examples scripts)
|
||||
|
||||
errors=0
|
||||
fail() { echo "ERROR $*"; errors=$((errors + 1)); }
|
||||
|
||||
# --- sorted, newline-delimited helpers -------------------------------------
|
||||
|
||||
# Canonical set: object-valued keys inside the "divisions" object. Scoping to
|
||||
# lines after the `"divisions": {` opener excludes both the wrapper key itself
|
||||
# and the string-valued "_note" key.
|
||||
canonical() {
|
||||
awk '/"divisions"[[:space:]]*:[[:space:]]*\{/{f=1; next} f' "$JSON" \
|
||||
| grep -oE '"[a-z0-9-]+"[[:space:]]*:[[:space:]]*\{' \
|
||||
| sed -E 's/"([a-z0-9-]+)".*/\1/' | sort -u
|
||||
}
|
||||
|
||||
# Actual division directories on disk (top-level dirs minus the excludes and
|
||||
# anything dot-prefixed).
|
||||
actual_dirs() {
|
||||
local d base
|
||||
for d in */; do
|
||||
base="${d%/}"
|
||||
[[ "$base" == .* ]] && continue
|
||||
case " ${NON_DIVISION_DIRS[*]} " in *" $base "*) continue ;; esac
|
||||
echo "$base"
|
||||
done | sort -u
|
||||
}
|
||||
|
||||
# Contents of a bash AGENT_DIRS=( ... ) array in the given file, one per line.
|
||||
agent_dirs_array() {
|
||||
awk '/AGENT_DIRS=\(/{f=1; next} f && /^\)/{exit} f{print}' "$1" \
|
||||
| tr ' \t' '\n\n' | grep -E '^[a-z0-9-]+$' | sort -u
|
||||
}
|
||||
|
||||
# Compare canonical vs a candidate set; report both directions.
|
||||
compare() {
|
||||
local label="$1" candidate="$2" canon
|
||||
canon="$(canonical)"
|
||||
local missing extra
|
||||
missing="$(comm -23 <(echo "$canon") <(echo "$candidate"))"
|
||||
extra="$(comm -13 <(echo "$canon") <(echo "$candidate"))"
|
||||
if [[ -n "$missing" ]]; then
|
||||
fail "$label is missing division(s) present in $JSON: $(echo "$missing" | tr '\n' ' ')"
|
||||
fi
|
||||
if [[ -n "$extra" ]]; then
|
||||
fail "$label has division(s) not in $JSON: $(echo "$extra" | tr '\n' ' ')"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- checks ----------------------------------------------------------------
|
||||
|
||||
[[ -f "$JSON" ]] || { echo "ERROR $JSON not found at repo root"; exit 1; }
|
||||
|
||||
compare "the agent directories on disk" "$(actual_dirs)"
|
||||
compare "scripts/convert.sh AGENT_DIRS" "$(agent_dirs_array scripts/convert.sh)"
|
||||
compare "scripts/lint-agents.sh AGENT_DIRS" "$(agent_dirs_array scripts/lint-agents.sh)"
|
||||
|
||||
# Workflow path filters: every canonical division must appear as `<div>/` in
|
||||
# the lint workflow, or new divisions silently skip CI.
|
||||
WF=".github/workflows/lint-agents.yml"
|
||||
if [[ -f "$WF" ]]; then
|
||||
while IFS= read -r div; do
|
||||
grep -qE "\b${div}/" "$WF" || fail "$WF has no path filter for division '$div'"
|
||||
done < <(canonical)
|
||||
else
|
||||
fail "$WF not found"
|
||||
fi
|
||||
|
||||
# Every entry must have label, icon, and color.
|
||||
while IFS= read -r div; do
|
||||
block="$(awk -v d="\"$div\"" '$0 ~ d"[[:space:]]*:[[:space:]]*\\{" {print; found=1; next} found && /\}/ {print; exit} found {print}' "$JSON")"
|
||||
for field in label icon color; do
|
||||
echo "$block" | grep -qE "\"$field\"[[:space:]]*:" \
|
||||
|| fail "division '$div' in $JSON is missing \"$field\""
|
||||
done
|
||||
done < <(canonical)
|
||||
|
||||
# --- result ----------------------------------------------------------------
|
||||
|
||||
count="$(canonical | wc -l | tr -d ' ')"
|
||||
if [[ $errors -gt 0 ]]; then
|
||||
echo ""
|
||||
echo "FAILED: $errors divisions consistency error(s). $JSON is the source of truth."
|
||||
exit 1
|
||||
fi
|
||||
echo "PASSED: $count divisions consistent across $JSON, directories, scripts, and CI."
|
||||
+1
-1
@@ -67,7 +67,7 @@ TODAY="$(date +%Y-%m-%d)"
|
||||
. "$SCRIPT_DIR/lib.sh"
|
||||
|
||||
AGENT_DIRS=(
|
||||
academic design engineering finance game-development gis marketing paid-media product project-management
|
||||
academic design engineering finance game-development gis integrations marketing paid-media product project-management
|
||||
sales security spatial-computing specialized strategy support testing
|
||||
)
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ AGENT_DIRS=(
|
||||
finance
|
||||
game-development
|
||||
gis
|
||||
integrations
|
||||
marketing
|
||||
paid-media
|
||||
product
|
||||
@@ -26,6 +27,7 @@ AGENT_DIRS=(
|
||||
security
|
||||
spatial-computing
|
||||
specialized
|
||||
strategy
|
||||
support
|
||||
testing
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user