codecruise 0.1.0
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/LICENSE +21 -0
- package/README.md +111 -0
- package/bin/codecruise.js +68 -0
- package/config/CLAUDE.md +107 -0
- package/config/agents/analyst.md +48 -0
- package/config/agents/architect-reviewer.md +161 -0
- package/config/agents/architect.md +119 -0
- package/config/agents/critic.md +63 -0
- package/config/agents/developer.md +96 -0
- package/config/agents/devops.md +81 -0
- package/config/agents/orchestrator.md +91 -0
- package/config/agents/planner.md +139 -0
- package/config/agents/retro.md +52 -0
- package/config/agents/reviewer.md +101 -0
- package/config/agents/security-reviewer.md +57 -0
- package/config/agents/stack/expo/AGENT.md +473 -0
- package/config/agents/stack/expo/rules/critical.md +427 -0
- package/config/agents/stack/expo/rules/native.md +455 -0
- package/config/agents/stack/expo/rules/navigation.md +445 -0
- package/config/agents/stack/expo/rules/performance.md +415 -0
- package/config/agents/stack/fastify/AGENT.md +397 -0
- package/config/agents/stack/fastify/rules/api-design.md +283 -0
- package/config/agents/stack/fastify/rules/critical.md +232 -0
- package/config/agents/stack/fastify/rules/queues.md +303 -0
- package/config/agents/stack/fastify/rules/security.md +384 -0
- package/config/agents/stack/index.yaml +48 -0
- package/config/agents/stack/nextjs/AGENT.md +421 -0
- package/config/agents/stack/nextjs/rules/components.md +413 -0
- package/config/agents/stack/nextjs/rules/critical.md +391 -0
- package/config/agents/stack/nextjs/rules/performance.md +403 -0
- package/config/agents/stack/nextjs/rules/styling.md +334 -0
- package/config/agents/stack/shared-ts/AGENT.md +384 -0
- package/config/agents/stack/shared-ts/rules/critical.md +315 -0
- package/config/agents/stack/shared-ts/rules/patterns.md +384 -0
- package/config/agents/stack/shared-ts/rules/zod.md +427 -0
- package/config/agents/tester.md +79 -0
- package/config/commands/architect-discuss.md +366 -0
- package/config/commands/architect-list.md +160 -0
- package/config/commands/architect-review.md +111 -0
- package/config/commands/architect.md +118 -0
- package/config/commands/compact.md +118 -0
- package/config/commands/companion.md +279 -0
- package/config/commands/dashboard.md +152 -0
- package/config/commands/doctor.md +227 -0
- package/config/commands/dogfood-report.md +101 -0
- package/config/commands/flags/run-autonomous.md +110 -0
- package/config/commands/flags/run-pause.md +80 -0
- package/config/commands/ingest.md +173 -0
- package/config/commands/init.md +128 -0
- package/config/commands/metrics.md +87 -0
- package/config/commands/parallel.md +320 -0
- package/config/commands/pause.md +55 -0
- package/config/commands/plan-review.md +130 -0
- package/config/commands/plan.md +216 -0
- package/config/commands/production-check.md +308 -0
- package/config/commands/refine.md +323 -0
- package/config/commands/resume.md +72 -0
- package/config/commands/retro.md +121 -0
- package/config/commands/retry.md +75 -0
- package/config/commands/role.md +310 -0
- package/config/commands/run.md +417 -0
- package/config/commands/scope.md +85 -0
- package/config/commands/setup-permissions.md +104 -0
- package/config/commands/skip.md +75 -0
- package/config/commands/spec-forge.md +213 -0
- package/config/commands/spec-help.md +194 -0
- package/config/commands/spec-patch.md +342 -0
- package/config/commands/spec-resolve.md +110 -0
- package/config/commands/spec-review.md +153 -0
- package/config/commands/status.md +114 -0
- package/config/commands/sync.md +131 -0
- package/config/commands/task.md +138 -0
- package/config/commands/verify.md +124 -0
- package/config/hooks/README.md +632 -0
- package/config/hooks/activity-log.sh +187 -0
- package/config/hooks/anti-rationalize.sh +52 -0
- package/config/hooks/capture-verification.sh +112 -0
- package/config/hooks/collect-metrics.sh +135 -0
- package/config/hooks/enforce-file-scope.sh +75 -0
- package/config/hooks/enforce-state-machine.sh +161 -0
- package/config/hooks/enforce-tdd.sh +180 -0
- package/config/hooks/format.sh +40 -0
- package/config/hooks/lib/activity-helpers.sh +162 -0
- package/config/hooks/lib/read-settings.sh +71 -0
- package/config/hooks/load-context-skills.sh +95 -0
- package/config/hooks/notify.sh +81 -0
- package/config/hooks/pre-commit.sample +35 -0
- package/config/hooks/protect-files.sh +63 -0
- package/config/hooks/track-agents.sh +41 -0
- package/config/hooks/track-commands.sh +37 -0
- package/config/hooks/track-enforcement.sh +44 -0
- package/config/hooks/track-ooda.sh +77 -0
- package/config/hooks/validate-commit-msg.sh +35 -0
- package/config/hooks/validate-plan.sh +213 -0
- package/config/hooks/verify-criteria.sh +46 -0
- package/config/hooks/verify-todo-completion.sh +140 -0
- package/config/rules/comments.md +25 -0
- package/config/rules/decision-rules.md +308 -0
- package/config/rules/hygiene.md +247 -0
- package/config/rules/pattern-detection.md +372 -0
- package/config/rules/profiles.md +193 -0
- package/config/rules/recovery.md +83 -0
- package/config/rules/scope-detection.md +213 -0
- package/config/rules/standards.md +127 -0
- package/config/rules/workflow.md +121 -0
- package/config/schemas.md +767 -0
- package/config/settings.json +195 -0
- package/config/skills/backend/SKILL.md +734 -0
- package/config/skills/database/SKILL.md +426 -0
- package/config/skills/frontend/SKILL.md +434 -0
- package/config/skills/git/SKILL.md +396 -0
- package/config/skills/index.yaml +36 -0
- package/config/skills/observability/SKILL.md +430 -0
- package/config/skills/package-dev/SKILL.md +498 -0
- package/config/skills/performance/SKILL.md +378 -0
- package/config/skills/resilience/SKILL.md +573 -0
- package/config/skills/testing/SKILL.md +398 -0
- package/config/skills/testing-patterns/SKILL.md +276 -0
- package/config/skills/typescript/SKILL.md +152 -0
- package/config/templates/CLAUDE.md +70 -0
- package/config/templates/README.md +117 -0
- package/config/templates/steering/adr-template.md +102 -0
- package/config/templates/steering/product.md +60 -0
- package/config/templates/steering/rfc-template.md +159 -0
- package/config/templates/steering/structure.md +146 -0
- package/config/templates/steering/tech.md +85 -0
- package/package.json +40 -0
- package/src/install.js +163 -0
- package/src/report.js +310 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# Security Rules - Fastify
|
|
2
|
+
|
|
3
|
+
Authentication, authorization, and protection patterns.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Authentication
|
|
8
|
+
|
|
9
|
+
### SEC-001: Use secure token generation
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { randomBytes, createHash } from 'crypto';
|
|
13
|
+
|
|
14
|
+
// For refresh tokens, API keys
|
|
15
|
+
function generateSecureToken(): string {
|
|
16
|
+
return randomBytes(32).toString('hex'); // 64 chars
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// For password reset tokens (shorter-lived)
|
|
20
|
+
function generateResetToken(): string {
|
|
21
|
+
return randomBytes(16).toString('hex'); // 32 chars
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Hash tokens before storing
|
|
25
|
+
function hashToken(token: string): string {
|
|
26
|
+
return createHash('sha256').update(token).digest('hex');
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### SEC-002: Implement JWT correctly
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import jwt from 'jsonwebtoken';
|
|
34
|
+
|
|
35
|
+
const ACCESS_TOKEN_EXPIRY = '15m';
|
|
36
|
+
const REFRESH_TOKEN_EXPIRY = '7d';
|
|
37
|
+
|
|
38
|
+
interface TokenPayload {
|
|
39
|
+
sub: string; // User ID
|
|
40
|
+
role: string;
|
|
41
|
+
type: 'access' | 'refresh';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function signAccessToken(userId: string, role: string): string {
|
|
45
|
+
return jwt.sign(
|
|
46
|
+
{ sub: userId, role, type: 'access' },
|
|
47
|
+
process.env.JWT_SECRET!,
|
|
48
|
+
{ expiresIn: ACCESS_TOKEN_EXPIRY }
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function signRefreshToken(userId: string): string {
|
|
53
|
+
return jwt.sign(
|
|
54
|
+
{ sub: userId, type: 'refresh' },
|
|
55
|
+
process.env.JWT_REFRESH_SECRET!, // Different secret!
|
|
56
|
+
{ expiresIn: REFRESH_TOKEN_EXPIRY }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### SEC-003: Store passwords securely
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import bcrypt from 'bcrypt';
|
|
65
|
+
|
|
66
|
+
const SALT_ROUNDS = 12;
|
|
67
|
+
|
|
68
|
+
async function hashPassword(password: string): Promise<string> {
|
|
69
|
+
return bcrypt.hash(password, SALT_ROUNDS);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function verifyPassword(password: string, hash: string): Promise<boolean> {
|
|
73
|
+
return bcrypt.compare(password, hash);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Never store plaintext passwords
|
|
77
|
+
// Never log passwords
|
|
78
|
+
// Never return password hash in API responses
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### SEC-004: Implement account lockout
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
const MAX_FAILED_ATTEMPTS = 5;
|
|
85
|
+
const LOCKOUT_DURATION = 15 * 60 * 1000; // 15 minutes
|
|
86
|
+
|
|
87
|
+
async function handleLogin(email: string, password: string) {
|
|
88
|
+
const user = await db.user.findUnique({ where: { email } });
|
|
89
|
+
|
|
90
|
+
if (!user) {
|
|
91
|
+
// Don't reveal if user exists
|
|
92
|
+
throw new AuthError('Invalid credentials');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (user.lockedUntil && user.lockedUntil > new Date()) {
|
|
96
|
+
throw new AuthError('Account locked. Try again later.');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const valid = await verifyPassword(password, user.password);
|
|
100
|
+
|
|
101
|
+
if (!valid) {
|
|
102
|
+
const attempts = user.failedAttempts + 1;
|
|
103
|
+
const updates: any = { failedAttempts: attempts };
|
|
104
|
+
|
|
105
|
+
if (attempts >= MAX_FAILED_ATTEMPTS) {
|
|
106
|
+
updates.lockedUntil = new Date(Date.now() + LOCKOUT_DURATION);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await db.user.update({ where: { id: user.id }, data: updates });
|
|
110
|
+
throw new AuthError('Invalid credentials');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Reset on success
|
|
114
|
+
await db.user.update({
|
|
115
|
+
where: { id: user.id },
|
|
116
|
+
data: { failedAttempts: 0, lockedUntil: null },
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return user;
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Authorization
|
|
126
|
+
|
|
127
|
+
### SEC-005: Implement RBAC
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
type Role = 'USER' | 'ADMIN' | 'SUPER_ADMIN';
|
|
131
|
+
|
|
132
|
+
const permissions: Record<Role, string[]> = {
|
|
133
|
+
USER: ['read:own', 'write:own'],
|
|
134
|
+
ADMIN: ['read:own', 'write:own', 'read:all', 'write:all'],
|
|
135
|
+
SUPER_ADMIN: ['read:own', 'write:own', 'read:all', 'write:all', 'manage:users'],
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
function hasPermission(role: Role, permission: string): boolean {
|
|
139
|
+
return permissions[role]?.includes(permission) ?? false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Middleware
|
|
143
|
+
function requirePermission(permission: string) {
|
|
144
|
+
return async (request: FastifyRequest, reply: FastifyReply) => {
|
|
145
|
+
if (!hasPermission(request.user.role, permission)) {
|
|
146
|
+
return reply.status(403).send({
|
|
147
|
+
error: { code: 'FORBIDDEN', message: 'Insufficient permissions' }
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### SEC-006: Validate resource ownership
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
async function getOrder(request: FastifyRequest<{ Params: { id: string } }>) {
|
|
158
|
+
const order = await db.order.findUnique({
|
|
159
|
+
where: { id: request.params.id },
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
if (!order) {
|
|
163
|
+
throw new NotFoundError('Order');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Validate ownership (unless admin)
|
|
167
|
+
if (order.userId !== request.user.id && request.user.role !== 'ADMIN') {
|
|
168
|
+
throw new ForbiddenError('Not your order');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return { data: order };
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### SEC-007: Use scoped tokens for specific actions
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// Email verification token
|
|
179
|
+
function generateVerificationToken(userId: string): string {
|
|
180
|
+
return jwt.sign(
|
|
181
|
+
{ sub: userId, action: 'verify-email' },
|
|
182
|
+
process.env.JWT_SECRET!,
|
|
183
|
+
{ expiresIn: '24h' }
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Password reset token
|
|
188
|
+
function generatePasswordResetToken(userId: string): string {
|
|
189
|
+
return jwt.sign(
|
|
190
|
+
{ sub: userId, action: 'reset-password' },
|
|
191
|
+
process.env.JWT_SECRET!,
|
|
192
|
+
{ expiresIn: '1h' }
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Verify with action check
|
|
197
|
+
function verifyActionToken(token: string, expectedAction: string) {
|
|
198
|
+
const payload = jwt.verify(token, process.env.JWT_SECRET!);
|
|
199
|
+
|
|
200
|
+
if (payload.action !== expectedAction) {
|
|
201
|
+
throw new AuthError('Invalid token');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return payload;
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Input Protection
|
|
211
|
+
|
|
212
|
+
### SEC-008: Sanitize user input
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import DOMPurify from 'isomorphic-dompurify';
|
|
216
|
+
|
|
217
|
+
// For HTML content (if allowed)
|
|
218
|
+
function sanitizeHtml(input: string): string {
|
|
219
|
+
return DOMPurify.sanitize(input, {
|
|
220
|
+
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p'],
|
|
221
|
+
ALLOWED_ATTR: ['href'],
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// For plain text - strip all HTML
|
|
226
|
+
function sanitizePlainText(input: string): string {
|
|
227
|
+
return DOMPurify.sanitize(input, { ALLOWED_TAGS: [] });
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### SEC-009: Prevent NoSQL injection (if using MongoDB)
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// BAD - user can inject operators
|
|
235
|
+
const user = await db.collection('users').findOne({
|
|
236
|
+
email: request.body.email, // { "$ne": null } would match all
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// GOOD - validate type
|
|
240
|
+
const emailSchema = z.string().email();
|
|
241
|
+
const email = emailSchema.parse(request.body.email);
|
|
242
|
+
|
|
243
|
+
// Or sanitize
|
|
244
|
+
function sanitizeMongoInput(input: unknown): unknown {
|
|
245
|
+
if (typeof input === 'object' && input !== null) {
|
|
246
|
+
const keys = Object.keys(input);
|
|
247
|
+
if (keys.some(k => k.startsWith('$'))) {
|
|
248
|
+
throw new ValidationError('Invalid input');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return input;
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### SEC-010: Rate limit sensitive endpoints
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import rateLimit from '@fastify/rate-limit';
|
|
259
|
+
|
|
260
|
+
// Global rate limit
|
|
261
|
+
await fastify.register(rateLimit, {
|
|
262
|
+
max: 100,
|
|
263
|
+
timeWindow: '1 minute',
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Stricter for auth
|
|
267
|
+
fastify.register(async (instance) => {
|
|
268
|
+
await instance.register(rateLimit, {
|
|
269
|
+
max: 5,
|
|
270
|
+
timeWindow: '15 minutes',
|
|
271
|
+
keyGenerator: (request) => request.ip,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
instance.post('/auth/login', loginHandler);
|
|
275
|
+
instance.post('/auth/register', registerHandler);
|
|
276
|
+
instance.post('/auth/forgot-password', forgotPasswordHandler);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Even stricter for password reset
|
|
280
|
+
fastify.post('/auth/reset-password', {
|
|
281
|
+
config: {
|
|
282
|
+
rateLimit: {
|
|
283
|
+
max: 3,
|
|
284
|
+
timeWindow: '1 hour',
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
}, resetPasswordHandler);
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Headers & CORS
|
|
293
|
+
|
|
294
|
+
### SEC-011: Set security headers
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
import helmet from '@fastify/helmet';
|
|
298
|
+
|
|
299
|
+
await fastify.register(helmet, {
|
|
300
|
+
contentSecurityPolicy: {
|
|
301
|
+
directives: {
|
|
302
|
+
defaultSrc: ["'self'"],
|
|
303
|
+
scriptSrc: ["'self'"],
|
|
304
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
305
|
+
imgSrc: ["'self'", 'data:', 'https:'],
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
hsts: {
|
|
309
|
+
maxAge: 31536000,
|
|
310
|
+
includeSubDomains: true,
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### SEC-012: Configure CORS properly
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import cors from '@fastify/cors';
|
|
319
|
+
|
|
320
|
+
await fastify.register(cors, {
|
|
321
|
+
origin: (origin, cb) => {
|
|
322
|
+
const allowedOrigins = [
|
|
323
|
+
'https://app.example.com',
|
|
324
|
+
'https://admin.example.com',
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
if (process.env.NODE_ENV === 'development') {
|
|
328
|
+
allowedOrigins.push('http://localhost:3000');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (!origin || allowedOrigins.includes(origin)) {
|
|
332
|
+
cb(null, true);
|
|
333
|
+
} else {
|
|
334
|
+
cb(new Error('Not allowed by CORS'), false);
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
credentials: true,
|
|
338
|
+
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Audit & Logging
|
|
345
|
+
|
|
346
|
+
### SEC-013: Log security events
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
// Log authentication events
|
|
350
|
+
logger.info('Login successful', { userId, ip: request.ip });
|
|
351
|
+
logger.warn('Login failed', { email, ip: request.ip, reason: 'invalid_password' });
|
|
352
|
+
logger.warn('Account locked', { userId, ip: request.ip });
|
|
353
|
+
|
|
354
|
+
// Log authorization failures
|
|
355
|
+
logger.warn('Access denied', {
|
|
356
|
+
userId: request.user?.id,
|
|
357
|
+
resource: request.url,
|
|
358
|
+
permission: 'write:all',
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// Log sensitive operations
|
|
362
|
+
logger.info('Password changed', { userId });
|
|
363
|
+
logger.info('User deleted', { deletedUserId, deletedBy: request.user.id });
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### SEC-014: Never log sensitive data
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
// BAD
|
|
370
|
+
logger.info('Login', { email, password });
|
|
371
|
+
logger.info('Payment', { cardNumber, cvv });
|
|
372
|
+
|
|
373
|
+
// GOOD
|
|
374
|
+
logger.info('Login', { email });
|
|
375
|
+
logger.info('Payment', { cardLast4: card.slice(-4), amount });
|
|
376
|
+
|
|
377
|
+
// Redact in error serialization
|
|
378
|
+
function serializeError(error: Error) {
|
|
379
|
+
return {
|
|
380
|
+
message: error.message,
|
|
381
|
+
// Never include: stack (in prod), request body, headers
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Stack Agents Registry
|
|
2
|
+
# Loaded at startup (~80 tokens)
|
|
3
|
+
# Full agents loaded on-demand when workspace matches
|
|
4
|
+
|
|
5
|
+
version: '1.0.0'
|
|
6
|
+
|
|
7
|
+
agents:
|
|
8
|
+
shared-ts:
|
|
9
|
+
description: TypeScript foundation - strict mode, patterns, zod schemas
|
|
10
|
+
triggers: ['*.ts', '*.tsx', 'typescript', 'types', 'zod', 'schema']
|
|
11
|
+
dependencies: []
|
|
12
|
+
priority: 1 # Load first (foundation)
|
|
13
|
+
token_budget: 800
|
|
14
|
+
|
|
15
|
+
nextjs:
|
|
16
|
+
description: Next.js App Router, Tailwind, shadcn/ui patterns
|
|
17
|
+
triggers: ['next.config', 'app/', 'pages/', 'tailwind', 'shadcn', '@/components']
|
|
18
|
+
dependencies: [shared-ts]
|
|
19
|
+
priority: 2
|
|
20
|
+
token_budget: 800
|
|
21
|
+
|
|
22
|
+
expo:
|
|
23
|
+
description: React Native + Expo SDK, navigation, native modules
|
|
24
|
+
triggers: ['app.json', 'expo', 'react-native', 'navigation', 'native']
|
|
25
|
+
dependencies: [shared-ts]
|
|
26
|
+
priority: 2
|
|
27
|
+
token_budget: 800
|
|
28
|
+
|
|
29
|
+
fastify:
|
|
30
|
+
description: Fastify APIs, BullMQ queues, RabbitMQ messaging
|
|
31
|
+
triggers: ['fastify', 'bullmq', 'rabbitmq', 'api/', 'routes/', 'queue']
|
|
32
|
+
dependencies: [shared-ts]
|
|
33
|
+
priority: 2
|
|
34
|
+
token_budget: 800
|
|
35
|
+
|
|
36
|
+
# Loading strategy:
|
|
37
|
+
# 1. This index always loaded (~80 tokens)
|
|
38
|
+
# 2. Orchestrator detects workspace agents from CLAUDE.md or package.json
|
|
39
|
+
# 3. Load shared-ts first if any TypeScript workspace
|
|
40
|
+
# 4. Load stack agents based on workspace triggers
|
|
41
|
+
# 5. Load rules on-demand when applying patterns (~200 tokens each)
|
|
42
|
+
|
|
43
|
+
# Token budget strategy:
|
|
44
|
+
# - Orchestrator: 500 tokens (always)
|
|
45
|
+
# - Per agent: 800 tokens (AGENT.md summary)
|
|
46
|
+
# - Per rule: 200 tokens (on-demand)
|
|
47
|
+
# - Max concurrent: 3 agents
|
|
48
|
+
# - Total max: 500 + (3 x 800) + (5 x 200) = 3,900 tokens
|