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.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/bin/codecruise.js +68 -0
  4. package/config/CLAUDE.md +107 -0
  5. package/config/agents/analyst.md +48 -0
  6. package/config/agents/architect-reviewer.md +161 -0
  7. package/config/agents/architect.md +119 -0
  8. package/config/agents/critic.md +63 -0
  9. package/config/agents/developer.md +96 -0
  10. package/config/agents/devops.md +81 -0
  11. package/config/agents/orchestrator.md +91 -0
  12. package/config/agents/planner.md +139 -0
  13. package/config/agents/retro.md +52 -0
  14. package/config/agents/reviewer.md +101 -0
  15. package/config/agents/security-reviewer.md +57 -0
  16. package/config/agents/stack/expo/AGENT.md +473 -0
  17. package/config/agents/stack/expo/rules/critical.md +427 -0
  18. package/config/agents/stack/expo/rules/native.md +455 -0
  19. package/config/agents/stack/expo/rules/navigation.md +445 -0
  20. package/config/agents/stack/expo/rules/performance.md +415 -0
  21. package/config/agents/stack/fastify/AGENT.md +397 -0
  22. package/config/agents/stack/fastify/rules/api-design.md +283 -0
  23. package/config/agents/stack/fastify/rules/critical.md +232 -0
  24. package/config/agents/stack/fastify/rules/queues.md +303 -0
  25. package/config/agents/stack/fastify/rules/security.md +384 -0
  26. package/config/agents/stack/index.yaml +48 -0
  27. package/config/agents/stack/nextjs/AGENT.md +421 -0
  28. package/config/agents/stack/nextjs/rules/components.md +413 -0
  29. package/config/agents/stack/nextjs/rules/critical.md +391 -0
  30. package/config/agents/stack/nextjs/rules/performance.md +403 -0
  31. package/config/agents/stack/nextjs/rules/styling.md +334 -0
  32. package/config/agents/stack/shared-ts/AGENT.md +384 -0
  33. package/config/agents/stack/shared-ts/rules/critical.md +315 -0
  34. package/config/agents/stack/shared-ts/rules/patterns.md +384 -0
  35. package/config/agents/stack/shared-ts/rules/zod.md +427 -0
  36. package/config/agents/tester.md +79 -0
  37. package/config/commands/architect-discuss.md +366 -0
  38. package/config/commands/architect-list.md +160 -0
  39. package/config/commands/architect-review.md +111 -0
  40. package/config/commands/architect.md +118 -0
  41. package/config/commands/compact.md +118 -0
  42. package/config/commands/companion.md +279 -0
  43. package/config/commands/dashboard.md +152 -0
  44. package/config/commands/doctor.md +227 -0
  45. package/config/commands/dogfood-report.md +101 -0
  46. package/config/commands/flags/run-autonomous.md +110 -0
  47. package/config/commands/flags/run-pause.md +80 -0
  48. package/config/commands/ingest.md +173 -0
  49. package/config/commands/init.md +128 -0
  50. package/config/commands/metrics.md +87 -0
  51. package/config/commands/parallel.md +320 -0
  52. package/config/commands/pause.md +55 -0
  53. package/config/commands/plan-review.md +130 -0
  54. package/config/commands/plan.md +216 -0
  55. package/config/commands/production-check.md +308 -0
  56. package/config/commands/refine.md +323 -0
  57. package/config/commands/resume.md +72 -0
  58. package/config/commands/retro.md +121 -0
  59. package/config/commands/retry.md +75 -0
  60. package/config/commands/role.md +310 -0
  61. package/config/commands/run.md +417 -0
  62. package/config/commands/scope.md +85 -0
  63. package/config/commands/setup-permissions.md +104 -0
  64. package/config/commands/skip.md +75 -0
  65. package/config/commands/spec-forge.md +213 -0
  66. package/config/commands/spec-help.md +194 -0
  67. package/config/commands/spec-patch.md +342 -0
  68. package/config/commands/spec-resolve.md +110 -0
  69. package/config/commands/spec-review.md +153 -0
  70. package/config/commands/status.md +114 -0
  71. package/config/commands/sync.md +131 -0
  72. package/config/commands/task.md +138 -0
  73. package/config/commands/verify.md +124 -0
  74. package/config/hooks/README.md +632 -0
  75. package/config/hooks/activity-log.sh +187 -0
  76. package/config/hooks/anti-rationalize.sh +52 -0
  77. package/config/hooks/capture-verification.sh +112 -0
  78. package/config/hooks/collect-metrics.sh +135 -0
  79. package/config/hooks/enforce-file-scope.sh +75 -0
  80. package/config/hooks/enforce-state-machine.sh +161 -0
  81. package/config/hooks/enforce-tdd.sh +180 -0
  82. package/config/hooks/format.sh +40 -0
  83. package/config/hooks/lib/activity-helpers.sh +162 -0
  84. package/config/hooks/lib/read-settings.sh +71 -0
  85. package/config/hooks/load-context-skills.sh +95 -0
  86. package/config/hooks/notify.sh +81 -0
  87. package/config/hooks/pre-commit.sample +35 -0
  88. package/config/hooks/protect-files.sh +63 -0
  89. package/config/hooks/track-agents.sh +41 -0
  90. package/config/hooks/track-commands.sh +37 -0
  91. package/config/hooks/track-enforcement.sh +44 -0
  92. package/config/hooks/track-ooda.sh +77 -0
  93. package/config/hooks/validate-commit-msg.sh +35 -0
  94. package/config/hooks/validate-plan.sh +213 -0
  95. package/config/hooks/verify-criteria.sh +46 -0
  96. package/config/hooks/verify-todo-completion.sh +140 -0
  97. package/config/rules/comments.md +25 -0
  98. package/config/rules/decision-rules.md +308 -0
  99. package/config/rules/hygiene.md +247 -0
  100. package/config/rules/pattern-detection.md +372 -0
  101. package/config/rules/profiles.md +193 -0
  102. package/config/rules/recovery.md +83 -0
  103. package/config/rules/scope-detection.md +213 -0
  104. package/config/rules/standards.md +127 -0
  105. package/config/rules/workflow.md +121 -0
  106. package/config/schemas.md +767 -0
  107. package/config/settings.json +195 -0
  108. package/config/skills/backend/SKILL.md +734 -0
  109. package/config/skills/database/SKILL.md +426 -0
  110. package/config/skills/frontend/SKILL.md +434 -0
  111. package/config/skills/git/SKILL.md +396 -0
  112. package/config/skills/index.yaml +36 -0
  113. package/config/skills/observability/SKILL.md +430 -0
  114. package/config/skills/package-dev/SKILL.md +498 -0
  115. package/config/skills/performance/SKILL.md +378 -0
  116. package/config/skills/resilience/SKILL.md +573 -0
  117. package/config/skills/testing/SKILL.md +398 -0
  118. package/config/skills/testing-patterns/SKILL.md +276 -0
  119. package/config/skills/typescript/SKILL.md +152 -0
  120. package/config/templates/CLAUDE.md +70 -0
  121. package/config/templates/README.md +117 -0
  122. package/config/templates/steering/adr-template.md +102 -0
  123. package/config/templates/steering/product.md +60 -0
  124. package/config/templates/steering/rfc-template.md +159 -0
  125. package/config/templates/steering/structure.md +146 -0
  126. package/config/templates/steering/tech.md +85 -0
  127. package/package.json +40 -0
  128. package/src/install.js +163 -0
  129. package/src/report.js +310 -0
