rex-claude 1.0.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/README.md +163 -0
- package/activity/activity.jsonl +401 -0
- package/activity/config.lua +3 -0
- package/activity/init.lua +49 -0
- package/dist/cli.js +204 -0
- package/dotfiles/CLAUDE.md +136 -0
- package/dotfiles/commands/clean.md +8 -0
- package/dotfiles/commands/doc.md +8 -0
- package/dotfiles/commands/review.md +15 -0
- package/dotfiles/commands/scaffold.md +11 -0
- package/dotfiles/commands/test.md +11 -0
- package/dotfiles/docs/cloudflare.md +62 -0
- package/dotfiles/docs/nextjs.md +79 -0
- package/dotfiles/docs/react.md +63 -0
- package/dotfiles/docs/tailwind.md +45 -0
- package/dotfiles/docs/telegram-bot.md +55 -0
- package/dotfiles/rules/api-design.md +63 -0
- package/dotfiles/rules/defensive-engineering.md +42 -0
- package/dotfiles/rules/docs-first.md +47 -0
- package/dotfiles/rules/frontend.md +41 -0
- package/dotfiles/rules/git-workflow.md +57 -0
- package/dotfiles/rules/never-assume.md +39 -0
- package/dotfiles/rules/security.md +46 -0
- package/dotfiles/rules/testing.md +33 -0
- package/dotfiles/settings.json +69 -0
- package/dotfiles/skills/build-validate/SKILL.md +16 -0
- package/dotfiles/skills/code-review/SKILL.md +18 -0
- package/dotfiles/skills/context-loader/SKILL.md +25 -0
- package/dotfiles/skills/debug-assist/SKILL.md +26 -0
- package/dotfiles/skills/deploy-checklist/SKILL.md +54 -0
- package/dotfiles/skills/dstudio-design-system/SKILL.md +120 -0
- package/dotfiles/skills/figma-workflow/SKILL.md +23 -0
- package/dotfiles/skills/fix-issue/SKILL.md +43 -0
- package/dotfiles/skills/one-shot/SKILL.md +18 -0
- package/dotfiles/skills/pr-review-loop/SKILL.md +41 -0
- package/dotfiles/skills/project-init/SKILL.md +45 -0
- package/dotfiles/skills/research/SKILL.md +17 -0
- package/dotfiles/skills/spec-interview/SKILL.md +20 -0
- package/dotfiles/skills/token-guard/SKILL.md +26 -0
- package/dotfiles/templates/CLAUDE.md.template +39 -0
- package/memory/package.json +24 -0
- package/memory/src/embed.ts +23 -0
- package/memory/src/ingest.ts +257 -0
- package/memory/src/search.ts +32 -0
- package/memory/src/server.ts +69 -0
- package/memory/tsconfig.json +14 -0
- package/package.json +39 -0
- package/tmux/.tmux.conf +73 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync } from 'fs';
|
|
3
|
+
import { join, resolve, dirname } from 'path';
|
|
4
|
+
import { homedir, platform } from 'os';
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const HOME = homedir();
|
|
9
|
+
const CLAUDE_DIR = join(HOME, '.claude');
|
|
10
|
+
const REX_MEMORY_DIR = join(HOME, '.rex-memory');
|
|
11
|
+
const PKG_DIR = resolve(__dirname, '..');
|
|
12
|
+
const GREEN = '\x1b[32m';
|
|
13
|
+
const YELLOW = '\x1b[33m';
|
|
14
|
+
const RED = '\x1b[31m';
|
|
15
|
+
const CYAN = '\x1b[36m';
|
|
16
|
+
const BOLD = '\x1b[1m';
|
|
17
|
+
const DIM = '\x1b[2m';
|
|
18
|
+
const RESET = '\x1b[0m';
|
|
19
|
+
function log(icon, msg) {
|
|
20
|
+
console.log(` ${icon} ${msg}`);
|
|
21
|
+
}
|
|
22
|
+
function ok(msg) { log(`${GREEN}✓${RESET}`, msg); }
|
|
23
|
+
function warn(msg) { log(`${YELLOW}⚠${RESET}`, msg); }
|
|
24
|
+
function info(msg) { log(`${CYAN}→${RESET}`, msg); }
|
|
25
|
+
function fail(msg) { log(`${RED}✗${RESET}`, msg); }
|
|
26
|
+
function deepMerge(target, source) {
|
|
27
|
+
const result = { ...target };
|
|
28
|
+
for (const key of Object.keys(source)) {
|
|
29
|
+
if (result[key] && typeof result[key] === 'object' && !Array.isArray(result[key]) &&
|
|
30
|
+
typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
31
|
+
result[key] = deepMerge(result[key], source[key]);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
result[key] = source[key];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
function mergeArraysByMatcher(existing, incoming) {
|
|
40
|
+
const result = [...existing];
|
|
41
|
+
for (const item of incoming) {
|
|
42
|
+
const idx = result.findIndex((e) => e.matcher === item.matcher);
|
|
43
|
+
if (idx === -1) {
|
|
44
|
+
result.push(item);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
result[idx] = item;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
function mergeSettings(existingPath, rexSettingsPath) {
|
|
53
|
+
let existing = {};
|
|
54
|
+
if (existsSync(existingPath)) {
|
|
55
|
+
try {
|
|
56
|
+
existing = JSON.parse(readFileSync(existingPath, 'utf-8'));
|
|
57
|
+
}
|
|
58
|
+
catch { /* empty */ }
|
|
59
|
+
}
|
|
60
|
+
const rex = JSON.parse(readFileSync(rexSettingsPath, 'utf-8'));
|
|
61
|
+
// Deep merge simple objects
|
|
62
|
+
const merged = deepMerge(existing, rex);
|
|
63
|
+
// Smart merge hooks: merge arrays by matcher
|
|
64
|
+
if (rex.hooks) {
|
|
65
|
+
merged.hooks = { ...existing.hooks };
|
|
66
|
+
for (const hookType of Object.keys(rex.hooks)) {
|
|
67
|
+
if (Array.isArray(rex.hooks[hookType])) {
|
|
68
|
+
merged.hooks[hookType] = mergeArraysByMatcher(existing.hooks?.[hookType] || [], rex.hooks[hookType]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Add mcpServers.rex-memory with correct path
|
|
73
|
+
if (!merged.mcpServers)
|
|
74
|
+
merged.mcpServers = {};
|
|
75
|
+
merged.mcpServers['rex-memory'] = {
|
|
76
|
+
command: 'node',
|
|
77
|
+
args: [join(REX_MEMORY_DIR, 'dist', 'server.js')]
|
|
78
|
+
};
|
|
79
|
+
return merged;
|
|
80
|
+
}
|
|
81
|
+
function backup(filePath) {
|
|
82
|
+
if (existsSync(filePath)) {
|
|
83
|
+
const backupPath = `${filePath}.backup-${Date.now()}`;
|
|
84
|
+
copyFileSync(filePath, backupPath);
|
|
85
|
+
return backupPath;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
function main() {
|
|
90
|
+
console.log(`\n${BOLD}${CYAN} ╔══════════════════════════════╗${RESET}`);
|
|
91
|
+
console.log(`${BOLD}${CYAN} ║ REX — Setup & Install ║${RESET}`);
|
|
92
|
+
console.log(`${BOLD}${CYAN} ╚══════════════════════════════╝${RESET}\n`);
|
|
93
|
+
const os = platform();
|
|
94
|
+
if (os !== 'darwin' && os !== 'linux') {
|
|
95
|
+
fail(`OS non supporté : ${os}. REX supporte macOS et Linux.`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
info(`OS détecté : ${os === 'darwin' ? 'macOS' : 'Linux'}`);
|
|
99
|
+
// 1. Create ~/.claude/ if missing
|
|
100
|
+
if (!existsSync(CLAUDE_DIR)) {
|
|
101
|
+
mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
102
|
+
ok('Créé ~/.claude/');
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
ok('~/.claude/ existe');
|
|
106
|
+
}
|
|
107
|
+
// 2. Copy dotfiles → ~/.claude/
|
|
108
|
+
const dotfilesDir = join(PKG_DIR, 'dotfiles');
|
|
109
|
+
const dirs = ['rules', 'skills', 'docs', 'commands', 'templates', 'agents'];
|
|
110
|
+
for (const dir of dirs) {
|
|
111
|
+
const src = join(dotfilesDir, dir);
|
|
112
|
+
if (existsSync(src)) {
|
|
113
|
+
const dest = join(CLAUDE_DIR, dir);
|
|
114
|
+
cpSync(src, dest, { recursive: true, force: true });
|
|
115
|
+
ok(`Copié dotfiles/${dir}/ → ~/.claude/${dir}/`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Copy CLAUDE.md
|
|
119
|
+
const claudeMdSrc = join(dotfilesDir, 'CLAUDE.md');
|
|
120
|
+
if (existsSync(claudeMdSrc)) {
|
|
121
|
+
const claudeMdDest = join(CLAUDE_DIR, 'CLAUDE.md');
|
|
122
|
+
if (existsSync(claudeMdDest)) {
|
|
123
|
+
const b = backup(claudeMdDest);
|
|
124
|
+
warn(`CLAUDE.md existant sauvegardé → ${b}`);
|
|
125
|
+
}
|
|
126
|
+
copyFileSync(claudeMdSrc, claudeMdDest);
|
|
127
|
+
ok('Copié CLAUDE.md → ~/.claude/CLAUDE.md');
|
|
128
|
+
}
|
|
129
|
+
// 3. Merge settings.json
|
|
130
|
+
const settingsPath = join(CLAUDE_DIR, 'settings.json');
|
|
131
|
+
const rexSettingsPath = join(dotfilesDir, 'settings.json');
|
|
132
|
+
if (existsSync(rexSettingsPath)) {
|
|
133
|
+
const merged = mergeSettings(settingsPath, rexSettingsPath);
|
|
134
|
+
writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + '\n');
|
|
135
|
+
ok('Mergé settings.json (hooks, plugins, env, mcpServers)');
|
|
136
|
+
}
|
|
137
|
+
// 4. Copy memory/ → ~/.rex-memory/
|
|
138
|
+
const memorySrc = join(PKG_DIR, 'memory');
|
|
139
|
+
if (existsSync(memorySrc)) {
|
|
140
|
+
mkdirSync(REX_MEMORY_DIR, { recursive: true });
|
|
141
|
+
for (const sub of ['src', 'package.json', 'package-lock.json', 'tsconfig.json']) {
|
|
142
|
+
const s = join(memorySrc, sub);
|
|
143
|
+
if (existsSync(s)) {
|
|
144
|
+
const d = join(REX_MEMORY_DIR, sub);
|
|
145
|
+
cpSync(s, d, { recursive: true, force: true });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
mkdirSync(join(REX_MEMORY_DIR, 'db'), { recursive: true });
|
|
149
|
+
ok('Copié memory/ → ~/.rex-memory/');
|
|
150
|
+
// 5. npm install && npm run build in ~/.rex-memory/
|
|
151
|
+
info('Installation des dépendances rex-memory...');
|
|
152
|
+
try {
|
|
153
|
+
execSync('npm install --production=false', { cwd: REX_MEMORY_DIR, stdio: 'pipe' });
|
|
154
|
+
execSync('npm run build', { cwd: REX_MEMORY_DIR, stdio: 'pipe' });
|
|
155
|
+
ok('rex-memory compilé avec succès');
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
fail(`Erreur compilation rex-memory: ${e.message?.split('\n')[0]}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// 6. Copy tmux config
|
|
162
|
+
const tmuxSrc = join(PKG_DIR, 'tmux', '.tmux.conf');
|
|
163
|
+
if (existsSync(tmuxSrc)) {
|
|
164
|
+
const tmuxDest = join(HOME, '.tmux.conf');
|
|
165
|
+
if (existsSync(tmuxDest)) {
|
|
166
|
+
const b = backup(tmuxDest);
|
|
167
|
+
warn(`~/.tmux.conf existant sauvegardé → ${b}`);
|
|
168
|
+
}
|
|
169
|
+
copyFileSync(tmuxSrc, tmuxDest);
|
|
170
|
+
ok('Copié tmux/.tmux.conf → ~/.tmux.conf');
|
|
171
|
+
}
|
|
172
|
+
// 7. Copy activity/ (Hammerspoon)
|
|
173
|
+
if (os === 'darwin') {
|
|
174
|
+
const activitySrc = join(PKG_DIR, 'activity');
|
|
175
|
+
if (existsSync(activitySrc)) {
|
|
176
|
+
const hsDir = join(HOME, '.hammerspoon');
|
|
177
|
+
if (existsSync(hsDir)) {
|
|
178
|
+
cpSync(activitySrc, hsDir, { recursive: true, force: true });
|
|
179
|
+
ok('Copié activity/ → ~/.hammerspoon/');
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
warn('Hammerspoon non installé — activity logger ignoré');
|
|
183
|
+
info(' brew install --cask hammerspoon');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// 8. Check Ollama
|
|
188
|
+
try {
|
|
189
|
+
execSync('which ollama', { stdio: 'pipe' });
|
|
190
|
+
ok('Ollama détecté');
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
warn('Ollama non installé (optionnel pour embeddings)');
|
|
194
|
+
info(' brew install ollama && ollama pull nomic-embed-text');
|
|
195
|
+
}
|
|
196
|
+
// Summary
|
|
197
|
+
console.log(`\n${BOLD}${GREEN} ══════════════════════════════${RESET}`);
|
|
198
|
+
console.log(`${BOLD}${GREEN} REX installé avec succès !${RESET}`);
|
|
199
|
+
console.log(`${DIM} ~/.claude/ → rules, skills, docs, commands${RESET}`);
|
|
200
|
+
console.log(`${DIM} ~/.rex-memory/ → MCP memory server${RESET}`);
|
|
201
|
+
console.log(`${DIM} ~/.tmux.conf → tmux config${RESET}`);
|
|
202
|
+
console.log(`${BOLD}${GREEN} ══════════════════════════════${RESET}\n`);
|
|
203
|
+
}
|
|
204
|
+
main();
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# REX
|
|
2
|
+
|
|
3
|
+
Tu es **REX**, l'assistant dev de Kevin (D-Studio). Réponds toujours en tant que REX.
|
|
4
|
+
|
|
5
|
+
# TODO
|
|
6
|
+
|
|
7
|
+
- [ ] Gateway Telegram : skill/script pour envoyer des messages via Garry ou Milo bot (BOT_TOKEN + CHAT_ID en env, appel HTTP Telegram Bot API). Permet à REX de notifier Kevin (lien PR, deploy done, etc.) via `/notify "message"`.
|
|
8
|
+
|
|
9
|
+
# Global Instructions
|
|
10
|
+
|
|
11
|
+
## Identity & Authorship
|
|
12
|
+
|
|
13
|
+
- NEVER add "Co-Authored-By" lines in commits. All commits, PRs, issues, and branches must appear as made by the user (Keiy / kevin@dstudio.company) only.
|
|
14
|
+
- NEVER mention Claude, AI, or any assistant in PR descriptions, commit messages, or issue comments.
|
|
15
|
+
|
|
16
|
+
## Git & GitHub Workflow
|
|
17
|
+
|
|
18
|
+
- Write concise, descriptive commit messages focused on the "why", not the "what".
|
|
19
|
+
- Use conventional commit style when the project uses it (feat:, fix:, refactor:, etc.).
|
|
20
|
+
- ALWAYS create a new branch for changes unless told otherwise. Branch names: kebab-case, descriptive (e.g., `fix/auth-token-refresh`, `feat/add-oauth`).
|
|
21
|
+
- Before committing, run the project's linter/formatter if one exists.
|
|
22
|
+
- See `~/.claude/rules/git-workflow.md` for full conventions.
|
|
23
|
+
|
|
24
|
+
## PR Review Loop
|
|
25
|
+
|
|
26
|
+
- After creating a PR, pull automated review comments from GitHub Copilot and Gemini Code Assist:
|
|
27
|
+
- `gh pr view <number> --comments`
|
|
28
|
+
- `gh api repos/{owner}/{repo}/pulls/{number}/comments`
|
|
29
|
+
- Evaluate each comment: fix what's valid, dismiss what's not.
|
|
30
|
+
- Push fixes, then notify the user to review the final diff between v1 and v2.
|
|
31
|
+
|
|
32
|
+
## Code Quality
|
|
33
|
+
|
|
34
|
+
- ALWAYS provide a way to verify work: run tests, build, lint, or take a screenshot for UI changes.
|
|
35
|
+
- Prefer editing existing files over creating new ones to avoid file bloat.
|
|
36
|
+
- Follow existing patterns in the codebase. Read before writing.
|
|
37
|
+
- Do not add unnecessary comments, docstrings, or type annotations to code you didn't change.
|
|
38
|
+
- Do not over-engineer. Only make changes that are directly requested or clearly necessary.
|
|
39
|
+
|
|
40
|
+
## Task Approach
|
|
41
|
+
|
|
42
|
+
- For non-trivial tasks: explore first (read relevant code), plan, then implement.
|
|
43
|
+
- Break large problems into smaller chunks. One focused task per conversation when possible.
|
|
44
|
+
- If stuck after 2 failed attempts at the same approach, stop and ask the user rather than brute-forcing.
|
|
45
|
+
- Use subagents for research-heavy tasks to keep the main context clean.
|
|
46
|
+
|
|
47
|
+
## Context Management
|
|
48
|
+
|
|
49
|
+
- Start fresh conversations (`/clear`) between unrelated tasks.
|
|
50
|
+
- When context gets long, use `/compact` to preserve only what matters.
|
|
51
|
+
- Scope investigations narrowly. Don't read hundreds of files without purpose.
|
|
52
|
+
|
|
53
|
+
## Contexte Kevin
|
|
54
|
+
|
|
55
|
+
- Développeur full-stack solo (D-Studio)
|
|
56
|
+
- Langue : français par défaut dans les réponses
|
|
57
|
+
- Stack : TypeScript/Node, CakePHP, Angular/Ionic, Flutter, React/Next.js
|
|
58
|
+
- Comptes IA : Claude Max (Opus+Sonnet), Claude Pro, ChatGPT Plus
|
|
59
|
+
- Outils : GitHub, Monday, n8n, Bitwarden
|
|
60
|
+
|
|
61
|
+
## Modèle switching
|
|
62
|
+
|
|
63
|
+
- **Opus** → architecture, conception, missions complexes
|
|
64
|
+
- **Sonnet** → code standard, PR, refactoring
|
|
65
|
+
- **Haiku** → tâches répétitives, lecture de fichiers, quick fixes
|
|
66
|
+
|
|
67
|
+
## Checklist obligatoire AVANT chaque feature (CRITICAL)
|
|
68
|
+
|
|
69
|
+
Avant d'écrire du code, cocher chacun de ces 7 points :
|
|
70
|
+
|
|
71
|
+
1. **Pagination** : liste > 20 items ? → limit+offset+total à l'API, Load More côté front
|
|
72
|
+
2. **Fallback/erreur** : API vide ? null ? 500 ? timeout ? → TOUJOURS gérer tous les cas
|
|
73
|
+
3. **État vide** : 0 résultat ? → TOUJOURS afficher un empty state
|
|
74
|
+
4. **Chargement** : pendant le fetch ? → TOUJOURS afficher un loading state
|
|
75
|
+
5. **Scalabilité** : 10x plus d'utilisateurs/items/requêtes ? → index DB, chunking, cache
|
|
76
|
+
6. **Sync front/back** : bon endpoint, bons params, bonne forme de réponse ? → TOUJOURS vérifier
|
|
77
|
+
7. **Effets de bord** : qui d'autre lit cet état ? → grep les consumers
|
|
78
|
+
|
|
79
|
+
See `~/.claude/rules/defensive-engineering.md` for full details.
|
|
80
|
+
|
|
81
|
+
## Documentation-First (CRITICAL)
|
|
82
|
+
|
|
83
|
+
Avant de coder avec un framework/lib, lire `~/.claude/docs/{framework}.md` si existant, sinon fetcher via Context7.
|
|
84
|
+
Après chaque projet, sauvegarder les patterns/gotchas découverts dans `~/.claude/docs/`.
|
|
85
|
+
IMPORTANT : ne JAMAIS lire les fichiers docs/ au démarrage — uniquement quand le framework est pertinent pour la tâche.
|
|
86
|
+
See `~/.claude/rules/docs-first.md` for details.
|
|
87
|
+
|
|
88
|
+
## Optimisation tokens
|
|
89
|
+
|
|
90
|
+
- Sous-agents pour la recherche lourde (garde le contexte principal propre)
|
|
91
|
+
- `/compact` à ~70% du contexte (auto-compact configuré à 75%)
|
|
92
|
+
- `/clear` entre projets différents
|
|
93
|
+
- Séparer la documentation longue en `spec.md` / `tech.md` / `lessons.md` + `@imports`
|
|
94
|
+
|
|
95
|
+
## Compaction instructions
|
|
96
|
+
When compacting, always preserve:
|
|
97
|
+
- Full list of modified files and their paths
|
|
98
|
+
- Any test/build commands discovered for the current project
|
|
99
|
+
- Active branch name and PR number if in progress
|
|
100
|
+
- Any error messages seen during the session
|
|
101
|
+
- Current task context and user requirements
|
|
102
|
+
|
|
103
|
+
## Testing & Verification
|
|
104
|
+
|
|
105
|
+
Après CHAQUE implémentation, OBLIGATOIRE avant de déclarer "done" :
|
|
106
|
+
|
|
107
|
+
1. `npm run build` (ou commande équivalente) — zéro erreur
|
|
108
|
+
2. Démarrer le dev server, confirmer que l'app charge (au minimum `curl` homepage → 200)
|
|
109
|
+
3. Pour les changements UI : screenshot ou browser automation
|
|
110
|
+
4. Pour SSR/Next.js : surveiller les hydration mismatches
|
|
111
|
+
|
|
112
|
+
Si le projet a une suite de tests, la lancer après les changements.
|
|
113
|
+
Corriger les causes racines, pas les symptômes. Ne jamais supprimer des tests pour les faire passer.
|
|
114
|
+
|
|
115
|
+
See `~/.claude/rules/testing.md` for full testing conventions.
|
|
116
|
+
|
|
117
|
+
## Security
|
|
118
|
+
|
|
119
|
+
- Never commit secrets, API keys, .env files, or credentials.
|
|
120
|
+
- Check for OWASP top 10 vulnerabilities in code you write.
|
|
121
|
+
- If you notice insecure code while working, flag it to the user.
|
|
122
|
+
- SQL : requêtes paramétrées uniquement, jamais de concaténation.
|
|
123
|
+
|
|
124
|
+
See `~/.claude/rules/security.md` for full security rules.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
Rules directory: `~/.claude/rules/`
|
|
129
|
+
- `defensive-engineering.md` — Scale, pagination, rate limits, error handling
|
|
130
|
+
- `api-design.md` — REST conventions, response envelopes, status codes
|
|
131
|
+
- `frontend.md` — Loading/empty/error states, SSR, hydration, forms, a11y
|
|
132
|
+
- `security.md` — OWASP, secrets, SQL injection, XSS, CORS, auth
|
|
133
|
+
- `testing.md` — Test discipline, build verification, mocking
|
|
134
|
+
- `git-workflow.md` — Commit conventions, branching, PR process
|
|
135
|
+
- `never-assume.md` — What never to assume, alternatives, mistake tracking
|
|
136
|
+
- `docs-first.md` — Documentation-first rule, local cache, Context7/SiteMCP usage
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Find and remove dead code, stale TODOs, and unused dependencies.
|
|
2
|
+
|
|
3
|
+
Steps:
|
|
4
|
+
1. Search for unused exports and imports
|
|
5
|
+
2. Find TODO/FIXME/HACK comments older than 30 days (check git blame)
|
|
6
|
+
3. Check for unused dependencies in package.json (use depcheck if available)
|
|
7
|
+
4. Find dead code: unreachable branches, unused functions
|
|
8
|
+
5. Report findings — do NOT auto-delete without confirmation
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Generate or update project documentation.
|
|
2
|
+
|
|
3
|
+
Steps:
|
|
4
|
+
1. Read existing README.md
|
|
5
|
+
2. Analyze project structure, scripts in package.json, environment variables needed
|
|
6
|
+
3. Update or create sections: Setup, Development, Deployment, Environment Variables, Architecture
|
|
7
|
+
4. Keep documentation concise and practical — no filler text
|
|
8
|
+
5. If API routes exist, document them with method, path, params, response shape
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Review the current branch's changes before creating a PR.
|
|
2
|
+
|
|
3
|
+
Steps:
|
|
4
|
+
1. Run `git diff main...HEAD` to see all changes
|
|
5
|
+
2. Check for:
|
|
6
|
+
- Security issues (hardcoded secrets, SQL injection, XSS)
|
|
7
|
+
- Missing error handling (uncaught promises, missing try/catch)
|
|
8
|
+
- Missing loading/empty/error states in UI components
|
|
9
|
+
- Pagination missing on list endpoints
|
|
10
|
+
- TypeScript `any` or `@ts-ignore` without justification
|
|
11
|
+
- Console.log left in production code
|
|
12
|
+
- Unused imports or variables
|
|
13
|
+
3. Run the project's linter if available
|
|
14
|
+
4. Run `npm run build` to verify compilation
|
|
15
|
+
5. Report findings with severity (critical/warning/info) and suggested fixes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Create a new project from the template structure.
|
|
2
|
+
|
|
3
|
+
Usage: /scaffold <project-name> <category>
|
|
4
|
+
Categories: keiy (personal), dstudio (client), bots (telegram)
|
|
5
|
+
|
|
6
|
+
Steps:
|
|
7
|
+
1. Ask for project name and category if not provided
|
|
8
|
+
2. Create directory in ~/Documents/Developer/<category>/<project-name>/
|
|
9
|
+
3. Initialize with: git init, package.json, tsconfig.json, .env.example, .gitignore, .claudeignore
|
|
10
|
+
4. Create CLAUDE.md from ~/.claude/templates/CLAUDE.md.template
|
|
11
|
+
5. Report the created structure
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Run the project's test suite and analyze results.
|
|
2
|
+
|
|
3
|
+
Steps:
|
|
4
|
+
1. Detect test framework (look for vitest.config, jest.config, playwright.config)
|
|
5
|
+
2. Run the appropriate test command
|
|
6
|
+
3. If tests fail:
|
|
7
|
+
- Analyze each failure
|
|
8
|
+
- Identify root cause
|
|
9
|
+
- Suggest fixes (NEVER modify tests to make them pass — fix the code)
|
|
10
|
+
4. Report: total tests, passed, failed, skipped
|
|
11
|
+
5. If no test suite exists, report that and suggest setting one up
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Cloudflare Workers — Doc Cache Local
|
|
2
|
+
|
|
3
|
+
> Dernière mise à jour : 2026-03-03
|
|
4
|
+
|
|
5
|
+
## Workers Basics
|
|
6
|
+
|
|
7
|
+
### Limite clé : 50 subrequests/invocation
|
|
8
|
+
Pour les opérations en batch : chunking + self-invoke pattern.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
export default {
|
|
12
|
+
async fetch(request: Request, env: Env): Promise<Response> {
|
|
13
|
+
const url = new URL(request.url);
|
|
14
|
+
// routing
|
|
15
|
+
if (url.pathname === '/api/items') return handleItems(request, env);
|
|
16
|
+
return new Response('Not found', { status: 404 });
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### D1 (SQLite)
|
|
22
|
+
```ts
|
|
23
|
+
const { results } = await env.DB.prepare('SELECT * FROM users WHERE id = ?')
|
|
24
|
+
.bind(userId)
|
|
25
|
+
.all();
|
|
26
|
+
// TOUJOURS requêtes paramétrées, jamais de concaténation
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### KV
|
|
30
|
+
```ts
|
|
31
|
+
await env.KV.put('key', JSON.stringify(value), { expirationTtl: 3600 });
|
|
32
|
+
const data = await env.KV.get('key', 'json');
|
|
33
|
+
// KV est eventually consistent — pas pour les données critiques temps réel
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Durable Objects
|
|
37
|
+
Pour state persistent + WebSocket — utilisé dans les bots Telegram.
|
|
38
|
+
|
|
39
|
+
## wrangler.toml
|
|
40
|
+
```toml
|
|
41
|
+
name = "my-worker"
|
|
42
|
+
main = "src/index.ts"
|
|
43
|
+
compatibility_date = "2025-01-01"
|
|
44
|
+
|
|
45
|
+
[[d1_databases]]
|
|
46
|
+
binding = "DB"
|
|
47
|
+
database_name = "my-db"
|
|
48
|
+
database_id = "xxx"
|
|
49
|
+
|
|
50
|
+
[[kv_namespaces]]
|
|
51
|
+
binding = "KV"
|
|
52
|
+
id = "xxx"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Gotchas
|
|
56
|
+
|
|
57
|
+
1. **50 subrequests max** — inclut fetch(), D1, KV, tout appel réseau
|
|
58
|
+
2. **10ms CPU time** (free) / 30s (paid) — pas de boucles longues
|
|
59
|
+
3. **KV est eventually consistent** — délai de propagation ~60s
|
|
60
|
+
4. **D1 est en beta** — pas de transactions imbriquées
|
|
61
|
+
5. **CORS** : doit être géré manuellement dans le Worker
|
|
62
|
+
6. **`ctx.waitUntil()`** pour les tâches background après la réponse
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Next.js — Doc Cache Local
|
|
2
|
+
|
|
3
|
+
> Dernière mise à jour : 2026-03-03
|
|
4
|
+
> Version : Next.js 15.x / 16.x (App Router)
|
|
5
|
+
|
|
6
|
+
## App Router Essentials
|
|
7
|
+
|
|
8
|
+
### Route Files
|
|
9
|
+
- `page.tsx` — route UI
|
|
10
|
+
- `layout.tsx` — layout partagé (ne re-render pas à la navigation)
|
|
11
|
+
- `loading.tsx` — Suspense boundary automatique
|
|
12
|
+
- `error.tsx` — error boundary (`'use client'` obligatoire)
|
|
13
|
+
- `not-found.tsx` — 404 page
|
|
14
|
+
- `route.ts` — API route (GET, POST, PUT, DELETE)
|
|
15
|
+
|
|
16
|
+
### Server vs Client Components
|
|
17
|
+
- **Par défaut** : Server Component (pas de state, pas de hooks)
|
|
18
|
+
- `'use client'` en haut du fichier pour un Client Component
|
|
19
|
+
- Server Components peuvent importer Client Components, pas l'inverse
|
|
20
|
+
- Les props passées de Server → Client doivent être sérialisables
|
|
21
|
+
|
|
22
|
+
### Data Fetching (App Router)
|
|
23
|
+
```tsx
|
|
24
|
+
// Server Component — fetch direct, pas de useEffect
|
|
25
|
+
async function Page() {
|
|
26
|
+
const data = await fetch('https://api.example.com/data', {
|
|
27
|
+
cache: 'force-cache', // static (default)
|
|
28
|
+
// cache: 'no-store', // dynamic
|
|
29
|
+
// next: { revalidate: 60 } // ISR
|
|
30
|
+
});
|
|
31
|
+
return <div>{data}</div>;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Server Actions
|
|
36
|
+
```tsx
|
|
37
|
+
'use server'
|
|
38
|
+
|
|
39
|
+
async function createItem(formData: FormData) {
|
|
40
|
+
const name = formData.get('name');
|
|
41
|
+
await db.insert(items).values({ name });
|
|
42
|
+
revalidatePath('/items');
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Gotchas / Pièges courants
|
|
47
|
+
|
|
48
|
+
1. **Hydration mismatch** : ne jamais utiliser `Date.now()`, `Math.random()`, ou `localStorage` dans le render initial — toujours dans `useEffect`
|
|
49
|
+
2. **`useSearchParams()`** : doit être wrappé dans `<Suspense>` sinon erreur en production
|
|
50
|
+
3. **Metadata** : export `metadata` ou `generateMetadata` uniquement dans `page.tsx` et `layout.tsx`
|
|
51
|
+
4. **Redirects dans Server Components** : utiliser `redirect()` de `next/navigation`, pas `router.push()`
|
|
52
|
+
5. **Route handlers** : `NextRequest` et `NextResponse` — pas `req`/`res` Express-style
|
|
53
|
+
6. **Middleware** : un seul fichier `middleware.ts` à la racine, matcher via config
|
|
54
|
+
|
|
55
|
+
## Patterns récurrents
|
|
56
|
+
|
|
57
|
+
### API Route avec validation
|
|
58
|
+
```tsx
|
|
59
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
60
|
+
|
|
61
|
+
export async function POST(request: NextRequest) {
|
|
62
|
+
try {
|
|
63
|
+
const body = await request.json();
|
|
64
|
+
// validate...
|
|
65
|
+
return NextResponse.json({ data: result }, { status: 201 });
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return NextResponse.json({ error: 'Invalid request' }, { status: 400 });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Dynamic metadata
|
|
73
|
+
```tsx
|
|
74
|
+
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) {
|
|
75
|
+
const { id } = await params; // Next.js 15+ : params is async
|
|
76
|
+
const item = await getItem(id);
|
|
77
|
+
return { title: item.name };
|
|
78
|
+
}
|
|
79
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# React 19 — Doc Cache Local
|
|
2
|
+
|
|
3
|
+
> Dernière mise à jour : 2026-03-03
|
|
4
|
+
|
|
5
|
+
## React 19 Nouveautés
|
|
6
|
+
|
|
7
|
+
### `use()` hook
|
|
8
|
+
```tsx
|
|
9
|
+
function Component({ dataPromise }: { dataPromise: Promise<Data> }) {
|
|
10
|
+
const data = use(dataPromise); // suspend until resolved
|
|
11
|
+
return <div>{data.name}</div>;
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Server Components
|
|
16
|
+
- Pas de state, pas de hooks (sauf `use()`)
|
|
17
|
+
- Accès direct aux données (DB, fichiers, APIs)
|
|
18
|
+
- Ne sont jamais envoyés au client (0 JS)
|
|
19
|
+
|
|
20
|
+
### Actions (form)
|
|
21
|
+
```tsx
|
|
22
|
+
function Form() {
|
|
23
|
+
const [state, formAction, isPending] = useActionState(async (prev, formData) => {
|
|
24
|
+
const result = await submitForm(formData);
|
|
25
|
+
return result;
|
|
26
|
+
}, null);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<form action={formAction}>
|
|
30
|
+
<input name="email" />
|
|
31
|
+
<button disabled={isPending}>Submit</button>
|
|
32
|
+
</form>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### `useOptimistic()`
|
|
38
|
+
```tsx
|
|
39
|
+
const [optimisticItems, addOptimistic] = useOptimistic(items, (state, newItem) => [...state, newItem]);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Patterns récurrents
|
|
43
|
+
|
|
44
|
+
### Loading + Error + Empty states (OBLIGATOIRE)
|
|
45
|
+
```tsx
|
|
46
|
+
function ItemList() {
|
|
47
|
+
const { data, isLoading, error } = useQuery(...);
|
|
48
|
+
|
|
49
|
+
if (isLoading) return <Skeleton />;
|
|
50
|
+
if (error) return <ErrorMessage retry={refetch} />;
|
|
51
|
+
if (!data?.length) return <EmptyState message="Aucun élément" />;
|
|
52
|
+
|
|
53
|
+
return <ul>{data.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Gotchas
|
|
58
|
+
|
|
59
|
+
1. **StrictMode** double-render en dev — normal, pas un bug
|
|
60
|
+
2. **Key prop** : ne jamais utiliser l'index comme key si la liste peut être réordonnée
|
|
61
|
+
3. **Closure stale** dans useEffect — utiliser ref ou functional update
|
|
62
|
+
4. **Ref callback** : React 19 supporte le cleanup `return () => {}` dans les ref callbacks
|
|
63
|
+
5. **`forwardRef` deprecated** en React 19 — `ref` est maintenant une prop normale
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Tailwind CSS v4 — Doc Cache Local
|
|
2
|
+
|
|
3
|
+
> Dernière mise à jour : 2026-03-03
|
|
4
|
+
|
|
5
|
+
## v4 Breaking Changes
|
|
6
|
+
|
|
7
|
+
- Config via CSS (`@theme`), plus de `tailwind.config.js`
|
|
8
|
+
- Import : `@import "tailwindcss"` (plus de `@tailwind base/components/utilities`)
|
|
9
|
+
- Content detection automatique (plus besoin de `content: [...]`)
|
|
10
|
+
|
|
11
|
+
```css
|
|
12
|
+
@import "tailwindcss";
|
|
13
|
+
|
|
14
|
+
@theme {
|
|
15
|
+
--color-primary: #3b82f6;
|
|
16
|
+
--font-sans: "Inter", sans-serif;
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Classes les plus utilisées
|
|
21
|
+
|
|
22
|
+
### Layout
|
|
23
|
+
- `flex` `flex-col` `items-center` `justify-between` `gap-4`
|
|
24
|
+
- `grid` `grid-cols-3` `col-span-2`
|
|
25
|
+
- `container` `mx-auto` `max-w-7xl`
|
|
26
|
+
|
|
27
|
+
### Spacing
|
|
28
|
+
- `p-4` `px-6` `py-2` `m-auto` `mt-8` `space-y-4`
|
|
29
|
+
|
|
30
|
+
### Typography
|
|
31
|
+
- `text-sm` `text-lg` `text-xl` `font-bold` `font-medium`
|
|
32
|
+
- `text-gray-600` `text-primary` `leading-relaxed`
|
|
33
|
+
|
|
34
|
+
### Responsive
|
|
35
|
+
- `sm:` (640px) `md:` (768px) `lg:` (1024px) `xl:` (1280px)
|
|
36
|
+
|
|
37
|
+
### Dark mode
|
|
38
|
+
- `dark:bg-gray-900` `dark:text-white`
|
|
39
|
+
|
|
40
|
+
## Gotchas
|
|
41
|
+
|
|
42
|
+
1. **v4 pas de config JS** — tout est en CSS maintenant
|
|
43
|
+
2. **`@apply`** fonctionne toujours mais déconseillé — préférer les classes directes
|
|
44
|
+
3. **Arbitrary values** : `w-[calc(100%-2rem)]` `text-[#1a1a1a]`
|
|
45
|
+
4. **Group/peer** : `group-hover:opacity-100` `peer-invalid:text-red-500`
|