@northbridge-security/secureai 0.1.13

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 (50) hide show
  1. package/.claude/README.md +122 -0
  2. package/.claude/commands/architect/clean.md +978 -0
  3. package/.claude/commands/architect/kiss.md +762 -0
  4. package/.claude/commands/architect/review.md +704 -0
  5. package/.claude/commands/catchup.md +90 -0
  6. package/.claude/commands/code.md +115 -0
  7. package/.claude/commands/commit.md +1218 -0
  8. package/.claude/commands/cover.md +1298 -0
  9. package/.claude/commands/fmea.md +275 -0
  10. package/.claude/commands/kaizen.md +312 -0
  11. package/.claude/commands/pr.md +503 -0
  12. package/.claude/commands/todo.md +99 -0
  13. package/.claude/commands/worktree.md +738 -0
  14. package/.claude/commands/wrapup.md +103 -0
  15. package/LICENSE +183 -0
  16. package/README.md +108 -0
  17. package/dist/cli.js +75634 -0
  18. package/docs/agents/devops-reviewer.md +889 -0
  19. package/docs/agents/kiss-simplifier.md +1088 -0
  20. package/docs/agents/typescript.md +8 -0
  21. package/docs/guides/README.md +109 -0
  22. package/docs/guides/agents.clean.arch.md +244 -0
  23. package/docs/guides/agents.clean.arch.ts.md +1314 -0
  24. package/docs/guides/agents.gotask.md +1037 -0
  25. package/docs/guides/agents.markdown.md +1209 -0
  26. package/docs/guides/agents.onepassword.md +285 -0
  27. package/docs/guides/agents.sonar.md +857 -0
  28. package/docs/guides/agents.tdd.md +838 -0
  29. package/docs/guides/agents.tdd.ts.md +1062 -0
  30. package/docs/guides/agents.typesript.md +1389 -0
  31. package/docs/guides/github-mcp.md +1075 -0
  32. package/package.json +130 -0
  33. package/packages/secureai-cli/src/cli.ts +21 -0
  34. package/tasks/README.md +880 -0
  35. package/tasks/aws.yml +64 -0
  36. package/tasks/bash.yml +118 -0
  37. package/tasks/bun.yml +738 -0
  38. package/tasks/claude.yml +183 -0
  39. package/tasks/docker.yml +420 -0
  40. package/tasks/docs.yml +127 -0
  41. package/tasks/git.yml +1336 -0
  42. package/tasks/gotask.yml +132 -0
  43. package/tasks/json.yml +77 -0
  44. package/tasks/markdown.yml +95 -0
  45. package/tasks/onepassword.yml +350 -0
  46. package/tasks/security.yml +102 -0
  47. package/tasks/sonar.yml +437 -0
  48. package/tasks/template.yml +74 -0
  49. package/tasks/vscode.yml +103 -0
  50. package/tasks/yaml.yml +121 -0
