@drunkcoding/agents-and-skills 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +9 -6
- package/README.md +1 -1
- package/package.json +1 -1
- package/plugins/html-effectiveness/.claude-plugin/plugin.json +1 -1
- package/plugins/html-effectiveness/README.md +9 -4
- package/plugins/html-effectiveness/agents/report-builder.md +4 -0
- package/plugins/html-effectiveness/assets/base.css +40 -20
- package/plugins/html-effectiveness/docs/template-gallery.md +28 -0
- package/plugins/html-effectiveness/scripts/_tmp.js +37 -0
- package/plugins/html-effectiveness/scripts/catalog.js +47 -0
- package/plugins/html-effectiveness/scripts/extract.js +104 -0
- package/plugins/html-effectiveness/scripts/render.js +2 -2
- package/plugins/html-effectiveness/skills/html-effectiveness/SKILL.md +4 -4
- package/plugins/html-effectiveness/templates/01-exploration-code-approaches.html.tmpl +400 -0
- package/plugins/html-effectiveness/templates/02-exploration-visual-designs.html.tmpl +456 -0
- package/plugins/html-effectiveness/templates/03-code-review-pr.html.tmpl +581 -0
- package/plugins/html-effectiveness/templates/04-code-understanding.html.tmpl +443 -0
- package/plugins/html-effectiveness/templates/05-design-system.html.tmpl +578 -0
- package/plugins/html-effectiveness/templates/06-component-variants.html.tmpl +557 -0
- package/plugins/html-effectiveness/templates/07-prototype-animation.html.tmpl +417 -0
- package/plugins/html-effectiveness/templates/08-prototype-interaction.html.tmpl +363 -0
- package/plugins/html-effectiveness/templates/09-slide-deck.html.tmpl +535 -0
- package/plugins/html-effectiveness/templates/10-svg-illustrations.html.tmpl +444 -0
- package/plugins/html-effectiveness/templates/11-status-report.html.tmpl +152 -0
- package/plugins/html-effectiveness/templates/12-incident-report.html.tmpl +547 -0
- package/plugins/html-effectiveness/templates/13-flowchart-diagram.html.tmpl +361 -0
- package/plugins/html-effectiveness/templates/14-research-feature-explainer.html.tmpl +348 -0
- package/plugins/html-effectiveness/templates/15-research-concept-explainer.html.tmpl +334 -0
- package/plugins/html-effectiveness/templates/16-implementation-plan.html.tmpl +657 -0
- package/plugins/html-effectiveness/templates/17-pr-writeup.html.tmpl +553 -0
- package/plugins/html-effectiveness/templates/18-editor-triage-board.html.tmpl +541 -0
- package/plugins/html-effectiveness/templates/19-editor-feature-flags.html.tmpl +627 -0
- package/plugins/html-effectiveness/templates/20-editor-prompt-tuner.html.tmpl +687 -0
- package/plugins/html-effectiveness/templates/manifest.json +436 -2
- package/plugins/html-effectiveness/tests/_tmp.test.js +58 -0
- package/plugins/html-effectiveness/tests/fixtures/01-exploration-code-approaches.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/02-exploration-visual-designs.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/03-code-review-pr.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/04-code-understanding.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/05-design-system.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/06-component-variants.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/07-prototype-animation.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/08-prototype-interaction.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/09-slide-deck.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/10-svg-illustrations.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/11-status-report.data.json +37 -0
- package/plugins/html-effectiveness/tests/fixtures/12-incident-report.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/13-flowchart-diagram.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/14-research-feature-explainer.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/15-research-concept-explainer.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/16-implementation-plan.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/17-pr-writeup.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/18-editor-triage-board.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/19-editor-feature-flags.data.json +3 -0
- package/plugins/html-effectiveness/tests/fixtures/20-editor-prompt-tuner.data.json +3 -0
- package/plugins/html-effectiveness/tests/manifest.test.js +61 -0
- package/plugins/plugin-validator/.claude-plugin/plugin.json +1 -1
- package/plugins/plugin-validator/skills/validate-agents/SKILL.md +2 -2
- package/plugins/plugin-validator/skills/validate-commands/SKILL.md +2 -2
- package/plugins/plugin-validator/skills/validate-hooks/SKILL.md +57 -18
- package/plugins/team-superpower/.claude-plugin/plugin.json +7 -39
- package/plugins/team-superpower/README.md +85 -24
- package/plugins/team-superpower/agents/backend-developer.md +82 -0
- package/plugins/team-superpower/agents/designer.md +5 -1
- package/plugins/team-superpower/agents/frontend-developer.md +96 -0
- package/plugins/team-superpower/agents/planner.md +144 -16
- package/plugins/team-superpower/agents/qa-engineer.md +30 -0
- package/plugins/team-superpower/agents/reviewer.md +68 -22
- package/plugins/team-superpower/agents/security-engineer.md +110 -0
- package/plugins/team-superpower/agents/software-architect.md +30 -0
- package/plugins/team-superpower/assets/CLAUDE.md.template +96 -0
- package/plugins/team-superpower/assets/ESCALATION.md +19 -5
- package/plugins/team-superpower/assets/SESSION_README.md +119 -11
- package/plugins/team-superpower/commands/team-feature-resume.md +72 -6
- package/plugins/team-superpower/commands/team-feature.md +329 -28
- package/plugins/team-superpower/hooks/hooks.json +0 -3
- package/plugins/team-superpower/hooks/task-completed.sh +54 -5
- package/plugins/team-superpower/hooks/task-created.sh +79 -4
- package/plugins/team-superpower/scripts/detect-stack.sh +434 -0
- package/plugins/team-superpower/scripts/parse-claudemd.sh +194 -0
- package/plugins/tech-graph/.claude-plugin/plugin.json +1 -1
- package/plugins/tech-graph/agents/tech-graph.md +8 -0
- package/plugins/team-superpower/agents/implementer.md +0 -46
|
@@ -5,13 +5,23 @@
|
|
|
5
5
|
# - task.title: string
|
|
6
6
|
#
|
|
7
7
|
# Title MUST start with one of: impl:, review:, meta:, block:
|
|
8
|
-
#
|
|
9
|
-
#
|
|
8
|
+
# v2 additions:
|
|
9
|
+
# - impl: tasks MUST carry a sub-prefix (be-, fe-, qa-fix-be-, qa-fix-fe-,
|
|
10
|
+
# review-fix-be-, review-fix-fe-, contract-update-, be-migration-,
|
|
11
|
+
# be-contract-publish-) — bare `impl:foo` is rejected.
|
|
12
|
+
# - Shape-marker file at docs/superpowers/sessions/<slug>.shape (written by
|
|
13
|
+
# the lead in phase 0) restricts which sub-prefixes are allowed:
|
|
14
|
+
# be-only → only `impl:be-*`, `impl:contract-update-*` allowed
|
|
15
|
+
# fe-only → only `impl:fe-*`, `impl:contract-update-*` allowed
|
|
16
|
+
# full-stack → all sub-prefixes allowed
|
|
17
|
+
# - Slug is taken from .task.metadata.slug or .metadata.slug, with fallback
|
|
18
|
+
# to the most-recent .shape file when only one exists.
|
|
10
19
|
|
|
11
20
|
set -euo pipefail
|
|
12
21
|
|
|
13
22
|
LOG_DIR="${CLAUDE_PROJECT_DIR:-$PWD}/.claude/hooks"
|
|
14
23
|
LOG_FILE="$LOG_DIR/log.jsonl"
|
|
24
|
+
SESSIONS_DIR="${CLAUDE_PROJECT_DIR:-$PWD}/docs/superpowers/sessions"
|
|
15
25
|
mkdir -p "$LOG_DIR"
|
|
16
26
|
|
|
17
27
|
ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
@@ -29,11 +39,36 @@ if ! command -v jq >/dev/null 2>&1; then
|
|
|
29
39
|
fi
|
|
30
40
|
|
|
31
41
|
title="$(printf '%s' "$payload" | jq -r '.task.title // .title // ""' 2>/dev/null || echo "")"
|
|
42
|
+
slug="$(printf '%s' "$payload" | jq -r '.task.metadata.slug // .metadata.slug // ""' 2>/dev/null || echo "")"
|
|
32
43
|
|
|
33
|
-
|
|
44
|
+
# Resolve shape from the slug's marker file. If slug is unknown but exactly
|
|
45
|
+
# one .shape file exists, use it (single in-flight feature is the common case).
|
|
46
|
+
shape=""
|
|
47
|
+
if [ -d "$SESSIONS_DIR" ]; then
|
|
48
|
+
shape_file=""
|
|
49
|
+
if [ -n "$slug" ] && [ -f "$SESSIONS_DIR/$slug.shape" ]; then
|
|
50
|
+
shape_file="$SESSIONS_DIR/$slug.shape"
|
|
51
|
+
else
|
|
52
|
+
count="$(find "$SESSIONS_DIR" -maxdepth 1 -type f -name '*.shape' 2>/dev/null | wc -l | tr -d ' ')"
|
|
53
|
+
if [ "$count" = "1" ]; then
|
|
54
|
+
shape_file="$(find "$SESSIONS_DIR" -maxdepth 1 -type f -name '*.shape' 2>/dev/null | head -n1)"
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
if [ -n "$shape_file" ] && [ -f "$shape_file" ]; then
|
|
58
|
+
shape="$(head -n1 "$shape_file" | tr -d '[:space:]')"
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
printf '{"ts":"%s","hook":"task-created","title":%s,"shape":%s}\n' \
|
|
63
|
+
"$ts" \
|
|
64
|
+
"$(printf '%s' "$title" | jq -Rs .)" \
|
|
65
|
+
"$(printf '%s' "$shape" | jq -Rs .)" \
|
|
66
|
+
>> "$LOG_FILE"
|
|
34
67
|
|
|
68
|
+
# Top-level prefix check
|
|
35
69
|
case "$title" in
|
|
36
|
-
|
|
70
|
+
review:*|meta:*|block:*) exit 0 ;;
|
|
71
|
+
impl:*) ;;
|
|
37
72
|
"")
|
|
38
73
|
echo "BAD_PREFIX: task title missing; must start with impl:|review:|meta:|block:" >&2
|
|
39
74
|
exit 2 ;;
|
|
@@ -41,3 +76,43 @@ case "$title" in
|
|
|
41
76
|
echo "BAD_PREFIX: task title must start with impl:|review:|meta:|block: (got: $title)" >&2
|
|
42
77
|
exit 2 ;;
|
|
43
78
|
esac
|
|
79
|
+
|
|
80
|
+
# At this point title starts with `impl:`. Strip prefix and require a known
|
|
81
|
+
# sub-prefix.
|
|
82
|
+
rest="${title#impl:}"
|
|
83
|
+
case "$rest" in
|
|
84
|
+
be-*|qa-fix-be-*|review-fix-be-*|be-migration-*|be-contract-publish-*)
|
|
85
|
+
sub="be"
|
|
86
|
+
;;
|
|
87
|
+
fe-*|qa-fix-fe-*|review-fix-fe-*)
|
|
88
|
+
sub="fe"
|
|
89
|
+
;;
|
|
90
|
+
contract-update-*)
|
|
91
|
+
sub="contract"
|
|
92
|
+
;;
|
|
93
|
+
*)
|
|
94
|
+
echo "BAD_PREFIX: impl: task requires a sub-prefix (be-|fe-|qa-fix-be-|qa-fix-fe-|review-fix-be-|review-fix-fe-|contract-update-|be-migration-|be-contract-publish-). Got: $title" >&2
|
|
95
|
+
exit 2 ;;
|
|
96
|
+
esac
|
|
97
|
+
|
|
98
|
+
# Shape-aware enforcement
|
|
99
|
+
case "$shape" in
|
|
100
|
+
be-only)
|
|
101
|
+
if [ "$sub" = "fe" ]; then
|
|
102
|
+
echo "SHAPE_REJECTED: shape is 'be-only'; impl:fe-* tasks are not allowed for this feature." >&2
|
|
103
|
+
exit 2
|
|
104
|
+
fi
|
|
105
|
+
;;
|
|
106
|
+
fe-only)
|
|
107
|
+
if [ "$sub" = "be" ]; then
|
|
108
|
+
echo "SHAPE_REJECTED: shape is 'fe-only'; impl:be-* tasks are not allowed for this feature." >&2
|
|
109
|
+
exit 2
|
|
110
|
+
fi
|
|
111
|
+
;;
|
|
112
|
+
full-stack|"")
|
|
113
|
+
# full-stack: anything goes. Empty shape: hook can't tell — accept and
|
|
114
|
+
# let the lead serialize/validate. Logged above for tuning.
|
|
115
|
+
;;
|
|
116
|
+
esac
|
|
117
|
+
|
|
118
|
+
exit 0
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# detect-stack.sh — best-effort filesystem detection for the team-superpower
|
|
3
|
+
# stack block. Emits a YAML block (the same shape that lives inside the
|
|
4
|
+
# ```team-superpower``` fence in CLAUDE.md) to stdout, plus exit codes:
|
|
5
|
+
#
|
|
6
|
+
# 0 confident detection (single BE candidate and/or single FE candidate)
|
|
7
|
+
# 1 no stack signals found (BE absent AND FE absent) — lead should escalate
|
|
8
|
+
# 2 ambiguous (multiple plausible BE languages, or BE/FE conflict that the
|
|
9
|
+
# owner must resolve) — lead writes stack.detected.md with both options
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# bash detect-stack.sh [<repo-root>]
|
|
13
|
+
#
|
|
14
|
+
# Dependencies: bash, find, grep, sed. jq is preferred but not required (we
|
|
15
|
+
# parse package.json with grep fallbacks when jq is missing).
|
|
16
|
+
#
|
|
17
|
+
# What this script does NOT do:
|
|
18
|
+
# - Write to CLAUDE.md. The spec forbids it. Output goes to stdout; the lead
|
|
19
|
+
# decides whether to write it to docs/superpowers/stack.detected.md.
|
|
20
|
+
# - Guess test commands when there is no signal — fields left blank with a
|
|
21
|
+
# `# CONFIRM:` comment for the owner to fill in.
|
|
22
|
+
|
|
23
|
+
set -euo pipefail
|
|
24
|
+
|
|
25
|
+
ROOT="${1:-$PWD}"
|
|
26
|
+
cd "$ROOT"
|
|
27
|
+
|
|
28
|
+
# ---- helpers --------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
has_file_glob() {
|
|
31
|
+
# has_file_glob <maxdepth> <name>
|
|
32
|
+
find . -maxdepth "$1" -type f -name "$2" 2>/dev/null | head -n1 | grep -q .
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
has_file_deep() {
|
|
36
|
+
# has_file_deep <name>
|
|
37
|
+
find . -type f -name "$1" -not -path './node_modules/*' -not -path './.git/*' -not -path './dist/*' -not -path './build/*' 2>/dev/null | head -n1 | grep -q .
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
read_pkg_field() {
|
|
41
|
+
# read_pkg_field <jq-expr> [default]
|
|
42
|
+
local expr="$1" default="${2:-}"
|
|
43
|
+
[ -f package.json ] || { printf '%s' "$default"; return; }
|
|
44
|
+
if command -v jq >/dev/null 2>&1; then
|
|
45
|
+
local v
|
|
46
|
+
v="$(jq -r "$expr // empty" package.json 2>/dev/null || true)"
|
|
47
|
+
printf '%s' "${v:-$default}"
|
|
48
|
+
else
|
|
49
|
+
printf '%s' "$default"
|
|
50
|
+
fi
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
pkg_has_dep() {
|
|
54
|
+
# pkg_has_dep <name>
|
|
55
|
+
[ -f package.json ] || return 1
|
|
56
|
+
if command -v jq >/dev/null 2>&1; then
|
|
57
|
+
jq -e --arg n "$1" '((.dependencies // {}) + (.devDependencies // {}) + (.peerDependencies // {})) | has($n)' package.json >/dev/null 2>&1
|
|
58
|
+
else
|
|
59
|
+
grep -qE "\"$1\"[[:space:]]*:" package.json
|
|
60
|
+
fi
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
pkg_script() {
|
|
64
|
+
# pkg_script <script-name> -> command string (empty if missing)
|
|
65
|
+
[ -f package.json ] || return 0
|
|
66
|
+
if command -v jq >/dev/null 2>&1; then
|
|
67
|
+
jq -r --arg n "$1" '.scripts[$n] // empty' package.json 2>/dev/null || true
|
|
68
|
+
fi
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# ---- backend detection ----------------------------------------------------
|
|
72
|
+
|
|
73
|
+
backend_language=""
|
|
74
|
+
backend_framework=""
|
|
75
|
+
backend_test_framework=""
|
|
76
|
+
backend_build_command=""
|
|
77
|
+
backend_test_command=""
|
|
78
|
+
backend_format_command=""
|
|
79
|
+
backend_migration_tool=""
|
|
80
|
+
backend_package_manager=""
|
|
81
|
+
backend_candidates=()
|
|
82
|
+
|
|
83
|
+
if has_file_glob 3 '*.csproj' || has_file_glob 2 '*.sln' || [ -f Directory.Build.props ]; then
|
|
84
|
+
backend_candidates+=("csharp")
|
|
85
|
+
fi
|
|
86
|
+
if [ -f pyproject.toml ] || [ -f setup.py ] || [ -f requirements.txt ] || [ -f Pipfile ]; then
|
|
87
|
+
backend_candidates+=("python")
|
|
88
|
+
fi
|
|
89
|
+
if [ -f go.mod ]; then
|
|
90
|
+
backend_candidates+=("go")
|
|
91
|
+
fi
|
|
92
|
+
if [ -f Cargo.toml ]; then
|
|
93
|
+
backend_candidates+=("rust")
|
|
94
|
+
fi
|
|
95
|
+
if [ -f pom.xml ] || [ -f build.gradle ] || [ -f build.gradle.kts ]; then
|
|
96
|
+
backend_candidates+=("java")
|
|
97
|
+
fi
|
|
98
|
+
# Node-ts as backend only when package.json exists, "type": "module", and no
|
|
99
|
+
# frontend signal in deps. We resolve this after FE detection.
|
|
100
|
+
|
|
101
|
+
if [ ${#backend_candidates[@]} -eq 1 ]; then
|
|
102
|
+
backend_language="${backend_candidates[0]}"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# Per-language fillers
|
|
106
|
+
case "$backend_language" in
|
|
107
|
+
csharp)
|
|
108
|
+
backend_framework="aspnetcore"
|
|
109
|
+
backend_build_command="dotnet build"
|
|
110
|
+
backend_test_command="dotnet test"
|
|
111
|
+
backend_format_command="dotnet format --verify-no-changes"
|
|
112
|
+
backend_package_manager="nuget"
|
|
113
|
+
# Test framework: scan csproj for hints
|
|
114
|
+
if find . -maxdepth 4 -type f -name '*.csproj' -exec grep -l -iE 'xunit' {} \; 2>/dev/null | head -n1 | grep -q .; then
|
|
115
|
+
backend_test_framework="xunit"
|
|
116
|
+
elif find . -maxdepth 4 -type f -name '*.csproj' -exec grep -l -iE 'nunit' {} \; 2>/dev/null | head -n1 | grep -q .; then
|
|
117
|
+
backend_test_framework="nunit"
|
|
118
|
+
elif find . -maxdepth 4 -type f -name '*.csproj' -exec grep -l -iE 'mstest' {} \; 2>/dev/null | head -n1 | grep -q .; then
|
|
119
|
+
backend_test_framework="mstest"
|
|
120
|
+
elif find . -maxdepth 4 -type f -name '*.csproj' -exec grep -l -iE 'reqnroll|specflow' {} \; 2>/dev/null | head -n1 | grep -q .; then
|
|
121
|
+
backend_test_framework="reqnroll"
|
|
122
|
+
else
|
|
123
|
+
backend_test_framework="# CONFIRM: xunit | nunit | mstest | reqnroll"
|
|
124
|
+
fi
|
|
125
|
+
# Migration tool: EF Core hint
|
|
126
|
+
if find . -maxdepth 4 -type f -name '*.csproj' -exec grep -l -iE 'Microsoft\.EntityFrameworkCore' {} \; 2>/dev/null | head -n1 | grep -q .; then
|
|
127
|
+
backend_migration_tool="ef-core"
|
|
128
|
+
else
|
|
129
|
+
backend_migration_tool="none"
|
|
130
|
+
fi
|
|
131
|
+
;;
|
|
132
|
+
python)
|
|
133
|
+
backend_framework="# CONFIRM: fastapi | django | flask | starlette | none"
|
|
134
|
+
if [ -f pyproject.toml ] && grep -qE '\[tool\.poetry\]' pyproject.toml; then
|
|
135
|
+
backend_package_manager="poetry"
|
|
136
|
+
backend_build_command="poetry build"
|
|
137
|
+
backend_test_command="poetry run pytest"
|
|
138
|
+
elif [ -f Pipfile ]; then
|
|
139
|
+
backend_package_manager="pipenv"
|
|
140
|
+
backend_build_command="# CONFIRM: build command"
|
|
141
|
+
backend_test_command="pipenv run pytest"
|
|
142
|
+
else
|
|
143
|
+
backend_package_manager="pip"
|
|
144
|
+
backend_build_command="# CONFIRM: build command (often none for python apps)"
|
|
145
|
+
backend_test_command="pytest"
|
|
146
|
+
fi
|
|
147
|
+
backend_test_framework="pytest"
|
|
148
|
+
backend_format_command="# CONFIRM: ruff format --check . | black --check . | none"
|
|
149
|
+
if grep -qiE 'alembic' pyproject.toml requirements.txt Pipfile 2>/dev/null; then
|
|
150
|
+
backend_migration_tool="alembic"
|
|
151
|
+
elif grep -qiE 'django' pyproject.toml requirements.txt Pipfile 2>/dev/null; then
|
|
152
|
+
backend_migration_tool="django-migrations"
|
|
153
|
+
else
|
|
154
|
+
backend_migration_tool="none"
|
|
155
|
+
fi
|
|
156
|
+
;;
|
|
157
|
+
go)
|
|
158
|
+
backend_framework="# CONFIRM: stdlib | gin | echo | chi | fiber"
|
|
159
|
+
backend_build_command="go build ./..."
|
|
160
|
+
backend_test_command="go test ./..."
|
|
161
|
+
backend_format_command="gofmt -l ."
|
|
162
|
+
backend_test_framework="go-testing"
|
|
163
|
+
backend_package_manager="go-modules"
|
|
164
|
+
backend_migration_tool="# CONFIRM: golang-migrate | goose | none"
|
|
165
|
+
;;
|
|
166
|
+
rust)
|
|
167
|
+
backend_framework="# CONFIRM: axum | actix | rocket | warp"
|
|
168
|
+
backend_build_command="cargo build"
|
|
169
|
+
backend_test_command="cargo test"
|
|
170
|
+
backend_format_command="cargo fmt -- --check"
|
|
171
|
+
backend_test_framework="cargo-test"
|
|
172
|
+
backend_package_manager="cargo"
|
|
173
|
+
backend_migration_tool="# CONFIRM: sqlx-migrate | refinery | none"
|
|
174
|
+
;;
|
|
175
|
+
java)
|
|
176
|
+
backend_framework="# CONFIRM: spring-boot | quarkus | micronaut | none"
|
|
177
|
+
if [ -f pom.xml ]; then
|
|
178
|
+
backend_build_command="mvn -B compile"
|
|
179
|
+
backend_test_command="mvn -B test"
|
|
180
|
+
backend_format_command="# CONFIRM: mvn spotless:check | none"
|
|
181
|
+
backend_package_manager="maven"
|
|
182
|
+
else
|
|
183
|
+
backend_build_command="./gradlew build"
|
|
184
|
+
backend_test_command="./gradlew test"
|
|
185
|
+
backend_format_command="# CONFIRM: ./gradlew spotlessCheck | none"
|
|
186
|
+
backend_package_manager="gradle"
|
|
187
|
+
fi
|
|
188
|
+
backend_test_framework="junit"
|
|
189
|
+
backend_migration_tool="# CONFIRM: flyway | liquibase | none"
|
|
190
|
+
;;
|
|
191
|
+
esac
|
|
192
|
+
|
|
193
|
+
# ---- frontend detection ---------------------------------------------------
|
|
194
|
+
|
|
195
|
+
frontend_language=""
|
|
196
|
+
frontend_framework=""
|
|
197
|
+
frontend_bundler=""
|
|
198
|
+
frontend_test_framework=""
|
|
199
|
+
frontend_e2e_framework=""
|
|
200
|
+
frontend_ui_library=""
|
|
201
|
+
frontend_package_manager=""
|
|
202
|
+
frontend_build_command=""
|
|
203
|
+
frontend_test_command=""
|
|
204
|
+
have_pkg=0
|
|
205
|
+
[ -f package.json ] && have_pkg=1
|
|
206
|
+
|
|
207
|
+
if [ "$have_pkg" -eq 1 ]; then
|
|
208
|
+
# Detect framework
|
|
209
|
+
if pkg_has_dep next; then frontend_framework="next"; frontend_bundler="next";
|
|
210
|
+
elif pkg_has_dep nuxt; then frontend_framework="nuxt"; frontend_bundler="nuxt";
|
|
211
|
+
elif pkg_has_dep react || pkg_has_dep react-dom; then frontend_framework="react";
|
|
212
|
+
elif pkg_has_dep vue; then frontend_framework="vue";
|
|
213
|
+
elif pkg_has_dep svelte; then frontend_framework="svelte";
|
|
214
|
+
elif pkg_has_dep solid-js; then frontend_framework="solid";
|
|
215
|
+
elif pkg_has_dep '@angular/core'; then frontend_framework="angular";
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
if [ -z "$frontend_bundler" ]; then
|
|
219
|
+
if has_file_glob 2 'vite.config.*'; then frontend_bundler="vite";
|
|
220
|
+
elif has_file_glob 2 'webpack.config.*'; then frontend_bundler="webpack";
|
|
221
|
+
elif has_file_glob 2 'rspack.config.*'; then frontend_bundler="rspack";
|
|
222
|
+
elif has_file_glob 2 'rollup.config.*'; then frontend_bundler="rollup";
|
|
223
|
+
elif has_file_glob 2 'next.config.*'; then frontend_bundler="next";
|
|
224
|
+
elif has_file_glob 2 'nuxt.config.*'; then frontend_bundler="nuxt";
|
|
225
|
+
fi
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
if [ -n "$frontend_framework" ] || [ -n "$frontend_bundler" ]; then
|
|
229
|
+
# Confirmed FE shape
|
|
230
|
+
frontend_language="typescript"
|
|
231
|
+
[ -f tsconfig.json ] || frontend_language="javascript"
|
|
232
|
+
|
|
233
|
+
if pkg_has_dep vitest; then frontend_test_framework="vitest";
|
|
234
|
+
elif pkg_has_dep jest; then frontend_test_framework="jest";
|
|
235
|
+
elif pkg_has_dep '@playwright/test'; then frontend_test_framework="none";
|
|
236
|
+
else frontend_test_framework="# CONFIRM: vitest | jest | none";
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
if pkg_has_dep '@playwright/test'; then frontend_e2e_framework="playwright";
|
|
240
|
+
elif pkg_has_dep cypress; then frontend_e2e_framework="cypress";
|
|
241
|
+
else frontend_e2e_framework="none";
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
if pkg_has_dep '@shadcn/ui' || [ -d components/ui ] && grep -RIl 'shadcn' components/ui 2>/dev/null | head -n1 | grep -q . ; then
|
|
245
|
+
frontend_ui_library="shadcn"
|
|
246
|
+
elif pkg_has_dep '@mui/material'; then frontend_ui_library="mui";
|
|
247
|
+
elif pkg_has_dep antd; then frontend_ui_library="antd";
|
|
248
|
+
elif pkg_has_dep tailwindcss; then frontend_ui_library="tailwind-only";
|
|
249
|
+
else frontend_ui_library="# CONFIRM: shadcn | mui | antd | tailwind-only | none";
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
# Package manager: lockfile is the source of truth
|
|
253
|
+
if [ -f pnpm-lock.yaml ]; then frontend_package_manager="pnpm";
|
|
254
|
+
elif [ -f yarn.lock ]; then frontend_package_manager="yarn";
|
|
255
|
+
elif [ -f bun.lockb ] || [ -f bun.lock ]; then frontend_package_manager="bun";
|
|
256
|
+
else frontend_package_manager="npm";
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
# Scripts → commands
|
|
260
|
+
local_build="$(pkg_script build)"
|
|
261
|
+
local_test="$(pkg_script test)"
|
|
262
|
+
if [ -n "$local_build" ]; then
|
|
263
|
+
frontend_build_command="$frontend_package_manager build"
|
|
264
|
+
fi
|
|
265
|
+
if [ -n "$local_test" ]; then
|
|
266
|
+
frontend_test_command="$frontend_package_manager test"
|
|
267
|
+
fi
|
|
268
|
+
fi
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
# Resolve node-ts-as-backend: package.json without any FE framework and with a
|
|
272
|
+
# server framework dep counts as a BE candidate.
|
|
273
|
+
if [ "$have_pkg" -eq 1 ] && [ -z "$frontend_framework" ] && [ -z "$frontend_bundler" ]; then
|
|
274
|
+
if pkg_has_dep express || pkg_has_dep fastify || pkg_has_dep koa || pkg_has_dep '@nestjs/core' || pkg_has_dep hono; then
|
|
275
|
+
backend_candidates+=("node-ts")
|
|
276
|
+
# If this was our only BE candidate, set it
|
|
277
|
+
if [ -z "$backend_language" ] && [ ${#backend_candidates[@]} -eq 1 ]; then
|
|
278
|
+
backend_language="node-ts"
|
|
279
|
+
fi
|
|
280
|
+
fi
|
|
281
|
+
fi
|
|
282
|
+
|
|
283
|
+
# Fill node-ts BE
|
|
284
|
+
if [ "$backend_language" = "node-ts" ]; then
|
|
285
|
+
if pkg_has_dep '@nestjs/core'; then backend_framework="nestjs";
|
|
286
|
+
elif pkg_has_dep fastify; then backend_framework="fastify";
|
|
287
|
+
elif pkg_has_dep express; then backend_framework="express";
|
|
288
|
+
elif pkg_has_dep koa; then backend_framework="koa";
|
|
289
|
+
elif pkg_has_dep hono; then backend_framework="hono";
|
|
290
|
+
fi
|
|
291
|
+
if pkg_has_dep vitest; then backend_test_framework="vitest";
|
|
292
|
+
elif pkg_has_dep jest; then backend_test_framework="jest";
|
|
293
|
+
else backend_test_framework="# CONFIRM: vitest | jest | mocha";
|
|
294
|
+
fi
|
|
295
|
+
if [ -f pnpm-lock.yaml ]; then backend_package_manager="pnpm";
|
|
296
|
+
elif [ -f yarn.lock ]; then backend_package_manager="yarn";
|
|
297
|
+
elif [ -f bun.lockb ] || [ -f bun.lock ]; then backend_package_manager="bun";
|
|
298
|
+
else backend_package_manager="npm";
|
|
299
|
+
fi
|
|
300
|
+
backend_build_command="$backend_package_manager build"
|
|
301
|
+
backend_test_command="$backend_package_manager test"
|
|
302
|
+
backend_format_command="# CONFIRM: prettier --check . | eslint . | none"
|
|
303
|
+
backend_migration_tool="# CONFIRM: prisma | typeorm | knex | none"
|
|
304
|
+
fi
|
|
305
|
+
|
|
306
|
+
# ---- contracts ------------------------------------------------------------
|
|
307
|
+
|
|
308
|
+
contracts_sot=""
|
|
309
|
+
contracts_openapi_path=""
|
|
310
|
+
contracts_ts_gen_command=""
|
|
311
|
+
|
|
312
|
+
if find . -maxdepth 5 -type f \( -name 'openapi.yaml' -o -name 'openapi.json' -o -name 'swagger.yaml' -o -name 'swagger.json' \) -not -path './node_modules/*' -not -path './.git/*' 2>/dev/null | head -n1 | grep -q .; then
|
|
313
|
+
contracts_sot="openapi"
|
|
314
|
+
contracts_openapi_path="$(find . -maxdepth 5 -type f \( -name 'openapi.yaml' -o -name 'openapi.json' -o -name 'swagger.yaml' \) -not -path './node_modules/*' -not -path './.git/*' 2>/dev/null | head -n1 | sed 's|^\./||')"
|
|
315
|
+
contracts_ts_gen_command="# CONFIRM: command that regenerates TS types from $contracts_openapi_path"
|
|
316
|
+
elif find . -maxdepth 5 -type f -name '*.proto' -not -path './node_modules/*' -not -path './.git/*' 2>/dev/null | head -n1 | grep -q .; then
|
|
317
|
+
contracts_sot="grpc"
|
|
318
|
+
elif find . -maxdepth 5 -type f -name 'schema.graphql' -not -path './node_modules/*' -not -path './.git/*' 2>/dev/null | head -n1 | grep -q .; then
|
|
319
|
+
contracts_sot="graphql"
|
|
320
|
+
elif [ -n "$backend_language" ] && [ -n "$frontend_framework" ]; then
|
|
321
|
+
contracts_sot="# CONFIRM: openapi | grpc | graphql | typescript | none"
|
|
322
|
+
else
|
|
323
|
+
contracts_sot="none"
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
# ---- CI -------------------------------------------------------------------
|
|
327
|
+
|
|
328
|
+
ci_provider=""
|
|
329
|
+
ci_workflow_path=""
|
|
330
|
+
|
|
331
|
+
if find .github/workflows -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null | head -n1 | grep -q .; then
|
|
332
|
+
ci_provider="github-actions"
|
|
333
|
+
ci_workflow_path="$(find .github/workflows -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null | head -n1)"
|
|
334
|
+
elif [ -f azure-pipelines.yml ] || [ -f .azure/pipelines.yml ]; then
|
|
335
|
+
ci_provider="azure-pipelines"
|
|
336
|
+
[ -f azure-pipelines.yml ] && ci_workflow_path="azure-pipelines.yml" || ci_workflow_path=".azure/pipelines.yml"
|
|
337
|
+
elif [ -f .gitlab-ci.yml ]; then
|
|
338
|
+
ci_provider="gitlab-ci"
|
|
339
|
+
ci_workflow_path=".gitlab-ci.yml"
|
|
340
|
+
elif [ -f .circleci/config.yml ]; then
|
|
341
|
+
ci_provider="circleci"
|
|
342
|
+
ci_workflow_path=".circleci/config.yml"
|
|
343
|
+
else
|
|
344
|
+
ci_provider="none"
|
|
345
|
+
fi
|
|
346
|
+
|
|
347
|
+
# ---- emit -----------------------------------------------------------------
|
|
348
|
+
|
|
349
|
+
if [ -z "$backend_language" ] && [ -z "$frontend_framework" ] && [ -z "$frontend_bundler" ]; then
|
|
350
|
+
echo "# detect-stack: no BE or FE signal found in $ROOT" >&2
|
|
351
|
+
exit 1
|
|
352
|
+
fi
|
|
353
|
+
|
|
354
|
+
emit_backend=0
|
|
355
|
+
emit_frontend=0
|
|
356
|
+
[ -n "$backend_language" ] && emit_backend=1
|
|
357
|
+
{ [ -n "$frontend_framework" ] || [ -n "$frontend_bundler" ]; } && emit_frontend=1
|
|
358
|
+
|
|
359
|
+
cat <<EOF
|
|
360
|
+
# Auto-generated by team-superpower detect-stack.sh on $(date -u +%Y-%m-%dT%H:%M:%SZ).
|
|
361
|
+
# Review every \`# CONFIRM:\` line, then paste into your CLAUDE.md inside a
|
|
362
|
+
# \`\`\`team-superpower fenced block. The plugin will NOT overwrite your CLAUDE.md.
|
|
363
|
+
EOF
|
|
364
|
+
|
|
365
|
+
if [ "$emit_backend" -eq 1 ]; then
|
|
366
|
+
cat <<EOF
|
|
367
|
+
backend:
|
|
368
|
+
language: ${backend_language}
|
|
369
|
+
framework: ${backend_framework:-# CONFIRM: framework}
|
|
370
|
+
test_framework: ${backend_test_framework:-# CONFIRM: test framework}
|
|
371
|
+
build_command: ${backend_build_command:-# CONFIRM: build command}
|
|
372
|
+
test_command: ${backend_test_command:-# CONFIRM: test command}
|
|
373
|
+
format_command: ${backend_format_command:-# CONFIRM: format check command (or 'none')}
|
|
374
|
+
migration_tool: ${backend_migration_tool:-none}
|
|
375
|
+
package_manager: ${backend_package_manager:-# CONFIRM: package manager}
|
|
376
|
+
EOF
|
|
377
|
+
else
|
|
378
|
+
echo "backend: none"
|
|
379
|
+
fi
|
|
380
|
+
|
|
381
|
+
if [ "$emit_frontend" -eq 1 ]; then
|
|
382
|
+
cat <<EOF
|
|
383
|
+
frontend:
|
|
384
|
+
language: ${frontend_language:-typescript}
|
|
385
|
+
framework: ${frontend_framework:-# CONFIRM: framework}
|
|
386
|
+
bundler: ${frontend_bundler:-# CONFIRM: bundler}
|
|
387
|
+
test_framework: ${frontend_test_framework:-none}
|
|
388
|
+
e2e_framework: ${frontend_e2e_framework:-none}
|
|
389
|
+
ui_library: ${frontend_ui_library:-none}
|
|
390
|
+
package_manager: ${frontend_package_manager:-npm}
|
|
391
|
+
build_command: ${frontend_build_command:-# CONFIRM: build command}
|
|
392
|
+
test_command: ${frontend_test_command:-# CONFIRM: test command}
|
|
393
|
+
EOF
|
|
394
|
+
else
|
|
395
|
+
echo "frontend: none"
|
|
396
|
+
fi
|
|
397
|
+
|
|
398
|
+
cat <<EOF
|
|
399
|
+
contracts:
|
|
400
|
+
source_of_truth: ${contracts_sot}
|
|
401
|
+
EOF
|
|
402
|
+
if [ -n "$contracts_openapi_path" ]; then
|
|
403
|
+
echo " openapi_path: $contracts_openapi_path"
|
|
404
|
+
fi
|
|
405
|
+
if [ -n "$contracts_ts_gen_command" ]; then
|
|
406
|
+
echo " ts_gen_command: \"$contracts_ts_gen_command\""
|
|
407
|
+
fi
|
|
408
|
+
|
|
409
|
+
cat <<EOF
|
|
410
|
+
ci:
|
|
411
|
+
provider: ${ci_provider}
|
|
412
|
+
EOF
|
|
413
|
+
if [ -n "$ci_workflow_path" ]; then
|
|
414
|
+
echo " workflow_path: $ci_workflow_path"
|
|
415
|
+
fi
|
|
416
|
+
cat <<EOF
|
|
417
|
+
required_checks: [] # CONFIRM: e.g. ["build", "test", "lint"]
|
|
418
|
+
poll_timeout_minutes: 20
|
|
419
|
+
security:
|
|
420
|
+
domain: # CONFIRM: payments | healthcare | generic | internal-only
|
|
421
|
+
pii: # CONFIRM: yes | no
|
|
422
|
+
public_endpoints: # CONFIRM: yes | no
|
|
423
|
+
data_at_rest: # CONFIRM: sql | nosql | none
|
|
424
|
+
EOF
|
|
425
|
+
|
|
426
|
+
# Exit with ambiguity warning when there are >1 BE candidates the script could
|
|
427
|
+
# not narrow down.
|
|
428
|
+
if [ ${#backend_candidates[@]} -gt 1 ]; then
|
|
429
|
+
uniq_list="$(printf '%s\n' "${backend_candidates[@]}" | sort -u | tr '\n' ',' | sed 's/,$//')"
|
|
430
|
+
echo "# AMBIGUOUS_BACKEND: multiple BE candidates found: $uniq_list — owner must confirm" >&2
|
|
431
|
+
exit 2
|
|
432
|
+
fi
|
|
433
|
+
|
|
434
|
+
exit 0
|