@drunkcoding/agents-and-skills 0.0.24 → 0.0.26

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.
Files changed (42) hide show
  1. package/.claude-plugin/marketplace.json +14 -33
  2. package/README.md +1 -0
  3. package/package.json +1 -1
  4. package/plugins/html-effectiveness/.claude-plugin/plugin.json +1 -1
  5. package/plugins/plugin-validator/.claude-plugin/plugin.json +1 -1
  6. package/plugins/team-share/.claude-plugin/plugin.json +17 -0
  7. package/plugins/team-share/README.md +30 -0
  8. package/plugins/team-share/agents/team-share.md +140 -0
  9. package/plugins/team-share/commands/team-share.md +4 -0
  10. package/plugins/tech-graph/.claude-plugin/plugin.json +1 -1
  11. package/plugins/auto-power/.claude-plugin/plugin.json +0 -17
  12. package/plugins/auto-power/README.md +0 -80
  13. package/plugins/auto-power/assets/CHECKPOINT_SCHEMA.md +0 -69
  14. package/plugins/auto-power/assets/ESCALATION_TEMPLATE.md +0 -67
  15. package/plugins/auto-power/commands/auto-power-resume.md +0 -32
  16. package/plugins/auto-power/commands/auto-power.md +0 -46
  17. package/plugins/auto-power/skills/auto-power-runtime/SKILL.md +0 -220
  18. package/plugins/team-superpower/.claude-plugin/plugin.json +0 -21
  19. package/plugins/team-superpower/README.md +0 -294
  20. package/plugins/team-superpower/agents/backend-developer.md +0 -221
  21. package/plugins/team-superpower/agents/feature-planner.md +0 -66
  22. package/plugins/team-superpower/agents/frontend-developer.md +0 -242
  23. package/plugins/team-superpower/agents/orchestrator.md +0 -83
  24. package/plugins/team-superpower/agents/qc-engineer.md +0 -84
  25. package/plugins/team-superpower/agents/security-engineer.md +0 -175
  26. package/plugins/team-superpower/agents/solution-architect.md +0 -80
  27. package/plugins/team-superpower/agents/team-leader.md +0 -100
  28. package/plugins/team-superpower/assets/AGENTS.md.template +0 -23
  29. package/plugins/team-superpower/assets/CLAUDE.md.template +0 -117
  30. package/plugins/team-superpower/assets/ESCALATION.md +0 -142
  31. package/plugins/team-superpower/assets/SESSION_README.md +0 -338
  32. package/plugins/team-superpower/commands/team-cleanup.md +0 -70
  33. package/plugins/team-superpower/commands/team-feature.md +0 -317
  34. package/plugins/team-superpower/hooks/hooks.json +0 -25
  35. package/plugins/team-superpower/hooks/task-completed.sh +0 -254
  36. package/plugins/team-superpower/hooks/task-created.sh +0 -174
  37. package/plugins/team-superpower/hooks/teammate-idle.sh +0 -149
  38. package/plugins/team-superpower/scripts/assess-complexity.sh +0 -194
  39. package/plugins/team-superpower/scripts/detect-stack.sh +0 -473
  40. package/plugins/team-superpower/scripts/parse-claudemd.sh +0 -194
  41. package/plugins/team-superpower/scripts/team-state.sh +0 -313
  42. 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 "$@"