@@ -0,0 +1,102 @@
1
+ # Security Tasks
2
+ # Provides local SAST scanning
3
+
4
+ version: "3"
5
+
6
+ vars:
7
+ DEFAULT_PATTERNS: "*.json,**/*.json,!node_modules/**,!dist/**,!.taskmaster/**"
8
+
9
+ tasks:
10
+ default:
11
+ desc: "Show available security tasks"
12
+ aliases: [help, h]
13
+ silent: true
14
+ cmds:
15
+ - |
16
+ # Color codes
17
+ GREEN='\033[0;32m'
18
+ YELLOW='\033[0;33m'
19
+ BOLD='\033[1m'
20
+ NC='\033[0m'
21
+
22
+ echo -e "${BOLD}Security${NC}"
23
+ echo ""
24
+ echo "Command Alias Description Examples"
25
+ echo "───────────────────────────────────────────────────────────────────────────────────────────────────"
26
+ echo -e " ${GREEN}task security:scan${NC} ${YELLOW}s${NC} Run semgrep security scanner "
27
+ echo -e " ${GREEN}task security:fix${NC} ${YELLOW}f${NC} Auto-fix security issues "
28
+ echo -e " ${GREEN}task security:trufflehog${NC} ${YELLOW}th${NC} Scan for secrets with truffleHog "
29
+
30
+ scan:
31
+ desc: Run security scanner on entire repo
32
+ aliases: [s]
33
+ silent: true
34
+ cmds:
35
+ - |
36
+ # Source secrets if .env has 1Password references
37
+ if [ -f .env ] && grep -q "op://" .env 2>/dev/null; then
38
+ TEMP_ENV=$(task op:export)
39
+ source "$TEMP_ENV"
40
+ fi
41
+ semgrep --config=auto --allow-local-builds -q
42
+
43
+ fix:
44
+ desc: Run security scanner on entire repo and automatically apply fixes
45
+ aliases: [sf]
46
+ silent: true
47
+ cmds:
48
+ - |
49
+ # Source secrets if .env has 1Password references
50
+ if [ -f .env ] && grep -q "op://" .env 2>/dev/null; then
51
+ TEMP_ENV=$(task op:export)
52
+ source "$TEMP_ENV"
53
+ fi
54
+ semgrep --config=auto --autofix --allow-local-builds -q
55
+
56
+ trufflehog:
57
+ desc: Run truffleHog secret scanner
58
+ silent: true
59
+ aliases: [th]
60
+ cmds:
61
+ - |
62
+ GREEN='\033[0;32m'
63
+ YELLOW='\033[0;33m'
64
+ RED='\033[0;31m'
65
+ NC='\033[0m'
66
+
67
+ echo -e "${YELLOW}🔍${NC} Running truffleHog secret scanner..."
68
+
69
+ # Check if trufflehog is installed, install if not
70
+ if ! command -v trufflehog >/dev/null 2>&1; then
71
+ echo -e "${YELLOW}⚠️${NC} trufflehog not installed, installing..."
72
+ brew install trufflehog
73
+ fi
74
+
75
+ # Create temporary exclusion file (regex patterns for directories to skip)
76
+ EXCLUDE_FILE=$(mktemp)
77
+ {
78
+ echo "node_modules/"
79
+ echo "__pycache__/"
80
+ echo "venv/"
81
+ echo "\.venv/"
82
+ echo "dist/"
83
+ echo "build/"
84
+ echo "\.git/"
85
+ echo "\.taskmaster/"
86
+ echo "\.logs/"
87
+ echo "coverage/"
88
+ } > "$EXCLUDE_FILE"
89
+
90
+ # Run truffleHog on current directory (exclude common build/dependency dirs)
91
+ echo "Scanning filesystem..."
92
+ if trufflehog filesystem . --exclude-paths="$EXCLUDE_FILE" --no-update --fail; then
93
+ echo -e "${GREEN}✓${NC} No secrets found by truffleHog"
94
+ EXIT_CODE=0
95
+ else
96
+ echo -e "${RED}❌${NC} truffleHog found secrets!"
97
+ EXIT_CODE=1
98
+ fi
99
+
100
+ # Clean up temporary file
101
+ rm -f "$EXCLUDE_FILE"
102
+ exit $EXIT_CODE
@@ -0,0 +1,437 @@
1
+ # SonarQube Code Quality Tasks
2
+ # Provides SonarQube analysis integration and issue management
3
+ #
4
+ # Configuration:
5
+ # - Set SONAR_TOKEN in .env file
6
+ # - Supports 1Password references: op://vault/item/field
7
+ # - Project key read from sonar-project.properties in repository root
8
+
9
+ version: "3"
10
+
11
+ vars:
12
+ REPORT_PATH: "{{.PWD}}/.logs/sonar"
13
+ SONAR_HOST: '{{.SONAR_HOST | default "https://sonarcloud.io"}}'
14
+
15
+ tasks:
16
+ default:
17
+ desc: "Show available SonarQube tasks"
18
+ aliases: [help, h]
19
+ silent: true
20
+ cmds:
21
+ - |
22
+ # Color codes
23
+ GREEN='\033[0;32m'
24
+ YELLOW='\033[0;33m'
25
+ BOLD='\033[1m'
26
+ NC='\033[0m'
27
+
28
+ echo -e "${BOLD}SonarQube Code Quality${NC}"
29
+ echo ""
30
+ echo "Command Alias Description Examples"
31
+ echo "───────────────────────────────────────────────────────────────────────────────────────────────────"
32
+ echo -e "${BOLD}Analysis:${NC}"
33
+ echo -e " ${GREEN}task sonar:scan${NC} ${YELLOW}s${NC} Run SonarQube analysis locally"
34
+ echo -e " ${GREEN}task sonar:download${NC} ${YELLOW}dl${NC} Download issues from SonarQube"
35
+ echo -e " ${GREEN}task sonar:issues${NC} ${YELLOW}i${NC} Display downloaded findings"
36
+ echo ""
37
+ echo -e "${BOLD}Configuration:${NC}"
38
+ echo -e " ${GREEN}task sonar:setup${NC} Install sonar-scanner CLI"
39
+ echo ""
40
+ echo -e "${BOLD}Usage Examples:${NC}"
41
+ echo -e " task sonar:download Download issues"
42
+ echo -e " task sonar:issues View findings"
43
+ echo -e " task sonar:scan Run local scan"
44
+ echo ""
45
+
46
+ setup:
47
+ desc: Install sonar-scanner CLI if not present
48
+ silent: true
49
+ cmds:
50
+ - |
51
+ # Check if already installed
52
+ if command -v sonar-scanner &> /dev/null; then
53
+ exit 0
54
+ fi
55
+
56
+ # Color codes
57
+ GREEN='\033[0;32m'
58
+ NC='\033[0m'
59
+
60
+ echo "Installing sonar-scanner..."
61
+
62
+ # Detect OS
63
+ OS="$(uname -s)"
64
+ case "$OS" in
65
+ Darwin)
66
+ if command -v brew &> /dev/null; then
67
+ brew install sonar-scanner --quiet
68
+ else
69
+ echo "Error: Homebrew not found. Install from https://brew.sh"
70
+ exit 1
71
+ fi
72
+ ;;
73
+ Linux)
74
+ # Download to local directory
75
+ INSTALL_DIR="$HOME/.local/bin"
76
+ mkdir -p "$INSTALL_DIR"
77
+
78
+ curl -sSL https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.8.0.2856-linux.zip -o /tmp/sonar-scanner.zip
79
+ unzip -q /tmp/sonar-scanner.zip -d /tmp/
80
+ mv /tmp/sonar-scanner-*/* "$INSTALL_DIR/"
81
+ rm -rf /tmp/sonar-scanner*
82
+
83
+ echo -e "${GREEN}✓${NC} Installed to $INSTALL_DIR"
84
+ ;;
85
+ *)
86
+ echo "Error: Unsupported OS. Install from https://docs.sonarcloud.io"
87
+ exit 1
88
+ ;;
89
+ esac
90
+
91
+ if command -v sonar-scanner &> /dev/null; then
92
+ echo -e "${GREEN}✓${NC} sonar-scanner installed"
93
+ fi
94
+
95
+ scan:
96
+ desc: Run SonarQube analysis locally
97
+ silent: true
98
+ dotenv: [".env"]
99
+ deps: [setup]
100
+ cmds:
101
+ - |
102
+ # Color codes
103
+ GREEN='\033[0;32m'
104
+ RED='\033[0;31m'
105
+ NC='\033[0m'
106
+
107
+ # Source secrets if .env has 1Password references
108
+ if [ -f .env ] && grep -q "op://" .env 2>/dev/null; then
109
+ source $(task op:export)
110
+
111
+ # Check if token is still encrypted (force a refresh)
112
+ if [[ "${SONAR_TOKEN:-}" == *"op://"* ]]; then
113
+ source $(task op:export:force)
114
+ fi
115
+ fi
116
+
117
+ # Validate token
118
+ if [ -z "$SONAR_TOKEN" ]; then
119
+ echo -e "${RED}❌ SONAR_TOKEN not set${NC}" >&2
120
+ echo "Add to .env: SONAR_TOKEN=your-token" >&2
121
+ exit 1
122
+ fi
123
+
124
+ # Create report directory
125
+ mkdir -p {{.REPORT_PATH}}
126
+
127
+ # Run scanner
128
+ sonar-scanner \
129
+ -Dsonar.host.url={{.SONAR_HOST}} \
130
+ -Dsonar.token=$SONAR_TOKEN \
131
+ -Dsonar.working.directory={{.REPORT_PATH}}/.scannerwork \
132
+ 2>&1 | tee {{.REPORT_PATH}}/scan.log > /dev/null
133
+
134
+ if [ $? -eq 0 ]; then
135
+ echo -e "${GREEN}✓${NC} Scan complete → {{.REPORT_PATH}}/scan.log"
136
+ else
137
+ echo -e "${RED}❌ Scan failed${NC}" >&2
138
+ exit 1
139
+ fi
140
+
141
+ download:
142
+ desc: Download SonarQube issues for project
143
+ aliases: [dl]
144
+ silent: true
145
+ dotenv: [".env"]
146
+ cmds:
147
+ - |
148
+ set -euo pipefail
149
+
150
+ # Color codes
151
+ GREEN='\033[0;32m'
152
+ RED='\033[0;31m'
153
+ CYAN='\033[0;36m'
154
+ NC='\033[0m'
155
+
156
+ # Source secrets if .env has 1Password references
157
+ if [ -f .env ] && grep -q "op://" .env 2>/dev/null; then
158
+ source $(task op:export)
159
+
160
+ # Check if token is still encrypted (force a refresh)
161
+ if [[ "${SONAR_TOKEN:-}" == *"op://"* ]]; then
162
+ source $(task op:export:force)
163
+ fi
164
+ fi
165
+
166
+ # Validate token
167
+ if [ -z "$SONAR_TOKEN" ]; then
168
+ echo -e "${RED}❌ SONAR_TOKEN not set${NC}" >&2
169
+ echo "Add to .env: SONAR_TOKEN=your-token" >&2
170
+ exit 1
171
+ fi
172
+
173
+ # Read project key from sonar-project.properties
174
+ if [ ! -f "sonar-project.properties" ]; then
175
+ echo -e "${RED}❌ sonar-project.properties not found${NC}" >&2
176
+ echo "Create configuration file in repository root" >&2
177
+ exit 1
178
+ fi
179
+
180
+ PROJECT_KEY=$(grep "^sonar.projectKey=" sonar-project.properties | cut -d'=' -f2)
181
+ if [ -z "$PROJECT_KEY" ]; then
182
+ echo -e "${RED}❌ sonar.projectKey not found in sonar-project.properties${NC}" >&2
183
+ exit 1
184
+ fi
185
+
186
+ # Detect PR context
187
+ CURRENT_BRANCH=$(task git:branch:current)
188
+ DEFAULT_BRANCH=$(task git:branch:default)
189
+ PR_NUMBER=$(gh pr list --head "$CURRENT_BRANCH" --base "$DEFAULT_BRANCH" --json number --jq '.[0].number' 2>/dev/null || echo "")
190
+
191
+ # Show what we're analyzing
192
+ if [ -n "$PR_NUMBER" ]; then
193
+ PR_TITLE=$(gh pr view "$PR_NUMBER" --json title --jq '.title' 2>/dev/null || echo "")
194
+ echo -e "${CYAN}ℹ${NC} Analyzing PR #$PR_NUMBER: $PR_TITLE"
195
+ echo -e "${CYAN}ℹ${NC} Project: $PROJECT_KEY"
196
+ echo -e "${CYAN}ℹ${NC} Branch: $CURRENT_BRANCH → $DEFAULT_BRANCH"
197
+ ANALYSIS_TYPE="PR #$PR_NUMBER"
198
+ else
199
+ echo -e "${CYAN}ℹ${NC} Analyzing default branch"
200
+ echo -e "${CYAN}ℹ${NC} Project: $PROJECT_KEY"
201
+ echo -e "${CYAN}ℹ${NC} Branch: $DEFAULT_BRANCH"
202
+ ANALYSIS_TYPE="Branch: $DEFAULT_BRANCH"
203
+ fi
204
+ echo ""
205
+
206
+ # Create report directory
207
+ mkdir -p {{.REPORT_PATH}}
208
+ TIMESTAMP=$(date +%Y%m%d-%H%M%S)
209
+ RAW_FILE="{{.REPORT_PATH}}/issues-${TIMESTAMP}-raw.json"
210
+ REPORT_FILE="{{.REPORT_PATH}}/issues-${TIMESTAMP}.txt"
211
+
212
+ # Fetch issues from SonarQube API (PR-specific or default branch)
213
+ if [ -n "$PR_NUMBER" ]; then
214
+ # Try PR-specific analysis first
215
+ ISSUES_API="{{.SONAR_HOST}}/api/issues/search?pullRequest=$PR_NUMBER&componentKeys=$PROJECT_KEY&ps=500"
216
+ HOTSPOTS_API="{{.SONAR_HOST}}/api/hotspots/search?pullRequest=$PR_NUMBER&projectKey=$PROJECT_KEY&ps=500"
217
+ QG_API="{{.SONAR_HOST}}/api/qualitygates/project_status?projectKey=$PROJECT_KEY&pullRequest=$PR_NUMBER"
218
+
219
+ # Fetch PR data
220
+ ISSUES_DATA=$(curl -s -X GET "$ISSUES_API" -H "Authorization: Bearer $SONAR_TOKEN")
221
+ PR_TOTAL=$(echo "$ISSUES_DATA" | jq '.total // 0')
222
+
223
+ # If PR analysis doesn't exist or is empty, fall back to default branch
224
+ if [ "$PR_TOTAL" -eq 0 ]; then
225
+ echo -e "${YELLOW}⚠${NC} PR analysis not found, showing default branch analysis"
226
+ ANALYSIS_TYPE="Branch: $DEFAULT_BRANCH (PR not analyzed)"
227
+ ISSUES_API="{{.SONAR_HOST}}/api/issues/search?componentKeys=$PROJECT_KEY&ps=500"
228
+ HOTSPOTS_API="{{.SONAR_HOST}}/api/hotspots/search?projectKey=$PROJECT_KEY&ps=500"
229
+ QG_API="{{.SONAR_HOST}}/api/qualitygates/project_status?projectKey=$PROJECT_KEY"
230
+ fi
231
+ else
232
+ # Default branch analysis
233
+ ISSUES_API="{{.SONAR_HOST}}/api/issues/search?componentKeys=$PROJECT_KEY&ps=500"
234
+ HOTSPOTS_API="{{.SONAR_HOST}}/api/hotspots/search?projectKey=$PROJECT_KEY&ps=500"
235
+ QG_API="{{.SONAR_HOST}}/api/qualitygates/project_status?projectKey=$PROJECT_KEY"
236
+ fi
237
+
238
+ # Fetch issues
239
+ ISSUES_DATA=$(curl -s -X GET "$ISSUES_API" -H "Authorization: Bearer $SONAR_TOKEN")
240
+ if [ $? -ne 0 ]; then
241
+ echo -e "${RED}❌ Failed to fetch issues${NC}" >&2
242
+ exit 1
243
+ fi
244
+
245
+ # Fetch security hotspots
246
+ HOTSPOTS_DATA=$(curl -s -X GET "$HOTSPOTS_API" -H "Authorization: Bearer $SONAR_TOKEN")
247
+
248
+ # Fetch quality gate status
249
+ QG_DATA=$(curl -s -X GET "$QG_API" -H "Authorization: Bearer $SONAR_TOKEN")
250
+
251
+ # Fetch coverage metrics
252
+ if [ -n "$PR_NUMBER" ]; then
253
+ MEASURES_API="{{.SONAR_HOST}}/api/measures/component?component=$PROJECT_KEY&pullRequest=$PR_NUMBER&metricKeys=new_coverage,new_lines_to_cover,new_uncovered_lines,coverage,lines_to_cover,uncovered_lines"
254
+ else
255
+ MEASURES_API="{{.SONAR_HOST}}/api/measures/component?component=$PROJECT_KEY&metricKeys=coverage,lines_to_cover,uncovered_lines"
256
+ fi
257
+ COVERAGE_DATA=$(curl -s -X GET "$MEASURES_API" -H "Authorization: Bearer $SONAR_TOKEN")
258
+
259
+ # Save raw responses
260
+ echo "$ISSUES_DATA" > "$RAW_FILE"
261
+ echo "$HOTSPOTS_DATA" > "{{.REPORT_PATH}}/hotspots-${TIMESTAMP}-raw.json"
262
+ echo "$QG_DATA" > "{{.REPORT_PATH}}/quality-gate-${TIMESTAMP}-raw.json"
263
+ echo "$COVERAGE_DATA" > "{{.REPORT_PATH}}/coverage-${TIMESTAMP}-raw.json"
264
+
265
+ # Count issues by severity
266
+ TOTAL=$(echo "$ISSUES_DATA" | jq '.total // 0')
267
+ BLOCKER=$(echo "$ISSUES_DATA" | jq '[.issues[] | select(.severity == "BLOCKER")] | length')
268
+ CRITICAL=$(echo "$ISSUES_DATA" | jq '[.issues[] | select(.severity == "CRITICAL")] | length')
269
+ MAJOR=$(echo "$ISSUES_DATA" | jq '[.issues[] | select(.severity == "MAJOR")] | length')
270
+ MINOR=$(echo "$ISSUES_DATA" | jq '[.issues[] | select(.severity == "MINOR")] | length')
271
+
272
+ # Count security hotspots
273
+ HOTSPOTS_TOTAL=$(echo "$HOTSPOTS_DATA" | jq '.paging.total // 0')
274
+ HOTSPOTS_TO_REVIEW=$(echo "$HOTSPOTS_DATA" | jq '[.hotspots[] | select(.status == "TO_REVIEW")] | length')
275
+ HOTSPOTS_REVIEWED=$(echo "$HOTSPOTS_DATA" | jq '[.hotspots[] | select(.status == "REVIEWED")] | length')
276
+
277
+ # Get quality gate status
278
+ QG_STATUS=$(echo "$QG_DATA" | jq -r '.projectStatus.status // "NONE"')
279
+ QG_CONDITIONS=$(echo "$QG_DATA" | jq -r '.projectStatus.conditions[]? | select(.status != "OK") | " - \(.metricKey): \(.actualValue) (threshold: \(.errorThreshold // .warningThreshold))"' 2>/dev/null || echo "")
280
+
281
+ # Generate report
282
+ {
283
+ echo "SonarQube Analysis Report"
284
+ echo "========================="
285
+ echo "Project: $PROJECT_KEY"
286
+ echo "Analysis: $ANALYSIS_TYPE"
287
+ echo "Date: $(date)"
288
+ echo ""
289
+
290
+ # Quality Gate Status
291
+ echo "Quality Gate: $QG_STATUS"
292
+ if [ "$QG_STATUS" != "OK" ] && [ -n "$QG_CONDITIONS" ]; then
293
+ echo "Failed Conditions:"
294
+ echo "$QG_CONDITIONS"
295
+ fi
296
+ echo ""
297
+
298
+ # Issues Summary
299
+ echo "Issues: $TOTAL"
300
+ if [ "$TOTAL" -gt 0 ]; then
301
+ echo "By Severity:"
302
+ [ "$BLOCKER" -gt 0 ] && echo " Blocker: $BLOCKER"
303
+ [ "$CRITICAL" -gt 0 ] && echo " Critical: $CRITICAL"
304
+ [ "$MAJOR" -gt 0 ] && echo " Major: $MAJOR"
305
+ [ "$MINOR" -gt 0 ] && echo " Minor: $MINOR"
306
+ fi
307
+ echo ""
308
+
309
+ # Security Hotspots Summary
310
+ echo "Security Hotspots: $HOTSPOTS_TOTAL"
311
+ if [ "$HOTSPOTS_TOTAL" -gt 0 ]; then
312
+ echo " To Review: $HOTSPOTS_TO_REVIEW"
313
+ echo " Reviewed: $HOTSPOTS_REVIEWED"
314
+ fi
315
+ echo ""
316
+
317
+ # Coverage Summary
318
+ COVERAGE=$(echo "$COVERAGE_DATA" | jq -r '.component.measures[]? | select(.metric == "coverage") | .value' 2>/dev/null || echo "")
319
+ NEW_COVERAGE=$(echo "$COVERAGE_DATA" | jq -r '.component.measures[]? | select(.metric == "new_coverage") | .periods[0].value' 2>/dev/null || echo "")
320
+ LINES_TO_COVER=$(echo "$COVERAGE_DATA" | jq -r '.component.measures[]? | select(.metric == "lines_to_cover") | .value' 2>/dev/null || echo "")
321
+ UNCOVERED_LINES=$(echo "$COVERAGE_DATA" | jq -r '.component.measures[]? | select(.metric == "uncovered_lines") | .value' 2>/dev/null || echo "")
322
+ NEW_LINES_TO_COVER=$(echo "$COVERAGE_DATA" | jq -r '.component.measures[]? | select(.metric == "new_lines_to_cover") | .periods[0].value' 2>/dev/null || echo "")
323
+ NEW_UNCOVERED_LINES=$(echo "$COVERAGE_DATA" | jq -r '.component.measures[]? | select(.metric == "new_uncovered_lines") | .periods[0].value' 2>/dev/null || echo "")
324
+
325
+ if [ -n "$COVERAGE" ] || [ -n "$NEW_COVERAGE" ]; then
326
+ echo "Coverage:"
327
+ if [ -n "$COVERAGE" ]; then
328
+ echo " Overall: ${COVERAGE}%"
329
+ if [ -n "$LINES_TO_COVER" ] && [ -n "$UNCOVERED_LINES" ]; then
330
+ COVERED_LINES=$((LINES_TO_COVER - UNCOVERED_LINES))
331
+ echo " Covered: $COVERED_LINES / $LINES_TO_COVER lines"
332
+ fi
333
+ fi
334
+ if [ -n "$NEW_COVERAGE" ]; then
335
+ echo " New Code: ${NEW_COVERAGE}%"
336
+ if [ -n "$NEW_LINES_TO_COVER" ] && [ -n "$NEW_UNCOVERED_LINES" ]; then
337
+ NEW_COVERED_LINES=$(echo "$NEW_LINES_TO_COVER - $NEW_UNCOVERED_LINES" | bc)
338
+ echo " Covered: $NEW_COVERED_LINES / $NEW_LINES_TO_COVER new lines"
339
+ fi
340
+ # Highlight if below 80% threshold
341
+ if [ -n "$NEW_COVERAGE" ] && (( $(echo "$NEW_COVERAGE < 80" | bc -l) )); then
342
+ echo " ⚠ Below 80% threshold"
343
+ fi
344
+ fi
345
+ echo ""
346
+ fi
347
+
348
+ # Detailed Issues
349
+ if [ "$TOTAL" -gt 0 ]; then
350
+ echo "═══════════════════════════════════════════════════════════════"
351
+ echo "DETAILED ISSUES"
352
+ echo "═══════════════════════════════════════════════════════════════"
353
+ echo ""
354
+
355
+ echo "$ISSUES_DATA" | jq -r '.issues[] |
356
+ "[\(.severity)] \(.component | split(":")[1]):\(.line // "??")\n" +
357
+ "Type: \(.type)\n" +
358
+ "Rule: \(.rule)\n" +
359
+ "Message: \(.message)\n" +
360
+ "───────────────────────────────────────────────────────────────"'
361
+ fi
362
+
363
+ # Detailed Security Hotspots
364
+ if [ "$HOTSPOTS_TOTAL" -gt 0 ]; then
365
+ echo ""
366
+ echo "═══════════════════════════════════════════════════════════════"
367
+ echo "SECURITY HOTSPOTS"
368
+ echo "═══════════════════════════════════════════════════════════════"
369
+ echo ""
370
+
371
+ echo "$HOTSPOTS_DATA" | jq -r '.hotspots[] |
372
+ "[\(.vulnerabilityProbability)] \(.component | split(":")[1]):\(.line // "??")\n" +
373
+ "Status: \(.status)\n" +
374
+ "Category: \(.securityCategory)\n" +
375
+ "Message: \(.message)\n" +
376
+ "───────────────────────────────────────────────────────────────"'
377
+ fi
378
+
379
+ # Summary at bottom
380
+ if [ "$TOTAL" -eq 0 ] && [ "$HOTSPOTS_TOTAL" -eq 0 ]; then
381
+ echo "No issues or security hotspots found."
382
+ fi
383
+ } > "$REPORT_FILE"
384
+
385
+ # Calculate total findings
386
+ TOTAL_FINDINGS=$((TOTAL + HOTSPOTS_TOTAL))
387
+
388
+ # Display summary
389
+ if [ "$QG_STATUS" != "OK" ]; then
390
+ echo -e "${RED}❌ Quality Gate: $QG_STATUS${NC}"
391
+ else
392
+ echo -e "${GREEN}✓${NC} Quality Gate: $QG_STATUS"
393
+ fi
394
+
395
+ if [ "$TOTAL_FINDINGS" -gt 0 ]; then
396
+ echo -e "${CYAN}ℹ${NC} Downloaded $TOTAL issues + $HOTSPOTS_TOTAL hotspots → $REPORT_FILE"
397
+
398
+ # Open in VSCode only if findings exist
399
+ if command -v code &> /dev/null; then
400
+ code "$REPORT_FILE" 2>/dev/null || true
401
+ elif command -v code-insiders &> /dev/null; then
402
+ code-insiders "$REPORT_FILE" 2>/dev/null || true
403
+ fi
404
+ else
405
+ echo -e "${GREEN}✓${NC} No issues or hotspots found"
406
+ fi
407
+
408
+ issues:
409
+ desc: Display most recent SonarQube findings
410
+ aliases: [i]
411
+ silent: true
412
+ cmds:
413
+ - |
414
+ # Color codes
415
+ GREEN='\033[0;32m'
416
+ RED='\033[0;31m'
417
+ NC='\033[0m'
418
+
419
+ # Find most recent report
420
+ LATEST_REPORT=$(ls -t {{.REPORT_PATH}}/issues-*.txt 2>/dev/null | head -1)
421
+
422
+ if [ -z "$LATEST_REPORT" ]; then
423
+ echo -e "${RED}❌ No reports found${NC}" >&2
424
+ echo "Run: task sonar:download" >&2
425
+ exit 1
426
+ fi
427
+
428
+ # Extract summary
429
+ TOTAL=$(grep "^Total issues:" "$LATEST_REPORT" | awk '{print $3}')
430
+ echo -e "${GREEN}✓${NC} Found $TOTAL issues → $LATEST_REPORT"
431
+
432
+ # Open in VSCode
433
+ if command -v code &> /dev/null; then
434
+ code "$LATEST_REPORT" 2>/dev/null || true
435
+ elif command -v code-insiders &> /dev/null; then
436
+ code-insiders "$LATEST_REPORT" 2>/dev/null || true
437
+ fi
@@ -0,0 +1,74 @@
1
+ # Taskfile.yml - Project task automation
2
+ # Generated by @northbridge-security/ai-toolkit
3
+ #
4
+ # This file references shared task definitions from the ai-toolkit npm package.
5
+ # Customize by adding project-specific tasks or overriding shared tasks.
6
+
7
+ version: '3'
8
+
9
+ # Path to ai-toolkit tasks in node_modules
10
+ vars:
11
+ TASK_LIB: node_modules/@northbridge-security/ai-toolkit/tasks
12
+
13
+ # Include shared task libraries
14
+ includes:
15
+ # Flatten common tasks (accessible as `task build`, `task lint`, etc.)
16
+ # common:
17
+ # taskfile: '{{.TASK_LIB}}/{{PROJECT_TYPE}}.yml'
18
+ # flatten: true
19
+ # optional: true
20
+
21
+ # Namespaced specialized tasks (accessible as `task git:hooks`, etc.)
22
+ git:
23
+ taskfile: '{{.TASK_LIB}}/git.yml'
24
+ optional: true
25
+
26
+ yaml:
27
+ taskfile: '{{.TASK_LIB}}/yaml.yml'
28
+ optional: true
29
+
30
+ markdown:
31
+ taskfile: '{{.TASK_LIB}}/markdown.yml'
32
+ optional: true
33
+
34
+ json:
35
+ taskfile: '{{.TASK_LIB}}/json.yml'
36
+ optional: true
37
+
38
+ bash:
39
+ taskfile: '{{.TASK_LIB}}/bash.yml'
40
+ optional: true
41
+
42
+ gotask:
43
+ taskfile: '{{.TASK_LIB}}/gotask.yml'
44
+ optional: true
45
+
46
+ claude:
47
+ taskfile: '{{.TASK_LIB}}/claude.yml'
48
+ optional: true
49
+
50
+ op:
51
+ taskfile: '{{.TASK_LIB}}/onepassword.yml'
52
+ aliases: [1p]
53
+ optional: true
54
+
55
+ {{GXP_SECTION}}
56
+ # Project-specific tasks
57
+ tasks:
58
+ default:
59
+ desc: 'Show available tasks'
60
+ aliases: [help, h]
61
+ silent: true
62
+ cmds:
63
+ - |
64
+ echo "Available Tasks"
65
+ echo ""
66
+ echo "Run 'task --list' to see all available tasks from ai-toolkit"
67
+ echo "Run 'task NAMESPACE:COMMAND' to execute a specific task"
68
+ echo ""
69
+ echo "Example commands:"
70
+ echo " task git:hooks # Install git hooks"
71
+ echo " task yaml:lint # Lint YAML files"
72
+ echo " task markdown:lint # Lint Markdown files"
73
+ echo ""
74
+ echo "Add your project-specific tasks to this Taskfile.yml"