Skip to content

Commit 707191a

Browse files
committed
fix: repair main CI validation install
1 parent 7a8ac2e commit 707191a

7 files changed

Lines changed: 1795 additions & 97 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ posthog/
8080
# Secrets (paranoid mode - never commit these)
8181
*secret*
8282
*SECRET*
83+
!scripts/lib/check-secrets.js
8384
*credentials*
8485
*CREDENTIALS*
8586
*.key

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ If Linear is unavailable, the Linear utility command should fail explicitly. The
133133
3. **Run commands sequentially** — pass the command file from [`commands/`](commands/) to Claude Code (e.g., paste `commands/create-issue.md` content and follow it)
134134
4. **Read the knowledge base first** — every command in the pipeline reads all files in [`knowledge/`](knowledge/) before generating output to avoid repeating past mistakes
135135
5. **Track gates, not just progress** — check `project-state.md` after each command; blocked = do not proceed
136-
6. **Validate repo changes** — run `bun run validate` from the repo root before accepting agent work
136+
6. **Validate repo changes** — run `bun install` and `bun run validate` from the repo root before accepting agent work
137137

138138
**Default tech stack** (used across all apps):
139139

@@ -146,9 +146,9 @@ If Linear is unavailable, the Linear utility command should fail explicitly. The
146146
**Environment setup per app:**
147147

148148
```bash
149+
bun install
149150
cd apps/[project-name]
150151
cp .env.local.example .env.local # fill in your keys
151-
bun install
152152
bun run dev
153153
```
154154

apps/clarity/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@neondatabase/serverless": "^0.10.4",
1717
"@prisma/adapter-neon": "^6.4.1",
1818
"@prisma/client": "^6.4.1",
19+
"@supabase/supabase-js": "^2.105.1",
1920
"framer-motion": "^12.35.2",
2021
"lucide-react": "^0.577.0",
2122
"next": "16.1.6",

apps/clarity/src/components/TaskBoard.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,14 @@ export default function TaskBoard() {
111111
}
112112
};
113113

