claude-toolkit 0.1.9
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/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/README.md +126 -0
- package/bin/cli.ts +112 -0
- package/core/agents/ct-code-reviewer.md +123 -0
- package/core/agents/ct-github-workflow.md +137 -0
- package/core/commands/ct/code-quality.md +59 -0
- package/core/commands/ct/onboard.md +84 -0
- package/core/commands/ct/pr-review.md +104 -0
- package/core/commands/ct/pr-summary.md +59 -0
- package/core/commands/ct/proto-check.md +74 -0
- package/core/commands/ct/ticket.md +71 -0
- package/core/hooks/skill-eval.js +381 -0
- package/core/hooks/skill-eval.sh +35 -0
- package/core/hooks/skill-rules.schema.json +112 -0
- package/core/skills/systematic-debugging/SKILL.md +44 -0
- package/core/skills/testing-patterns/SKILL.md +52 -0
- package/core/skills/typescript-conventions/SKILL.md +57 -0
- package/core/skills/verification-before-completion/SKILL.md +42 -0
- package/docs/README.md +49 -0
- package/docs/agents/code-reviewer.md +76 -0
- package/docs/agents/github-workflow.md +98 -0
- package/docs/best-practices/solidjs/README.md +43 -0
- package/docs/best-practices/solidjs/anti-patterns.md +166 -0
- package/docs/best-practices/solidjs/component-patterns.md +131 -0
- package/docs/best-practices/solidjs/context-and-global-state.md +131 -0
- package/docs/best-practices/solidjs/control-flow.md +124 -0
- package/docs/best-practices/solidjs/data-fetching.md +205 -0
- package/docs/best-practices/solidjs/effects-and-lifecycle.md +113 -0
- package/docs/best-practices/solidjs/performance.md +100 -0
- package/docs/best-practices/solidjs/props-patterns.md +100 -0
- package/docs/best-practices/solidjs/reactivity-model.md +104 -0
- package/docs/best-practices/solidjs/signals-and-state.md +78 -0
- package/docs/best-practices/solidjs/stores-and-nested-state.md +111 -0
- package/docs/best-practices/solidjs/typescript-integration.md +186 -0
- package/docs/best-practices/typescript/README.md +45 -0
- package/docs/best-practices/typescript/any-and-unknown.md +73 -0
- package/docs/best-practices/typescript/deriving-vs-decoupling.md +83 -0
- package/docs/best-practices/typescript/discriminated-unions.md +75 -0
- package/docs/best-practices/typescript/enums-alternatives.md +72 -0
- package/docs/best-practices/typescript/essential-patterns.md +119 -0
- package/docs/best-practices/typescript/generics-patterns.md +105 -0
- package/docs/best-practices/typescript/micro-opinions.md +87 -0
- package/docs/best-practices/typescript/runtime-validation.md +62 -0
- package/docs/best-practices/typescript/satisfies-operator.md +100 -0
- package/docs/best-practices/typescript/tsconfig-cheat-sheet.md +129 -0
- package/docs/best-practices/typescript/type-organization.md +64 -0
- package/docs/best-practices/typescript/type-vs-interface.md +80 -0
- package/docs/commands/code-quality.md +42 -0
- package/docs/commands/onboard.md +72 -0
- package/docs/commands/pr-review.md +102 -0
- package/docs/commands/pr-summary.md +50 -0
- package/docs/commands/proto-check.md +59 -0
- package/docs/commands/ticket.md +56 -0
- package/docs/skills/systematic-debugging.md +70 -0
- package/docs/skills/testing-patterns.md +89 -0
- package/docs/skills/typescript-conventions.md +137 -0
- package/docs/skills/verification-before-completion.md +91 -0
- package/docs/stacks/cloudflare-d1-kv.md +110 -0
- package/docs/stacks/i18n-typesafe.md +141 -0
- package/docs/stacks/protobuf-contracts.md +85 -0
- package/docs/stacks/rust-wasm-patterns.md +106 -0
- package/docs/stacks/solidjs-patterns.md +110 -0
- package/docs/stacks/vanilla-extract-patterns.md +115 -0
- package/package.json +58 -0
- package/src/generator.ts +317 -0
- package/src/index.ts +30 -0
- package/src/types.ts +85 -0
- package/src/utils.ts +53 -0
- package/stacks/cloudflare/skills/cloudflare-d1-kv/SKILL.md +84 -0
- package/stacks/cloudflare/stack.json +26 -0
- package/stacks/i18n-typesafe/skills/i18n-typesafe/SKILL.md +64 -0
- package/stacks/i18n-typesafe/stack.json +25 -0
- package/stacks/protobuf/skills/protobuf-contracts/SKILL.md +78 -0
- package/stacks/protobuf/stack.json +25 -0
- package/stacks/rust-wasm/skills/rust-wasm-patterns/SKILL.md +76 -0
- package/stacks/rust-wasm/stack.json +26 -0
- package/stacks/solidjs/skills/solidjs-patterns/SKILL.md +66 -0
- package/stacks/solidjs/stack.json +52 -0
- package/stacks/vanilla-extract/skills/vanilla-extract-patterns/SKILL.md +76 -0
- package/stacks/vanilla-extract/stack.json +40 -0
- package/templates/claude-toolkit.config.ts +34 -0
- package/templates/configs/biome.base.json +35 -0
- package/templates/configs/tsconfig.base.json +16 -0
- package/templates/skill-rules.base.json +98 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** Available stack identifiers */
|
|
2
|
+
export type StackName =
|
|
3
|
+
| "solidjs"
|
|
4
|
+
| "vanilla-extract"
|
|
5
|
+
| "rust-wasm"
|
|
6
|
+
| "protobuf"
|
|
7
|
+
| "cloudflare"
|
|
8
|
+
| "i18n-typesafe"
|
|
9
|
+
| (string & {});
|
|
10
|
+
|
|
11
|
+
/** Hook configuration for post-tool-use automation */
|
|
12
|
+
export interface HookConfig {
|
|
13
|
+
/** Formatter command (e.g., "bun run prettier --write") */
|
|
14
|
+
formatter?: string;
|
|
15
|
+
/** Test runner command (e.g., "bun run vitest run") */
|
|
16
|
+
testRunner?: string;
|
|
17
|
+
/** Type checker command (e.g., "bun run tsc --noEmit") */
|
|
18
|
+
typeCheck?: string;
|
|
19
|
+
/** Additional check commands (e.g., ["cargo check --target wasm32-unknown-unknown"]) */
|
|
20
|
+
extraChecks?: string[];
|
|
21
|
+
/** Dependency install command (e.g., "bun install") — inferred from packageManager if omitted */
|
|
22
|
+
installCommand?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Git workflow configuration */
|
|
26
|
+
export interface GitConfig {
|
|
27
|
+
/** Branch name prefix (e.g., "wq" → "wq/feature-name") */
|
|
28
|
+
branchPrefix?: string;
|
|
29
|
+
/** Branches protected from direct edits */
|
|
30
|
+
protectedBranches?: string[];
|
|
31
|
+
/** Commit format (default: conventional commits) */
|
|
32
|
+
commitFormat?: "conventional" | "freeform";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Project metadata used in CLAUDE.md generation */
|
|
36
|
+
export interface ProjectInfo {
|
|
37
|
+
/** Project name */
|
|
38
|
+
name?: string;
|
|
39
|
+
/** Short project description */
|
|
40
|
+
description?: string;
|
|
41
|
+
/** Key directories and their purpose */
|
|
42
|
+
directories?: Record<string, string>;
|
|
43
|
+
/** Common dev commands */
|
|
44
|
+
commands?: Record<string, string>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Main toolkit configuration */
|
|
48
|
+
export interface ClaudeToolkitConfig {
|
|
49
|
+
/** Stack packs to activate */
|
|
50
|
+
stacks: StackName[];
|
|
51
|
+
/** Package manager to use */
|
|
52
|
+
packageManager: "bun" | "npm" | "pnpm" | "yarn";
|
|
53
|
+
/** Hook automation config */
|
|
54
|
+
hooks?: HookConfig;
|
|
55
|
+
/** Directory → skill mappings (merged with stack defaults) */
|
|
56
|
+
directoryMappings?: Record<string, string>;
|
|
57
|
+
/** Git workflow config */
|
|
58
|
+
git?: GitConfig;
|
|
59
|
+
/** Project info for CLAUDE.md generation */
|
|
60
|
+
project?: ProjectInfo;
|
|
61
|
+
/** Additional custom skills to include (paths relative to project root) */
|
|
62
|
+
customSkills?: string[];
|
|
63
|
+
/** Skills to exclude from core set */
|
|
64
|
+
excludeSkills?: string[];
|
|
65
|
+
/** Whether to scaffold base configs (biome.json, tsconfig.json) on init. Default: true */
|
|
66
|
+
scaffoldConfigs?: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Stack pack metadata (loaded from stack.json) */
|
|
70
|
+
export interface StackPack {
|
|
71
|
+
name: string;
|
|
72
|
+
description: string;
|
|
73
|
+
defaultMappings: Record<string, string>;
|
|
74
|
+
fileExtensions: string[];
|
|
75
|
+
hookOverrides?: Partial<HookConfig>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Resolved configuration after merging core + stacks + project config */
|
|
79
|
+
export interface ResolvedConfig {
|
|
80
|
+
config: ClaudeToolkitConfig;
|
|
81
|
+
skills: string[];
|
|
82
|
+
directoryMappings: Record<string, string>;
|
|
83
|
+
hooks: HookConfig;
|
|
84
|
+
stacks: StackPack[];
|
|
85
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import {
|
|
3
|
+
copyFile,
|
|
4
|
+
mkdir,
|
|
5
|
+
readdir,
|
|
6
|
+
readFile,
|
|
7
|
+
writeFile,
|
|
8
|
+
} from "node:fs/promises";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
|
|
11
|
+
/** Recursively copy a directory */
|
|
12
|
+
export async function copyDir(src: string, dest: string): Promise<void> {
|
|
13
|
+
await mkdir(dest, { recursive: true });
|
|
14
|
+
const entries = await readdir(src, { withFileTypes: true });
|
|
15
|
+
for (const entry of entries) {
|
|
16
|
+
const srcPath = join(src, entry.name);
|
|
17
|
+
const destPath = join(dest, entry.name);
|
|
18
|
+
if (entry.isDirectory()) {
|
|
19
|
+
await copyDir(srcPath, destPath);
|
|
20
|
+
} else {
|
|
21
|
+
await mkdir(dirname(destPath), { recursive: true });
|
|
22
|
+
await copyFile(srcPath, destPath);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Write a file, creating parent directories as needed */
|
|
28
|
+
export async function writeFileEnsureDir(
|
|
29
|
+
filePath: string,
|
|
30
|
+
content: string,
|
|
31
|
+
): Promise<void> {
|
|
32
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
33
|
+
await writeFile(filePath, content, "utf-8");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Read JSON file with type */
|
|
37
|
+
export async function readJson<T>(filePath: string): Promise<T> {
|
|
38
|
+
const content = await readFile(filePath, "utf-8");
|
|
39
|
+
return JSON.parse(content) as T;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Check if a path exists */
|
|
43
|
+
export function exists(filePath: string): boolean {
|
|
44
|
+
return existsSync(filePath);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Simple template replacement: {{key}} → value */
|
|
48
|
+
export function renderTemplate(
|
|
49
|
+
template: string,
|
|
50
|
+
vars: Record<string, string>,
|
|
51
|
+
): string {
|
|
52
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? "");
|
|
53
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ct-cloudflare-d1-kv
|
|
3
|
+
description: Cloudflare D1 SQL database and KV cache patterns
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# D1 and KV Patterns
|
|
7
|
+
|
|
8
|
+
## D1 Conventions
|
|
9
|
+
|
|
10
|
+
Always use prepared statements with `bind()`. Never concatenate SQL.
|
|
11
|
+
|
|
12
|
+
```rust
|
|
13
|
+
let stmt = db.prepare("SELECT * FROM users WHERE id = ?1").bind(&[id.into()])?;
|
|
14
|
+
let user = stmt.first::<User>(None).await?;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Batch related queries in a single round trip (transactional -- all succeed or all fail):
|
|
18
|
+
|
|
19
|
+
```rust
|
|
20
|
+
let results = db.batch(vec![
|
|
21
|
+
db.prepare("INSERT INTO events (id, name) VALUES (?1, ?2)").bind(&[id.into(), name.into()])?,
|
|
22
|
+
db.prepare("INSERT INTO event_members (event_id, user_id) VALUES (?1, ?2)").bind(&[id.into(), uid.into()])?,
|
|
23
|
+
]).await?;
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Schema Rules
|
|
27
|
+
|
|
28
|
+
- `TEXT` for IDs (UUIDs/nanoids, not auto-increment).
|
|
29
|
+
- `TEXT` for timestamps in ISO 8601.
|
|
30
|
+
- Always add `created_at` and `updated_at` columns.
|
|
31
|
+
- Always index columns used in WHERE/JOIN.
|
|
32
|
+
- Use `IF NOT EXISTS`/`IF EXISTS` for idempotent migrations.
|
|
33
|
+
|
|
34
|
+
### Migration Workflow
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
wrangler d1 migrations create DB "add_users_table"
|
|
38
|
+
# Edit migrations/0001_add_users_table.sql, then:
|
|
39
|
+
wrangler d1 migrations apply DB --local # test locally first
|
|
40
|
+
wrangler d1 migrations apply DB --remote
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## KV Conventions
|
|
44
|
+
|
|
45
|
+
KV is eventually-consistent. Use for read-heavy caching only, not primary data.
|
|
46
|
+
|
|
47
|
+
### Read-Through Cache
|
|
48
|
+
|
|
49
|
+
```rust
|
|
50
|
+
async fn get_cached(kv: &KvStore, db: &D1Database, id: &str) -> Result<User> {
|
|
51
|
+
let key = format!("user:{}", id);
|
|
52
|
+
if let Some(cached) = kv.get(&key).text().await? {
|
|
53
|
+
if let Ok(user) = serde_json::from_str::<User>(&cached) { return Ok(user); }
|
|
54
|
+
}
|
|
55
|
+
let user = db.prepare("SELECT * FROM users WHERE id = ?1")
|
|
56
|
+
.bind(&[id.into()])?.first::<User>(None).await?.ok_or(AppError::NotFound)?;
|
|
57
|
+
kv.put(&key, &serde_json::to_string(&user)?)?.expiration_ttl(3600).execute().await?;
|
|
58
|
+
Ok(user)
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Invalidate on writes: `kv.delete(&format!("user:{}", id)).await?;`
|
|
63
|
+
|
|
64
|
+
### Key Naming
|
|
65
|
+
|
|
66
|
+
Colon-separated hierarchical: `user:{id}`, `user:{id}:profile`, `cache:feed:{uid}:page:{n}`
|
|
67
|
+
|
|
68
|
+
### TTL Guidelines
|
|
69
|
+
|
|
70
|
+
| Data | TTL | Reason |
|
|
71
|
+
|------|-----|--------|
|
|
72
|
+
| User profile | 1h | Infrequent changes |
|
|
73
|
+
| Session | 24h | Matches session lifetime |
|
|
74
|
+
| Config | 5min | Needs fast propagation |
|
|
75
|
+
| Public listings | 15min | Freshness vs load balance |
|
|
76
|
+
|
|
77
|
+
## Anti-Patterns
|
|
78
|
+
|
|
79
|
+
1. **Unbounded queries** -- Always LIMIT. D1 has 5MB response cap.
|
|
80
|
+
2. **Missing indexes** -- D1 is SQLite; no index = full table scan.
|
|
81
|
+
3. **KV as primary store** -- Eventually consistent, no queries. Cache only.
|
|
82
|
+
4. **Skipping batch** -- Each D1 call is a network round trip.
|
|
83
|
+
5. **KV without TTL** -- Stale data persists indefinitely.
|
|
84
|
+
6. **Untested migrations** -- Always `--local` before `--remote`.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ct-cloudflare-d1-kv",
|
|
3
|
+
"description": "Cloudflare D1 database and KV cache patterns",
|
|
4
|
+
"defaultMappings": {
|
|
5
|
+
"src/db": "ct-cloudflare-d1-kv",
|
|
6
|
+
"migrations": "ct-cloudflare-d1-kv"
|
|
7
|
+
},
|
|
8
|
+
"fileExtensions": ["sql"],
|
|
9
|
+
"skillRules": {
|
|
10
|
+
"ct-cloudflare-d1-kv": {
|
|
11
|
+
"description": "Cloudflare D1 SQL and KV cache patterns",
|
|
12
|
+
"priority": 7,
|
|
13
|
+
"triggers": {
|
|
14
|
+
"keywords": ["d1", "kv", "cloudflare", "wrangler", "database", "cache", "sql", "migration"],
|
|
15
|
+
"keywordPatterns": ["\\bd1\\b", "\\bkv\\b", "\\bwrangler\\b", "\\bmigration\\b"],
|
|
16
|
+
"pathPatterns": ["**/migrations/**", "**/*.sql", "**/db/**", "**/wrangler.toml"],
|
|
17
|
+
"intentPatterns": [
|
|
18
|
+
"(?:create|add|run).*(?:migration|query|table)",
|
|
19
|
+
"(?:cache|store|fetch).*(?:data|value|key)"
|
|
20
|
+
],
|
|
21
|
+
"contentPatterns": ["D1Database", "KVNamespace", "env\\.DB", "env\\.KV", "prepared\\("]
|
|
22
|
+
},
|
|
23
|
+
"relatedSkills": ["ct-rust-wasm-patterns"]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ct-i18n-typesafe
|
|
3
|
+
description: Typesafe-i18n internationalization patterns for SolidJS
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Typesafe-i18n Patterns
|
|
7
|
+
|
|
8
|
+
Fully type-safe translations. Missing/misspelled keys cause TypeScript errors at compile time.
|
|
9
|
+
|
|
10
|
+
## Base Locale (Source of Truth)
|
|
11
|
+
|
|
12
|
+
All types are derived from the base locale. Other locales implement `Translation` (type-checked against base).
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// src/i18n/en/index.ts
|
|
16
|
+
const en = {
|
|
17
|
+
common: {
|
|
18
|
+
welcome: 'Welcome, {name:string}!',
|
|
19
|
+
itemCount: '{count:number} {{item|items}}',
|
|
20
|
+
save: 'Save',
|
|
21
|
+
cancel: 'Cancel',
|
|
22
|
+
},
|
|
23
|
+
events: {
|
|
24
|
+
title: 'Events',
|
|
25
|
+
create: 'Create event',
|
|
26
|
+
noEvents: 'No events yet',
|
|
27
|
+
attendees: '{count:number} {{attendee|attendees}}',
|
|
28
|
+
},
|
|
29
|
+
} satisfies BaseTranslation;
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Other locales mirror this structure with `satisfies Translation`. Missing keys or wrong param types cause compile errors.
|
|
33
|
+
|
|
34
|
+
## Usage in Components
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
const { LL } = useI18nContext();
|
|
38
|
+
// Simple: LL().common.save()
|
|
39
|
+
// Params: LL().common.welcome({ name: 'Alice' })
|
|
40
|
+
// Plural: LL().events.attendees({ count: 3 }) -> "3 attendees"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Wrap app in `<I18nProvider locale="en">` after calling `loadLocale('en')`.
|
|
44
|
+
|
|
45
|
+
## Plural Rules
|
|
46
|
+
|
|
47
|
+
`{{singular|plural}}` selected by preceding number param. For complex plural languages (e.g. Arabic): `{{one|two|many}}`.
|
|
48
|
+
|
|
49
|
+
## Parameterized Translations
|
|
50
|
+
|
|
51
|
+
Types are inferred from base locale: `{name:string}`, `{count:number}`. TypeScript enforces correct params at call sites.
|
|
52
|
+
|
|
53
|
+
## Organization
|
|
54
|
+
|
|
55
|
+
Organize by feature (common, auth, events), one level deep. Avoid deep nesting -- it makes keys verbose.
|
|
56
|
+
|
|
57
|
+
## Anti-Patterns
|
|
58
|
+
|
|
59
|
+
1. **Hardcoded strings** -- Every user-visible string goes through `LL()`.
|
|
60
|
+
2. **Missing translations** -- Caught at compile time, but only if `tsc --noEmit` runs in CI.
|
|
61
|
+
3. **HTML in translations** -- Use component composition instead.
|
|
62
|
+
4. **Dynamic key access** -- `LL()[dynamicKey]()` bypasses type safety. Use conditional rendering.
|
|
63
|
+
5. **Forgetting loadLocale** -- Must call before rendering or get runtime errors.
|
|
64
|
+
6. **Over-splitting namespaces** -- Group by feature, not by component.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ct-i18n-typesafe",
|
|
3
|
+
"description": "Typesafe internationalization with typesafe-i18n",
|
|
4
|
+
"defaultMappings": {
|
|
5
|
+
"src/locales": "ct-i18n-typesafe",
|
|
6
|
+
"src/i18n": "ct-i18n-typesafe"
|
|
7
|
+
},
|
|
8
|
+
"fileExtensions": [],
|
|
9
|
+
"skillRules": {
|
|
10
|
+
"ct-i18n-typesafe": {
|
|
11
|
+
"description": "Typesafe-i18n internationalization patterns",
|
|
12
|
+
"priority": 6,
|
|
13
|
+
"triggers": {
|
|
14
|
+
"keywords": ["i18n", "translation", "locale", "typesafe-i18n", "localization", "translate"],
|
|
15
|
+
"keywordPatterns": ["\\bi18n\\b", "\\blocale\\b", "\\btranslat"],
|
|
16
|
+
"pathPatterns": ["**/locales/**", "**/i18n/**"],
|
|
17
|
+
"intentPatterns": [
|
|
18
|
+
"(?:add|create).*(?:translation|text|locale)",
|
|
19
|
+
"(?:translat).*(?:key|string)"
|
|
20
|
+
],
|
|
21
|
+
"contentPatterns": ["useI18nContext", "LL\\.", "baseLocale", "Locales"]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ct-protobuf-contracts
|
|
3
|
+
description: Protocol Buffer definitions and code generation for frontend/backend contracts
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Protobuf Contracts
|
|
7
|
+
|
|
8
|
+
Proto3 contracts between frontend (TypeScript via protoc-gen-ts) and backend (Rust via prost).
|
|
9
|
+
|
|
10
|
+
## Conventions
|
|
11
|
+
|
|
12
|
+
- PascalCase messages, snake_case fields.
|
|
13
|
+
- Fields 1-15 cost 1 byte -- reserve for frequent fields.
|
|
14
|
+
- Enums always start with `UNSPECIFIED = 0`.
|
|
15
|
+
- Package with versioning: `package myapp.v1;`
|
|
16
|
+
|
|
17
|
+
## Message Patterns
|
|
18
|
+
|
|
19
|
+
```protobuf
|
|
20
|
+
message UserProfile {
|
|
21
|
+
string id = 1;
|
|
22
|
+
string email = 2;
|
|
23
|
+
string display_name = 3;
|
|
24
|
+
optional string avatar_url = 4;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Request/Response pairs
|
|
28
|
+
message GetUserRequest { string user_id = 1; }
|
|
29
|
+
message GetUserResponse { UserProfile user = 1; }
|
|
30
|
+
|
|
31
|
+
// Pagination
|
|
32
|
+
message ListEventsRequest { int32 page_size = 1; string page_token = 2; }
|
|
33
|
+
message ListEventsResponse { repeated Event events = 1; string next_page_token = 2; int32 total_count = 3; }
|
|
34
|
+
|
|
35
|
+
// Reserved fields -- never reuse removed field numbers
|
|
36
|
+
message Event {
|
|
37
|
+
reserved 3, 8;
|
|
38
|
+
reserved "old_field";
|
|
39
|
+
string id = 1;
|
|
40
|
+
string name = 2;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Service Definitions
|
|
45
|
+
|
|
46
|
+
```protobuf
|
|
47
|
+
service UserService {
|
|
48
|
+
rpc GetUser(GetUserRequest) returns (GetUserResponse);
|
|
49
|
+
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
|
|
50
|
+
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Code Generation
|
|
55
|
+
|
|
56
|
+
Frontend: `buf generate --template buf.gen.ts.yaml`
|
|
57
|
+
Backend (build.rs):
|
|
58
|
+
```rust
|
|
59
|
+
prost_build::Config::new()
|
|
60
|
+
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
|
|
61
|
+
.compile_protos(&["proto/user.proto"], &["proto/"])?;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Versioning Rules
|
|
65
|
+
|
|
66
|
+
- Never remove/renumber fields -- use `reserved`.
|
|
67
|
+
- Never change field types -- add a new field.
|
|
68
|
+
- Adding fields and enum values is safe.
|
|
69
|
+
- Run `buf breaking --against .git#branch=main` before merging.
|
|
70
|
+
|
|
71
|
+
## Anti-Patterns
|
|
72
|
+
|
|
73
|
+
1. **Reusing field numbers** -- Causes data corruption. Always reserve removed numbers.
|
|
74
|
+
2. **Deep nesting** -- Hurts readability and evolution. Flatten where practical.
|
|
75
|
+
3. **Missing UNSPECIFIED** -- Proto3 default is 0; without UNSPECIFIED it maps to a real value.
|
|
76
|
+
4. **Proto2 syntax** -- Always proto3 for new projects.
|
|
77
|
+
5. **Skipping buf lint** -- Inconsistent naming compounds. Lint early.
|
|
78
|
+
6. **Large messages** -- Not for multi-MB payloads. Use streaming/chunking.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ct-protobuf-contracts",
|
|
3
|
+
"description": "Protobuf contracts between frontend and backend",
|
|
4
|
+
"defaultMappings": {
|
|
5
|
+
"proto": "ct-protobuf-contracts"
|
|
6
|
+
},
|
|
7
|
+
"fileExtensions": ["proto"],
|
|
8
|
+
"skillRules": {
|
|
9
|
+
"ct-protobuf-contracts": {
|
|
10
|
+
"description": "Protocol Buffer definitions and code generation",
|
|
11
|
+
"priority": 8,
|
|
12
|
+
"triggers": {
|
|
13
|
+
"keywords": ["protobuf", "proto", "protocol buffer", "grpc", "prost", "protoc", "buf"],
|
|
14
|
+
"keywordPatterns": ["\\bproto\\b", "\\bprotobuf\\b", "\\bprost\\b", "\\bbuf\\b"],
|
|
15
|
+
"pathPatterns": ["**/*.proto", "**/proto/**", "**/*.pb.ts"],
|
|
16
|
+
"intentPatterns": [
|
|
17
|
+
"(?:define|create|add).*(?:message|service|rpc|contract)",
|
|
18
|
+
"(?:proto|protobuf).*(?:schema|type|definition)"
|
|
19
|
+
],
|
|
20
|
+
"contentPatterns": ["syntax.*proto3", "message\\s+\\w+", "service\\s+\\w+", "rpc\\s+\\w+"]
|
|
21
|
+
},
|
|
22
|
+
"relatedSkills": ["ct-rust-wasm-patterns"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ct-rust-wasm-patterns
|
|
3
|
+
description: Rust WASM patterns for Cloudflare Workers with worker-rs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rust WASM Patterns (Cloudflare Workers)
|
|
7
|
+
|
|
8
|
+
Target: `wasm32-unknown-unknown`. Crate type: `cdylib`. Always set `console_error_panic_hook`.
|
|
9
|
+
|
|
10
|
+
## Routing and Handlers
|
|
11
|
+
|
|
12
|
+
```rust
|
|
13
|
+
#[event(fetch)]
|
|
14
|
+
async fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> {
|
|
15
|
+
console_error_panic_hook::set_once();
|
|
16
|
+
Router::new()
|
|
17
|
+
.get_async("/api/users/:id", get_user)
|
|
18
|
+
.post_async("/api/users", create_user)
|
|
19
|
+
.run(req, env).await
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async fn get_user(req: Request, ctx: RouteContext<()>) -> Result<Response> {
|
|
23
|
+
let id = ctx.param("id").ok_or_else(|| Error::RustError("Missing id".into()))?;
|
|
24
|
+
let db = ctx.env.d1("DB")?;
|
|
25
|
+
match db.prepare("SELECT * FROM users WHERE id = ?1").bind(&[id.into()])?.first::<User>(None).await? {
|
|
26
|
+
Some(user) => Response::from_json(&user),
|
|
27
|
+
None => Response::error("Not found", 404),
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Error Handling
|
|
33
|
+
|
|
34
|
+
Define a custom error type converting to `worker::Error`:
|
|
35
|
+
|
|
36
|
+
```rust
|
|
37
|
+
#[derive(Debug)]
|
|
38
|
+
pub enum AppError { NotFound(String), BadRequest(String), Internal(String), Unauthorized }
|
|
39
|
+
|
|
40
|
+
impl From<AppError> for worker::Error {
|
|
41
|
+
fn from(e: AppError) -> Self { worker::Error::RustError(format!("{:?}", e)) }
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Env Bindings
|
|
46
|
+
|
|
47
|
+
```rust
|
|
48
|
+
let db = env.d1("DB")?; // D1
|
|
49
|
+
let kv = env.kv("MY_KV")?; // KV
|
|
50
|
+
let bucket = env.bucket("MY_BUCKET")?; // R2
|
|
51
|
+
let key = env.secret("API_KEY")?.to_string(); // Secrets
|
|
52
|
+
let val = env.var("CONFIG_VAR")?.to_string(); // Vars
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Serde Conventions
|
|
56
|
+
|
|
57
|
+
```rust
|
|
58
|
+
#[derive(Serialize, Deserialize, Debug)]
|
|
59
|
+
pub struct User {
|
|
60
|
+
pub id: String,
|
|
61
|
+
pub email: String,
|
|
62
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
63
|
+
pub avatar_url: Option<String>,
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Parse body: `let body: CreateUserRequest = req.json().await?;`
|
|
68
|
+
|
|
69
|
+
## Anti-Patterns
|
|
70
|
+
|
|
71
|
+
1. **Panics in production** -- Always `Result`, never `unwrap()`/`expect()`.
|
|
72
|
+
2. **Blocking ops** -- WASM is single-threaded. All I/O must be `async`.
|
|
73
|
+
3. **Large binaries** -- Minimal deps, use `wasm-opt`, target <1MB compressed.
|
|
74
|
+
4. **Buffering large files** -- 128MB memory limit. Stream, don't buffer.
|
|
75
|
+
5. **Raw SQL concatenation** -- Always `bind()` prepared statements.
|
|
76
|
+
6. **Missing CORS** -- Handle OPTIONS preflight for API workers.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ct-rust-wasm-patterns",
|
|
3
|
+
"description": "Rust WASM for Cloudflare Workers with worker-rs",
|
|
4
|
+
"defaultMappings": {
|
|
5
|
+
"worker": "ct-rust-wasm-patterns",
|
|
6
|
+
"worker/src": "ct-rust-wasm-patterns"
|
|
7
|
+
},
|
|
8
|
+
"fileExtensions": ["rs", "toml"],
|
|
9
|
+
"skillRules": {
|
|
10
|
+
"ct-rust-wasm-patterns": {
|
|
11
|
+
"description": "Rust WASM patterns for Cloudflare Workers",
|
|
12
|
+
"priority": 8,
|
|
13
|
+
"triggers": {
|
|
14
|
+
"keywords": ["rust", "wasm", "worker", "cargo", "wasm-bindgen", "worker-rs", "wrangler"],
|
|
15
|
+
"keywordPatterns": ["\\brust\\b", "\\bwasm\\b", "\\bcargo\\b", "\\bworker\\b"],
|
|
16
|
+
"pathPatterns": ["**/*.rs", "**/Cargo.toml", "**/worker/**"],
|
|
17
|
+
"intentPatterns": [
|
|
18
|
+
"(?:create|build|add).*(?:endpoint|route|handler|worker)",
|
|
19
|
+
"(?:rust|wasm).*(?:function|module|binding)"
|
|
20
|
+
],
|
|
21
|
+
"contentPatterns": ["worker::.*", "wasm_bindgen", "pub async fn", "Result<", "#\\[worker::"]
|
|
22
|
+
},
|
|
23
|
+
"relatedSkills": ["ct-protobuf-contracts", "ct-cloudflare-d1-kv"]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ct-solidjs-patterns
|
|
3
|
+
description: SolidJS reactivity, signals, effects, and component patterns
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SolidJS Patterns
|
|
7
|
+
|
|
8
|
+
Components run **once**. Only reactive expressions re-execute. This is not React.
|
|
9
|
+
|
|
10
|
+
## Primitives
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
const [count, setCount] = createSignal(0); // always call: count()
|
|
14
|
+
const doubled = createMemo(() => count() * 2); // cached derived value
|
|
15
|
+
createEffect(() => console.log(count())); // side effect on change
|
|
16
|
+
const [data] = createResource(userId, fetchUser); // async: data(), data.loading, data.error
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Control Flow
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<Show when={ok()} fallback={<Fallback />}><Content /></Show>
|
|
23
|
+
<For each={items()}>{(item, i) => <div>{item.name} at {i()}</div>}</For>
|
|
24
|
+
<Switch fallback={<Default />}>
|
|
25
|
+
<Match when={state() === "loading"}><Spinner /></Match>
|
|
26
|
+
<Match when={state() === "error"}><Error /></Match>
|
|
27
|
+
</Switch>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Props
|
|
31
|
+
|
|
32
|
+
**CRITICAL: Never destructure props.** Destructuring breaks reactivity.
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
// WRONG -- reads once, never updates
|
|
36
|
+
const Comp = ({ name }) => <div>{name}</div>;
|
|
37
|
+
|
|
38
|
+
// CORRECT
|
|
39
|
+
const Comp = (props) => <div>{props.name}</div>;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`mergeProps` for defaults, `splitProps` to separate groups:
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
const merged = mergeProps({ color: "blue" }, props);
|
|
46
|
+
const [local, others] = splitProps(props, ["class", "style"]);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Stores
|
|
50
|
+
|
|
51
|
+
`createStore` for deep nested state with path-based updates:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
const [state, setState] = createStore({ user: { settings: { theme: "dark" } } });
|
|
55
|
+
setState("user", "settings", "theme", "light");
|
|
56
|
+
setState("items", items => [...items, newItem]);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Anti-Patterns
|
|
60
|
+
|
|
61
|
+
1. **Destructuring props** -- Breaks reactivity. Always `props.x`.
|
|
62
|
+
2. **createEffect for derived values** -- Use `createMemo` instead.
|
|
63
|
+
3. **Treating it like React** -- No re-render cycle. Only reactive expressions update.
|
|
64
|
+
4. **Manual keys in For** -- `<For>` is keyed by reference automatically.
|
|
65
|
+
5. **Signals outside reactive context** -- `count()` at top level captures once. Wrap in JSX or effects.
|
|
66
|
+
6. **Forgetting to call signals** -- `count` is a getter, `count()` is the value.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ct-solidjs-patterns",
|
|
3
|
+
"description": "SolidJS reactivity, components, and resource patterns",
|
|
4
|
+
"defaultMappings": {
|
|
5
|
+
"src/components": "ct-solidjs-patterns",
|
|
6
|
+
"src/hooks": "ct-solidjs-patterns",
|
|
7
|
+
"src/pages": "ct-solidjs-patterns"
|
|
8
|
+
},
|
|
9
|
+
"fileExtensions": ["tsx", "jsx"],
|
|
10
|
+
"skillRules": {
|
|
11
|
+
"ct-solidjs-patterns": {
|
|
12
|
+
"description": "SolidJS reactivity, signals, effects, and component patterns",
|
|
13
|
+
"priority": 8,
|
|
14
|
+
"triggers": {
|
|
15
|
+
"keywords": [
|
|
16
|
+
"solid",
|
|
17
|
+
"solidjs",
|
|
18
|
+
"signal",
|
|
19
|
+
"createSignal",
|
|
20
|
+
"createEffect",
|
|
21
|
+
"createMemo",
|
|
22
|
+
"createResource",
|
|
23
|
+
"Show",
|
|
24
|
+
"For",
|
|
25
|
+
"Switch",
|
|
26
|
+
"Match"
|
|
27
|
+
],
|
|
28
|
+
"keywordPatterns": [
|
|
29
|
+
"\\bcreateSignal\\b",
|
|
30
|
+
"\\bcreateEffect\\b",
|
|
31
|
+
"\\bcreateResource\\b",
|
|
32
|
+
"\\bSolid\\b"
|
|
33
|
+
],
|
|
34
|
+
"pathPatterns": ["**/*.tsx", "**/*.jsx", "**/components/**"],
|
|
35
|
+
"intentPatterns": [
|
|
36
|
+
"(?:create|build|add).*(?:component|page|view)",
|
|
37
|
+
"(?:reactive|signal|effect|memo)"
|
|
38
|
+
],
|
|
39
|
+
"contentPatterns": [
|
|
40
|
+
"createSignal",
|
|
41
|
+
"createEffect",
|
|
42
|
+
"createMemo",
|
|
43
|
+
"createResource",
|
|
44
|
+
"<Show",
|
|
45
|
+
"<For",
|
|
46
|
+
"<Switch"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"relatedSkills": ["ct-vanilla-extract-patterns", "ct-testing-patterns"]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|