@hustle-together/api-dev-tools 3.12.16 → 4.5.3
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/adr-requests/.gitkeep +10 -0
- package/.claude/agents/adr-researcher.md +109 -0
- package/.claude/agents/visual-analyzer.md +183 -0
- package/.claude/api-dev-state.json +10 -0
- package/.claude/documentation-audit.json +114 -0
- package/.claude/registry.json +289 -0
- package/.claude/settings.json +45 -1
- package/.claude/settings.local.json +1 -7
- package/.claude/workflow-logs/None.json +49 -0
- package/.claude/workflow-logs/session-20251230-143727.json +106 -0
- package/.skills/adr-deep-research/SKILL.md +351 -0
- package/.skills/api-create/SKILL.md +34 -20
- package/.skills/api-research/SKILL.md +130 -0
- package/.skills/docs-update/SKILL.md +205 -0
- package/.skills/hustle-brand/SKILL.md +368 -0
- package/.skills/hustle-build/SKILL.md +365 -38
- package/.skills/parallel-spawn/SKILL.md +212 -0
- package/.skills/ralph-continue/SKILL.md +151 -0
- package/.skills/ralph-loop/SKILL.md +341 -0
- package/.skills/ralph-status/SKILL.md +87 -0
- package/.skills/refactor/SKILL.md +59 -0
- package/.skills/shadcn/SKILL.md +522 -0
- package/.skills/test-all/SKILL.md +210 -0
- package/.skills/test-builds/SKILL.md +208 -0
- package/.skills/test-debug/SKILL.md +212 -0
- package/.skills/test-e2e/SKILL.md +168 -0
- package/.skills/test-review/SKILL.md +707 -0
- package/.skills/test-unit/SKILL.md +143 -0
- package/.skills/test-visual/SKILL.md +301 -0
- package/.skills/token-report/SKILL.md +132 -0
- package/CHANGELOG.md +488 -0
- package/README.md +346 -53
- package/bin/cli.js +359 -123
- package/hooks/__pycache__/api-workflow-check.cpython-314.pyc +0 -0
- package/hooks/__pycache__/auto-answer.cpython-314.pyc +0 -0
- package/hooks/__pycache__/cache-research.cpython-314.pyc +0 -0
- package/hooks/__pycache__/check-api-routes.cpython-314.pyc +0 -0
- package/hooks/__pycache__/check-playwright-setup.cpython-314.pyc +0 -0
- package/hooks/__pycache__/check-storybook-setup.cpython-314.pyc +0 -0
- package/hooks/__pycache__/check-update.cpython-314.pyc +0 -0
- package/hooks/__pycache__/completion-promise-detector.cpython-314.pyc +0 -0
- package/hooks/__pycache__/context-capacity-warning.cpython-314.pyc +0 -0
- package/hooks/__pycache__/detect-interruption.cpython-314.pyc +0 -0
- package/hooks/__pycache__/docs-update-check.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-a11y-audit.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-brand-guide.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-component-type-confirm.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-deep-research.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-disambiguation.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-documentation.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-dry-run.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-environment.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-external-research.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-freshness.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-interview.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-page-components.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-page-data-schema.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-questions-sourced.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-refactor.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-research.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-schema-from-interview.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-schema.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-scope.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-tdd-red.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-ui-disambiguation.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-ui-interview.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-verify.cpython-314.pyc +0 -0
- package/hooks/__pycache__/generate-adr-options.cpython-314.pyc +0 -0
- package/hooks/__pycache__/generate-manifest-entry.cpython-314.pyc +0 -0
- package/hooks/__pycache__/hook_utils.cpython-314.pyc +0 -0
- package/hooks/__pycache__/notify-input-needed.cpython-314.pyc +0 -0
- package/hooks/__pycache__/notify-phase-complete.cpython-314.pyc +0 -0
- package/hooks/__pycache__/ntfy-on-question.cpython-314.pyc +0 -0
- package/hooks/__pycache__/orchestrator-completion.cpython-314.pyc +0 -0
- package/hooks/__pycache__/orchestrator-handoff.cpython-314.pyc +0 -0
- package/hooks/__pycache__/orchestrator-session-startup.cpython-314.pyc +0 -0
- package/hooks/__pycache__/parallel-orchestrator.cpython-314.pyc +0 -0
- package/hooks/__pycache__/periodic-reground.cpython-314.pyc +0 -0
- package/hooks/__pycache__/project-document-prompt.cpython-314.pyc +0 -0
- package/hooks/__pycache__/remote-question-proxy.cpython-314.pyc +0 -0
- package/hooks/__pycache__/remote-question-server.cpython-314.pyc +0 -0
- package/hooks/__pycache__/run-code-review.cpython-314.pyc +0 -0
- package/hooks/__pycache__/run-visual-qa.cpython-314.pyc +0 -0
- package/hooks/__pycache__/session-logger.cpython-314.pyc +0 -0
- package/hooks/__pycache__/session-startup.cpython-314.pyc +0 -0
- package/hooks/__pycache__/track-scope-coverage.cpython-314.pyc +0 -0
- package/hooks/__pycache__/track-token-usage.cpython-314.pyc +0 -0
- package/hooks/__pycache__/track-tool-use.cpython-314.pyc +0 -0
- package/hooks/__pycache__/update-adr-decision.cpython-314.pyc +0 -0
- package/hooks/__pycache__/update-api-showcase.cpython-314.pyc +0 -0
- package/hooks/__pycache__/update-registry.cpython-314.pyc +0 -0
- package/hooks/__pycache__/update-ui-showcase.cpython-314.pyc +0 -0
- package/hooks/__pycache__/verify-after-green.cpython-314.pyc +0 -0
- package/hooks/__pycache__/verify-implementation.cpython-314.pyc +0 -0
- package/hooks/api-workflow-check.py +34 -0
- package/hooks/auto-answer.py +97 -20
- package/{.claude/hooks → hooks}/completion-promise-detector.py +0 -0
- package/{.claude/hooks → hooks}/context-capacity-warning.py +0 -0
- package/{.claude/hooks → hooks}/docs-update-check.py +0 -0
- package/{.claude/hooks → hooks}/enforce-dry-run.py +0 -0
- package/hooks/enforce-external-research.py +25 -0
- package/hooks/enforce-interview.py +20 -0
- package/{.claude/hooks → hooks}/generate-adr-options.py +0 -0
- package/{.claude/hooks → hooks}/hook_utils.py +0 -0
- package/hooks/ntfy-on-question.py +15 -2
- package/hooks/orchestrator-handoff.py +81 -3
- package/{.claude/hooks → hooks}/parallel-orchestrator.py +0 -0
- package/hooks/periodic-reground.py +40 -0
- package/{.claude/hooks → hooks}/remote-question-server.py +0 -0
- package/hooks/run-code-review.py +176 -29
- package/{.claude/hooks → hooks}/run-visual-qa.py +0 -0
- package/hooks/session-logger.py +27 -1
- package/hooks/session-startup.py +113 -0
- package/{.claude/hooks → hooks}/update-adr-decision.py +0 -0
- package/package.json +1 -1
- package/templates/.skills/hustle-interview/SKILL.md +174 -0
- package/templates/adr-viewer/_components/ADRViewer.tsx +326 -0
- package/templates/api-dev-state.json +33 -1
- package/templates/brand-page/page.tsx +645 -0
- package/templates/component/Component.visual.spec.ts +30 -24
- package/templates/eslint-plugin-zod-schema/index.js +446 -0
- package/templates/eslint-plugin-zod-schema/package.json +26 -0
- package/templates/github-workflows/security.yml +274 -0
- package/templates/hustle-build-defaults.json +53 -1
- package/templates/page/page.e2e.test.ts +30 -26
- package/templates/performance-budgets.json +63 -5
- package/templates/registry.json +279 -3
- package/templates/review-dashboard/page.tsx +510 -0
- package/templates/settings.json +74 -7
- package/templates/ui-showcase/_components/UIShowcase.tsx +47 -0
- package/templates/ui-showcase/_components/VisualTestingDashboard.tsx +579 -0
- package/.claude/commands/hustle-combine.md +0 -1089
- package/.claude/commands/hustle-ui-create-page.md +0 -1078
- package/.claude/commands/hustle-ui-create.md +0 -1058
- package/.claude/hooks/auto-answer.py +0 -305
- package/.claude/hooks/cache-research.py +0 -337
- package/.claude/hooks/check-api-routes.py +0 -168
- package/.claude/hooks/check-playwright-setup.py +0 -103
- package/.claude/hooks/check-storybook-setup.py +0 -81
- package/.claude/hooks/check-update.py +0 -132
- package/.claude/hooks/detect-interruption.py +0 -165
- package/.claude/hooks/enforce-a11y-audit.py +0 -202
- package/.claude/hooks/enforce-brand-guide.py +0 -241
- package/.claude/hooks/enforce-component-type-confirm.py +0 -97
- package/.claude/hooks/enforce-freshness.py +0 -184
- package/.claude/hooks/enforce-page-components.py +0 -186
- package/.claude/hooks/enforce-page-data-schema.py +0 -155
- package/.claude/hooks/enforce-questions-sourced.py +0 -146
- package/.claude/hooks/enforce-schema-from-interview.py +0 -248
- package/.claude/hooks/enforce-ui-disambiguation.py +0 -108
- package/.claude/hooks/enforce-ui-interview.py +0 -130
- package/.claude/hooks/generate-manifest-entry.py +0 -1161
- package/.claude/hooks/lib/__init__.py +0 -1
- package/.claude/hooks/lib/greptile.py +0 -355
- package/.claude/hooks/lib/ntfy.py +0 -209
- package/.claude/hooks/notify-input-needed.py +0 -73
- package/.claude/hooks/notify-phase-complete.py +0 -90
- package/.claude/hooks/ntfy-on-question.py +0 -240
- package/.claude/hooks/orchestrator-completion.py +0 -313
- package/.claude/hooks/orchestrator-handoff.py +0 -267
- package/.claude/hooks/orchestrator-session-startup.py +0 -146
- package/.claude/hooks/run-code-review.py +0 -393
- package/.claude/hooks/session-logger.py +0 -323
- package/.claude/hooks/test-orchestrator-reground.py +0 -248
- package/.claude/hooks/track-scope-coverage.py +0 -220
- package/.claude/hooks/track-token-usage.py +0 -121
- package/.claude/hooks/update-api-showcase.py +0 -161
- package/.claude/hooks/update-registry.py +0 -352
- package/.claude/hooks/update-ui-showcase.py +0 -224
- package/.claude/test-auto-answer-bot.py +0 -183
- package/.claude/test-completion-detector.py +0 -263
- package/.claude/test-orchestrator-state.json +0 -20
- package/.claude/test-orchestrator.sh +0 -271
- /package/{.claude/commands → commands}/hustle-build.md +0 -0
- /package/{.claude/hooks → hooks}/lib/__pycache__/__init__.cpython-314.pyc +0 -0
- /package/{.claude/hooks → hooks}/lib/__pycache__/greptile.cpython-314.pyc +0 -0
- /package/{.claude/hooks → hooks}/lib/__pycache__/ntfy.cpython-314.pyc +0 -0
- /package/{.claude/hooks → hooks}/project-document-prompt.py +0 -0
- /package/{.claude/hooks → hooks}/remote-question-proxy.py +0 -0
- /package/{.claude/hooks → hooks}/update-testing-checklist.py +0 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# Security Audit Workflow
|
|
2
|
+
# Copy to: .github/workflows/security.yml
|
|
3
|
+
#
|
|
4
|
+
# Features:
|
|
5
|
+
# - Dependency vulnerability scanning (npm/pnpm audit)
|
|
6
|
+
# - License compliance checking
|
|
7
|
+
# - Secret detection (gitleaks)
|
|
8
|
+
# - Static analysis (Semgrep)
|
|
9
|
+
#
|
|
10
|
+
# See: docs/SECURITY-AUDIT.md
|
|
11
|
+
|
|
12
|
+
name: Security Audit
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
push:
|
|
16
|
+
branches: [main, master]
|
|
17
|
+
pull_request:
|
|
18
|
+
branches: [main, master]
|
|
19
|
+
schedule:
|
|
20
|
+
# Run weekly on Sundays at midnight
|
|
21
|
+
- cron: '0 0 * * 0'
|
|
22
|
+
workflow_dispatch: # Allow manual trigger
|
|
23
|
+
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
security-events: write
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
dependency-audit:
|
|
30
|
+
name: Dependency Audit
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- name: Checkout repository
|
|
34
|
+
uses: actions/checkout@v4
|
|
35
|
+
|
|
36
|
+
- name: Setup Node.js
|
|
37
|
+
uses: actions/setup-node@v4
|
|
38
|
+
with:
|
|
39
|
+
node-version: '20'
|
|
40
|
+
|
|
41
|
+
- name: Detect package manager
|
|
42
|
+
id: detect-pm
|
|
43
|
+
run: |
|
|
44
|
+
if [ -f "pnpm-lock.yaml" ]; then
|
|
45
|
+
echo "manager=pnpm" >> $GITHUB_OUTPUT
|
|
46
|
+
elif [ -f "yarn.lock" ]; then
|
|
47
|
+
echo "manager=yarn" >> $GITHUB_OUTPUT
|
|
48
|
+
else
|
|
49
|
+
echo "manager=npm" >> $GITHUB_OUTPUT
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
- name: Setup pnpm
|
|
53
|
+
if: steps.detect-pm.outputs.manager == 'pnpm'
|
|
54
|
+
uses: pnpm/action-setup@v2
|
|
55
|
+
with:
|
|
56
|
+
version: 9
|
|
57
|
+
|
|
58
|
+
- name: Install dependencies
|
|
59
|
+
run: |
|
|
60
|
+
if [ "${{ steps.detect-pm.outputs.manager }}" == "pnpm" ]; then
|
|
61
|
+
pnpm install --frozen-lockfile
|
|
62
|
+
elif [ "${{ steps.detect-pm.outputs.manager }}" == "yarn" ]; then
|
|
63
|
+
yarn install --frozen-lockfile
|
|
64
|
+
else
|
|
65
|
+
npm ci
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
- name: Run dependency audit
|
|
69
|
+
id: audit
|
|
70
|
+
continue-on-error: true
|
|
71
|
+
run: |
|
|
72
|
+
if [ "${{ steps.detect-pm.outputs.manager }}" == "pnpm" ]; then
|
|
73
|
+
pnpm audit --audit-level=moderate > audit-results.txt 2>&1 || true
|
|
74
|
+
pnpm audit --json > audit-results.json 2>&1 || true
|
|
75
|
+
elif [ "${{ steps.detect-pm.outputs.manager }}" == "yarn" ]; then
|
|
76
|
+
yarn audit --level moderate > audit-results.txt 2>&1 || true
|
|
77
|
+
yarn audit --json > audit-results.json 2>&1 || true
|
|
78
|
+
else
|
|
79
|
+
npm audit --audit-level=moderate > audit-results.txt 2>&1 || true
|
|
80
|
+
npm audit --json > audit-results.json 2>&1 || true
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
- name: Parse audit results
|
|
84
|
+
id: parse
|
|
85
|
+
run: |
|
|
86
|
+
if [ -f audit-results.json ]; then
|
|
87
|
+
# Extract vulnerability counts (handles both npm and pnpm formats)
|
|
88
|
+
CRITICAL=$(cat audit-results.json | jq '.metadata.vulnerabilities.critical // .vulnerabilities.critical // 0' 2>/dev/null || echo "0")
|
|
89
|
+
HIGH=$(cat audit-results.json | jq '.metadata.vulnerabilities.high // .vulnerabilities.high // 0' 2>/dev/null || echo "0")
|
|
90
|
+
MODERATE=$(cat audit-results.json | jq '.metadata.vulnerabilities.moderate // .vulnerabilities.moderate // 0' 2>/dev/null || echo "0")
|
|
91
|
+
|
|
92
|
+
echo "critical=$CRITICAL" >> $GITHUB_OUTPUT
|
|
93
|
+
echo "high=$HIGH" >> $GITHUB_OUTPUT
|
|
94
|
+
echo "moderate=$MODERATE" >> $GITHUB_OUTPUT
|
|
95
|
+
|
|
96
|
+
# Fail on critical or high
|
|
97
|
+
if [ "$CRITICAL" -gt 0 ] || [ "$HIGH" -gt 0 ]; then
|
|
98
|
+
echo "status=failed" >> $GITHUB_OUTPUT
|
|
99
|
+
else
|
|
100
|
+
echo "status=passed" >> $GITHUB_OUTPUT
|
|
101
|
+
fi
|
|
102
|
+
else
|
|
103
|
+
echo "status=unknown" >> $GITHUB_OUTPUT
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
- name: Upload audit results
|
|
107
|
+
uses: actions/upload-artifact@v4
|
|
108
|
+
with:
|
|
109
|
+
name: dependency-audit
|
|
110
|
+
path: |
|
|
111
|
+
audit-results.txt
|
|
112
|
+
audit-results.json
|
|
113
|
+
|
|
114
|
+
- name: Create summary
|
|
115
|
+
run: |
|
|
116
|
+
echo "## Dependency Audit Results" >> $GITHUB_STEP_SUMMARY
|
|
117
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
118
|
+
echo "| Severity | Count |" >> $GITHUB_STEP_SUMMARY
|
|
119
|
+
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
|
|
120
|
+
echo "| Critical | ${{ steps.parse.outputs.critical || 'N/A' }} |" >> $GITHUB_STEP_SUMMARY
|
|
121
|
+
echo "| High | ${{ steps.parse.outputs.high || 'N/A' }} |" >> $GITHUB_STEP_SUMMARY
|
|
122
|
+
echo "| Moderate | ${{ steps.parse.outputs.moderate || 'N/A' }} |" >> $GITHUB_STEP_SUMMARY
|
|
123
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
124
|
+
if [ "${{ steps.parse.outputs.status }}" == "failed" ]; then
|
|
125
|
+
echo "**Status: FAILED** - Critical or high vulnerabilities found" >> $GITHUB_STEP_SUMMARY
|
|
126
|
+
else
|
|
127
|
+
echo "**Status: PASSED**" >> $GITHUB_STEP_SUMMARY
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
- name: Fail on critical/high vulnerabilities
|
|
131
|
+
if: steps.parse.outputs.status == 'failed'
|
|
132
|
+
run: |
|
|
133
|
+
echo "::error::Found ${{ steps.parse.outputs.critical }} critical and ${{ steps.parse.outputs.high }} high vulnerabilities"
|
|
134
|
+
exit 1
|
|
135
|
+
|
|
136
|
+
license-check:
|
|
137
|
+
name: License Compliance
|
|
138
|
+
runs-on: ubuntu-latest
|
|
139
|
+
steps:
|
|
140
|
+
- name: Checkout repository
|
|
141
|
+
uses: actions/checkout@v4
|
|
142
|
+
|
|
143
|
+
- name: Setup Node.js
|
|
144
|
+
uses: actions/setup-node@v4
|
|
145
|
+
with:
|
|
146
|
+
node-version: '20'
|
|
147
|
+
|
|
148
|
+
- name: Detect package manager
|
|
149
|
+
id: detect-pm
|
|
150
|
+
run: |
|
|
151
|
+
if [ -f "pnpm-lock.yaml" ]; then
|
|
152
|
+
echo "manager=pnpm" >> $GITHUB_OUTPUT
|
|
153
|
+
elif [ -f "yarn.lock" ]; then
|
|
154
|
+
echo "manager=yarn" >> $GITHUB_OUTPUT
|
|
155
|
+
else
|
|
156
|
+
echo "manager=npm" >> $GITHUB_OUTPUT
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
- name: Setup pnpm
|
|
160
|
+
if: steps.detect-pm.outputs.manager == 'pnpm'
|
|
161
|
+
uses: pnpm/action-setup@v2
|
|
162
|
+
with:
|
|
163
|
+
version: 9
|
|
164
|
+
|
|
165
|
+
- name: Install dependencies
|
|
166
|
+
run: |
|
|
167
|
+
if [ "${{ steps.detect-pm.outputs.manager }}" == "pnpm" ]; then
|
|
168
|
+
pnpm install --frozen-lockfile
|
|
169
|
+
elif [ "${{ steps.detect-pm.outputs.manager }}" == "yarn" ]; then
|
|
170
|
+
yarn install --frozen-lockfile
|
|
171
|
+
else
|
|
172
|
+
npm ci
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
- name: Check licenses
|
|
176
|
+
run: |
|
|
177
|
+
npx license-checker --production --onlyAllow 'MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;CC0-1.0;Unlicense;0BSD;CC-BY-4.0;CC-BY-3.0;Python-2.0;Artistic-2.0;Zlib;WTFPL' > license-results.txt 2>&1 || true
|
|
178
|
+
|
|
179
|
+
# Check for failures
|
|
180
|
+
if grep -q "FAILED" license-results.txt; then
|
|
181
|
+
echo "::error::Non-approved licenses found"
|
|
182
|
+
cat license-results.txt
|
|
183
|
+
exit 1
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
- name: Upload license results
|
|
187
|
+
uses: actions/upload-artifact@v4
|
|
188
|
+
with:
|
|
189
|
+
name: license-check
|
|
190
|
+
path: license-results.txt
|
|
191
|
+
|
|
192
|
+
secret-scan:
|
|
193
|
+
name: Secret Scanning
|
|
194
|
+
runs-on: ubuntu-latest
|
|
195
|
+
steps:
|
|
196
|
+
- name: Checkout repository
|
|
197
|
+
uses: actions/checkout@v4
|
|
198
|
+
with:
|
|
199
|
+
fetch-depth: 0 # Full history for secret scanning
|
|
200
|
+
|
|
201
|
+
- name: Gitleaks scan
|
|
202
|
+
uses: gitleaks/gitleaks-action@v2
|
|
203
|
+
env:
|
|
204
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
205
|
+
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} # Optional for enterprise
|
|
206
|
+
|
|
207
|
+
sast:
|
|
208
|
+
name: Static Analysis (SAST)
|
|
209
|
+
runs-on: ubuntu-latest
|
|
210
|
+
steps:
|
|
211
|
+
- name: Checkout repository
|
|
212
|
+
uses: actions/checkout@v4
|
|
213
|
+
|
|
214
|
+
- name: Run Semgrep
|
|
215
|
+
uses: returntocorp/semgrep-action@v1
|
|
216
|
+
with:
|
|
217
|
+
config: >-
|
|
218
|
+
p/security-audit
|
|
219
|
+
p/typescript
|
|
220
|
+
p/react
|
|
221
|
+
p/nodejs
|
|
222
|
+
p/jwt
|
|
223
|
+
p/sql-injection
|
|
224
|
+
p/xss
|
|
225
|
+
env:
|
|
226
|
+
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} # Optional for dashboard
|
|
227
|
+
|
|
228
|
+
# Summary job that depends on all checks
|
|
229
|
+
security-summary:
|
|
230
|
+
name: Security Summary
|
|
231
|
+
runs-on: ubuntu-latest
|
|
232
|
+
needs: [dependency-audit, license-check, secret-scan, sast]
|
|
233
|
+
if: always()
|
|
234
|
+
steps:
|
|
235
|
+
- name: Check all results
|
|
236
|
+
run: |
|
|
237
|
+
echo "## Security Audit Summary" >> $GITHUB_STEP_SUMMARY
|
|
238
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
239
|
+
echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY
|
|
240
|
+
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
|
|
241
|
+
|
|
242
|
+
# Dependency audit
|
|
243
|
+
if [ "${{ needs.dependency-audit.result }}" == "success" ]; then
|
|
244
|
+
echo "| Dependency Audit | :white_check_mark: Passed |" >> $GITHUB_STEP_SUMMARY
|
|
245
|
+
else
|
|
246
|
+
echo "| Dependency Audit | :x: Failed |" >> $GITHUB_STEP_SUMMARY
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
# License check
|
|
250
|
+
if [ "${{ needs.license-check.result }}" == "success" ]; then
|
|
251
|
+
echo "| License Compliance | :white_check_mark: Passed |" >> $GITHUB_STEP_SUMMARY
|
|
252
|
+
else
|
|
253
|
+
echo "| License Compliance | :x: Failed |" >> $GITHUB_STEP_SUMMARY
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
# Secret scan
|
|
257
|
+
if [ "${{ needs.secret-scan.result }}" == "success" ]; then
|
|
258
|
+
echo "| Secret Scanning | :white_check_mark: Passed |" >> $GITHUB_STEP_SUMMARY
|
|
259
|
+
else
|
|
260
|
+
echo "| Secret Scanning | :x: Failed |" >> $GITHUB_STEP_SUMMARY
|
|
261
|
+
fi
|
|
262
|
+
|
|
263
|
+
# SAST
|
|
264
|
+
if [ "${{ needs.sast.result }}" == "success" ]; then
|
|
265
|
+
echo "| Static Analysis | :white_check_mark: Passed |" >> $GITHUB_STEP_SUMMARY
|
|
266
|
+
else
|
|
267
|
+
echo "| Static Analysis | :x: Failed |" >> $GITHUB_STEP_SUMMARY
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
- name: Fail if any check failed
|
|
271
|
+
if: contains(needs.*.result, 'failure')
|
|
272
|
+
run: |
|
|
273
|
+
echo "::error::One or more security checks failed"
|
|
274
|
+
exit 1
|
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "hustle-build-defaults",
|
|
3
|
-
"description": "Pre-configured default answers for
|
|
3
|
+
"description": "Pre-configured default answers for autonomous mode. Interviews use these defaults unless overridden.",
|
|
4
|
+
"version": "4.5.0",
|
|
5
|
+
"_usage": "Copy to .claude/hustle-build-defaults.json to customize for your project",
|
|
6
|
+
|
|
7
|
+
"adr": {
|
|
8
|
+
"enabled": true,
|
|
9
|
+
"significant_decisions": {
|
|
10
|
+
"database": ["supabase", "firebase", "postgres", "mysql", "mongodb", "sqlite", "planetscale", "neon"],
|
|
11
|
+
"auth": ["api key", "oauth", "jwt", "session", "cookie", "basic auth", "api-key", "bearer"],
|
|
12
|
+
"cache": ["redis", "memcached", "in-memory", "cdn", "edge", "vercel kv"],
|
|
13
|
+
"hosting": ["vercel", "netlify", "aws", "cloudflare", "railway", "render", "fly.io"],
|
|
14
|
+
"state": ["redux", "zustand", "jotai", "context", "mobx", "recoil", "valtio"],
|
|
15
|
+
"styling": ["tailwind", "css modules", "styled-components", "emotion", "vanilla-extract"]
|
|
16
|
+
},
|
|
17
|
+
"min_options_for_adr": 2
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
"autonomous": {
|
|
21
|
+
"enabled": true,
|
|
22
|
+
"skip_interviews": true,
|
|
23
|
+
"use_defaults_for_questions": true,
|
|
24
|
+
"ralph_wiggum_loops": true,
|
|
25
|
+
"max_iterations": 25,
|
|
26
|
+
"emit_promises": true,
|
|
27
|
+
"auto_fix_visual_issues": true,
|
|
28
|
+
"auto_fix_review_issues": true,
|
|
29
|
+
"auto_refactor": true
|
|
30
|
+
},
|
|
4
31
|
|
|
5
32
|
"orchestrator": {
|
|
6
33
|
"auth_required": true,
|
|
@@ -80,5 +107,30 @@
|
|
|
80
107
|
"accessibility": "accessibility",
|
|
81
108
|
"layout": "layout",
|
|
82
109
|
"seo": "seo"
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
"remote_questions": {
|
|
113
|
+
"enabled": false,
|
|
114
|
+
"port": 8765,
|
|
115
|
+
"wait_mode": false,
|
|
116
|
+
"_instructions": [
|
|
117
|
+
"Set enabled=true or REMOTE_QUESTIONS_ENABLED=true to activate",
|
|
118
|
+
"Start the server: python hooks/remote-question-server.py",
|
|
119
|
+
"Access locally: http://localhost:8765",
|
|
120
|
+
"Access from phone on same network: http://YOUR_COMPUTER_IP:8765",
|
|
121
|
+
"Click 'Enable' in dashboard header for browser notifications",
|
|
122
|
+
"Set wait_mode=true to block until remote answer is received"
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
"ntfy": {
|
|
127
|
+
"enabled": true,
|
|
128
|
+
"topic": "api-dev-tools-test",
|
|
129
|
+
"server": "https://ntfy.sh",
|
|
130
|
+
"notify_on": ["questions", "phase_complete", "errors", "completion"],
|
|
131
|
+
"_instructions": [
|
|
132
|
+
"Subscribe at ntfy.sh/<topic> on your phone",
|
|
133
|
+
"Notifications sent for: questions needing input, phase completions, errors, workflow completion"
|
|
134
|
+
]
|
|
83
135
|
}
|
|
84
136
|
}
|
|
@@ -37,36 +37,40 @@ test.describe("__PAGE_NAME__ Page", () => {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
// ===================================
|
|
40
|
-
// Responsive Tests
|
|
40
|
+
// Responsive Tests (7 Viewports)
|
|
41
41
|
// ===================================
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
43
|
+
// All 7 viewports from performance-budgets.json
|
|
44
|
+
const viewports = [
|
|
45
|
+
{ name: "mobile-portrait", width: 375, height: 667 },
|
|
46
|
+
{ name: "mobile-notch", width: 393, height: 852 },
|
|
47
|
+
{ name: "mobile-landscape", width: 667, height: 375 },
|
|
48
|
+
{ name: "tablet-portrait", width: 768, height: 1024 },
|
|
49
|
+
{ name: "tablet-landscape", width: 1024, height: 768 },
|
|
50
|
+
{ name: "small-desktop", width: 1280, height: 720 },
|
|
51
|
+
{ name: "desktop", width: 1920, height: 1080 },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
for (const viewport of viewports) {
|
|
55
|
+
test(`should be responsive on ${viewport.name} (${viewport.width}x${viewport.height})`, async ({
|
|
56
|
+
page,
|
|
57
|
+
}) => {
|
|
58
|
+
await page.setViewportSize({
|
|
59
|
+
width: viewport.width,
|
|
60
|
+
height: viewport.height,
|
|
61
|
+
});
|
|
62
|
+
await page.goto("/__PAGE_ROUTE__");
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
await page.goto("/__PAGE_ROUTE__");
|
|
64
|
+
// Verify main content is visible
|
|
65
|
+
await expect(page.getByRole("main")).toBeVisible();
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
// Verify no horizontal scroll
|
|
68
|
+
const body = await page.locator("body");
|
|
69
|
+
const scrollWidth = await body.evaluate((el) => el.scrollWidth);
|
|
70
|
+
const clientWidth = await body.evaluate((el) => el.clientWidth);
|
|
71
|
+
expect(scrollWidth).toBeLessThanOrEqual(clientWidth + 1);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
70
74
|
|
|
71
75
|
// ===================================
|
|
72
76
|
// Accessibility Tests
|
|
@@ -48,11 +48,69 @@
|
|
|
48
48
|
},
|
|
49
49
|
|
|
50
50
|
"responsive": {
|
|
51
|
-
"description": "Responsive breakpoints to test",
|
|
51
|
+
"description": "Responsive breakpoints to test (7 comprehensive viewports)",
|
|
52
52
|
"breakpoints": [
|
|
53
|
-
{
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
{
|
|
54
|
+
"name": "mobile-portrait",
|
|
55
|
+
"width": 375,
|
|
56
|
+
"height": 667,
|
|
57
|
+
"description": "iPhone SE baseline",
|
|
58
|
+
"safeArea": null
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "mobile-notch",
|
|
62
|
+
"width": 393,
|
|
63
|
+
"height": 852,
|
|
64
|
+
"description": "iPhone 14 Pro with notch and home indicator",
|
|
65
|
+
"safeArea": {
|
|
66
|
+
"top": 47,
|
|
67
|
+
"bottom": 34,
|
|
68
|
+
"left": 0,
|
|
69
|
+
"right": 0
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "mobile-landscape",
|
|
74
|
+
"width": 667,
|
|
75
|
+
"height": 375,
|
|
76
|
+
"description": "iPhone rotated landscape",
|
|
77
|
+
"safeArea": null
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "tablet-portrait",
|
|
81
|
+
"width": 768,
|
|
82
|
+
"height": 1024,
|
|
83
|
+
"description": "iPad Mini baseline",
|
|
84
|
+
"safeArea": null
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "tablet-landscape",
|
|
88
|
+
"width": 1024,
|
|
89
|
+
"height": 768,
|
|
90
|
+
"description": "iPad Mini rotated",
|
|
91
|
+
"safeArea": null
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"name": "small-desktop",
|
|
95
|
+
"width": 1280,
|
|
96
|
+
"height": 720,
|
|
97
|
+
"description": "Laptop screens, 720p displays",
|
|
98
|
+
"safeArea": null
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"name": "desktop",
|
|
102
|
+
"width": 1920,
|
|
103
|
+
"height": 1080,
|
|
104
|
+
"description": "Standard desktop 1080p",
|
|
105
|
+
"safeArea": null
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
"safeAreaCSS": {
|
|
109
|
+
"description": "CSS environment variables for safe area insets",
|
|
110
|
+
"top": "env(safe-area-inset-top, 0px)",
|
|
111
|
+
"bottom": "env(safe-area-inset-bottom, 0px)",
|
|
112
|
+
"left": "env(safe-area-inset-left, 0px)",
|
|
113
|
+
"right": "env(safe-area-inset-right, 0px)"
|
|
114
|
+
}
|
|
57
115
|
}
|
|
58
116
|
}
|