Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
224 changes: 141 additions & 83 deletions .github/scripts/generate-architecture.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
#!/usr/bin/env bash
# generate-architecture.sh — Query GitNexus knowledge graph and generate ARCHITECTURE.md
# generate-architecture.sh — Update data-driven sections in ARCHITECTURE.md
# Uses marker-based partial updates to preserve hand-curated content.
# Usage: bash .github/scripts/generate-architecture.sh
# Requires: npx (with gitnexus), jq, git
# Output: Writes ARCHITECTURE.md to the current directory
# Requires: npx (with gitnexus), jq, awk
# Output: Updates content between <!-- auto:* --> markers in ARCHITECTURE.md
set -euo pipefail

ARCH_FILE="ARCHITECTURE.md"
REPO_NAME="${GITNEXUS_REPO:-}"

# ── Dependency validation ─────────────────────────────────────────────
check_deps() {
local missing=()
for cmd in jq npx; do
for cmd in jq npx awk; do
if ! command -v "$cmd" &>/dev/null; then
missing+=("$cmd")
fi
Expand All @@ -21,110 +25,164 @@ check_deps() {

check_deps

# ── Query GitNexus for architectural data ─────────────────────────────
query_gitnexus() {
local query="$1"
local result
if ! result=$(npx gitnexus query "$query" --limit 10 2>&1); then
echo "::warning::GitNexus query failed for '$query': $result" >&2
echo '{"processes":[]}'
# ── Resolve repo name ────────────────────────────────────────────────
resolve_repo() {
if [[ -n "$REPO_NAME" ]]; then
echo "$REPO_NAME"
return
fi
# Validate we got valid JSON
if ! echo "$result" | jq -e '.processes' >/dev/null 2>&1; then
echo "::warning::GitNexus returned invalid JSON for '$query'" >&2
echo '{"processes":[]}'
return
# Check how many repos are indexed; parse "Indexed Repositories (N)" header
local repo_list
repo_list=$(npx gitnexus list 2>&1 || true)
local count
count=$(echo "$repo_list" | grep -oP 'Indexed Repositories \(\K[0-9]+' 2>/dev/null || echo "1")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Using grep -P may break on environments where grep lacks PCRE support (e.g., macOS default grep).

Because -P isn’t supported by BSD grep, this will return an empty match on those systems and cause count to default to 1, potentially skipping --repo even when multiple repos are indexed. Please use a more portable parsing approach (e.g., sed/awk, grep -E with an adjusted pattern, or have gitnexus list output a machine-readable format consumable by jq).

if [[ "$count" -le 1 ]]; then
# Single repo — no --repo flag needed
echo ""
else
# Multiple repos — use current directory name as repo identifier
basename "$(pwd)"
fi
echo "$result"
}

echo "Querying GitNexus knowledge graph..." >&2
REPO=$(resolve_repo)
REPO_FLAG=""
if [[ -n "$REPO" ]]; then
REPO_FLAG="--repo $REPO"
fi

ENTRY_POINTS=$(query_gitnexus "entry points main CLI")
CORE_MODULES=$(query_gitnexus "core modules services")
DATA_FLOW=$(query_gitnexus "data flow pipeline")
AGENTS=$(query_gitnexus "agent orchestration")
# ── Validate ARCHITECTURE.md exists with markers ─────────────────────
if [[ ! -f "$ARCH_FILE" ]]; then
echo "::error::$ARCH_FILE not found. Cannot perform marker-based update." >&2
exit 1
fi

# ── Extract process summaries ─────────────────────────────────────────
extract_summaries() {
echo "$1" | jq -r '[.processes[]? | .summary] | join("\n")' 2>/dev/null || echo "none"
}
if ! grep -q '<!-- auto:overview -->' "$ARCH_FILE"; then
echo "::error::$ARCH_FILE missing <!-- auto:overview --> marker. Add markers before running." >&2
exit 1
fi

ENTRY_SUMMARY=$(extract_summaries "$ENTRY_POINTS")
CORE_SUMMARY=$(extract_summaries "$CORE_MODULES")
DATA_SUMMARY=$(extract_summaries "$DATA_FLOW")
AGENT_SUMMARY=$(extract_summaries "$AGENTS")

# ── Count files by extension ──────────────────────────────────────────
PY_COUNT=$(find . -name '*.py' -not -path './.git/*' -not -path './.gitnexus/*' | wc -l)
RS_COUNT=$(find . -name '*.rs' -not -path './.git/*' -not -path './.gitnexus/*' | wc -l)
YML_COUNT=$(find . -name '*.yml' -path './.github/*' | wc -l)
MD_COUNT=$(find . -name '*.md' -not -path './.git/*' -not -path './.gitnexus/*' | wc -l)

# ── Helper: render section ────────────────────────────────────────────
render_section() {
local title="$1"
local summary="$2"
local fallback="$3"

echo "## $title"
echo ""
if [[ -n "$summary" && "$summary" != "none" ]]; then
echo "$summary" | while IFS= read -r line; do
[[ -n "$line" ]] && echo "- $line"
done
else
echo "- $fallback"
if ! grep -q '<!-- /auto:overview -->' "$ARCH_FILE"; then
echo "::error::$ARCH_FILE missing <!-- /auto:overview --> closing marker." >&2
exit 1
fi

# ── Query GitNexus for graph stats ───────────────────────────────────
cypher_query() {
local query="$1"
local result
# shellcheck disable=SC2086
if ! result=$(npx gitnexus cypher $REPO_FLAG "$query" 2>&1); then
echo "::warning::Cypher query failed: $result" >&2
echo ""
return
fi
echo ""
echo "$result"
}

# ── Generate ARCHITECTURE.md ──────────────────────────────────────────
GENERATED_DATE=$(date -u '+%Y-%m-%d %H:%M UTC')

cat > ARCHITECTURE.md <<EOF
# SuperClaude Architecture
echo "Querying GitNexus knowledge graph..." >&2

> Auto-generated by GitNexus knowledge graph analysis.
> Last updated: ${GENERATED_DATE}
SYMBOL_COUNT=$(cypher_query 'MATCH (n) RETURN count(n) as c' | jq -r '.markdown' | tail -1 | tr -d '| ' || echo "0")
EDGE_COUNT=$(cypher_query 'MATCH ()-[r]->() RETURN count(r) as c' | jq -r '.markdown' | tail -1 | tr -d '| ' || echo "0")
PROCESS_COUNT=$(cypher_query 'MATCH (p:Process) RETURN count(p) as c' | jq -r '.markdown' | tail -1 | tr -d '| ' || echo "0")
Comment on lines +71 to +87

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

parsed=$(
  if ! out=$(npx gitnexus cypher 'THIS IS NOT CYPHER' 2>&1); then
    printf '%s' ""
  else
    printf '%s' "$out"
  fi | jq -r '.markdown' | tail -1 | tr -d '| ' || echo "0"
)

printf 'Parsed count after a forced cypher failure: %s\n' "$parsed"
test "$parsed" = "0"

Repository: Tony363/SuperClaude

Length of output: 149


🏁 Script executed:

cat -n .github/scripts/generate-architecture.sh | head -100

Repository: Tony363/SuperClaude

Length of output: 3875


🏁 Script executed:

sed -n '88,150p' .github/scripts/generate-architecture.sh

Repository: Tony363/SuperClaude

Length of output: 1964


🏁 Script executed:

sed -n '150,200p' .github/scripts/generate-architecture.sh

Repository: Tony363/SuperClaude

Length of output: 1262


Fail the workflow when GitNexus stats cannot be parsed.

If gitnexus cypher fails or its output shape changes, cypher_query() returns an empty string and lines 85-87 coerce the parse failure to 0. The script then keeps going and publishes a plausible-looking ARCHITECTURE.md with bogus counts (all metrics become "0") instead of failing fast. Downstream validation only checks file structure (line count, headings, sections), not metric correctness.

🛠️ Suggested fix
+extract_count() {
+  jq -er '.markdown' | tail -1 | tr -d '| ' | grep -E '^[0-9]+$'
+}
+
 cypher_query() {
   local query="$1"
   local result
   # shellcheck disable=SC2086
-  if ! result=$(npx gitnexus cypher $REPO_FLAG "$query" 2>&1); then
-    echo "::warning::Cypher query failed: $result" >&2
-    echo ""
-    return
-  fi
+  result=$(npx gitnexus cypher $REPO_FLAG "$query" 2>&1) || {
+    echo "::error::Cypher query failed: $result" >&2
+    exit 1
+  }
   echo "$result"
 }
 
-SYMBOL_COUNT=$(cypher_query 'MATCH (n) RETURN count(n) as c' | jq -r '.markdown' | tail -1 | tr -d '| ' || echo "0")
-EDGE_COUNT=$(cypher_query 'MATCH ()-[r]->() RETURN count(r) as c' | jq -r '.markdown' | tail -1 | tr -d '| ' || echo "0")
-PROCESS_COUNT=$(cypher_query 'MATCH (p:Process) RETURN count(p) as c' | jq -r '.markdown' | tail -1 | tr -d '| ' || echo "0")
+SYMBOL_COUNT=$(cypher_query 'MATCH (n) RETURN count(n) as c' | extract_count)
+EDGE_COUNT=$(cypher_query 'MATCH ()-[r]->() RETURN count(r) as c' | extract_count)
+PROCESS_COUNT=$(cypher_query 'MATCH (p:Process) RETURN count(p) as c' | extract_count)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cypher_query() {
local query="$1"
local result
# shellcheck disable=SC2086
if ! result=$(npx gitnexus cypher $REPO_FLAG "$query" 2>&1); then
echo "::warning::Cypher query failed: $result" >&2
echo ""
return
fi
echo ""
echo "$result"
}
# ── Generate ARCHITECTURE.md ──────────────────────────────────────────
GENERATED_DATE=$(date -u '+%Y-%m-%d %H:%M UTC')
cat > ARCHITECTURE.md <<EOF
# SuperClaude Architecture
echo "Querying GitNexus knowledge graph..." >&2
> Auto-generated by GitNexus knowledge graph analysis.
> Last updated: ${GENERATED_DATE}
SYMBOL_COUNT=$(cypher_query 'MATCH (n) RETURN count(n) as c' | jq -r '.markdown' | tail -1 | tr -d '| ' || echo "0")
EDGE_COUNT=$(cypher_query 'MATCH ()-[r]->() RETURN count(r) as c' | jq -r '.markdown' | tail -1 | tr -d '| ' || echo "0")
PROCESS_COUNT=$(cypher_query 'MATCH (p:Process) RETURN count(p) as c' | jq -r '.markdown' | tail -1 | tr -d '| ' || echo "0")
extract_count() {
jq -er '.markdown' | tail -1 | tr -d '| ' | grep -E '^[0-9]+$'
}
cypher_query() {
local query="$1"
local result
# shellcheck disable=SC2086
result=$(npx gitnexus cypher $REPO_FLAG "$query" 2>&1) || {
echo "::error::Cypher query failed: $result" >&2
exit 1
}
echo "$result"
}
echo "Querying GitNexus knowledge graph..." >&2
SYMBOL_COUNT=$(cypher_query 'MATCH (n) RETURN count(n) as c' | extract_count)
EDGE_COUNT=$(cypher_query 'MATCH ()-[r]->() RETURN count(r) as c' | extract_count)
PROCESS_COUNT=$(cypher_query 'MATCH (p:Process) RETURN count(p) as c' | extract_count)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/scripts/generate-architecture.sh around lines 71 - 87, The current
cypher_query() call can silently produce empty output and the subsequent
assignments to SYMBOL_COUNT, EDGE_COUNT, and PROCESS_COUNT coerce failures to
"0"; change this so failures are fatal: have cypher_query() return a non-zero
exit code on error (preserve and echo the error text), remove the "|| echo '0'"
fallback, and after each jq parse (for SYMBOL_COUNT, EDGE_COUNT, PROCESS_COUNT)
validate the result is non-empty and numeric (e.g., regex test or grep -E
'^[0-9]+$'); if any parse fails, write a clear error via >&2 with the
cypher_query output and exit 1 so the workflow fails rather than producing bogus
"0" metrics. Ensure you reference the cypher_query function and the
SYMBOL_COUNT/EDGE_COUNT/PROCESS_COUNT assignments when making the changes.


## Codebase Overview
# Format numbers with commas
format_number() {
echo "$1" | sed ':a;s/\B[0-9]\{3\}\>$/,&/;ta'
Comment on lines +90 to +91

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Number formatting function is brittle and likely not doing full thousands-grouping as intended.

This sed pattern only inserts a single comma at the end and relies on non‑portable \B/\> regex extensions, so values like 1234567 may not become 1,234,567, and behavior can vary between sed implementations. Consider a POSIX‑compatible sed/awk approach for repeated grouping, or drop the formatting and keep raw counts to avoid cross‑platform issues.

}

| Metric | Count |
SYMBOL_FMT=$(format_number "$SYMBOL_COUNT")
EDGE_FMT=$(format_number "$EDGE_COUNT")
PROCESS_FMT=$(format_number "$PROCESS_COUNT")

echo " Symbols: $SYMBOL_FMT, Relationships: $EDGE_FMT, Flows: $PROCESS_FMT" >&2

# ── Count files by extension (with correct exclusions) ───────────────
PY_COUNT=$(find . -name '*.py' \
-not -path './.git/*' \
-not -path './.gitnexus/*' \
-not -path './.venv/*' \
-not -path './venv/*' \
-not -path './node_modules/*' \
| wc -l | tr -d ' ')

RS_COUNT=$(find . -name '*.rs' \
-not -path './.git/*' \
-not -path './.gitnexus/*' \
-not -path './target/*' \
| wc -l | tr -d ' ')

YML_COUNT=$(find .github/workflows -maxdepth 1 -name '*.yml' 2>/dev/null | wc -l | tr -d ' ')

MD_COUNT=$(find . \( -name '*.md' -o -name '*.rst' \) \
-not -path './.git/*' \
-not -path './.gitnexus/*' \
-not -path './.venv/*' \
-not -path './venv/*' \
-not -path './node_modules/*' \
-not -path './target/*' \
| wc -l | tr -d ' ')

echo " Files: py=$PY_COUNT rs=$RS_COUNT yml=$YML_COUNT md=$MD_COUNT" >&2

# ── Build replacement content for overview section ───────────────────
OVERVIEW_CONTENT="| Metric | Count |
|--------|-------|
| Total symbols | ${SYMBOL_FMT} |
| Relationships | ${EDGE_FMT} |
| Execution flows | ${PROCESS_FMT} |
| Python files | ${PY_COUNT} |
| Rust files | ${RS_COUNT} |
| GitHub workflows | ${YML_COUNT} |
| Documentation files | ${MD_COUNT} |

EOF

{
render_section "Entry Points" "$ENTRY_SUMMARY" "No entry points detected"
render_section "Core Modules" "$CORE_SUMMARY" "No core modules detected"
render_section "Data Flow" "$DATA_SUMMARY" "No data flows detected"
render_section "Agent Orchestration" "$AGENT_SUMMARY" "No agent orchestration detected"
echo "---"
echo "*Generated by [GitNexus](https://github.com/nicholasgriffintn/gitnexus) code intelligence*"
} >> ARCHITECTURE.md

# ── Validate output ───────────────────────────────────────────────────
if [[ ! -s ARCHITECTURE.md ]]; then
echo "::error::Generated ARCHITECTURE.md is empty" >&2
| Documentation files | ${MD_COUNT} |"

# ── Replace content between markers ──────────────────────────────────
replace_marker_content() {
local file="$1"
local marker="$2"
local content="$3"
local start_marker="<!-- auto:${marker} -->"
local end_marker="<!-- /auto:${marker} -->"

awk -v start="$start_marker" -v end="$end_marker" -v replacement="$content" '
$0 == start {
print
print replacement
skip = 1
next
}
$0 == end {
print
skip = 0
next
}
!skip { print }
' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file"
}

replace_marker_content "$ARCH_FILE" "overview" "$OVERVIEW_CONTENT"

# ── Update timestamp ─────────────────────────────────────────────────
GENERATED_DATE=$(date -u '+%Y-%m-%d %H:%M UTC')
sed -i "s|^> Last updated:.*|> Last updated: ${GENERATED_DATE}|" "$ARCH_FILE"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): In-place sed -i usage is not portable between GNU sed and BSD/macOS sed.

On macOS/BSD, sed -i requires a backup suffix (e.g. -i ''), while GNU sed rejects an empty string, so this command will fail on at least one platform. To keep the script portable, either branch on the detected platform when calling sed, or avoid -i by writing to a temporary file and moving it back (as you do in replace_marker_content).


# ── Validate output ──────────────────────────────────────────────────
LINE_COUNT=$(wc -l < "$ARCH_FILE")
if [[ "$LINE_COUNT" -lt 100 ]]; then
echo "::error::Generated $ARCH_FILE has only $LINE_COUNT lines (expected >100). Marker replacement may have failed." >&2
exit 1
fi

if ! head -1 ARCHITECTURE.md | grep -q '^# '; then
echo "::error::Generated ARCHITECTURE.md does not start with a heading" >&2
if ! head -1 "$ARCH_FILE" | grep -q '^# '; then
echo "::error::$ARCH_FILE does not start with a heading" >&2
exit 1
fi

for section in "Codebase Overview" "Entry Points" "Core Modules"; do
if ! grep -q "## $section" ARCHITECTURE.md; then
echo "::error::Generated ARCHITECTURE.md missing expected section: $section" >&2
for section in "Codebase Overview" "Architecture Diagram" "Functional Areas" "Key Execution Flows" "Testing Architecture"; do
if ! grep -q "## $section" "$ARCH_FILE"; then
echo "::error::$ARCH_FILE missing expected section: $section" >&2
exit 1
fi
done

echo "ARCHITECTURE.md generated successfully ($(wc -l < ARCHITECTURE.md) lines)" >&2
echo "ARCHITECTURE.md updated successfully ($LINE_COUNT lines)" >&2
7 changes: 4 additions & 3 deletions .github/workflows/update-architecture.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [main]
paths-ignore:
- 'ARCHITECTURE.md'
- 'AGENTS.md'
- '.github/workflows/update-architecture.yml'
workflow_dispatch:

Expand Down Expand Up @@ -53,12 +54,12 @@ jobs:
- name: Check for changes
id: changes
run: |
if git diff --quiet ARCHITECTURE.md 2>/dev/null; then
if git diff --quiet ARCHITECTURE.md AGENTS.md 2>/dev/null; then
echo "changed=false" >> "$GITHUB_OUTPUT"
echo "No changes to ARCHITECTURE.md"
echo "No changes detected"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
echo "ARCHITECTURE.md has been updated"
echo "Architecture docs have been updated"
fi

- name: Create pull request
Expand Down
6 changes: 4 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
# SuperClaude Architecture

> Auto-generated by GitNexus knowledge graph analysis.
> Last updated: 2026-04-01 UTC
> Last updated: 2026-04-03 23:33 UTC

## Codebase Overview

<!-- auto:overview -->
| Metric | Count |
|--------|-------|
| Total symbols | 8,994 |
| Relationships | 21,155 |
| Execution flows | 300 |
| Python files | 217 |
| Rust files | 88 |
| GitHub workflows | 23 |
| GitHub workflows | 20 |
| Documentation files | 270 |
<!-- /auto:overview -->
Comment on lines +8 to +18

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Manually update the preserved CI/CD sections to match this new count.

The overview now says 20 workflows, but the hand-curated sections later in this file still say CI/CD (23 workflows) on Line 100 and 23 GitHub Actions workflows on Line 265. Line 101 still describes a 3-phase review, and Line 269 still lists the removed claude-review-phase1/2/3.yml files. As-is, the architecture map contradicts itself.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ARCHITECTURE.md` around lines 8 - 18, Update the hand-curated CI/CD sections
in ARCHITECTURE.md to match the auto overview: change both occurrences of "23
workflows" (the "CI/CD (23 workflows)" heading and "23 GitHub Actions
workflows") to "20 workflows", remove or revise the obsolete "3-phase review"
wording and any references to the removed claude-review-phase1/2/3.yml files so
the document no longer lists those three workflows, and ensure the surrounding
descriptive text (the paragraph referencing the review phases) reflects the
current single/updated review process; search for the exact strings "CI/CD (23
workflows)", "23 GitHub Actions workflows", "3-phase", and
"claude-review-phase1/2/3.yml" to locate and edit the lines.


SuperClaude is a framework for augmenting Claude Code with structured commands, agent personas, quality orchestration, and MCP integrations. It ships as an installable CLI toolkit with both Python and Rust components.

Expand Down
Loading