@drunkcoding/agents-and-skills 0.0.25 → 0.0.27
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 +14 -33
- package/README.md +1 -0
- package/package.json +1 -1
- package/plugins/html-effectiveness/.claude-plugin/plugin.json +1 -1
- package/plugins/plugin-validator/.claude-plugin/plugin.json +1 -1
- package/plugins/team-share/.claude-plugin/plugin.json +17 -0
- package/plugins/team-share/README.md +30 -0
- package/plugins/team-share/agents/team-share.md +206 -0
- package/plugins/team-share/commands/team-share.md +4 -0
- package/plugins/tech-graph/.claude-plugin/plugin.json +1 -1
- package/plugins/auto-power/.claude-plugin/plugin.json +0 -17
- package/plugins/auto-power/README.md +0 -80
- package/plugins/auto-power/assets/CHECKPOINT_SCHEMA.md +0 -69
- package/plugins/auto-power/assets/ESCALATION_TEMPLATE.md +0 -67
- package/plugins/auto-power/commands/auto-power-resume.md +0 -32
- package/plugins/auto-power/commands/auto-power.md +0 -46
- package/plugins/auto-power/skills/auto-power-runtime/SKILL.md +0 -220
- package/plugins/team-superpower/.claude-plugin/plugin.json +0 -21
- package/plugins/team-superpower/README.md +0 -294
- package/plugins/team-superpower/agents/backend-developer.md +0 -221
- package/plugins/team-superpower/agents/feature-planner.md +0 -66
- package/plugins/team-superpower/agents/frontend-developer.md +0 -242
- package/plugins/team-superpower/agents/qc-engineer.md +0 -84
- package/plugins/team-superpower/agents/security-engineer.md +0 -175
- package/plugins/team-superpower/agents/solution-architect.md +0 -80
- package/plugins/team-superpower/agents/team-leader.md +0 -100
- package/plugins/team-superpower/assets/AGENTS.md.template +0 -23
- package/plugins/team-superpower/assets/CLAUDE.md.template +0 -117
- package/plugins/team-superpower/assets/ESCALATION.md +0 -142
- package/plugins/team-superpower/assets/SESSION_README.md +0 -337
- package/plugins/team-superpower/commands/team-cleanup.md +0 -70
- package/plugins/team-superpower/commands/team-feature.md +0 -319
- package/plugins/team-superpower/hooks/hooks.json +0 -25
- package/plugins/team-superpower/hooks/task-completed.sh +0 -254
- package/plugins/team-superpower/hooks/task-created.sh +0 -174
- package/plugins/team-superpower/hooks/teammate-idle.sh +0 -142
- package/plugins/team-superpower/scripts/assess-complexity.sh +0 -194
- package/plugins/team-superpower/scripts/detect-stack.sh +0 -473
- package/plugins/team-superpower/scripts/parse-claudemd.sh +0 -194
- package/plugins/team-superpower/scripts/team-state.sh +0 -313
- package/plugins/team-superpower/scripts/wave-collision-check.sh +0 -60
|
@@ -1,473 +0,0 @@
|
|
|
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
|
-
# ---- subcommand: detect-side ---------------------------------------------
|
|
26
|
-
# Usage:
|
|
27
|
-
# bash detect-stack.sh detect-side "<launch message>"
|
|
28
|
-
# Outputs one of: be-only | fe-only | mixed | none
|
|
29
|
-
# Used by scripts/assess-complexity.sh for the rung-2 single-side signal.
|
|
30
|
-
if [ "${1:-}" = "detect-side" ]; then
|
|
31
|
-
shift
|
|
32
|
-
text="${1:-}"
|
|
33
|
-
# Normalize to lowercase for case-insensitive matching.
|
|
34
|
-
lc="$(printf '%s' "$text" | tr '[:upper:]' '[:lower:]')"
|
|
35
|
-
|
|
36
|
-
be_keywords=(
|
|
37
|
-
"endpoint" "api" "/api" "/auth" "/health" "route" "controller" "service"
|
|
38
|
-
"repository" "schema" "migration" "database" "column" "table"
|
|
39
|
-
"queue" "worker" "cron" "webhook" "grpc" "rpc"
|
|
40
|
-
)
|
|
41
|
-
fe_keywords=(
|
|
42
|
-
"component" "page" "form" "button" "modal" "input" "field"
|
|
43
|
-
"ui" "layout" "css" "tailwind" "tsx" "jsx" "react" "vue"
|
|
44
|
-
"svelte" "stylesheet" "wireframe" "design system" "screen"
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
has_be=0
|
|
48
|
-
has_fe=0
|
|
49
|
-
for kw in "${be_keywords[@]}"; do
|
|
50
|
-
case "$lc" in *"$kw"*) has_be=1; break ;; esac
|
|
51
|
-
done
|
|
52
|
-
for kw in "${fe_keywords[@]}"; do
|
|
53
|
-
case "$lc" in *"$kw"*) has_fe=1; break ;; esac
|
|
54
|
-
done
|
|
55
|
-
|
|
56
|
-
if [ "$has_be" -eq 1 ] && [ "$has_fe" -eq 1 ]; then echo "mixed"
|
|
57
|
-
elif [ "$has_be" -eq 1 ]; then echo "be-only"
|
|
58
|
-
elif [ "$has_fe" -eq 1 ]; then echo "fe-only"
|
|
59
|
-
else echo "none"
|
|
60
|
-
fi
|
|
61
|
-
exit 0
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
ROOT="${1:-$PWD}"
|
|
65
|
-
cd "$ROOT"
|
|
66
|
-
|
|
67
|
-
# ---- helpers --------------------------------------------------------------
|
|
68
|
-
|
|
69
|
-
has_file_glob() {
|
|
70
|
-
# has_file_glob <maxdepth> <name>
|
|
71
|
-
find . -maxdepth "$1" -type f -name "$2" 2>/dev/null | head -n1 | grep -q .
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
has_file_deep() {
|
|
75
|
-
# has_file_deep <name>
|
|
76
|
-
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 .
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
read_pkg_field() {
|
|
80
|
-
# read_pkg_field <jq-expr> [default]
|
|
81
|
-
local expr="$1" default="${2:-}"
|
|
82
|
-
[ -f package.json ] || { printf '%s' "$default"; return; }
|
|
83
|
-
if command -v jq >/dev/null 2>&1; then
|
|
84
|
-
local v
|
|
85
|
-
v="$(jq -r "$expr // empty" package.json 2>/dev/null || true)"
|
|
86
|
-
printf '%s' "${v:-$default}"
|
|
87
|
-
else
|
|
88
|
-
printf '%s' "$default"
|
|
89
|
-
fi
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
pkg_has_dep() {
|
|
93
|
-
# pkg_has_dep <name>
|
|
94
|
-
[ -f package.json ] || return 1
|
|
95
|
-
if command -v jq >/dev/null 2>&1; then
|
|
96
|
-
jq -e --arg n "$1" '((.dependencies // {}) + (.devDependencies // {}) + (.peerDependencies // {})) | has($n)' package.json >/dev/null 2>&1
|
|
97
|
-
else
|
|
98
|
-
grep -qE "\"$1\"[[:space:]]*:" package.json
|
|
99
|
-
fi
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
pkg_script() {
|
|
103
|
-
# pkg_script <script-name> -> command string (empty if missing)
|
|
104
|
-
[ -f package.json ] || return 0
|
|
105
|
-
if command -v jq >/dev/null 2>&1; then
|
|
106
|
-
jq -r --arg n "$1" '.scripts[$n] // empty' package.json 2>/dev/null || true
|
|
107
|
-
fi
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
# ---- backend detection ----------------------------------------------------
|
|
111
|
-
|
|
112
|
-
backend_language=""
|
|
113
|
-
backend_framework=""
|
|
114
|
-
backend_test_framework=""
|
|
115
|
-
backend_build_command=""
|
|
116
|
-
backend_test_command=""
|
|
117
|
-
backend_format_command=""
|
|
118
|
-
backend_migration_tool=""
|
|
119
|
-
backend_package_manager=""
|
|
120
|
-
backend_candidates=()
|
|
121
|
-
|
|
122
|
-
if has_file_glob 3 '*.csproj' || has_file_glob 2 '*.sln' || [ -f Directory.Build.props ]; then
|
|
123
|
-
backend_candidates+=("csharp")
|
|
124
|
-
fi
|
|
125
|
-
if [ -f pyproject.toml ] || [ -f setup.py ] || [ -f requirements.txt ] || [ -f Pipfile ]; then
|
|
126
|
-
backend_candidates+=("python")
|
|
127
|
-
fi
|
|
128
|
-
if [ -f go.mod ]; then
|
|
129
|
-
backend_candidates+=("go")
|
|
130
|
-
fi
|
|
131
|
-
if [ -f Cargo.toml ]; then
|
|
132
|
-
backend_candidates+=("rust")
|
|
133
|
-
fi
|
|
134
|
-
if [ -f pom.xml ] || [ -f build.gradle ] || [ -f build.gradle.kts ]; then
|
|
135
|
-
backend_candidates+=("java")
|
|
136
|
-
fi
|
|
137
|
-
# Node-ts as backend only when package.json exists, "type": "module", and no
|
|
138
|
-
# frontend signal in deps. We resolve this after FE detection.
|
|
139
|
-
|
|
140
|
-
if [ ${#backend_candidates[@]} -eq 1 ]; then
|
|
141
|
-
backend_language="${backend_candidates[0]}"
|
|
142
|
-
fi
|
|
143
|
-
|
|
144
|
-
# Per-language fillers
|
|
145
|
-
case "$backend_language" in
|
|
146
|
-
csharp)
|
|
147
|
-
backend_framework="aspnetcore"
|
|
148
|
-
backend_build_command="dotnet build"
|
|
149
|
-
backend_test_command="dotnet test"
|
|
150
|
-
backend_format_command="dotnet format --verify-no-changes"
|
|
151
|
-
backend_package_manager="nuget"
|
|
152
|
-
# Test framework: scan csproj for hints
|
|
153
|
-
if find . -maxdepth 4 -type f -name '*.csproj' -exec grep -l -iE 'xunit' {} \; 2>/dev/null | head -n1 | grep -q .; then
|
|
154
|
-
backend_test_framework="xunit"
|
|
155
|
-
elif find . -maxdepth 4 -type f -name '*.csproj' -exec grep -l -iE 'nunit' {} \; 2>/dev/null | head -n1 | grep -q .; then
|
|
156
|
-
backend_test_framework="nunit"
|
|
157
|
-
elif find . -maxdepth 4 -type f -name '*.csproj' -exec grep -l -iE 'mstest' {} \; 2>/dev/null | head -n1 | grep -q .; then
|
|
158
|
-
backend_test_framework="mstest"
|
|
159
|
-
elif find . -maxdepth 4 -type f -name '*.csproj' -exec grep -l -iE 'reqnroll|specflow' {} \; 2>/dev/null | head -n1 | grep -q .; then
|
|
160
|
-
backend_test_framework="reqnroll"
|
|
161
|
-
else
|
|
162
|
-
backend_test_framework="# CONFIRM: xunit | nunit | mstest | reqnroll"
|
|
163
|
-
fi
|
|
164
|
-
# Migration tool: EF Core hint
|
|
165
|
-
if find . -maxdepth 4 -type f -name '*.csproj' -exec grep -l -iE 'Microsoft\.EntityFrameworkCore' {} \; 2>/dev/null | head -n1 | grep -q .; then
|
|
166
|
-
backend_migration_tool="ef-core"
|
|
167
|
-
else
|
|
168
|
-
backend_migration_tool="none"
|
|
169
|
-
fi
|
|
170
|
-
;;
|
|
171
|
-
python)
|
|
172
|
-
backend_framework="# CONFIRM: fastapi | django | flask | starlette | none"
|
|
173
|
-
if [ -f pyproject.toml ] && grep -qE '\[tool\.poetry\]' pyproject.toml; then
|
|
174
|
-
backend_package_manager="poetry"
|
|
175
|
-
backend_build_command="poetry build"
|
|
176
|
-
backend_test_command="poetry run pytest"
|
|
177
|
-
elif [ -f Pipfile ]; then
|
|
178
|
-
backend_package_manager="pipenv"
|
|
179
|
-
backend_build_command="# CONFIRM: build command"
|
|
180
|
-
backend_test_command="pipenv run pytest"
|
|
181
|
-
else
|
|
182
|
-
backend_package_manager="pip"
|
|
183
|
-
backend_build_command="# CONFIRM: build command (often none for python apps)"
|
|
184
|
-
backend_test_command="pytest"
|
|
185
|
-
fi
|
|
186
|
-
backend_test_framework="pytest"
|
|
187
|
-
backend_format_command="# CONFIRM: ruff format --check . | black --check . | none"
|
|
188
|
-
if grep -qiE 'alembic' pyproject.toml requirements.txt Pipfile 2>/dev/null; then
|
|
189
|
-
backend_migration_tool="alembic"
|
|
190
|
-
elif grep -qiE 'django' pyproject.toml requirements.txt Pipfile 2>/dev/null; then
|
|
191
|
-
backend_migration_tool="django-migrations"
|
|
192
|
-
else
|
|
193
|
-
backend_migration_tool="none"
|
|
194
|
-
fi
|
|
195
|
-
;;
|
|
196
|
-
go)
|
|
197
|
-
backend_framework="# CONFIRM: stdlib | gin | echo | chi | fiber"
|
|
198
|
-
backend_build_command="go build ./..."
|
|
199
|
-
backend_test_command="go test ./..."
|
|
200
|
-
backend_format_command="gofmt -l ."
|
|
201
|
-
backend_test_framework="go-testing"
|
|
202
|
-
backend_package_manager="go-modules"
|
|
203
|
-
backend_migration_tool="# CONFIRM: golang-migrate | goose | none"
|
|
204
|
-
;;
|
|
205
|
-
rust)
|
|
206
|
-
backend_framework="# CONFIRM: axum | actix | rocket | warp"
|
|
207
|
-
backend_build_command="cargo build"
|
|
208
|
-
backend_test_command="cargo test"
|
|
209
|
-
backend_format_command="cargo fmt -- --check"
|
|
210
|
-
backend_test_framework="cargo-test"
|
|
211
|
-
backend_package_manager="cargo"
|
|
212
|
-
backend_migration_tool="# CONFIRM: sqlx-migrate | refinery | none"
|
|
213
|
-
;;
|
|
214
|
-
java)
|
|
215
|
-
backend_framework="# CONFIRM: spring-boot | quarkus | micronaut | none"
|
|
216
|
-
if [ -f pom.xml ]; then
|
|
217
|
-
backend_build_command="mvn -B compile"
|
|
218
|
-
backend_test_command="mvn -B test"
|
|
219
|
-
backend_format_command="# CONFIRM: mvn spotless:check | none"
|
|
220
|
-
backend_package_manager="maven"
|
|
221
|
-
else
|
|
222
|
-
backend_build_command="./gradlew build"
|
|
223
|
-
backend_test_command="./gradlew test"
|
|
224
|
-
backend_format_command="# CONFIRM: ./gradlew spotlessCheck | none"
|
|
225
|
-
backend_package_manager="gradle"
|
|
226
|
-
fi
|
|
227
|
-
backend_test_framework="junit"
|
|
228
|
-
backend_migration_tool="# CONFIRM: flyway | liquibase | none"
|
|
229
|
-
;;
|
|
230
|
-
esac
|
|
231
|
-
|
|
232
|
-
# ---- frontend detection ---------------------------------------------------
|
|
233
|
-
|
|
234
|
-
frontend_language=""
|
|
235
|
-
frontend_framework=""
|
|
236
|
-
frontend_bundler=""
|
|
237
|
-
frontend_test_framework=""
|
|
238
|
-
frontend_e2e_framework=""
|
|
239
|
-
frontend_ui_library=""
|
|
240
|
-
frontend_package_manager=""
|
|
241
|
-
frontend_build_command=""
|
|
242
|
-
frontend_test_command=""
|
|
243
|
-
have_pkg=0
|
|
244
|
-
[ -f package.json ] && have_pkg=1
|
|
245
|
-
|
|
246
|
-
if [ "$have_pkg" -eq 1 ]; then
|
|
247
|
-
# Detect framework
|
|
248
|
-
if pkg_has_dep next; then frontend_framework="next"; frontend_bundler="next";
|
|
249
|
-
elif pkg_has_dep nuxt; then frontend_framework="nuxt"; frontend_bundler="nuxt";
|
|
250
|
-
elif pkg_has_dep react || pkg_has_dep react-dom; then frontend_framework="react";
|
|
251
|
-
elif pkg_has_dep vue; then frontend_framework="vue";
|
|
252
|
-
elif pkg_has_dep svelte; then frontend_framework="svelte";
|
|
253
|
-
elif pkg_has_dep solid-js; then frontend_framework="solid";
|
|
254
|
-
elif pkg_has_dep '@angular/core'; then frontend_framework="angular";
|
|
255
|
-
fi
|
|
256
|
-
|
|
257
|
-
if [ -z "$frontend_bundler" ]; then
|
|
258
|
-
if has_file_glob 2 'vite.config.*'; then frontend_bundler="vite";
|
|
259
|
-
elif has_file_glob 2 'webpack.config.*'; then frontend_bundler="webpack";
|
|
260
|
-
elif has_file_glob 2 'rspack.config.*'; then frontend_bundler="rspack";
|
|
261
|
-
elif has_file_glob 2 'rollup.config.*'; then frontend_bundler="rollup";
|
|
262
|
-
elif has_file_glob 2 'next.config.*'; then frontend_bundler="next";
|
|
263
|
-
elif has_file_glob 2 'nuxt.config.*'; then frontend_bundler="nuxt";
|
|
264
|
-
fi
|
|
265
|
-
fi
|
|
266
|
-
|
|
267
|
-
if [ -n "$frontend_framework" ] || [ -n "$frontend_bundler" ]; then
|
|
268
|
-
# Confirmed FE shape
|
|
269
|
-
frontend_language="typescript"
|
|
270
|
-
[ -f tsconfig.json ] || frontend_language="javascript"
|
|
271
|
-
|
|
272
|
-
if pkg_has_dep vitest; then frontend_test_framework="vitest";
|
|
273
|
-
elif pkg_has_dep jest; then frontend_test_framework="jest";
|
|
274
|
-
elif pkg_has_dep '@playwright/test'; then frontend_test_framework="none";
|
|
275
|
-
else frontend_test_framework="# CONFIRM: vitest | jest | none";
|
|
276
|
-
fi
|
|
277
|
-
|
|
278
|
-
if pkg_has_dep '@playwright/test'; then frontend_e2e_framework="playwright";
|
|
279
|
-
elif pkg_has_dep cypress; then frontend_e2e_framework="cypress";
|
|
280
|
-
else frontend_e2e_framework="none";
|
|
281
|
-
fi
|
|
282
|
-
|
|
283
|
-
if pkg_has_dep '@shadcn/ui' || [ -d components/ui ] && grep -RIl 'shadcn' components/ui 2>/dev/null | head -n1 | grep -q . ; then
|
|
284
|
-
frontend_ui_library="shadcn"
|
|
285
|
-
elif pkg_has_dep '@mui/material'; then frontend_ui_library="mui";
|
|
286
|
-
elif pkg_has_dep antd; then frontend_ui_library="antd";
|
|
287
|
-
elif pkg_has_dep tailwindcss; then frontend_ui_library="tailwind-only";
|
|
288
|
-
else frontend_ui_library="# CONFIRM: shadcn | mui | antd | tailwind-only | none";
|
|
289
|
-
fi
|
|
290
|
-
|
|
291
|
-
# Package manager: lockfile is the source of truth
|
|
292
|
-
if [ -f pnpm-lock.yaml ]; then frontend_package_manager="pnpm";
|
|
293
|
-
elif [ -f yarn.lock ]; then frontend_package_manager="yarn";
|
|
294
|
-
elif [ -f bun.lockb ] || [ -f bun.lock ]; then frontend_package_manager="bun";
|
|
295
|
-
else frontend_package_manager="npm";
|
|
296
|
-
fi
|
|
297
|
-
|
|
298
|
-
# Scripts → commands
|
|
299
|
-
local_build="$(pkg_script build)"
|
|
300
|
-
local_test="$(pkg_script test)"
|
|
301
|
-
if [ -n "$local_build" ]; then
|
|
302
|
-
frontend_build_command="$frontend_package_manager build"
|
|
303
|
-
fi
|
|
304
|
-
if [ -n "$local_test" ]; then
|
|
305
|
-
frontend_test_command="$frontend_package_manager test"
|
|
306
|
-
fi
|
|
307
|
-
fi
|
|
308
|
-
fi
|
|
309
|
-
|
|
310
|
-
# Resolve node-ts-as-backend: package.json without any FE framework and with a
|
|
311
|
-
# server framework dep counts as a BE candidate.
|
|
312
|
-
if [ "$have_pkg" -eq 1 ] && [ -z "$frontend_framework" ] && [ -z "$frontend_bundler" ]; then
|
|
313
|
-
if pkg_has_dep express || pkg_has_dep fastify || pkg_has_dep koa || pkg_has_dep '@nestjs/core' || pkg_has_dep hono; then
|
|
314
|
-
backend_candidates+=("node-ts")
|
|
315
|
-
# If this was our only BE candidate, set it
|
|
316
|
-
if [ -z "$backend_language" ] && [ ${#backend_candidates[@]} -eq 1 ]; then
|
|
317
|
-
backend_language="node-ts"
|
|
318
|
-
fi
|
|
319
|
-
fi
|
|
320
|
-
fi
|
|
321
|
-
|
|
322
|
-
# Fill node-ts BE
|
|
323
|
-
if [ "$backend_language" = "node-ts" ]; then
|
|
324
|
-
if pkg_has_dep '@nestjs/core'; then backend_framework="nestjs";
|
|
325
|
-
elif pkg_has_dep fastify; then backend_framework="fastify";
|
|
326
|
-
elif pkg_has_dep express; then backend_framework="express";
|
|
327
|
-
elif pkg_has_dep koa; then backend_framework="koa";
|
|
328
|
-
elif pkg_has_dep hono; then backend_framework="hono";
|
|
329
|
-
fi
|
|
330
|
-
if pkg_has_dep vitest; then backend_test_framework="vitest";
|
|
331
|
-
elif pkg_has_dep jest; then backend_test_framework="jest";
|
|
332
|
-
else backend_test_framework="# CONFIRM: vitest | jest | mocha";
|
|
333
|
-
fi
|
|
334
|
-
if [ -f pnpm-lock.yaml ]; then backend_package_manager="pnpm";
|
|
335
|
-
elif [ -f yarn.lock ]; then backend_package_manager="yarn";
|
|
336
|
-
elif [ -f bun.lockb ] || [ -f bun.lock ]; then backend_package_manager="bun";
|
|
337
|
-
else backend_package_manager="npm";
|
|
338
|
-
fi
|
|
339
|
-
backend_build_command="$backend_package_manager build"
|
|
340
|
-
backend_test_command="$backend_package_manager test"
|
|
341
|
-
backend_format_command="# CONFIRM: prettier --check . | eslint . | none"
|
|
342
|
-
backend_migration_tool="# CONFIRM: prisma | typeorm | knex | none"
|
|
343
|
-
fi
|
|
344
|
-
|
|
345
|
-
# ---- contracts ------------------------------------------------------------
|
|
346
|
-
|
|
347
|
-
contracts_sot=""
|
|
348
|
-
contracts_openapi_path=""
|
|
349
|
-
contracts_ts_gen_command=""
|
|
350
|
-
|
|
351
|
-
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
|
|
352
|
-
contracts_sot="openapi"
|
|
353
|
-
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|^\./||')"
|
|
354
|
-
contracts_ts_gen_command="# CONFIRM: command that regenerates TS types from $contracts_openapi_path"
|
|
355
|
-
elif find . -maxdepth 5 -type f -name '*.proto' -not -path './node_modules/*' -not -path './.git/*' 2>/dev/null | head -n1 | grep -q .; then
|
|
356
|
-
contracts_sot="grpc"
|
|
357
|
-
elif find . -maxdepth 5 -type f -name 'schema.graphql' -not -path './node_modules/*' -not -path './.git/*' 2>/dev/null | head -n1 | grep -q .; then
|
|
358
|
-
contracts_sot="graphql"
|
|
359
|
-
elif [ -n "$backend_language" ] && [ -n "$frontend_framework" ]; then
|
|
360
|
-
contracts_sot="# CONFIRM: openapi | grpc | graphql | typescript | none"
|
|
361
|
-
else
|
|
362
|
-
contracts_sot="none"
|
|
363
|
-
fi
|
|
364
|
-
|
|
365
|
-
# ---- CI -------------------------------------------------------------------
|
|
366
|
-
|
|
367
|
-
ci_provider=""
|
|
368
|
-
ci_workflow_path=""
|
|
369
|
-
|
|
370
|
-
if find .github/workflows -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null | head -n1 | grep -q .; then
|
|
371
|
-
ci_provider="github-actions"
|
|
372
|
-
ci_workflow_path="$(find .github/workflows -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) 2>/dev/null | head -n1)"
|
|
373
|
-
elif [ -f azure-pipelines.yml ] || [ -f .azure/pipelines.yml ]; then
|
|
374
|
-
ci_provider="azure-pipelines"
|
|
375
|
-
[ -f azure-pipelines.yml ] && ci_workflow_path="azure-pipelines.yml" || ci_workflow_path=".azure/pipelines.yml"
|
|
376
|
-
elif [ -f .gitlab-ci.yml ]; then
|
|
377
|
-
ci_provider="gitlab-ci"
|
|
378
|
-
ci_workflow_path=".gitlab-ci.yml"
|
|
379
|
-
elif [ -f .circleci/config.yml ]; then
|
|
380
|
-
ci_provider="circleci"
|
|
381
|
-
ci_workflow_path=".circleci/config.yml"
|
|
382
|
-
else
|
|
383
|
-
ci_provider="none"
|
|
384
|
-
fi
|
|
385
|
-
|
|
386
|
-
# ---- emit -----------------------------------------------------------------
|
|
387
|
-
|
|
388
|
-
if [ -z "$backend_language" ] && [ -z "$frontend_framework" ] && [ -z "$frontend_bundler" ]; then
|
|
389
|
-
echo "# detect-stack: no BE or FE signal found in $ROOT" >&2
|
|
390
|
-
exit 1
|
|
391
|
-
fi
|
|
392
|
-
|
|
393
|
-
emit_backend=0
|
|
394
|
-
emit_frontend=0
|
|
395
|
-
[ -n "$backend_language" ] && emit_backend=1
|
|
396
|
-
{ [ -n "$frontend_framework" ] || [ -n "$frontend_bundler" ]; } && emit_frontend=1
|
|
397
|
-
|
|
398
|
-
cat <<EOF
|
|
399
|
-
# Auto-generated by team-superpower detect-stack.sh on $(date -u +%Y-%m-%dT%H:%M:%SZ).
|
|
400
|
-
# Review every \`# CONFIRM:\` line, then paste into your CLAUDE.md inside a
|
|
401
|
-
# \`\`\`team-superpower fenced block. The plugin will NOT overwrite your CLAUDE.md.
|
|
402
|
-
EOF
|
|
403
|
-
|
|
404
|
-
if [ "$emit_backend" -eq 1 ]; then
|
|
405
|
-
cat <<EOF
|
|
406
|
-
backend:
|
|
407
|
-
language: ${backend_language}
|
|
408
|
-
framework: ${backend_framework:-# CONFIRM: framework}
|
|
409
|
-
test_framework: ${backend_test_framework:-# CONFIRM: test framework}
|
|
410
|
-
build_command: ${backend_build_command:-# CONFIRM: build command}
|
|
411
|
-
test_command: ${backend_test_command:-# CONFIRM: test command}
|
|
412
|
-
format_command: ${backend_format_command:-# CONFIRM: format check command (or 'none')}
|
|
413
|
-
migration_tool: ${backend_migration_tool:-none}
|
|
414
|
-
package_manager: ${backend_package_manager:-# CONFIRM: package manager}
|
|
415
|
-
EOF
|
|
416
|
-
else
|
|
417
|
-
echo "backend: none"
|
|
418
|
-
fi
|
|
419
|
-
|
|
420
|
-
if [ "$emit_frontend" -eq 1 ]; then
|
|
421
|
-
cat <<EOF
|
|
422
|
-
frontend:
|
|
423
|
-
language: ${frontend_language:-typescript}
|
|
424
|
-
framework: ${frontend_framework:-# CONFIRM: framework}
|
|
425
|
-
bundler: ${frontend_bundler:-# CONFIRM: bundler}
|
|
426
|
-
test_framework: ${frontend_test_framework:-none}
|
|
427
|
-
e2e_framework: ${frontend_e2e_framework:-none}
|
|
428
|
-
ui_library: ${frontend_ui_library:-none}
|
|
429
|
-
package_manager: ${frontend_package_manager:-npm}
|
|
430
|
-
build_command: ${frontend_build_command:-# CONFIRM: build command}
|
|
431
|
-
test_command: ${frontend_test_command:-# CONFIRM: test command}
|
|
432
|
-
EOF
|
|
433
|
-
else
|
|
434
|
-
echo "frontend: none"
|
|
435
|
-
fi
|
|
436
|
-
|
|
437
|
-
cat <<EOF
|
|
438
|
-
contracts:
|
|
439
|
-
source_of_truth: ${contracts_sot}
|
|
440
|
-
EOF
|
|
441
|
-
if [ -n "$contracts_openapi_path" ]; then
|
|
442
|
-
echo " openapi_path: $contracts_openapi_path"
|
|
443
|
-
fi
|
|
444
|
-
if [ -n "$contracts_ts_gen_command" ]; then
|
|
445
|
-
echo " ts_gen_command: \"$contracts_ts_gen_command\""
|
|
446
|
-
fi
|
|
447
|
-
|
|
448
|
-
cat <<EOF
|
|
449
|
-
ci:
|
|
450
|
-
provider: ${ci_provider}
|
|
451
|
-
EOF
|
|
452
|
-
if [ -n "$ci_workflow_path" ]; then
|
|
453
|
-
echo " workflow_path: $ci_workflow_path"
|
|
454
|
-
fi
|
|
455
|
-
cat <<EOF
|
|
456
|
-
required_checks: [] # CONFIRM: e.g. ["build", "test", "lint"]
|
|
457
|
-
poll_timeout_minutes: 20
|
|
458
|
-
security:
|
|
459
|
-
domain: # CONFIRM: payments | healthcare | generic | internal-only
|
|
460
|
-
pii: # CONFIRM: yes | no
|
|
461
|
-
public_endpoints: # CONFIRM: yes | no
|
|
462
|
-
data_at_rest: # CONFIRM: sql | nosql | none
|
|
463
|
-
EOF
|
|
464
|
-
|
|
465
|
-
# Exit with ambiguity warning when there are >1 BE candidates the script could
|
|
466
|
-
# not narrow down.
|
|
467
|
-
if [ ${#backend_candidates[@]} -gt 1 ]; then
|
|
468
|
-
uniq_list="$(printf '%s\n' "${backend_candidates[@]}" | sort -u | tr '\n' ',' | sed 's/,$//')"
|
|
469
|
-
echo "# AMBIGUOUS_BACKEND: multiple BE candidates found: $uniq_list — owner must confirm" >&2
|
|
470
|
-
exit 2
|
|
471
|
-
fi
|
|
472
|
-
|
|
473
|
-
exit 0
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# parse-claudemd.sh — extract the team-superpower YAML block from CLAUDE.md.
|
|
3
|
-
#
|
|
4
|
-
# Usage:
|
|
5
|
-
# parse-claudemd.sh extract [<path-to-CLAUDE.md>] prints the YAML block to stdout
|
|
6
|
-
# parse-claudemd.sh shape [<path-to-CLAUDE.md>] prints "full-stack" | "be-only" | "fe-only" | "none"
|
|
7
|
-
# parse-claudemd.sh get <dotted.key> [<path>] prints a single scalar value
|
|
8
|
-
# (limited: top-level group + leaf, e.g. backend.test_command)
|
|
9
|
-
#
|
|
10
|
-
# Exit codes:
|
|
11
|
-
# 0 success
|
|
12
|
-
# 1 CLAUDE.md missing or contains no `team-superpower` fenced block
|
|
13
|
-
# 2 bad arguments
|
|
14
|
-
# 3 key not found (only for `get`)
|
|
15
|
-
#
|
|
16
|
-
# Notes:
|
|
17
|
-
# - The fenced block is recognised by the literal opener "```team-superpower"
|
|
18
|
-
# (with no language indent) and the matching "```" closer.
|
|
19
|
-
# - Comments starting with `#` (after optional whitespace) are stripped before
|
|
20
|
-
# value extraction. Inline `# comment` tails are also stripped.
|
|
21
|
-
# - This is a deliberately tiny parser. It does NOT validate the schema.
|
|
22
|
-
# Schema validation is the lead's job (it halts on bad values per the spec).
|
|
23
|
-
|
|
24
|
-
set -euo pipefail
|
|
25
|
-
|
|
26
|
-
usage() {
|
|
27
|
-
sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
CLAUDE_FILE_DEFAULT="${CLAUDE_PROJECT_DIR:-$PWD}/CLAUDE.md"
|
|
31
|
-
|
|
32
|
-
extract_block() {
|
|
33
|
-
local file="$1"
|
|
34
|
-
[ -f "$file" ] || return 1
|
|
35
|
-
# awk: print lines strictly between the opener and closer fences.
|
|
36
|
-
awk '
|
|
37
|
-
/^```team-superpower[[:space:]]*$/ { in_block = 1; next }
|
|
38
|
-
in_block && /^```[[:space:]]*$/ { in_block = 0; exit }
|
|
39
|
-
in_block { print }
|
|
40
|
-
' "$file"
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
block_has_content() {
|
|
44
|
-
# Returns 0 if the YAML block extracted to stdin has at least one non-comment,
|
|
45
|
-
# non-blank line; 1 otherwise.
|
|
46
|
-
local payload
|
|
47
|
-
payload="$(cat || true)"
|
|
48
|
-
printf '%s' "$payload" | grep -vE '^[[:space:]]*(#|$)' | head -n1 | grep -q .
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
# Strip an inline `# comment` tail (but not `#` inside a quoted string — we
|
|
52
|
-
# tolerate the simple case here, the spec doesn't ship complex YAML values).
|
|
53
|
-
strip_comment() {
|
|
54
|
-
sed -E 's/[[:space:]]+#[^"\x27]*$//' | sed -E 's/^[[:space:]]*#.*$//'
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
cmd_extract() {
|
|
58
|
-
local file="${1:-$CLAUDE_FILE_DEFAULT}"
|
|
59
|
-
if [ ! -f "$file" ]; then
|
|
60
|
-
echo "parse-claudemd: $file does not exist" >&2
|
|
61
|
-
return 1
|
|
62
|
-
fi
|
|
63
|
-
local block
|
|
64
|
-
block="$(extract_block "$file")"
|
|
65
|
-
if [ -z "$block" ] || ! printf '%s' "$block" | block_has_content; then
|
|
66
|
-
echo "parse-claudemd: no \`team-superpower\` block found in $file" >&2
|
|
67
|
-
return 1
|
|
68
|
-
fi
|
|
69
|
-
printf '%s\n' "$block"
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
# Read a top-level scalar like `backend: none` or detect whether a group is
|
|
73
|
-
# explicitly "none".
|
|
74
|
-
group_is_none() {
|
|
75
|
-
# group_is_none <yaml-block-on-stdin> <group-name>
|
|
76
|
-
local group="$2"
|
|
77
|
-
awk -v g="$group" '
|
|
78
|
-
BEGIN { found = 0 }
|
|
79
|
-
{
|
|
80
|
-
sub(/[[:space:]]+#[^"\x27]*$/, "")
|
|
81
|
-
sub(/^[[:space:]]*#.*$/, "")
|
|
82
|
-
}
|
|
83
|
-
$0 ~ "^"g":[[:space:]]+none[[:space:]]*$" { found = 1; exit }
|
|
84
|
-
END { exit found ? 0 : 1 }
|
|
85
|
-
' <<<"$1"
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
# A group is "present" if there is `group:` followed (on subsequent indented
|
|
89
|
-
# lines) by at least one non-comment, non-blank key/value pair.
|
|
90
|
-
group_is_present() {
|
|
91
|
-
# group_is_present <yaml-block> <group-name>
|
|
92
|
-
local group="$2"
|
|
93
|
-
awk -v g="$group" '
|
|
94
|
-
BEGIN { state = 0; has_keys = 0 }
|
|
95
|
-
{
|
|
96
|
-
line = $0
|
|
97
|
-
# strip comments
|
|
98
|
-
sub(/[[:space:]]+#[^"\x27]*$/, "", line)
|
|
99
|
-
sub(/^[[:space:]]*#.*$/, "", line)
|
|
100
|
-
}
|
|
101
|
-
line ~ "^"g":[[:space:]]*$" { state = 1; next }
|
|
102
|
-
line ~ "^"g":[[:space:]]+none" { state = 0; exit }
|
|
103
|
-
state == 1 && line ~ /^[^[:space:]]/ { state = 0 }
|
|
104
|
-
state == 1 && line ~ /^[[:space:]]+[A-Za-z_][A-Za-z0-9_]*:/ { has_keys = 1 }
|
|
105
|
-
END { exit has_keys ? 0 : 1 }
|
|
106
|
-
' <<<"$1"
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
cmd_shape() {
|
|
110
|
-
local file="${1:-$CLAUDE_FILE_DEFAULT}"
|
|
111
|
-
local block
|
|
112
|
-
block="$(cmd_extract "$file")" || return 1
|
|
113
|
-
|
|
114
|
-
local be_present=1 fe_present=1
|
|
115
|
-
group_is_present "$block" backend || be_present=0
|
|
116
|
-
group_is_present "$block" frontend || fe_present=0
|
|
117
|
-
|
|
118
|
-
if [ "$be_present" -eq 1 ] && [ "$fe_present" -eq 1 ]; then echo "full-stack"
|
|
119
|
-
elif [ "$be_present" -eq 1 ]; then echo "be-only"
|
|
120
|
-
elif [ "$fe_present" -eq 1 ]; then echo "fe-only"
|
|
121
|
-
else echo "none"
|
|
122
|
-
fi
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
cmd_get() {
|
|
126
|
-
local key="${1:-}"
|
|
127
|
-
local file="${2:-$CLAUDE_FILE_DEFAULT}"
|
|
128
|
-
if [ -z "$key" ]; then usage >&2; return 2; fi
|
|
129
|
-
local block
|
|
130
|
-
block="$(cmd_extract "$file")" || return 1
|
|
131
|
-
|
|
132
|
-
case "$key" in
|
|
133
|
-
*.*)
|
|
134
|
-
local group leaf
|
|
135
|
-
group="${key%%.*}"
|
|
136
|
-
leaf="${key#*.}"
|
|
137
|
-
local value
|
|
138
|
-
value="$(awk -v g="$group" -v k="$leaf" '
|
|
139
|
-
BEGIN { in_group = 0; pat = "^[[:space:]]+" k "[[:space:]]*:[[:space:]]*" }
|
|
140
|
-
{
|
|
141
|
-
line = $0
|
|
142
|
-
sub(/[[:space:]]+#[^"\x27]*$/, "", line)
|
|
143
|
-
sub(/^[[:space:]]*#.*$/, "", line)
|
|
144
|
-
}
|
|
145
|
-
line ~ "^"g":[[:space:]]*$" { in_group = 1; next }
|
|
146
|
-
in_group && line ~ /^[^[:space:]]/ { in_group = 0 }
|
|
147
|
-
in_group && line ~ pat {
|
|
148
|
-
v = line
|
|
149
|
-
sub(pat, "", v)
|
|
150
|
-
sub(/[[:space:]]+$/, "", v)
|
|
151
|
-
gsub(/^"|"$/, "", v)
|
|
152
|
-
gsub(/^\x27|\x27$/, "", v)
|
|
153
|
-
print v
|
|
154
|
-
exit
|
|
155
|
-
}
|
|
156
|
-
' <<<"$block")"
|
|
157
|
-
if [ -z "$value" ]; then return 3; fi
|
|
158
|
-
printf '%s\n' "$value"
|
|
159
|
-
;;
|
|
160
|
-
*)
|
|
161
|
-
# Top-level scalar (e.g. "backend" for `backend: none`)
|
|
162
|
-
local value
|
|
163
|
-
value="$(awk -v k="$key" '
|
|
164
|
-
BEGIN { pat = "^" k ":[[:space:]]+" }
|
|
165
|
-
{
|
|
166
|
-
line = $0
|
|
167
|
-
sub(/[[:space:]]+#[^"\x27]*$/, "", line)
|
|
168
|
-
sub(/^[[:space:]]*#.*$/, "", line)
|
|
169
|
-
}
|
|
170
|
-
line ~ pat {
|
|
171
|
-
v = line; sub(pat, "", v); sub(/[[:space:]]+$/, "", v)
|
|
172
|
-
print v
|
|
173
|
-
exit
|
|
174
|
-
}
|
|
175
|
-
' <<<"$block")"
|
|
176
|
-
if [ -z "$value" ]; then return 3; fi
|
|
177
|
-
printf '%s\n' "$value"
|
|
178
|
-
;;
|
|
179
|
-
esac
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
main() {
|
|
183
|
-
local sub="${1:-}"
|
|
184
|
-
shift || true
|
|
185
|
-
case "$sub" in
|
|
186
|
-
extract) cmd_extract "$@" ;;
|
|
187
|
-
shape) cmd_shape "$@" ;;
|
|
188
|
-
get) cmd_get "$@" ;;
|
|
189
|
-
-h|--help|"") usage; [ -z "$sub" ] && return 2 || return 0 ;;
|
|
190
|
-
*) echo "unknown subcommand: $sub" >&2; usage >&2; return 2 ;;
|
|
191
|
-
esac
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
main "$@"
|