@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.
- package/.claude/README.md +122 -0
- package/.claude/commands/architect/clean.md +978 -0
- package/.claude/commands/architect/kiss.md +762 -0
- package/.claude/commands/architect/review.md +704 -0
- package/.claude/commands/catchup.md +90 -0
- package/.claude/commands/code.md +115 -0
- package/.claude/commands/commit.md +1218 -0
- package/.claude/commands/cover.md +1298 -0
- package/.claude/commands/fmea.md +275 -0
- package/.claude/commands/kaizen.md +312 -0
- package/.claude/commands/pr.md +503 -0
- package/.claude/commands/todo.md +99 -0
- package/.claude/commands/worktree.md +738 -0
- package/.claude/commands/wrapup.md +103 -0
- package/LICENSE +183 -0
- package/README.md +108 -0
- package/dist/cli.js +75634 -0
- package/docs/agents/devops-reviewer.md +889 -0
- package/docs/agents/kiss-simplifier.md +1088 -0
- package/docs/agents/typescript.md +8 -0
- package/docs/guides/README.md +109 -0
- package/docs/guides/agents.clean.arch.md +244 -0
- package/docs/guides/agents.clean.arch.ts.md +1314 -0
- package/docs/guides/agents.gotask.md +1037 -0
- package/docs/guides/agents.markdown.md +1209 -0
- package/docs/guides/agents.onepassword.md +285 -0
- package/docs/guides/agents.sonar.md +857 -0
- package/docs/guides/agents.tdd.md +838 -0
- package/docs/guides/agents.tdd.ts.md +1062 -0
- package/docs/guides/agents.typesript.md +1389 -0
- package/docs/guides/github-mcp.md +1075 -0
- package/package.json +130 -0
- package/packages/secureai-cli/src/cli.ts +21 -0
- package/tasks/README.md +880 -0
- package/tasks/aws.yml +64 -0
- package/tasks/bash.yml +118 -0
- package/tasks/bun.yml +738 -0
- package/tasks/claude.yml +183 -0
- package/tasks/docker.yml +420 -0
- package/tasks/docs.yml +127 -0
- package/tasks/git.yml +1336 -0
- package/tasks/gotask.yml +132 -0
- package/tasks/json.yml +77 -0
- package/tasks/markdown.yml +95 -0
- package/tasks/onepassword.yml +350 -0
- package/tasks/security.yml +102 -0
- package/tasks/sonar.yml +437 -0
- package/tasks/template.yml +74 -0
- package/tasks/vscode.yml +103 -0
- package/tasks/yaml.yml +121 -0
package/tasks/bun.yml
ADDED
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
# https://taskfile.dev
|
|
2
|
+
|
|
3
|
+
version: "3"
|
|
4
|
+
|
|
5
|
+
tasks:
|
|
6
|
+
core:info:
|
|
7
|
+
silent: true
|
|
8
|
+
cmds:
|
|
9
|
+
- |
|
|
10
|
+
# Color codes
|
|
11
|
+
GREEN='\033[0;32m'
|
|
12
|
+
YELLOW='\033[0;33m'
|
|
13
|
+
BLUE='\033[0;34m'
|
|
14
|
+
BOLD='\033[1m'
|
|
15
|
+
NC='\033[0m' # No Color
|
|
16
|
+
|
|
17
|
+
echo -e "${BOLD}Development:${NC}"
|
|
18
|
+
echo -e " ${GREEN}task install${NC} ${YELLOW}i${NC} Clean install (frozen lockfile, no scripts)"
|
|
19
|
+
echo -e " ${GREEN}task install:quick${NC} ${YELLOW}iq${NC} Quick install (allows lockfile updates)"
|
|
20
|
+
echo -e " ${GREEN}task build${NC} ${YELLOW}b${NC} Build the project"
|
|
21
|
+
echo -e " ${GREEN}task test${NC} ${YELLOW}t${NC} Run unit tests"
|
|
22
|
+
echo -e " ${GREEN}task test:integration${NC} ${YELLOW}ti${NC} Run integration tests"
|
|
23
|
+
echo -e " ${GREEN}task test:coverage${NC} ${YELLOW}cov${NC} Run tests with coverage FILES=\"...\" or DIFF=true"
|
|
24
|
+
echo -e " ${GREEN}task test:coverage:report${NC} ${YELLOW}covr${NC} Show coverage report with low files THRESHOLD=80 (default)"
|
|
25
|
+
echo -e " ${GREEN}task lint${NC} ${YELLOW}l${NC} Run linter FILES=\"...\""
|
|
26
|
+
echo -e " ${GREEN}task lint:fix${NC} ${YELLOW}lf${NC} Run linter with auto-fix FILES=\"...\""
|
|
27
|
+
echo -e " ${GREEN}task dev${NC} ${YELLOW}d${NC} Run in development mode"
|
|
28
|
+
echo ""
|
|
29
|
+
|
|
30
|
+
install:
|
|
31
|
+
desc: Clean install dependencies (removes caches, uses frozen lockfile, ignores scripts)
|
|
32
|
+
aliases: [i]
|
|
33
|
+
silent: true
|
|
34
|
+
cmds:
|
|
35
|
+
- |
|
|
36
|
+
GREEN='\033[0;32m'
|
|
37
|
+
YELLOW='\033[0;33m'
|
|
38
|
+
NC='\033[0m'
|
|
39
|
+
|
|
40
|
+
# Clean local caches first for reproducible builds
|
|
41
|
+
# See: https://www.lekman.com/blog/2025/11/securing-your-javascript-dependencies-what-every-organisation-needs-to-know/
|
|
42
|
+
|
|
43
|
+
# Remove node_modules
|
|
44
|
+
if [ -d "node_modules" ]; then
|
|
45
|
+
rm -rf node_modules
|
|
46
|
+
echo -e "${GREEN}✓${NC} Removed node_modules/"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Remove bun's local cache
|
|
50
|
+
if [ -d ".bun" ]; then
|
|
51
|
+
rm -rf .bun
|
|
52
|
+
echo -e "${GREEN}✓${NC} Removed .bun/"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Remove SST cache (serverless framework)
|
|
56
|
+
if [ -d ".sst" ]; then
|
|
57
|
+
rm -rf .sst
|
|
58
|
+
echo -e "${GREEN}✓${NC} Removed .sst/"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Remove SST output directory
|
|
62
|
+
if [ -d ".open-next" ]; then
|
|
63
|
+
rm -rf .open-next
|
|
64
|
+
echo -e "${GREEN}✓${NC} Removed .open-next/"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Secure install with frozen lockfile and no lifecycle scripts
|
|
68
|
+
# --frozen-lockfile: Ensures exact versions from bun.lockb (fails if lockfile needs update)
|
|
69
|
+
# --ignore-scripts: Prevents execution of potentially malicious postinstall scripts
|
|
70
|
+
echo -e "${GREEN}▶${NC} Installing dependencies (frozen lockfile, no scripts)..."
|
|
71
|
+
bun install --frozen-lockfile --ignore-scripts
|
|
72
|
+
|
|
73
|
+
echo -e "${GREEN}✓${NC} Dependencies installed securely"
|
|
74
|
+
|
|
75
|
+
install:quick:
|
|
76
|
+
desc: Quick install (no clean, updates lockfile if needed)
|
|
77
|
+
aliases: [iq]
|
|
78
|
+
silent: true
|
|
79
|
+
cmds:
|
|
80
|
+
- bun install
|
|
81
|
+
|
|
82
|
+
build:
|
|
83
|
+
desc: Build the project
|
|
84
|
+
aliases: [b]
|
|
85
|
+
silent: true
|
|
86
|
+
cmds:
|
|
87
|
+
- bun turbo build
|
|
88
|
+
sources:
|
|
89
|
+
- src/**/*.ts
|
|
90
|
+
generates:
|
|
91
|
+
- dist/**/*.js
|
|
92
|
+
|
|
93
|
+
rebuild:
|
|
94
|
+
aliases: [rb]
|
|
95
|
+
desc: First cleans project build artifacts, logs, and temporary files, then rebuilds the project
|
|
96
|
+
silent: true
|
|
97
|
+
cmds:
|
|
98
|
+
- task clean
|
|
99
|
+
- task install
|
|
100
|
+
- task build
|
|
101
|
+
|
|
102
|
+
clean:
|
|
103
|
+
desc: Clean build artifacts, logs, and temporary files (use venv=true to also delete venv)
|
|
104
|
+
aliases: [c]
|
|
105
|
+
silent: true
|
|
106
|
+
vars:
|
|
107
|
+
DELETE_VENV: '{{.venv | default "false"}}'
|
|
108
|
+
cmds:
|
|
109
|
+
- |
|
|
110
|
+
# Color codes
|
|
111
|
+
GREEN='\033[0;32m'
|
|
112
|
+
YELLOW='\033[0;33m'
|
|
113
|
+
NC='\033[0m'
|
|
114
|
+
|
|
115
|
+
# Get repository root
|
|
116
|
+
REPO_ROOT=$(task git:repo:root)
|
|
117
|
+
|
|
118
|
+
# Clean build artifacts and dependency caches
|
|
119
|
+
rm -rf dist
|
|
120
|
+
rm -rf node_modules
|
|
121
|
+
rm -rf .bun
|
|
122
|
+
rm -rf .sst
|
|
123
|
+
rm -rf .open-next
|
|
124
|
+
echo -e "${GREEN}✓${NC} Removed dist/, node_modules/, .bun/, .sst/, .open-next/"
|
|
125
|
+
|
|
126
|
+
# Clean .logs contents (keep the directory)
|
|
127
|
+
if [ -d .logs ]; then
|
|
128
|
+
find .logs -mindepth 1 -delete
|
|
129
|
+
echo -e "${GREEN}✓${NC} Cleaned .logs/ contents"
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
# Delete *.local.md files in root folder only (not subfolders)
|
|
133
|
+
# EXCEPT: .bugs.local.md and *.report.local.md
|
|
134
|
+
LOCAL_MD_COUNT=$(find . -maxdepth 1 -name "*.local.md" -type f ! -name ".bugs.local.md" ! -name "*.report.local.md" | wc -l | tr -d ' ')
|
|
135
|
+
if [ "$LOCAL_MD_COUNT" -gt 0 ]; then
|
|
136
|
+
find . -maxdepth 1 -name "*.local.md" -type f ! -name ".bugs.local.md" ! -name "*.report.local.md" -delete
|
|
137
|
+
echo -e "${GREEN}✓${NC} Deleted $LOCAL_MD_COUNT *.local.md file(s) from root (preserved .bugs.local.md and *.report.local.md)"
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
# Delete *.txt files in root folder only (not subfolders)
|
|
141
|
+
TXT_COUNT=$(find . -maxdepth 1 -name "*.txt" -type f | wc -l | tr -d ' ')
|
|
142
|
+
if [ "$TXT_COUNT" -gt 0 ]; then
|
|
143
|
+
find . -maxdepth 1 -name "*.txt" -type f -delete
|
|
144
|
+
echo -e "${GREEN}✓${NC} Deleted $TXT_COUNT *.txt file(s) from root"
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# Delete *.log files in root folder only (not subfolders)
|
|
148
|
+
LOG_COUNT=$(find . -maxdepth 1 -name "*.log" -type f | wc -l | tr -d ' ')
|
|
149
|
+
if [ "$LOG_COUNT" -gt 0 ]; then
|
|
150
|
+
find . -maxdepth 1 -name "*.log" -type f -delete
|
|
151
|
+
echo -e "${GREEN}✓${NC} Deleted $LOG_COUNT *.log file(s) from root"
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
# Delete TypeScript build cache files (recursive)
|
|
155
|
+
TSBUILDINFO_COUNT=$(find . -name "*.tsbuildinfo" -type f | wc -l | tr -d ' ')
|
|
156
|
+
if [ "$TSBUILDINFO_COUNT" -gt 0 ]; then
|
|
157
|
+
find . -name "*.tsbuildinfo" -type f -delete
|
|
158
|
+
echo -e "${GREEN}✓${NC} Deleted $TSBUILDINFO_COUNT *.tsbuildinfo file(s)"
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# Delete .DS_Store files (macOS metadata, recursive)
|
|
162
|
+
DS_STORE_COUNT=$(find . -name ".DS_Store" -type f | wc -l | tr -d ' ')
|
|
163
|
+
if [ "$DS_STORE_COUNT" -gt 0 ]; then
|
|
164
|
+
find . -name ".DS_Store" -type f -delete
|
|
165
|
+
echo -e "${GREEN}✓${NC} Deleted $DS_STORE_COUNT .DS_Store file(s)"
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
# Optionally delete venv
|
|
169
|
+
{{if eq .DELETE_VENV "true"}}
|
|
170
|
+
if [ -d venv ]; then
|
|
171
|
+
rm -rf venv
|
|
172
|
+
echo -e "${GREEN}✓${NC} Deleted venv/"
|
|
173
|
+
fi
|
|
174
|
+
{{end}}
|
|
175
|
+
|
|
176
|
+
# Delete all git worktrees (except main worktree)
|
|
177
|
+
WORKTREE_COUNT=$(git worktree list --porcelain | grep -c "^worktree " || echo 0)
|
|
178
|
+
# Subtract 1 for the main worktree
|
|
179
|
+
if [ "$WORKTREE_COUNT" -gt 1 ]; then
|
|
180
|
+
REMOVED_COUNT=0
|
|
181
|
+
git worktree list --porcelain | grep "^worktree " | cut -d' ' -f2 | while read -r worktree_path; do
|
|
182
|
+
# Skip main worktree (current directory)
|
|
183
|
+
if [ "$worktree_path" != "$REPO_ROOT" ] && [ "$worktree_path" != "." ]; then
|
|
184
|
+
git worktree remove "$worktree_path" --force 2>/dev/null || true
|
|
185
|
+
REMOVED_COUNT=$((REMOVED_COUNT + 1))
|
|
186
|
+
fi
|
|
187
|
+
done
|
|
188
|
+
|
|
189
|
+
# Prune stale worktree administrative files
|
|
190
|
+
git worktree prune 2>/dev/null || true
|
|
191
|
+
|
|
192
|
+
ACTUAL_COUNT=$((WORKTREE_COUNT - 1))
|
|
193
|
+
echo -e "${GREEN}✓${NC} Removed $ACTUAL_COUNT git worktree(s)"
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
dev:
|
|
197
|
+
desc: Run in development mode
|
|
198
|
+
aliases: [d]
|
|
199
|
+
silent: true
|
|
200
|
+
cmds:
|
|
201
|
+
- bun run dev
|
|
202
|
+
|
|
203
|
+
lint:
|
|
204
|
+
desc: Run linter
|
|
205
|
+
aliases: [l]
|
|
206
|
+
silent: true
|
|
207
|
+
vars:
|
|
208
|
+
FILE_PATTERN: '{{.FILES | default "."}}'
|
|
209
|
+
deps:
|
|
210
|
+
- task: yaml:lint
|
|
211
|
+
vars: { FILES: "{{.FILES}}" }
|
|
212
|
+
- task: markdown:lint
|
|
213
|
+
vars: { FILES: "{{.FILES}}" }
|
|
214
|
+
cmds:
|
|
215
|
+
- |
|
|
216
|
+
GREEN='\033[0;32m'
|
|
217
|
+
YELLOW='\033[0;33m'
|
|
218
|
+
RED='\033[0;31m'
|
|
219
|
+
NC='\033[0m'
|
|
220
|
+
|
|
221
|
+
# Run typecheck only if FILES not specified (needs full project)
|
|
222
|
+
{{if not .FILES}}
|
|
223
|
+
task typecheck
|
|
224
|
+
{{end}}
|
|
225
|
+
|
|
226
|
+
# Detect ESLint
|
|
227
|
+
HAS_ESLINT=false
|
|
228
|
+
if [ -f "eslint.config.js" ] || [ -f "eslint.config.mjs" ] || [ -f "eslint.config.cjs" ] || \
|
|
229
|
+
[ -f ".eslintrc.js" ] || [ -f ".eslintrc.cjs" ] || [ -f ".eslintrc.json" ] || [ -f ".eslintrc" ] || \
|
|
230
|
+
grep -q '"eslintConfig"' package.json 2>/dev/null; then
|
|
231
|
+
HAS_ESLINT=true
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
# Detect Biome
|
|
235
|
+
HAS_BIOME=false
|
|
236
|
+
if [ -f "biome.json" ] || [ -f "biome.jsonc" ]; then
|
|
237
|
+
HAS_BIOME=true
|
|
238
|
+
fi
|
|
239
|
+
|
|
240
|
+
# Track overall exit code
|
|
241
|
+
FINAL_EXIT=0
|
|
242
|
+
|
|
243
|
+
# Run ESLint - exits 0 for warnings, 1 for errors
|
|
244
|
+
if [ "$HAS_ESLINT" = true ]; then
|
|
245
|
+
echo -e "${GREEN}▶${NC} Running ESLint..."
|
|
246
|
+
bunx eslint {{.FILE_PATTERN}} || FINAL_EXIT=$?
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
# Run Biome lint
|
|
250
|
+
if [ "$HAS_BIOME" = true ]; then
|
|
251
|
+
echo -e "${GREEN}▶${NC} Running Biome lint..."
|
|
252
|
+
bunx biome lint {{.FILE_PATTERN}} || FINAL_EXIT=$?
|
|
253
|
+
fi
|
|
254
|
+
|
|
255
|
+
if [ "$HAS_ESLINT" = false ] && [ "$HAS_BIOME" = false ]; then
|
|
256
|
+
echo -e "${YELLOW}⚠${NC} No linter configuration found (ESLint or Biome)"
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
exit $FINAL_EXIT
|
|
260
|
+
|
|
261
|
+
lint:fix:
|
|
262
|
+
desc: Run linter with auto-fix
|
|
263
|
+
aliases: [lf]
|
|
264
|
+
silent: true
|
|
265
|
+
vars:
|
|
266
|
+
FILE_PATTERN: '{{.FILES | default "."}}'
|
|
267
|
+
deps:
|
|
268
|
+
- task: yaml:lint:fix
|
|
269
|
+
vars: { FILES: "{{.FILES}}" }
|
|
270
|
+
- task: markdown:lint:fix
|
|
271
|
+
vars: { FILES: "{{.FILES}}" }
|
|
272
|
+
cmds:
|
|
273
|
+
- |
|
|
274
|
+
GREEN='\033[0;32m'
|
|
275
|
+
YELLOW='\033[0;33m'
|
|
276
|
+
NC='\033[0m'
|
|
277
|
+
|
|
278
|
+
# Detect ESLint
|
|
279
|
+
HAS_ESLINT=false
|
|
280
|
+
if [ -f "eslint.config.js" ] || [ -f "eslint.config.mjs" ] || [ -f "eslint.config.cjs" ] || \
|
|
281
|
+
[ -f ".eslintrc.js" ] || [ -f ".eslintrc.cjs" ] || [ -f ".eslintrc.json" ] || [ -f ".eslintrc" ] || \
|
|
282
|
+
grep -q '"eslintConfig"' package.json 2>/dev/null; then
|
|
283
|
+
HAS_ESLINT=true
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
# Detect Biome
|
|
287
|
+
HAS_BIOME=false
|
|
288
|
+
if [ -f "biome.json" ] || [ -f "biome.jsonc" ]; then
|
|
289
|
+
HAS_BIOME=true
|
|
290
|
+
fi
|
|
291
|
+
|
|
292
|
+
# Detect Prettier
|
|
293
|
+
HAS_PRETTIER=false
|
|
294
|
+
if [ -f ".prettierrc" ] || [ -f ".prettierrc.json" ] || [ -f ".prettierrc.js" ] || \
|
|
295
|
+
[ -f ".prettierrc.cjs" ] || [ -f ".prettierrc.mjs" ] || [ -f "prettier.config.js" ] || \
|
|
296
|
+
[ -f "prettier.config.cjs" ] || [ -f "prettier.config.mjs" ] || \
|
|
297
|
+
grep -q '"prettier"' package.json 2>/dev/null; then
|
|
298
|
+
HAS_PRETTIER=true
|
|
299
|
+
fi
|
|
300
|
+
|
|
301
|
+
# Run linters and formatters
|
|
302
|
+
if [ "$HAS_ESLINT" = true ]; then
|
|
303
|
+
echo -e "${GREEN}▶${NC} Running ESLint with auto-fix..."
|
|
304
|
+
bunx eslint --fix {{.FILE_PATTERN}}
|
|
305
|
+
fi
|
|
306
|
+
|
|
307
|
+
if [ "$HAS_BIOME" = true ]; then
|
|
308
|
+
echo -e "${GREEN}▶${NC} Running Biome lint with auto-fix..."
|
|
309
|
+
bunx biome lint --write {{.FILE_PATTERN}}
|
|
310
|
+
fi
|
|
311
|
+
|
|
312
|
+
if [ "$HAS_PRETTIER" = true ]; then
|
|
313
|
+
echo -e "${GREEN}▶${NC} Running Prettier..."
|
|
314
|
+
bunx prettier --write --log-level=warn {{.FILE_PATTERN}}
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
if [ "$HAS_ESLINT" = false ] && [ "$HAS_BIOME" = false ] && [ "$HAS_PRETTIER" = false ]; then
|
|
318
|
+
echo -e "${YELLOW}⚠${NC} No linter/formatter configuration found (ESLint, Biome, or Prettier)"
|
|
319
|
+
fi
|
|
320
|
+
|
|
321
|
+
format:
|
|
322
|
+
desc: Format code
|
|
323
|
+
aliases: [f]
|
|
324
|
+
silent: true
|
|
325
|
+
cmds:
|
|
326
|
+
- |
|
|
327
|
+
GREEN='\033[0;32m'
|
|
328
|
+
YELLOW='\033[0;33m'
|
|
329
|
+
NC='\033[0m'
|
|
330
|
+
|
|
331
|
+
# Detect Biome
|
|
332
|
+
HAS_BIOME=false
|
|
333
|
+
if [ -f "biome.json" ] || [ -f "biome.jsonc" ]; then
|
|
334
|
+
HAS_BIOME=true
|
|
335
|
+
fi
|
|
336
|
+
|
|
337
|
+
# Detect Prettier
|
|
338
|
+
HAS_PRETTIER=false
|
|
339
|
+
if [ -f ".prettierrc" ] || [ -f ".prettierrc.json" ] || [ -f ".prettierrc.js" ] || \
|
|
340
|
+
[ -f ".prettierrc.cjs" ] || [ -f ".prettierrc.mjs" ] || [ -f "prettier.config.js" ] || \
|
|
341
|
+
[ -f "prettier.config.cjs" ] || [ -f "prettier.config.mjs" ] || \
|
|
342
|
+
grep -q '"prettier"' package.json 2>/dev/null; then
|
|
343
|
+
HAS_PRETTIER=true
|
|
344
|
+
fi
|
|
345
|
+
|
|
346
|
+
# Run formatters (Biome first if both exist, as it's faster)
|
|
347
|
+
if [ "$HAS_BIOME" = true ]; then
|
|
348
|
+
echo -e "${GREEN}▶${NC} Running Biome format..."
|
|
349
|
+
bunx biome format --write .
|
|
350
|
+
fi
|
|
351
|
+
|
|
352
|
+
if [ "$HAS_PRETTIER" = true ]; then
|
|
353
|
+
echo -e "${GREEN}▶${NC} Running Prettier..."
|
|
354
|
+
bunx prettier --write --log-level=warn .
|
|
355
|
+
fi
|
|
356
|
+
|
|
357
|
+
if [ "$HAS_BIOME" = false ] && [ "$HAS_PRETTIER" = false ]; then
|
|
358
|
+
echo -e "${YELLOW}⚠${NC} No formatter configuration found (Biome or Prettier)"
|
|
359
|
+
fi
|
|
360
|
+
|
|
361
|
+
typecheck:
|
|
362
|
+
desc: Type check TypeScript
|
|
363
|
+
aliases: [tc]
|
|
364
|
+
silent: true
|
|
365
|
+
cmds:
|
|
366
|
+
- bun turbo typecheck
|
|
367
|
+
|
|
368
|
+
test:setup-dirs:
|
|
369
|
+
desc: Create test output directories in root and all packages
|
|
370
|
+
internal: true
|
|
371
|
+
silent: true
|
|
372
|
+
cmds:
|
|
373
|
+
- |
|
|
374
|
+
# Create .logs directories in root and each package (turbo runs in package dirs)
|
|
375
|
+
mkdir -p .logs/test-results .logs/coverage
|
|
376
|
+
for pkg in packages/*/; do
|
|
377
|
+
mkdir -p "$pkg/.logs/test-results" "$pkg/coverage"
|
|
378
|
+
done
|
|
379
|
+
|
|
380
|
+
test:
|
|
381
|
+
desc: Run unit tests (excludes integration tests)
|
|
382
|
+
aliases: [t]
|
|
383
|
+
silent: true
|
|
384
|
+
deps: [test:setup-dirs]
|
|
385
|
+
cmds:
|
|
386
|
+
- |
|
|
387
|
+
source "$(task op:export)"
|
|
388
|
+
bun turbo test
|
|
389
|
+
|
|
390
|
+
test:integration:
|
|
391
|
+
desc: Run integration tests (tests that interact with real external systems)
|
|
392
|
+
aliases: [ti]
|
|
393
|
+
silent: true
|
|
394
|
+
deps: [test:setup-dirs]
|
|
395
|
+
cmds:
|
|
396
|
+
- |
|
|
397
|
+
source "$(task op:export)"
|
|
398
|
+
bun turbo test:integration
|
|
399
|
+
|
|
400
|
+
test:coverage:
|
|
401
|
+
desc: Run tests with coverage (use FILES="pattern" or DIFF=true for staged files)
|
|
402
|
+
aliases: [cov]
|
|
403
|
+
silent: true
|
|
404
|
+
deps: [test:setup-dirs]
|
|
405
|
+
vars:
|
|
406
|
+
FILE_PATTERN: '{{.FILES | default ""}}'
|
|
407
|
+
DIFF_MODE: '{{.DIFF | default "false"}}'
|
|
408
|
+
cmds:
|
|
409
|
+
- |
|
|
410
|
+
# Export 1Password secrets
|
|
411
|
+
source "$(task op:export)"
|
|
412
|
+
|
|
413
|
+
EXIT_CODE=0
|
|
414
|
+
|
|
415
|
+
if [ "{{.DIFF_MODE}}" = "true" ]; then
|
|
416
|
+
FILE_LIST=$(git diff --staged --name-only --diff-filter=d '*.ts' '*.tsx' | tr '\n' ' ')
|
|
417
|
+
if [ -z "$FILE_LIST" ]; then
|
|
418
|
+
echo "No TypeScript files staged"
|
|
419
|
+
exit 0
|
|
420
|
+
fi
|
|
421
|
+
export FILES="$FILE_LIST"
|
|
422
|
+
bun run test:coverage || EXIT_CODE=$?
|
|
423
|
+
elif [ -n "{{.FILE_PATTERN}}" ]; then
|
|
424
|
+
export FILES="{{.FILE_PATTERN}}"
|
|
425
|
+
bun run test:coverage || EXIT_CODE=$?
|
|
426
|
+
else
|
|
427
|
+
# Use turbo for full coverage run (caching, parallelism)
|
|
428
|
+
bunx turbo test:coverage || EXIT_CODE=$?
|
|
429
|
+
fi
|
|
430
|
+
|
|
431
|
+
# Always show report, then exit with original exit code
|
|
432
|
+
task test:coverage:report
|
|
433
|
+
exit $EXIT_CODE
|
|
434
|
+
|
|
435
|
+
test:coverage:report:
|
|
436
|
+
desc: Display coverage report summary from existing test results
|
|
437
|
+
aliases: [covr]
|
|
438
|
+
silent: true
|
|
439
|
+
vars:
|
|
440
|
+
THRESHOLD: '{{.THRESHOLD | default "80"}}'
|
|
441
|
+
cmds:
|
|
442
|
+
- |
|
|
443
|
+
# Color codes
|
|
444
|
+
RED='\033[0;31m'
|
|
445
|
+
YELLOW='\033[0;33m'
|
|
446
|
+
GREEN='\033[0;32m'
|
|
447
|
+
CYAN='\033[0;36m'
|
|
448
|
+
BOLD='\033[1m'
|
|
449
|
+
NC='\033[0m'
|
|
450
|
+
|
|
451
|
+
# Ensure .tmp directory exists for temp files
|
|
452
|
+
mkdir -p .tmp
|
|
453
|
+
|
|
454
|
+
# Find all lcov files in packages (monorepo) or root
|
|
455
|
+
# Excludes .logs/ directory (old debug runs)
|
|
456
|
+
LCOV_FILES=$(find . -path "./packages/*/coverage/lcov.info" -o -path "./coverage/lcov.info" 2>/dev/null | grep -v node_modules | grep -v "\.logs/")
|
|
457
|
+
|
|
458
|
+
echo ""
|
|
459
|
+
echo -e "${BOLD}Coverage Report${NC}"
|
|
460
|
+
echo ""
|
|
461
|
+
|
|
462
|
+
if [ -n "$LCOV_FILES" ]; then
|
|
463
|
+
# Aggregate coverage from all lcov files
|
|
464
|
+
TOTAL_HIT=0
|
|
465
|
+
TOTAL_FOUND=0
|
|
466
|
+
|
|
467
|
+
for LCOV_FILE in $LCOV_FILES; do
|
|
468
|
+
HIT=$(grep "^LH:" "$LCOV_FILE" 2>/dev/null | awk -F: '{sum+=$2} END {print sum+0}')
|
|
469
|
+
FOUND=$(grep "^LF:" "$LCOV_FILE" 2>/dev/null | awk -F: '{sum+=$2} END {print sum+0}')
|
|
470
|
+
TOTAL_HIT=$((TOTAL_HIT + HIT))
|
|
471
|
+
TOTAL_FOUND=$((TOTAL_FOUND + FOUND))
|
|
472
|
+
|
|
473
|
+
# Extract package name from path (handle root ./coverage/lcov.info)
|
|
474
|
+
# Check for root patterns first, before removing suffixes
|
|
475
|
+
PKG_NAME=$(echo "$LCOV_FILE" | sed -e 's|^\./coverage/lcov\.info$|root|' -e 's|^\./\.logs/coverage/lcov\.info$|root|' -e 's|^\./packages/||' -e 's|/coverage/lcov\.info$||')
|
|
476
|
+
if [ "$FOUND" -gt 0 ]; then
|
|
477
|
+
PKG_PCT=$(awk "BEGIN {printf \"%.1f\", ($HIT/$FOUND)*100}")
|
|
478
|
+
# Color code: green >= 80%, yellow >= 50%, red < 50%
|
|
479
|
+
if awk "BEGIN {exit !($PKG_PCT >= 80)}"; then
|
|
480
|
+
PKG_COLOR="$GREEN"
|
|
481
|
+
elif awk "BEGIN {exit !($PKG_PCT >= 50)}"; then
|
|
482
|
+
PKG_COLOR="$YELLOW"
|
|
483
|
+
else
|
|
484
|
+
PKG_COLOR="$RED"
|
|
485
|
+
fi
|
|
486
|
+
echo -e " ${CYAN}${PKG_NAME}:${NC} ${HIT}/${FOUND} (${PKG_COLOR}${PKG_PCT}%${NC})"
|
|
487
|
+
fi
|
|
488
|
+
done
|
|
489
|
+
|
|
490
|
+
if [ "$TOTAL_FOUND" -gt 0 ]; then
|
|
491
|
+
COVERAGE_PCT=$(awk "BEGIN {printf \"%.1f\", ($TOTAL_HIT/$TOTAL_FOUND)*100}")
|
|
492
|
+
# Color code total
|
|
493
|
+
if awk "BEGIN {exit !($COVERAGE_PCT >= 80)}"; then
|
|
494
|
+
TOTAL_COLOR="$GREEN"
|
|
495
|
+
elif awk "BEGIN {exit !($COVERAGE_PCT >= 50)}"; then
|
|
496
|
+
TOTAL_COLOR="$YELLOW"
|
|
497
|
+
else
|
|
498
|
+
TOTAL_COLOR="$RED"
|
|
499
|
+
fi
|
|
500
|
+
echo ""
|
|
501
|
+
echo -e " ${BOLD}Total:${NC} ${TOTAL_HIT}/${TOTAL_FOUND} (${TOTAL_COLOR}${COVERAGE_PCT}%${NC})"
|
|
502
|
+
fi
|
|
503
|
+
|
|
504
|
+
# Parse all lcov files to find files below threshold
|
|
505
|
+
# Process each lcov file separately to preserve package context
|
|
506
|
+
> .tmp/low_coverage_raw.txt
|
|
507
|
+
for LCOV_FILE in $LCOV_FILES; do
|
|
508
|
+
# Extract package prefix from lcov path (e.g., packages/secureai-core/)
|
|
509
|
+
PKG_PREFIX=$(echo "$LCOV_FILE" | sed -n 's|\./\(packages/[^/]*/\)coverage/lcov.info|\1|p')
|
|
510
|
+
|
|
511
|
+
awk -v threshold="{{.THRESHOLD}}" -v pkg_prefix="$PKG_PREFIX" '
|
|
512
|
+
/^SF:/ {
|
|
513
|
+
file = substr($0, 4)
|
|
514
|
+
# Skip cross-package references (files starting with ../)
|
|
515
|
+
if (file ~ /^\.\.\//) {
|
|
516
|
+
skip = 1
|
|
517
|
+
next
|
|
518
|
+
}
|
|
519
|
+
# Prepend package prefix to relative paths (src/... or tests/...)
|
|
520
|
+
if (pkg_prefix != "" && file ~ /^src\//) {
|
|
521
|
+
file = pkg_prefix file
|
|
522
|
+
} else if (pkg_prefix != "" && file ~ /^tests\//) {
|
|
523
|
+
file = pkg_prefix file
|
|
524
|
+
}
|
|
525
|
+
# Check exclusion patterns (matching bunfig.toml)
|
|
526
|
+
skip = 0
|
|
527
|
+
if (file ~ /\.system\.ts$/) skip = 1
|
|
528
|
+
if (file ~ /systems\.ts$/) skip = 1
|
|
529
|
+
if (file ~ /\.cli\.ts$/) skip = 1
|
|
530
|
+
if (file ~ /\/index\.ts$/) skip = 1
|
|
531
|
+
if (file ~ /\/interfaces\.ts$/) skip = 1
|
|
532
|
+
if (file ~ /\/mock\.ts$/) skip = 1
|
|
533
|
+
if (file ~ /\/mocks\.ts$/) skip = 1
|
|
534
|
+
if (file ~ /\/mocks\//) skip = 1
|
|
535
|
+
if (file ~ /test-doubles\.ts$/) skip = 1
|
|
536
|
+
if (file ~ /^tests\//) skip = 1
|
|
537
|
+
if (file ~ /\.test\.ts$/) skip = 1
|
|
538
|
+
if (file ~ /\/setup\.ts$/) skip = 1
|
|
539
|
+
if (file ~ /\/tools\/plan-/) skip = 1
|
|
540
|
+
if (file ~ /\/tools\/pii-/) skip = 1
|
|
541
|
+
if (file ~ /\/tools\/secret-/) skip = 1
|
|
542
|
+
if (file ~ /in-memory/) skip = 1
|
|
543
|
+
if (file ~ /\.system\.js$/) skip = 1
|
|
544
|
+
}
|
|
545
|
+
/^LF:/ { lines_found = substr($0, 4) }
|
|
546
|
+
/^LH:/ { lines_hit = substr($0, 4) }
|
|
547
|
+
/^end_of_record/ {
|
|
548
|
+
if (lines_found > 0 && skip == 0) {
|
|
549
|
+
coverage = (lines_hit / lines_found) * 100
|
|
550
|
+
if (coverage < threshold) {
|
|
551
|
+
printf "%s|%.1f\n", file, coverage
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
file = ""; lines_found = 0; lines_hit = 0; skip = 0
|
|
555
|
+
}
|
|
556
|
+
' "$LCOV_FILE" >> .tmp/low_coverage_raw.txt
|
|
557
|
+
done
|
|
558
|
+
|
|
559
|
+
# Deduplicate by file (keep first occurrence - lowest will be sorted first)
|
|
560
|
+
sort -t'|' -k2 -n .tmp/low_coverage_raw.txt 2>/dev/null | \
|
|
561
|
+
awk -F'|' '!seen[$1]++ { print $1 "|" $2 }' | \
|
|
562
|
+
sort -t'|' -k2 -n > .tmp/low_coverage_files.txt
|
|
563
|
+
rm -f .tmp/low_coverage_raw.txt
|
|
564
|
+
|
|
565
|
+
echo ""
|
|
566
|
+
if [ -s .tmp/low_coverage_files.txt ]; then
|
|
567
|
+
FILE_COUNT=$(wc -l < .tmp/low_coverage_files.txt | tr -d ' ')
|
|
568
|
+
echo -e "${BOLD}Files Below {{.THRESHOLD}}%${NC} (${FILE_COUNT} files)"
|
|
569
|
+
echo ""
|
|
570
|
+
echo -e " ${CYAN}Coverage File${NC}"
|
|
571
|
+
echo -e " ${CYAN}───────── ────────────────────────────────────────────────────${NC}"
|
|
572
|
+
awk -F'|' -v red="$RED" -v yellow="$YELLOW" -v nc="$NC" '{
|
|
573
|
+
color = ($2 < 50) ? red : yellow
|
|
574
|
+
printf " %s%7.1f%% %s%s\n", color, $2, nc, $1
|
|
575
|
+
}' .tmp/low_coverage_files.txt
|
|
576
|
+
echo ""
|
|
577
|
+
echo -e " ${RED}$FILE_COUNT file(s) below threshold${NC}"
|
|
578
|
+
else
|
|
579
|
+
echo -e " ${GREEN}✓ All files meet {{.THRESHOLD}}% coverage${NC}"
|
|
580
|
+
fi
|
|
581
|
+
rm -f .tmp/low_coverage_files.txt
|
|
582
|
+
else
|
|
583
|
+
echo -e " ${YELLOW}No coverage files found${NC}"
|
|
584
|
+
echo -e " ${CYAN}Run:${NC} task test:coverage"
|
|
585
|
+
fi
|
|
586
|
+
|
|
587
|
+
# Find all junit files in packages (monorepo) or root
|
|
588
|
+
JUNIT_FILES=$(find . -path "./packages/*/.logs/test-results/junit.xml" -o -path "./.logs/test-results/junit.xml" 2>/dev/null | grep -v node_modules)
|
|
589
|
+
|
|
590
|
+
echo ""
|
|
591
|
+
echo -e "${BOLD}Test Results${NC}"
|
|
592
|
+
echo ""
|
|
593
|
+
|
|
594
|
+
if [ -n "$JUNIT_FILES" ]; then
|
|
595
|
+
TOTAL_TESTS=0
|
|
596
|
+
TOTAL_FAILURES=0
|
|
597
|
+
|
|
598
|
+
for JUNIT_FILE in $JUNIT_FILES; do
|
|
599
|
+
TESTS=$(grep -o 'tests="[0-9]*"' "$JUNIT_FILE" 2>/dev/null | head -1 | grep -o '[0-9]*' || echo "0")
|
|
600
|
+
FAILURES=$(grep -o 'failures="[0-9]*"' "$JUNIT_FILE" 2>/dev/null | head -1 | grep -o '[0-9]*' || echo "0")
|
|
601
|
+
TOTAL_TESTS=$((TOTAL_TESTS + TESTS))
|
|
602
|
+
TOTAL_FAILURES=$((TOTAL_FAILURES + FAILURES))
|
|
603
|
+
|
|
604
|
+
# Extract package name from path
|
|
605
|
+
PKG_NAME=$(echo "$JUNIT_FILE" | sed 's|^\./packages/||' | sed 's|/\.logs/test-results/junit.xml||' | sed 's|^\./\.logs/test-results/junit.xml|root|')
|
|
606
|
+
if [ "$FAILURES" = "0" ]; then
|
|
607
|
+
echo -e " ${GREEN}✓${NC} ${PKG_NAME}: ${TESTS} tests"
|
|
608
|
+
else
|
|
609
|
+
echo -e " ${RED}✗${NC} ${PKG_NAME}: ${FAILURES}/${TESTS} failed"
|
|
610
|
+
fi
|
|
611
|
+
done
|
|
612
|
+
|
|
613
|
+
echo ""
|
|
614
|
+
if [ "$TOTAL_FAILURES" = "0" ]; then
|
|
615
|
+
echo -e " ${GREEN}✓ ${TOTAL_TESTS} tests passed${NC}"
|
|
616
|
+
else
|
|
617
|
+
echo -e " ${RED}✗ ${TOTAL_FAILURES} failures${NC} out of ${TOTAL_TESTS} tests"
|
|
618
|
+
fi
|
|
619
|
+
else
|
|
620
|
+
echo -e " ${YELLOW}No test result files found${NC}"
|
|
621
|
+
echo -e " ${CYAN}Run:${NC} task test:coverage"
|
|
622
|
+
fi
|
|
623
|
+
echo ""
|
|
624
|
+
|
|
625
|
+
test:clear:
|
|
626
|
+
desc: Clear test caches, coverage reports, and result files
|
|
627
|
+
aliases: [tcl]
|
|
628
|
+
silent: true
|
|
629
|
+
cmds:
|
|
630
|
+
- bun run packages/secureai-cli/src/cli.ts test-clear {{.CLI_ARGS}}
|
|
631
|
+
|
|
632
|
+
ci:
|
|
633
|
+
desc: Run CI checks (lint, typecheck, test, build)
|
|
634
|
+
silent: true
|
|
635
|
+
deps:
|
|
636
|
+
- lint
|
|
637
|
+
- typecheck
|
|
638
|
+
- test
|
|
639
|
+
- build
|
|
640
|
+
|
|
641
|
+
# Settings shortcuts (flattened from claude namespace for convenience)
|
|
642
|
+
so:
|
|
643
|
+
desc: Open Claude settings.json in detected IDE
|
|
644
|
+
silent: true
|
|
645
|
+
cmds:
|
|
646
|
+
- task claude:settings:open
|
|
647
|
+
|
|
648
|
+
ss:
|
|
649
|
+
desc: Show current Claude settings
|
|
650
|
+
silent: true
|
|
651
|
+
cmds:
|
|
652
|
+
- task claude:settings:show
|
|
653
|
+
|
|
654
|
+
sp:
|
|
655
|
+
desc: Print Claude settings file path
|
|
656
|
+
silent: true
|
|
657
|
+
cmds:
|
|
658
|
+
- task claude:settings:path
|
|
659
|
+
|
|
660
|
+
# Cross-platform IDE file opener utility
|
|
661
|
+
# Opens file in the IDE that invoked the task (detects from environment/process)
|
|
662
|
+
ide:open:
|
|
663
|
+
desc: Open file in current IDE (detects invoking IDE from environment)
|
|
664
|
+
aliases: [io]
|
|
665
|
+
silent: true
|
|
666
|
+
vars:
|
|
667
|
+
FILE_PATH: '{{.FILE | default ""}}'
|
|
668
|
+
cmds:
|
|
669
|
+
- |
|
|
670
|
+
FILE_PATH="{{.FILE_PATH}}"
|
|
671
|
+
if [ -z "$FILE_PATH" ]; then
|
|
672
|
+
echo "Usage: task ide:open FILE=\"path/to/file\""
|
|
673
|
+
exit 1
|
|
674
|
+
fi
|
|
675
|
+
|
|
676
|
+
# Expand ~ to home directory
|
|
677
|
+
FILE_PATH="${FILE_PATH/#\~/$HOME}"
|
|
678
|
+
|
|
679
|
+
# Detect which IDE invoked us by checking environment variables
|
|
680
|
+
# VSCODE_CODE_CACHE_PATH contains the IDE name in the path
|
|
681
|
+
IDE_CMD=""
|
|
682
|
+
|
|
683
|
+
if [ -n "$VSCODE_CODE_CACHE_PATH" ]; then
|
|
684
|
+
if [[ "$VSCODE_CODE_CACHE_PATH" == *"Cursor"* ]]; then
|
|
685
|
+
IDE_CMD="cursor"
|
|
686
|
+
elif [[ "$VSCODE_CODE_CACHE_PATH" == *"Code - Insiders"* ]]; then
|
|
687
|
+
IDE_CMD="code-insiders"
|
|
688
|
+
elif [[ "$VSCODE_CODE_CACHE_PATH" == *"Code"* ]]; then
|
|
689
|
+
IDE_CMD="code"
|
|
690
|
+
fi
|
|
691
|
+
fi
|
|
692
|
+
|
|
693
|
+
# Fallback: check __CFBundleIdentifier on macOS
|
|
694
|
+
if [ -z "$IDE_CMD" ] && [ -n "$__CFBundleIdentifier" ]; then
|
|
695
|
+
case "$__CFBundleIdentifier" in
|
|
696
|
+
com.todesktop.230313mzl4w4u92)
|
|
697
|
+
IDE_CMD="cursor"
|
|
698
|
+
;;
|
|
699
|
+
com.microsoft.VSCodeInsiders)
|
|
700
|
+
IDE_CMD="code-insiders"
|
|
701
|
+
;;
|
|
702
|
+
com.microsoft.VSCode)
|
|
703
|
+
IDE_CMD="code"
|
|
704
|
+
;;
|
|
705
|
+
esac
|
|
706
|
+
fi
|
|
707
|
+
|
|
708
|
+
# External terminal fallback: code > code-insiders > cursor
|
|
709
|
+
if [ -z "$IDE_CMD" ]; then
|
|
710
|
+
if command -v code &> /dev/null; then
|
|
711
|
+
IDE_CMD="code"
|
|
712
|
+
elif command -v code-insiders &> /dev/null; then
|
|
713
|
+
IDE_CMD="code-insiders"
|
|
714
|
+
elif command -v cursor &> /dev/null; then
|
|
715
|
+
IDE_CMD="cursor"
|
|
716
|
+
fi
|
|
717
|
+
fi
|
|
718
|
+
|
|
719
|
+
if [ -z "$IDE_CMD" ]; then
|
|
720
|
+
# Fallback: try platform-specific open
|
|
721
|
+
case "$(uname -s)" in
|
|
722
|
+
Darwin)
|
|
723
|
+
open "$FILE_PATH"
|
|
724
|
+
;;
|
|
725
|
+
Linux)
|
|
726
|
+
xdg-open "$FILE_PATH" 2>/dev/null || echo "No IDE found"
|
|
727
|
+
;;
|
|
728
|
+
MINGW*|MSYS*|CYGWIN*)
|
|
729
|
+
start "" "$FILE_PATH"
|
|
730
|
+
;;
|
|
731
|
+
*)
|
|
732
|
+
echo "No IDE found. Install cursor, code, or code-insiders"
|
|
733
|
+
exit 1
|
|
734
|
+
;;
|
|
735
|
+
esac
|
|
736
|
+
else
|
|
737
|
+
"$IDE_CMD" "$FILE_PATH"
|
|
738
|
+
fi
|