engrm 0.1.0 → 0.2.1
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/README.md +214 -73
- package/bin/build.mjs +97 -0
- package/bin/engrm.mjs +13 -0
- package/dist/cli.js +2712 -0
- package/dist/hooks/elicitation-result.js +1786 -0
- package/dist/hooks/post-tool-use.js +2357 -0
- package/dist/hooks/pre-compact.js +1321 -0
- package/dist/hooks/sentinel.js +1168 -0
- package/dist/hooks/session-start.js +1473 -0
- package/dist/hooks/stop.js +1834 -0
- package/dist/server.js +16628 -0
- package/package.json +34 -5
- package/packs/api-best-practices.json +182 -0
- package/packs/nextjs-patterns.json +68 -0
- package/packs/node-security.json +68 -0
- package/packs/python-django.json +68 -0
- package/packs/react-gotchas.json +182 -0
- package/packs/typescript-patterns.json +67 -0
- package/packs/web-security.json +182 -0
- package/.mcp.json +0 -9
- package/AUTH-DESIGN.md +0 -436
- package/BRIEF.md +0 -197
- package/CLAUDE.md +0 -44
- package/COMPETITIVE.md +0 -174
- package/CONTEXT-OPTIMIZATION.md +0 -305
- package/INFRASTRUCTURE.md +0 -252
- package/MARKET.md +0 -230
- package/PLAN.md +0 -278
- package/SENTINEL.md +0 -293
- package/SERVER-API-PLAN.md +0 -553
- package/SPEC.md +0 -843
- package/SWOT.md +0 -148
- package/SYNC-ARCHITECTURE.md +0 -294
- package/VIBE-CODER-STRATEGY.md +0 -250
- package/bun.lock +0 -375
- package/hooks/post-tool-use.ts +0 -144
- package/hooks/session-start.ts +0 -64
- package/hooks/stop.ts +0 -131
- package/mem-page.html +0 -1305
- package/src/capture/dedup.test.ts +0 -103
- package/src/capture/dedup.ts +0 -76
- package/src/capture/extractor.test.ts +0 -245
- package/src/capture/extractor.ts +0 -330
- package/src/capture/quality.test.ts +0 -168
- package/src/capture/quality.ts +0 -104
- package/src/capture/retrospective.test.ts +0 -115
- package/src/capture/retrospective.ts +0 -121
- package/src/capture/scanner.test.ts +0 -131
- package/src/capture/scanner.ts +0 -100
- package/src/capture/scrubber.test.ts +0 -144
- package/src/capture/scrubber.ts +0 -181
- package/src/cli.ts +0 -517
- package/src/config.ts +0 -238
- package/src/context/inject.test.ts +0 -940
- package/src/context/inject.ts +0 -382
- package/src/embeddings/backfill.ts +0 -50
- package/src/embeddings/embedder.test.ts +0 -76
- package/src/embeddings/embedder.ts +0 -139
- package/src/lifecycle/aging.test.ts +0 -103
- package/src/lifecycle/aging.ts +0 -36
- package/src/lifecycle/compaction.test.ts +0 -264
- package/src/lifecycle/compaction.ts +0 -190
- package/src/lifecycle/purge.test.ts +0 -100
- package/src/lifecycle/purge.ts +0 -37
- package/src/lifecycle/scheduler.test.ts +0 -120
- package/src/lifecycle/scheduler.ts +0 -101
- package/src/provisioning/browser-auth.ts +0 -172
- package/src/provisioning/provision.test.ts +0 -198
- package/src/provisioning/provision.ts +0 -94
- package/src/register.test.ts +0 -167
- package/src/register.ts +0 -178
- package/src/server.ts +0 -436
- package/src/storage/migrations.test.ts +0 -244
- package/src/storage/migrations.ts +0 -261
- package/src/storage/outbox.test.ts +0 -229
- package/src/storage/outbox.ts +0 -131
- package/src/storage/projects.test.ts +0 -137
- package/src/storage/projects.ts +0 -184
- package/src/storage/sqlite.test.ts +0 -798
- package/src/storage/sqlite.ts +0 -934
- package/src/storage/vec.test.ts +0 -198
- package/src/sync/auth.test.ts +0 -76
- package/src/sync/auth.ts +0 -68
- package/src/sync/client.ts +0 -183
- package/src/sync/engine.test.ts +0 -94
- package/src/sync/engine.ts +0 -127
- package/src/sync/pull.test.ts +0 -279
- package/src/sync/pull.ts +0 -170
- package/src/sync/push.test.ts +0 -117
- package/src/sync/push.ts +0 -230
- package/src/tools/get.ts +0 -34
- package/src/tools/pin.ts +0 -47
- package/src/tools/save.test.ts +0 -301
- package/src/tools/save.ts +0 -231
- package/src/tools/search.test.ts +0 -69
- package/src/tools/search.ts +0 -181
- package/src/tools/timeline.ts +0 -64
- package/tsconfig.json +0 -22
package/package.json
CHANGED
|
@@ -1,28 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "engrm",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Cross-device, team-shared memory layer for AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "
|
|
6
|
+
"main": "dist/server.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"engrm": "
|
|
8
|
+
"engrm": "dist/cli.js"
|
|
9
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin/",
|
|
12
|
+
"dist/",
|
|
13
|
+
"packs/",
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"LICENSE-SENTINEL"
|
|
16
|
+
],
|
|
10
17
|
"scripts": {
|
|
11
18
|
"start": "bun run src/server.ts",
|
|
12
19
|
"init": "bun run src/cli.ts init",
|
|
13
|
-
"test": "bun test"
|
|
20
|
+
"test": "bun test",
|
|
21
|
+
"build": "bun run bin/build.mjs",
|
|
22
|
+
"prepublishOnly": "bun run build"
|
|
14
23
|
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18.0.0"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"engrm",
|
|
29
|
+
"ai-memory",
|
|
30
|
+
"claude-code",
|
|
31
|
+
"mcp",
|
|
32
|
+
"coding-agent",
|
|
33
|
+
"context-injection",
|
|
34
|
+
"sentinel",
|
|
35
|
+
"code-audit",
|
|
36
|
+
"cross-device",
|
|
37
|
+
"team-memory",
|
|
38
|
+
"developer-tools"
|
|
39
|
+
],
|
|
15
40
|
"dependencies": {
|
|
41
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.74",
|
|
16
42
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
17
43
|
"@xenova/transformers": "^2.17.2",
|
|
44
|
+
"better-sqlite3": "^12.6.2",
|
|
18
45
|
"sqlite-vec": "^0.1.7-alpha.2"
|
|
19
46
|
},
|
|
20
47
|
"devDependencies": {
|
|
48
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
21
49
|
"@types/bun": "^1.2.19",
|
|
22
50
|
"typescript": "^5.8.3"
|
|
23
51
|
},
|
|
52
|
+
"homepage": "https://engrm.dev",
|
|
24
53
|
"license": "FSL-1.1-ALv2",
|
|
25
|
-
"author": "
|
|
54
|
+
"author": "Engrm <hello@engrm.dev> (https://engrm.dev)",
|
|
26
55
|
"repository": {
|
|
27
56
|
"type": "git",
|
|
28
57
|
"url": "https://github.com/unimpossible/candengo-mem"
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "api-best-practices",
|
|
3
|
+
"description": "REST API design patterns, error handling, and operational best practices",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"observations": [
|
|
6
|
+
{
|
|
7
|
+
"type": "pattern",
|
|
8
|
+
"title": "Use appropriate HTTP status codes — not everything is 200",
|
|
9
|
+
"narrative": "Status codes convey meaning to clients. 200 for success, 201 for created, 204 for no content, 400 for bad request, 401 for unauthorized, 403 for forbidden, 404 for not found, 409 for conflict, 422 for validation errors, 429 for rate limited, 500 for server error.",
|
|
10
|
+
"facts": ["201 Created for POST that creates a resource", "204 No Content for successful DELETE", "422 Unprocessable Entity for validation failures", "429 Too Many Requests with Retry-After header"],
|
|
11
|
+
"concepts": ["http-status-codes", "rest-api", "error-responses"]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"type": "pattern",
|
|
15
|
+
"title": "Version your API from day one — /v1/ prefix or header",
|
|
16
|
+
"narrative": "API versioning prevents breaking changes for existing clients. URL prefix (/v1/) is most common and easily cacheable. Header versioning is cleaner but harder to test.",
|
|
17
|
+
"facts": ["URL versioning: /v1/users, /v2/users", "Header versioning: Accept: application/vnd.api+json;version=1", "Never make breaking changes to an existing version", "Deprecate old versions with sunset headers"],
|
|
18
|
+
"concepts": ["api-versioning", "backwards-compatibility", "breaking-changes"]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"type": "pattern",
|
|
22
|
+
"title": "Consistent error response format across all endpoints",
|
|
23
|
+
"narrative": "Every error response should follow the same structure. Clients should be able to parse errors without knowing which endpoint returned them.",
|
|
24
|
+
"facts": ["Include: status, error code (machine-readable), message (human-readable)", "Optional: details array for field-level validation errors", "Example: {\"error\": {\"code\": \"INVALID_EMAIL\", \"message\": \"...\", \"details\": [...]}}", "Document all possible error codes"],
|
|
25
|
+
"concepts": ["error-format", "api-design", "error-codes"]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"type": "pattern",
|
|
29
|
+
"title": "Paginate list endpoints — never return unbounded results",
|
|
30
|
+
"narrative": "List endpoints must support pagination. Cursor-based pagination is more reliable than offset-based for real-time data.",
|
|
31
|
+
"facts": ["Cursor pagination: ?after=<cursor>&limit=20", "Offset pagination: ?page=2&per_page=20 (breaks with insertions)", "Return total count and next page cursor/link", "Default limit of 20-50, max limit of 100"],
|
|
32
|
+
"concepts": ["pagination", "cursor-pagination", "list-endpoints"]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"type": "pattern",
|
|
36
|
+
"title": "Use plural nouns for resource endpoints — /users not /user",
|
|
37
|
+
"narrative": "RESTful URLs represent collections. Use plural nouns (/users, /orders) and let HTTP methods express the action (GET, POST, PUT, DELETE).",
|
|
38
|
+
"facts": ["GET /users — list users", "POST /users — create user", "GET /users/123 — get specific user", "Don't use verbs: /getUsers, /createUser is wrong"],
|
|
39
|
+
"concepts": ["rest-naming", "url-design", "resource-naming"]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"type": "pattern",
|
|
43
|
+
"title": "Implement idempotent operations — same request, same result",
|
|
44
|
+
"narrative": "GET, PUT, DELETE should be idempotent (multiple identical requests produce the same result). For POST, use Idempotency-Key headers to prevent duplicate creation.",
|
|
45
|
+
"facts": ["GET: naturally idempotent (read-only)", "PUT: replace entire resource, naturally idempotent", "DELETE: deleting twice should return 204/404, not error", "POST: use Idempotency-Key header for payment/order creation"],
|
|
46
|
+
"concepts": ["idempotency", "idempotency-key", "safe-retries"]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"type": "pattern",
|
|
50
|
+
"title": "Rate limit all endpoints — use token bucket or sliding window",
|
|
51
|
+
"narrative": "Rate limiting protects against abuse, DoS, and runaway clients. Return 429 with Retry-After header. Different limits for authenticated vs anonymous users.",
|
|
52
|
+
"facts": ["Token bucket: smooth, allows short bursts", "Sliding window: strict, predictable limits", "Return X-RateLimit-Limit, X-RateLimit-Remaining headers", "Higher limits for authenticated users"],
|
|
53
|
+
"concepts": ["rate-limiting", "throttling", "api-protection"]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"type": "pattern",
|
|
57
|
+
"title": "Validate all input at the boundary — use schema validation",
|
|
58
|
+
"narrative": "Validate request bodies, query parameters, and path parameters at the API boundary. Use schema validation libraries (Zod, Joi, JSON Schema) for consistent enforcement.",
|
|
59
|
+
"facts": ["Validate types, ranges, formats, required fields", "Return 422 with field-level error details", "Reject unknown fields to prevent mass assignment", "Schema doubles as documentation"],
|
|
60
|
+
"concepts": ["input-validation", "schema-validation", "zod", "api-security"]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"type": "pattern",
|
|
64
|
+
"title": "Use ETags and If-None-Match for HTTP caching",
|
|
65
|
+
"narrative": "ETags enable conditional requests. The server returns ETag headers; clients send If-None-Match on subsequent requests. Server returns 304 Not Modified if unchanged.",
|
|
66
|
+
"facts": ["ETag: hash or version of the response body", "Client sends If-None-Match: <etag> on next request", "Server returns 304 if unchanged (saves bandwidth)", "Also useful for optimistic concurrency (If-Match for PUT)"],
|
|
67
|
+
"concepts": ["etag", "http-caching", "conditional-requests", "304"]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"type": "pattern",
|
|
71
|
+
"title": "Use PATCH for partial updates — PUT replaces the entire resource",
|
|
72
|
+
"narrative": "PUT semantics require sending the complete resource. PATCH updates only the fields provided. Use JSON Merge Patch (RFC 7396) for simple cases.",
|
|
73
|
+
"facts": ["PUT: client sends complete resource, server replaces it", "PATCH: client sends only changed fields", "JSON Merge Patch: {\"name\": \"new\"} updates only name", "Return the updated resource in the response"],
|
|
74
|
+
"concepts": ["patch", "partial-update", "put-vs-patch"]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"type": "pattern",
|
|
78
|
+
"title": "Structured logging with request ID for traceability",
|
|
79
|
+
"narrative": "Every request should get a unique ID (X-Request-Id). Log it with all related operations. This enables tracing a request across services.",
|
|
80
|
+
"facts": ["Generate UUID request ID in middleware", "Return it in response headers", "Include in all log entries for that request", "Propagate to downstream service calls"],
|
|
81
|
+
"concepts": ["request-id", "structured-logging", "tracing", "observability"]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"type": "pattern",
|
|
85
|
+
"title": "Health check endpoint: GET /health with dependency status",
|
|
86
|
+
"narrative": "Load balancers and monitoring need a health endpoint. Return 200 when healthy, 503 when degraded. Include dependency status (DB, cache, queues).",
|
|
87
|
+
"facts": ["GET /health — lightweight, no auth required", "Include: status, version, uptime, dependencies", "Check database connectivity, cache, external services", "Separate liveness (/healthz) from readiness (/readyz)"],
|
|
88
|
+
"concepts": ["health-check", "monitoring", "load-balancer", "kubernetes"]
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"type": "pattern",
|
|
92
|
+
"title": "Use bearer tokens in Authorization header — not query params",
|
|
93
|
+
"narrative": "Authentication tokens belong in the Authorization header (Bearer <token>). Query parameter tokens are logged in access logs and browser history.",
|
|
94
|
+
"facts": ["Authorization: Bearer <token>", "Query param tokens leak in logs, referrers, bookmarks", "API keys in X-Api-Key header for server-to-server", "Never send tokens in GET request body"],
|
|
95
|
+
"concepts": ["authentication", "bearer-token", "api-key", "security"]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"type": "pattern",
|
|
99
|
+
"title": "Implement request/response compression — gzip or brotli",
|
|
100
|
+
"narrative": "Compress API responses to reduce bandwidth. Most frameworks support this via middleware. Check Accept-Encoding header and set Content-Encoding.",
|
|
101
|
+
"facts": ["Brotli: better compression ratio, slower to compress", "Gzip: widely supported, fast enough for dynamic content", "Don't compress already-compressed formats (images, video)", "Set Vary: Accept-Encoding for proper caching"],
|
|
102
|
+
"concepts": ["compression", "gzip", "brotli", "performance"]
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"type": "pattern",
|
|
106
|
+
"title": "Soft delete resources — don't hard delete by default",
|
|
107
|
+
"narrative": "Instead of DELETE removing data permanently, set a deleted_at timestamp. This allows recovery, audit trails, and referential integrity.",
|
|
108
|
+
"facts": ["Add deleted_at column (null = active)", "Filter deleted records in all queries by default", "Provide purge endpoint for permanent deletion (admin only)", "Cascade soft deletes to dependent resources"],
|
|
109
|
+
"concepts": ["soft-delete", "data-retention", "audit-trail"]
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"type": "pattern",
|
|
113
|
+
"title": "Document APIs with OpenAPI/Swagger — generated from code",
|
|
114
|
+
"narrative": "API documentation must be accurate and up-to-date. Generate OpenAPI specs from code annotations or schema definitions. Serve interactive docs at /docs.",
|
|
115
|
+
"facts": ["OpenAPI 3.1 is the current standard", "Generate from code (not hand-written) to stay in sync", "Swagger UI or Redoc for interactive documentation", "Include request/response examples"],
|
|
116
|
+
"concepts": ["openapi", "swagger", "api-documentation"]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"type": "pattern",
|
|
120
|
+
"title": "Use webhooks for async notifications — don't make clients poll",
|
|
121
|
+
"narrative": "For events that happen asynchronously (payment completed, build finished), push notifications via webhooks instead of requiring clients to poll.",
|
|
122
|
+
"facts": ["Sign webhook payloads with HMAC for verification", "Retry with exponential backoff on delivery failure", "Include event type and timestamp in payload", "Provide webhook testing/replay in dashboard"],
|
|
123
|
+
"concepts": ["webhooks", "async-events", "push-notifications"]
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"type": "pattern",
|
|
127
|
+
"title": "Graceful degradation: circuit breaker for downstream services",
|
|
128
|
+
"narrative": "When a downstream service fails, don't let every request timeout. Use a circuit breaker pattern that fails fast after detecting failures, then periodically retries.",
|
|
129
|
+
"facts": ["Closed (normal) → Open (fast fail) → Half-Open (test)", "Track failure rate in sliding window", "Return cached/default response when circuit is open", "Libraries: opossum (Node.js), resilience4j (Java)"],
|
|
130
|
+
"concepts": ["circuit-breaker", "resilience", "graceful-degradation"]
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"type": "discovery",
|
|
134
|
+
"title": "GraphQL vs REST: use GraphQL for complex, nested data needs",
|
|
135
|
+
"narrative": "GraphQL excels when clients need flexible queries over related data. REST is simpler for CRUD operations. Choose based on your data access patterns.",
|
|
136
|
+
"facts": ["GraphQL: single endpoint, client specifies needed fields", "REST: multiple endpoints, server decides response shape", "GraphQL N+1 problem: use DataLoader for batching", "REST is better for simple CRUD and caching"],
|
|
137
|
+
"concepts": ["graphql", "rest", "api-design", "data-fetching"]
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"type": "pattern",
|
|
141
|
+
"title": "Implement request timeouts and connection limits",
|
|
142
|
+
"narrative": "Every outgoing HTTP call needs a timeout. Every server needs connection limits. Without these, slow clients or downstream services can exhaust resources.",
|
|
143
|
+
"facts": ["Set connect timeout (5s) and read timeout (30s) separately", "Limit concurrent connections per client", "Use keepalive connections for repeated calls to same host", "Set server-side request timeout (e.g., 30s) as safety net"],
|
|
144
|
+
"concepts": ["timeouts", "connection-limits", "resource-management"]
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"type": "pattern",
|
|
148
|
+
"title": "Return created resource on POST with Location header",
|
|
149
|
+
"narrative": "POST endpoints that create resources should return 201 with the created resource in the body and a Location header pointing to the new resource URL.",
|
|
150
|
+
"facts": ["201 Created with body containing the new resource", "Location: /v1/users/123 header for the new resource URL", "Include server-generated fields (id, created_at)", "Saves client from making a follow-up GET"],
|
|
151
|
+
"concepts": ["post-response", "201-created", "location-header"]
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"type": "pattern",
|
|
155
|
+
"title": "Use correlation IDs across microservices for distributed tracing",
|
|
156
|
+
"narrative": "In microservice architectures, propagate a correlation ID from the entry point through all service calls. This enables end-to-end request tracing.",
|
|
157
|
+
"facts": ["Generate at API gateway or first service", "Propagate via X-Correlation-Id or traceparent header", "Include in all log entries across services", "Tools: OpenTelemetry, Jaeger, Zipkin"],
|
|
158
|
+
"concepts": ["correlation-id", "distributed-tracing", "microservices", "opentelemetry"]
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"type": "pattern",
|
|
162
|
+
"title": "Handle N+1 queries in API endpoints — batch database calls",
|
|
163
|
+
"narrative": "When returning a list of resources with related data, load all relations in one query instead of one query per item. This is the most common API performance issue.",
|
|
164
|
+
"facts": ["Use JOIN or WHERE IN (...) for batch loading", "ORMs have eager loading: include/with/joinedload", "DataLoader pattern batches within a request cycle", "N+1 turns 1 query into 101 queries for 100 items"],
|
|
165
|
+
"concepts": ["n-plus-one", "batch-loading", "query-optimization", "orm"]
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"type": "pattern",
|
|
169
|
+
"title": "Use Content-Type negotiation — support JSON at minimum",
|
|
170
|
+
"narrative": "Always set Content-Type on responses. Check Accept header on requests to return the right format. Return 406 Not Acceptable if you can't serve the requested format.",
|
|
171
|
+
"facts": ["application/json is the default for REST APIs", "Check Accept header for content negotiation", "Return Content-Type: application/json on all JSON responses", "406 Not Acceptable if format is unsupported"],
|
|
172
|
+
"concepts": ["content-negotiation", "content-type", "accept-header"]
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"type": "pattern",
|
|
176
|
+
"title": "Implement API key rotation without downtime",
|
|
177
|
+
"narrative": "Allow multiple active API keys per account. Clients generate a new key, update their config, then revoke the old key. Never force immediate key invalidation.",
|
|
178
|
+
"facts": ["Support multiple concurrent API keys", "Provide key creation and revocation endpoints", "Set expiry dates on keys for automatic rotation", "Log key usage to detect which keys are active"],
|
|
179
|
+
"concepts": ["api-key-rotation", "key-management", "zero-downtime"]
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nextjs-patterns",
|
|
3
|
+
"description": "Next.js App Router patterns, data fetching, and deployment tips",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"stacks": ["nextjs", "react"],
|
|
6
|
+
"observations": [
|
|
7
|
+
{
|
|
8
|
+
"type": "pattern",
|
|
9
|
+
"title": "Default to Server Components — add 'use client' only when needed",
|
|
10
|
+
"narrative": "In the App Router, components are Server Components by default. Only add 'use client' for interactivity (hooks, event handlers, browser APIs). Keep the client boundary as low in the tree as possible.",
|
|
11
|
+
"concepts": ["nextjs", "server-components", "app-router"]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"type": "pattern",
|
|
15
|
+
"title": "Fetch data in Server Components, not useEffect",
|
|
16
|
+
"narrative": "Server Components can be async — fetch data directly with await. No loading spinners, no client-side waterfalls. The HTML arrives fully rendered.",
|
|
17
|
+
"concepts": ["nextjs", "data-fetching", "server-components"]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"type": "bugfix",
|
|
21
|
+
"title": "Next.js caching can serve stale data in production",
|
|
22
|
+
"narrative": "Next.js aggressively caches fetch() results and route segments. Use revalidatePath/revalidateTag after mutations, or set { cache: 'no-store' } for real-time data.",
|
|
23
|
+
"concepts": ["nextjs", "caching", "revalidation", "stale-data"]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"type": "pattern",
|
|
27
|
+
"title": "Use route groups (parentheses) for layout organization",
|
|
28
|
+
"narrative": "Folders wrapped in parentheses like (marketing) and (dashboard) create route groups — they organize code and share layouts without affecting the URL path.",
|
|
29
|
+
"concepts": ["nextjs", "route-groups", "app-router", "layouts"]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "pattern",
|
|
33
|
+
"title": "Server Actions for form mutations",
|
|
34
|
+
"narrative": "Use 'use server' functions for form handling. They run on the server, can access the DB directly, and work without JavaScript. Pair with useFormState for loading/error states.",
|
|
35
|
+
"concepts": ["nextjs", "server-actions", "forms", "mutations"]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"type": "discovery",
|
|
39
|
+
"title": "Parallel routes (@slot) for complex layouts",
|
|
40
|
+
"narrative": "Parallel routes using @folder convention let you render multiple pages in the same layout simultaneously. Useful for dashboards with independent loading states.",
|
|
41
|
+
"concepts": ["nextjs", "parallel-routes", "layouts"]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"type": "pattern",
|
|
45
|
+
"title": "Use loading.tsx for instant loading states",
|
|
46
|
+
"narrative": "Place a loading.tsx file in any route directory for automatic Suspense-wrapped loading UI. It shows immediately while the page component fetches data.",
|
|
47
|
+
"concepts": ["nextjs", "loading", "suspense", "ux"]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"type": "bugfix",
|
|
51
|
+
"title": "Dynamic route params are always strings",
|
|
52
|
+
"narrative": "Route params like [id] are always strings, even if they look like numbers. Always parseInt() or validate before using as numeric IDs in database queries.",
|
|
53
|
+
"concepts": ["nextjs", "dynamic-routes", "params", "validation"]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"type": "pattern",
|
|
57
|
+
"title": "Middleware for auth, redirects, and headers",
|
|
58
|
+
"narrative": "middleware.ts runs at the edge before every request. Use it for auth checks, locale detection, and response headers. Keep it fast — no heavy computation or DB calls.",
|
|
59
|
+
"concepts": ["nextjs", "middleware", "auth", "edge"]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"type": "pattern",
|
|
63
|
+
"title": "Use next/image for automatic image optimization",
|
|
64
|
+
"narrative": "The Image component automatically resizes, converts to WebP/AVIF, and lazy-loads images. Always specify width/height or use fill for responsive images to prevent layout shift.",
|
|
65
|
+
"concepts": ["nextjs", "image", "optimization", "performance"]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-security",
|
|
3
|
+
"description": "Node.js and JavaScript security patterns and common vulnerabilities",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"stacks": ["javascript", "bun", "typescript"],
|
|
6
|
+
"observations": [
|
|
7
|
+
{
|
|
8
|
+
"type": "pattern",
|
|
9
|
+
"title": "Always validate and sanitize user input at boundaries",
|
|
10
|
+
"narrative": "Never trust input from HTTP requests, WebSocket messages, or CLI arguments. Use Zod, Joi, or similar to validate shape and types before processing.",
|
|
11
|
+
"concepts": ["security", "input-validation", "xss", "injection"]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"type": "bugfix",
|
|
15
|
+
"title": "Prototype pollution via object spread and Object.assign",
|
|
16
|
+
"narrative": "Merging user-controlled objects with Object.assign or spread can overwrite __proto__. Use Object.create(null) for lookup objects, or validate keys before merging.",
|
|
17
|
+
"concepts": ["security", "prototype-pollution", "javascript"]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"type": "pattern",
|
|
21
|
+
"title": "Use parameterized queries — never interpolate SQL",
|
|
22
|
+
"narrative": "Template literals in SQL queries enable SQL injection. Always use parameterized queries: db.query('SELECT * FROM users WHERE id = ?', [id]).",
|
|
23
|
+
"concepts": ["security", "sql-injection", "database", "parameterized"]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"type": "pattern",
|
|
27
|
+
"title": "Store secrets in environment variables, never in code",
|
|
28
|
+
"narrative": "API keys, database passwords, and tokens must come from env vars or a secret manager. Add .env to .gitignore. Use dotenv or platform-native secret injection.",
|
|
29
|
+
"concepts": ["security", "secrets", "environment-variables"]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "pattern",
|
|
33
|
+
"title": "Set security headers: CSP, HSTS, X-Frame-Options",
|
|
34
|
+
"narrative": "Configure Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options, and X-Frame-Options on all HTTP responses. Use helmet middleware in Express.",
|
|
35
|
+
"concepts": ["security", "headers", "csp", "hsts"]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"type": "bugfix",
|
|
39
|
+
"title": "SSRF risk from user-supplied URLs in server fetch",
|
|
40
|
+
"narrative": "If your server fetches URLs from user input, attackers can reach internal services (169.254.169.254, localhost). Validate URLs against an allowlist of domains and block private IP ranges.",
|
|
41
|
+
"concepts": ["security", "ssrf", "fetch", "url-validation"]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"type": "pattern",
|
|
45
|
+
"title": "Use bcrypt or argon2 for password hashing",
|
|
46
|
+
"narrative": "Never store passwords with SHA-256 or MD5 — they're too fast. Use bcrypt (cost factor 10+) or argon2id. These are deliberately slow and resistant to GPU attacks.",
|
|
47
|
+
"concepts": ["security", "passwords", "hashing", "bcrypt"]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"type": "pattern",
|
|
51
|
+
"title": "Rate limit authentication endpoints",
|
|
52
|
+
"narrative": "Apply strict rate limiting (e.g., 5 attempts per minute per IP) to login, registration, and password reset endpoints. Use sliding window counters, not simple token buckets.",
|
|
53
|
+
"concepts": ["security", "rate-limiting", "brute-force", "authentication"]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"type": "bugfix",
|
|
57
|
+
"title": "Path traversal in file operations",
|
|
58
|
+
"narrative": "User-controlled file paths like '../../../etc/passwd' can escape intended directories. Always resolve paths with path.resolve() and verify the result starts with your allowed directory.",
|
|
59
|
+
"concepts": ["security", "path-traversal", "file-access"]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"type": "pattern",
|
|
63
|
+
"title": "Use crypto.timingSafeEqual for secret comparison",
|
|
64
|
+
"narrative": "Comparing secrets with === is vulnerable to timing attacks. Use crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b)) for constant-time comparison of tokens and signatures.",
|
|
65
|
+
"concepts": ["security", "timing-attack", "crypto", "comparison"]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "python-django",
|
|
3
|
+
"description": "Python and Django patterns, pitfalls, and best practices",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"stacks": ["python", "django"],
|
|
6
|
+
"observations": [
|
|
7
|
+
{
|
|
8
|
+
"type": "pattern",
|
|
9
|
+
"title": "Use dataclasses or Pydantic for structured data",
|
|
10
|
+
"narrative": "Prefer @dataclass or Pydantic BaseModel over plain dicts for structured data. You get type hints, validation, and IDE support instead of stringly-typed key access.",
|
|
11
|
+
"concepts": ["python", "dataclass", "pydantic", "typing"]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"type": "bugfix",
|
|
15
|
+
"title": "Mutable default arguments are shared across calls",
|
|
16
|
+
"narrative": "def f(items=[]): is a classic Python bug — the list is shared across all calls. Use def f(items=None): items = items or [] instead.",
|
|
17
|
+
"concepts": ["python", "mutable-default", "gotcha"]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"type": "pattern",
|
|
21
|
+
"title": "Django select_related and prefetch_related prevent N+1",
|
|
22
|
+
"narrative": "Always use .select_related() for ForeignKey and .prefetch_related() for ManyToMany/reverse FK when accessing related objects in loops. Without them, each access fires a separate query.",
|
|
23
|
+
"concepts": ["django", "orm", "n+1", "performance"]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"type": "pattern",
|
|
27
|
+
"title": "Use Django F() expressions for atomic field updates",
|
|
28
|
+
"narrative": "Model.objects.filter(pk=id).update(count=F('count') + 1) is atomic and avoids race conditions. Direct Python arithmetic (obj.count += 1; obj.save()) is not atomic.",
|
|
29
|
+
"concepts": ["django", "orm", "f-expression", "concurrency"]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "discovery",
|
|
33
|
+
"title": "Python match/case for structural pattern matching",
|
|
34
|
+
"narrative": "Python 3.10+ match/case supports destructuring dicts, objects, and sequences. match response.status_code: case 200: ... is cleaner than if/elif chains for multi-branch logic.",
|
|
35
|
+
"concepts": ["python", "match-case", "pattern-matching"]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"type": "pattern",
|
|
39
|
+
"title": "Use context managers for resource cleanup",
|
|
40
|
+
"narrative": "Always use 'with' statements for files, DB connections, locks. Write custom context managers with @contextmanager for any acquire/release pattern.",
|
|
41
|
+
"concepts": ["python", "context-manager", "resource-management"]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"type": "bugfix",
|
|
45
|
+
"title": "Django timezone-aware datetime handling",
|
|
46
|
+
"narrative": "Use django.utils.timezone.now() not datetime.now(). Naive datetimes cause comparison bugs and warnings. Set USE_TZ=True in settings.",
|
|
47
|
+
"concepts": ["django", "timezone", "datetime"]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"type": "pattern",
|
|
51
|
+
"title": "Type hints with Protocol for duck typing",
|
|
52
|
+
"narrative": "Use typing.Protocol for structural (duck) typing instead of ABC. Protocols don't require inheritance — any class with matching methods satisfies the protocol.",
|
|
53
|
+
"concepts": ["python", "protocol", "duck-typing", "typing"]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"type": "pattern",
|
|
57
|
+
"title": "Django signals considered harmful for critical logic",
|
|
58
|
+
"narrative": "Avoid Django signals (post_save, pre_delete) for critical business logic. They run silently, are hard to debug, and create implicit coupling. Call service functions explicitly.",
|
|
59
|
+
"concepts": ["django", "signals", "architecture"]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"type": "pattern",
|
|
63
|
+
"title": "Use __slots__ for memory-efficient value objects",
|
|
64
|
+
"narrative": "Add __slots__ = ('x', 'y') to classes with many instances. Eliminates per-instance __dict__, reducing memory by 40-50%. Tradeoff: no dynamic attributes.",
|
|
65
|
+
"concepts": ["python", "slots", "memory", "optimization"]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|