@@ -0,0 +1,232 @@
1
+ # Critical Rules - Fastify
2
+
3
+ Must-follow rules. Violations block PR merge.
4
+
5
+ ---
6
+
7
+ ## Error Handling
8
+
9
+ ### CRIT-001: Always use typed errors
10
+
11
+ ```typescript
12
+ // BAD
13
+ throw new Error('User not found');
14
+
15
+ // GOOD
16
+ import { NotFoundError } from '@/lib/errors';
17
+ throw new NotFoundError('User', userId);
18
+ ```
19
+
20
+ ### CRIT-002: Never expose internal errors to clients
21
+
22
+ ```typescript
23
+ // BAD
24
+ reply.status(500).send({ error: err.message, stack: err.stack });
25
+
26
+ // GOOD
27
+ fastify.setErrorHandler((error, request, reply) => {
28
+ if (error instanceof AppError) {
29
+ return reply.status(error.statusCode).send({
30
+ error: { code: error.code, message: error.message }
31
+ });
32
+ }
33
+
34
+ // Log internal error, send generic message
35
+ request.log.error(error);
36
+ return reply.status(500).send({
37
+ error: { code: 'INTERNAL_ERROR', message: 'Something went wrong' }
38
+ });
39
+ });
40
+ ```
41
+
42
+ ### CRIT-003: Always handle async errors
43
+
44
+ ```typescript
45
+ // BAD - unhandled promise rejection
46
+ fastify.get('/users', async (request) => {
47
+ const users = await db.users.findMany(); // Could throw
48
+ return users;
49
+ });
50
+
51
+ // GOOD - Fastify handles this automatically with async handlers
52
+ // But for callbacks or event handlers:
53
+ worker.on('error', (error) => {
54
+ logger.error('Worker error', { error });
55
+ // Graceful recovery or shutdown
56
+ });
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Validation
62
+
63
+ ### CRIT-004: Always validate request input
64
+
65
+ ```typescript
66
+ // BAD - no validation
67
+ fastify.post('/users', async (request) => {
68
+ const user = await db.users.create(request.body);
69
+ });
70
+
71
+ // GOOD - schema validation
72
+ fastify.post('/users', {
73
+ schema: {
74
+ body: zodToJsonSchema(createUserSchema),
75
+ }
76
+ }, async (request) => {
77
+ // request.body is validated
78
+ });
79
+ ```
80
+
81
+ ### CRIT-005: Validate environment variables at startup
82
+
83
+ ```typescript
84
+ // lib/env.ts
85
+ import { z } from 'zod';
86
+
87
+ const envSchema = z.object({
88
+ DATABASE_URL: z.string().url(),
89
+ REDIS_URL: z.string().url(),
90
+ JWT_SECRET: z.string().min(32),
91
+ NODE_ENV: z.enum(['development', 'test', 'production']),
92
+ });
93
+
94
+ export const env = envSchema.parse(process.env);
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Security
100
+
101
+ ### CRIT-006: Never log sensitive data
102
+
103
+ ```typescript
104
+ // BAD
105
+ logger.info('Login attempt', { email, password });
106
+
107
+ // GOOD
108
+ logger.info('Login attempt', { email, hasPassword: !!password });
109
+ ```
110
+
111
+ ### CRIT-007: Always use parameterized queries
112
+
113
+ ```typescript
114
+ // BAD - SQL injection
115
+ const users = await db.$queryRaw`SELECT * FROM users WHERE id = ${userId}`;
116
+
117
+ // GOOD - parameterized (Prisma handles this)
118
+ const users = await db.user.findUnique({ where: { id: userId } });
119
+
120
+ // If raw SQL needed:
121
+ const users = await db.$queryRaw(
122
+ Prisma.sql`SELECT * FROM users WHERE id = ${userId}`
123
+ );
124
+ ```
125
+
126
+ ### CRIT-008: Validate file uploads
127
+
128
+ ```typescript
129
+ // Always check:
130
+ // - File size limits
131
+ // - Allowed MIME types
132
+ // - Sanitize filenames
133
+
134
+ fastify.register(multipart, {
135
+ limits: {
136
+ fileSize: 5 * 1024 * 1024, // 5MB
137
+ files: 1,
138
+ },
139
+ });
140
+
141
+ // In handler
142
+ const allowedMimes = ['image/jpeg', 'image/png', 'image/webp'];
143
+ if (!allowedMimes.includes(file.mimetype)) {
144
+ throw new ValidationError('Invalid file type');
145
+ }
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Resource Management
151
+
152
+ ### CRIT-009: Always close connections on shutdown
153
+
154
+ ```typescript
155
+ // index.ts
156
+ const shutdown = async () => {
157
+ await fastify.close();
158
+ await prisma.$disconnect();
159
+ await redis.quit();
160
+ await rabbitConnection.close();
161
+ process.exit(0);
162
+ };
163
+
164
+ process.on('SIGTERM', shutdown);
165
+ process.on('SIGINT', shutdown);
166
+ ```
167
+
168
+ ### CRIT-010: Set timeouts on external calls
169
+
170
+ ```typescript
171
+ // BAD - could hang forever
172
+ const response = await fetch(externalApi);
173
+
174
+ // GOOD - with timeout
175
+ const controller = new AbortController();
176
+ const timeout = setTimeout(() => controller.abort(), 5000);
177
+
178
+ try {
179
+ const response = await fetch(externalApi, { signal: controller.signal });
180
+ } finally {
181
+ clearTimeout(timeout);
182
+ }
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Queue Safety
188
+
189
+ ### CRIT-011: Always acknowledge/reject messages
190
+
191
+ ```typescript
192
+ // BAD - message stuck in limbo
193
+ channel.consume(queue, async (msg) => {
194
+ await processMessage(msg);
195
+ // Forgot to ack!
196
+ });
197
+
198
+ // GOOD
199
+ channel.consume(queue, async (msg) => {
200
+ if (!msg) return;
201
+
202
+ try {
203
+ await processMessage(msg);
204
+ channel.ack(msg);
205
+ } catch (error) {
206
+ // Reject with requeue=false to avoid infinite loop
207
+ channel.nack(msg, false, false);
208
+ }
209
+ });
210
+ ```
211
+
212
+ ### CRIT-012: Make queue jobs idempotent
213
+
214
+ ```typescript
215
+ // BAD - could double-charge
216
+ async function processPayment(job) {
217
+ await chargeCard(job.data.amount);
218
+ }
219
+
220
+ // GOOD - idempotent
221
+ async function processPayment(job) {
222
+ const existing = await db.payment.findUnique({
223
+ where: { idempotencyKey: job.data.idempotencyKey }
224
+ });
225
+
226
+ if (existing) return existing;
227
+
228
+ return await db.payment.create({
229
+ data: { ...job.data, status: 'completed' }
230
+ });
231
+ }
232
+ ```
@@ -0,0 +1,303 @@
1
+ # Queue Rules - BullMQ & RabbitMQ
2
+
3
+ Patterns for job queues and message brokers.
4
+
5
+ ---
6
+
7
+ ## BullMQ Patterns
8
+
9
+ ### QUEUE-001: Define typed job data
10
+
11
+ ```typescript
12
+ // types/jobs.ts
13
+ export interface EmailJob {
14
+ to: string;
15
+ template: 'welcome' | 'reset-password' | 'notification';
16
+ data: Record<string, unknown>;
17
+ }
18
+
19
+ export interface ProcessOrderJob {
20
+ orderId: string;
21
+ userId: string;
22
+ items: Array<{ productId: string; quantity: number }>;
23
+ }
24
+
25
+ // Queue with type
26
+ const emailQueue = new Queue<EmailJob>('email', { connection });
27
+ ```
28
+
29
+ ### QUEUE-002: Configure appropriate retry strategies
30
+
31
+ ```typescript
32
+ // Transient failures (network, rate limits)
33
+ await queue.add('send-email', data, {
34
+ attempts: 3,
35
+ backoff: {
36
+ type: 'exponential',
37
+ delay: 1000, // 1s, 2s, 4s
38
+ },
39
+ });
40
+
41
+ // Critical jobs (payments)
42
+ await queue.add('process-payment', data, {
43
+ attempts: 5,
44
+ backoff: {
45
+ type: 'exponential',
46
+ delay: 5000, // 5s, 10s, 20s, 40s, 80s
47
+ },
48
+ });
49
+
50
+ // Non-critical (analytics)
51
+ await queue.add('track-event', data, {
52
+ attempts: 1, // Fire and forget
53
+ removeOnComplete: true,
54
+ });
55
+ ```
56
+
57
+ ### QUEUE-003: Set job timeouts
58
+
59
+ ```typescript
60
+ const worker = new Worker<EmailJob>('email', processor, {
61
+ connection,
62
+ lockDuration: 30000, // 30s lock
63
+ stalledInterval: 15000, // Check for stalled every 15s
64
+ });
65
+
66
+ // Or per-job
67
+ await queue.add('long-task', data, {
68
+ timeout: 60000, // 1 minute max
69
+ });
70
+ ```
71
+
72
+ ### QUEUE-004: Handle worker lifecycle
73
+
74
+ ```typescript
75
+ const worker = new Worker('email', processor, { connection });
76
+
77
+ // Graceful shutdown
78
+ async function shutdown() {
79
+ await worker.close(); // Waits for current job
80
+ await queue.close();
81
+ process.exit(0);
82
+ }
83
+
84
+ process.on('SIGTERM', shutdown);
85
+ process.on('SIGINT', shutdown);
86
+
87
+ // Error handling
88
+ worker.on('failed', (job, err) => {
89
+ logger.error('Job failed', {
90
+ jobId: job?.id,
91
+ name: job?.name,
92
+ error: err.message,
93
+ attempts: job?.attemptsMade,
94
+ });
95
+ });
96
+
97
+ worker.on('error', (err) => {
98
+ logger.error('Worker error', { error: err.message });
99
+ });
100
+ ```
101
+
102
+ ### QUEUE-005: Use job progress for long tasks
103
+
104
+ ```typescript
105
+ const worker = new Worker('video-process', async (job) => {
106
+ const steps = ['download', 'transcode', 'upload', 'cleanup'];
107
+
108
+ for (let i = 0; i < steps.length; i++) {
109
+ await performStep(steps[i], job.data);
110
+ await job.updateProgress((i + 1) / steps.length * 100);
111
+ }
112
+ });
113
+
114
+ // Monitor progress
115
+ const job = await queue.add('process', { videoId: '123' });
116
+ job.on('progress', (progress) => {
117
+ console.log(`Progress: ${progress}%`);
118
+ });
119
+ ```
120
+
121
+ ---
122
+
123
+ ## RabbitMQ Patterns
124
+
125
+ ### QUEUE-006: Use durable queues and persistent messages
126
+
127
+ ```typescript
128
+ // Durable queue survives broker restart
129
+ await channel.assertQueue('orders', { durable: true });
130
+
131
+ // Persistent message survives broker restart
132
+ channel.sendToQueue('orders', Buffer.from(JSON.stringify(order)), {
133
+ persistent: true,
134
+ });
135
+ ```
136
+
137
+ ### QUEUE-007: Implement dead letter queues
138
+
139
+ ```typescript
140
+ // Main queue with DLX
141
+ await channel.assertQueue('orders', {
142
+ durable: true,
143
+ arguments: {
144
+ 'x-dead-letter-exchange': 'orders.dlx',
145
+ 'x-dead-letter-routing-key': 'orders.failed',
146
+ },
147
+ });
148
+
149
+ // Dead letter queue
150
+ await channel.assertExchange('orders.dlx', 'direct', { durable: true });
151
+ await channel.assertQueue('orders.failed', { durable: true });
152
+ await channel.bindQueue('orders.failed', 'orders.dlx', 'orders.failed');
153
+
154
+ // Consumer - failed messages go to DLQ after nack
155
+ channel.consume('orders', async (msg) => {
156
+ try {
157
+ await processOrder(JSON.parse(msg.content.toString()));
158
+ channel.ack(msg);
159
+ } catch (error) {
160
+ // nack with requeue=false sends to DLQ
161
+ channel.nack(msg, false, false);
162
+ }
163
+ });
164
+ ```
165
+
166
+ ### QUEUE-008: Use prefetch for fair dispatch
167
+
168
+ ```typescript
169
+ // Don't send more than 10 messages to this worker at once
170
+ channel.prefetch(10);
171
+
172
+ // For CPU-intensive tasks, use lower prefetch
173
+ channel.prefetch(1);
174
+ ```
175
+
176
+ ### QUEUE-009: Use topic exchanges for flexible routing
177
+
178
+ ```typescript
179
+ // Publisher
180
+ await channel.assertExchange('events', 'topic', { durable: true });
181
+
182
+ channel.publish('events', 'order.created', Buffer.from(JSON.stringify(order)));
183
+ channel.publish('events', 'order.shipped', Buffer.from(JSON.stringify(order)));
184
+ channel.publish('events', 'user.registered', Buffer.from(JSON.stringify(user)));
185
+
186
+ // Consumer - subscribe to patterns
187
+ await channel.assertQueue('order-notifications');
188
+ await channel.bindQueue('order-notifications', 'events', 'order.*');
189
+
190
+ await channel.assertQueue('all-events');
191
+ await channel.bindQueue('all-events', 'events', '#'); // All messages
192
+ ```
193
+
194
+ ### QUEUE-010: Implement request-reply pattern
195
+
196
+ ```typescript
197
+ // Requester
198
+ const replyQueue = await channel.assertQueue('', { exclusive: true });
199
+ const correlationId = uuid();
200
+
201
+ const response = new Promise((resolve) => {
202
+ channel.consume(replyQueue.queue, (msg) => {
203
+ if (msg.properties.correlationId === correlationId) {
204
+ resolve(JSON.parse(msg.content.toString()));
205
+ }
206
+ });
207
+ });
208
+
209
+ channel.sendToQueue('rpc-queue', Buffer.from(JSON.stringify(request)), {
210
+ correlationId,
211
+ replyTo: replyQueue.queue,
212
+ });
213
+
214
+ const result = await response;
215
+
216
+ // Responder
217
+ channel.consume('rpc-queue', async (msg) => {
218
+ const request = JSON.parse(msg.content.toString());
219
+ const response = await processRequest(request);
220
+
221
+ channel.sendToQueue(
222
+ msg.properties.replyTo,
223
+ Buffer.from(JSON.stringify(response)),
224
+ { correlationId: msg.properties.correlationId }
225
+ );
226
+
227
+ channel.ack(msg);
228
+ });
229
+ ```
230
+
231
+ ---
232
+
233
+ ## Shared Patterns
234
+
235
+ ### QUEUE-011: Log job lifecycle
236
+
237
+ ```typescript
238
+ // BullMQ
239
+ worker.on('active', (job) => {
240
+ logger.info('Job started', { jobId: job.id, name: job.name });
241
+ });
242
+
243
+ worker.on('completed', (job, result) => {
244
+ logger.info('Job completed', {
245
+ jobId: job.id,
246
+ duration: Date.now() - job.processedOn!,
247
+ });
248
+ });
249
+
250
+ // RabbitMQ - wrap consumer
251
+ function createConsumer(handler) {
252
+ return async (msg) => {
253
+ const start = Date.now();
254
+ logger.info('Message received', { queue, deliveryTag: msg.fields.deliveryTag });
255
+
256
+ try {
257
+ await handler(msg);
258
+ logger.info('Message processed', { duration: Date.now() - start });
259
+ } catch (error) {
260
+ logger.error('Message failed', { error: error.message });
261
+ throw error;
262
+ }
263
+ };
264
+ }
265
+ ```
266
+
267
+ ### QUEUE-012: Use unique job IDs for deduplication
268
+
269
+ ```typescript
270
+ // BullMQ - same jobId = same job (deduped)
271
+ await queue.add('process', data, {
272
+ jobId: `order-${orderId}`, // Won't duplicate if already exists
273
+ });
274
+
275
+ // RabbitMQ - use message ID
276
+ channel.sendToQueue('orders', buffer, {
277
+ messageId: `order-${orderId}-${Date.now()}`,
278
+ });
279
+ ```
280
+
281
+ ### QUEUE-013: Monitor queue health
282
+
283
+ ```typescript
284
+ // BullMQ
285
+ async function getQueueHealth(queue: Queue) {
286
+ const [waiting, active, completed, failed] = await Promise.all([
287
+ queue.getWaitingCount(),
288
+ queue.getActiveCount(),
289
+ queue.getCompletedCount(),
290
+ queue.getFailedCount(),
291
+ ]);
292
+
293
+ return { waiting, active, completed, failed };
294
+ }
295
+
296
+ // Expose via health endpoint
297
+ fastify.get('/health/queues', async () => {
298
+ return {
299
+ email: await getQueueHealth(emailQueue),
300
+ orders: await getQueueHealth(orderQueue),
301
+ };
302
+ });
303
+ ```