114-
const markDone = async (task: Task) => {
114+
const markDone = async (task: Task, completedAtMs: number) => {
115115
// Optimistic UI
116116
setTasks((prev) => prev.filter((t) => t.id !== task.id));
117117

118118
posthog?.capture('task_completed', {
119119
task_id: task.id,
120120
category: task.category,
121-
time_since_creation: Date.now() - new Date(task.created_at).getTime(),
121+
time_since_creation: completedAtMs - new Date(task.created_at).getTime(),
122122
});
123123

124124
try {
@@ -235,7 +235,7 @@ export default function TaskBoard() {
235235
>
236236
<div className="flex gap-3">
237237
<button
238-
onClick={() => markDone(task)}
238+
onClick={() => markDone(task, Date.now())}
239239
className="mt-0.5 text-neutral-600 hover:text-emerald-400 transition-colors flex-shrink-0"
240240
>
241241
<CheckCircle2 className="w-5 h-5" />

bun.lock

Lines changed: 1668 additions & 92 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
"version": "1.0.0",
44
"private": true,
55
"description": "AI Product Operating System — command-driven development framework with specialized AI agents",
6+
"workspaces": [
7+
"apps/clarity",
8+
"apps/research-copilot"
9+
],
610
"scripts": {
711
"prepare": "husky",
812
"lint": "bun run --cwd apps/clarity lint",

scripts/lib/check-secrets.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Pre-commit hook: Scan staged files for secrets.
5+
* Catches API keys, tokens, passwords, and connection strings
6+
* that .gitignore might miss (e.g., hardcoded in source files).
7+
*/
8+
9+
const { execSync } = require('child_process');
10+
const fs = require('fs');
11+
const path = require('path');
12+
13+
const CONFIG = {
14+
patterns: [
15+
// Generic secrets
16+
{ regex: /(?:api[_-]?key|apikey)\s*[:=]\s*['"][A-Za-z0-9_\-]{20,}['"]/gi, label: 'API key' },
17+
{ regex: /(?:secret|token)\s*[:=]\s*['"][A-Za-z0-9_\-]{20,}['"]/gi, label: 'Secret/Token' },
18+
{ regex: /(?:password|passwd|pwd)\s*[:=]\s*['"][^'"]{8,}['"]/gi, label: 'Password' },
19+
20+
// Supabase
21+
{ regex: /eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.[A-Za-z0-9_-]+/g, label: 'Supabase JWT' },
22+
{ regex: /sbp_[a-f0-9]{40}/g, label: 'Supabase service key' },
23+
24+
// PostHog
25+
{ regex: /phc_[A-Za-z0-9]{30,}/g, label: 'PostHog API key' },
26+
27+
// Google / Gemini
28+
{ regex: /AIza[A-Za-z0-9_\\-]{35}/g, label: 'Google API key' },
29+
30+
// Neon DB
31+
{ regex: /postgresql:\/\/[^:]+:[^@]+@[^/]+/g, label: 'Database connection string' },
32+
33+
// Private keys
34+
{ regex: /-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----/g, label: 'Private key' },
35+
36+
// Generic high-entropy strings (base64, 40+ chars assigned to suspect vars)
37+
{ regex: /(?:SUPABASE|POSTHOG|NEON|GEMINI|GOOGLE)[_A-Z]*\s*=\s*['"]?[A-Za-z0-9+/=_\-]{30,}['"]?/g, label: 'Environment variable with secret' },
38+
],
39+
allowlistPaths: [
40+
'.env.example',
41+
'.env.local.example',
42+
'scripts/lib/check-secrets.js', // This file contains regex patterns, not real secrets
43+
],
44+
};
45+
46+
function getStagedFiles() {
47+
try {
48+
const output = execSync('git diff --cached --name-only --diff-filter=ACMR', {
49+
encoding: 'utf8',
50+
});
51+
return output.trim().split('\n').filter(Boolean);
52+
} catch {
53+
return [];
54+
}
55+
}
56+
57+
function checkFile(filePath) {
58+
const violations = [];
59+
60+
if (CONFIG.allowlistPaths.some((allowed) => filePath.endsWith(allowed))) {
61+
return violations;
62+
}
63+
64+
// Skip binary files
65+
const ext = path.extname(filePath).toLowerCase();
66+
if (['.png', '.jpg', '.jpeg', '.gif', '.ico', '.woff', '.woff2', '.ttf', '.eot'].includes(ext)) {
67+
return violations;
68+
}
69+
70+
try {
71+
const content = fs.readFileSync(filePath, 'utf8');
72+
const lines = content.split('\n');
73+
74+
for (let i = 0; i < lines.length; i++) {
75+
const line = lines[i];
76+
for (const pattern of CONFIG.patterns) {
77+
if (pattern.regex.test(line)) {
78+
violations.push({
79+
file: filePath,
80+
line: i + 1,
81+
label: pattern.label,
82+
snippet: line.trim().substring(0, 80),
83+
});
84+
// Reset regex lastIndex for global patterns
85+
pattern.regex.lastIndex = 0;
86+
}
87+
}
88+
}
89+
} catch {
90+
// Skip unreadable files
91+
}
92+
93+
return violations;
94+
}
95+
96+
// Main
97+
const files = getStagedFiles();
98+
const allViolations = [];
99+
100+
for (const file of files) {
101+
allViolations.push(...checkFile(file));
102+
}
103+
104+
if (allViolations.length > 0) {
105+
console.error('\n🔐 SECRET SCANNING FAILED\n');
106+
console.error('The following staged files appear to contain secrets:\n');
107+
for (const v of allViolations) {
108+
console.error(` ${v.file}:${v.line}${v.label}`);
109+
console.error(` ${v.snippet}\n`);
110+
}
111+
console.error('Remove the secrets and use environment variables instead.');
112+
console.error('If this is a false positive, add the file to CONFIG.allowlistPaths in scripts/lib/check-secrets.js\n');
113+
process.exit(1);
114+
}
115+
116+
console.log('✓ Secret scan passed');

0 commit comments

Comments
 (0)