rex-claude 1.1.7 → 2.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/dist/guards/completion-guard.sh +40 -0
- package/dist/guards/dangerous-cmd-guard.sh +50 -0
- package/dist/guards/scope-guard.sh +16 -0
- package/dist/guards/session-summary.sh +42 -0
- package/dist/guards/test-protect-guard.sh +15 -0
- package/dist/guards/ui-checklist-guard.sh +44 -0
- package/dist/index.js +454 -0
- package/dist/init-YMRG5ZXU.js +248 -0
- package/dist/optimize-NE47FMOP.js +111 -0
- package/package.json +26 -22
- package/README.md +0 -163
- package/activity/activity.jsonl +0 -443
- package/activity/config.lua +0 -3
- package/activity/init.lua +0 -49
- package/dist/cli.js +0 -504
- package/dotfiles/CLAUDE.md +0 -136
- package/dotfiles/commands/clean.md +0 -8
- package/dotfiles/commands/doc.md +0 -8
- package/dotfiles/commands/review.md +0 -15
- package/dotfiles/commands/scaffold.md +0 -11
- package/dotfiles/commands/test.md +0 -11
- package/dotfiles/docs/cloudflare.md +0 -62
- package/dotfiles/docs/nextjs.md +0 -79
- package/dotfiles/docs/react.md +0 -63
- package/dotfiles/docs/tailwind.md +0 -45
- package/dotfiles/docs/telegram-bot.md +0 -55
- package/dotfiles/rules/api-design.md +0 -63
- package/dotfiles/rules/defensive-engineering.md +0 -42
- package/dotfiles/rules/docs-first.md +0 -47
- package/dotfiles/rules/frontend.md +0 -41
- package/dotfiles/rules/git-workflow.md +0 -57
- package/dotfiles/rules/never-assume.md +0 -39
- package/dotfiles/rules/security.md +0 -46
- package/dotfiles/rules/testing.md +0 -33
- package/dotfiles/settings.json +0 -80
- package/dotfiles/skills/build-validate/SKILL.md +0 -16
- package/dotfiles/skills/code-review/SKILL.md +0 -18
- package/dotfiles/skills/context-loader/SKILL.md +0 -25
- package/dotfiles/skills/debug-assist/SKILL.md +0 -26
- package/dotfiles/skills/deploy-checklist/SKILL.md +0 -54
- package/dotfiles/skills/dstudio-design-system/SKILL.md +0 -120
- package/dotfiles/skills/figma-workflow/SKILL.md +0 -23
- package/dotfiles/skills/fix-issue/SKILL.md +0 -43
- package/dotfiles/skills/one-shot/SKILL.md +0 -18
- package/dotfiles/skills/pr-review-loop/SKILL.md +0 -41
- package/dotfiles/skills/project-init/SKILL.md +0 -45
- package/dotfiles/skills/research/SKILL.md +0 -17
- package/dotfiles/skills/spec-interview/SKILL.md +0 -20
- package/dotfiles/skills/token-guard/SKILL.md +0 -26
- package/dotfiles/templates/CLAUDE.md.template +0 -39
- package/memory/package.json +0 -24
- package/memory/src/embed.ts +0 -23
- package/memory/src/ingest.ts +0 -257
- package/memory/src/search.ts +0 -32
- package/memory/src/server.ts +0 -69
- package/memory/tsconfig.json +0 -14
- package/tmux/.tmux.conf +0 -73
package/dist/cli.js
DELETED
|
@@ -1,504 +0,0 @@
|
|
|
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) { console.log(` ${icon} ${msg}`); }
|
|
20
|
-
function ok(msg) { log(`${GREEN}✓${RESET}`, msg); }
|
|
21
|
-
function warn(msg) { log(`${YELLOW}⚠${RESET}`, msg); }
|
|
22
|
-
function info(msg) { log(`${CYAN}→${RESET}`, msg); }
|
|
23
|
-
function fail(msg) { log(`${RED}✗${RESET}`, msg); }
|
|
24
|
-
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
25
|
-
function safeWhich(bin, fallback) {
|
|
26
|
-
try {
|
|
27
|
-
return execSync(`which ${bin}`, { stdio: 'pipe' }).toString().trim() || fallback;
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
return fallback;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function ensureDir(dirPath) {
|
|
34
|
-
try {
|
|
35
|
-
mkdirSync(dirPath, { recursive: true });
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
catch (e) {
|
|
39
|
-
warn(`Impossible de créer ${dirPath}: ${e.message?.split('\n')[0]}`);
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function safeReadFile(filePath) {
|
|
44
|
-
try {
|
|
45
|
-
return readFileSync(filePath, 'utf-8');
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function safeWriteFile(filePath, content) {
|
|
52
|
-
try {
|
|
53
|
-
writeFileSync(filePath, content);
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
catch (e) {
|
|
57
|
-
warn(`Impossible d'écrire ${filePath}: ${e.message?.split('\n')[0]}`);
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function backup(filePath) {
|
|
62
|
-
if (!existsSync(filePath))
|
|
63
|
-
return null;
|
|
64
|
-
const backupPath = `${filePath}.backup-${Date.now()}`;
|
|
65
|
-
try {
|
|
66
|
-
copyFileSync(filePath, backupPath);
|
|
67
|
-
return backupPath;
|
|
68
|
-
}
|
|
69
|
-
catch (e) {
|
|
70
|
-
warn(`Backup impossible pour ${filePath}: ${e.message?.split('\n')[0]}`);
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
function loadPlist(plistPath) {
|
|
75
|
-
try {
|
|
76
|
-
execSync(`launchctl unload "${plistPath}" 2>/dev/null; launchctl load "${plistPath}"`, { stdio: 'pipe' });
|
|
77
|
-
}
|
|
78
|
-
catch (e) {
|
|
79
|
-
throw new Error(`launchctl load: ${e.message?.split('\n')[0]}`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
// ─── Settings merge ───────────────────────────────────────────────────────────
|
|
83
|
-
function deepMerge(target, source) {
|
|
84
|
-
const result = { ...target };
|
|
85
|
-
for (const key of Object.keys(source)) {
|
|
86
|
-
if (result[key] && typeof result[key] === 'object' && !Array.isArray(result[key]) &&
|
|
87
|
-
typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
88
|
-
result[key] = deepMerge(result[key], source[key]);
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
result[key] = source[key];
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return result;
|
|
95
|
-
}
|
|
96
|
-
function mergeArraysByMatcher(existing, incoming) {
|
|
97
|
-
const result = [...existing];
|
|
98
|
-
for (const item of incoming) {
|
|
99
|
-
const idx = result.findIndex((e) => e.matcher === item.matcher);
|
|
100
|
-
if (idx === -1)
|
|
101
|
-
result.push(item);
|
|
102
|
-
else
|
|
103
|
-
result[idx] = item;
|
|
104
|
-
}
|
|
105
|
-
return result;
|
|
106
|
-
}
|
|
107
|
-
function mergeSettings(existingPath, rexSettingsPath) {
|
|
108
|
-
let existing = {};
|
|
109
|
-
if (existsSync(existingPath)) {
|
|
110
|
-
try {
|
|
111
|
-
existing = JSON.parse(readFileSync(existingPath, 'utf-8'));
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
warn('settings.json existant corrompu — réinitialisé');
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
let rex = {};
|
|
118
|
-
try {
|
|
119
|
-
rex = JSON.parse(readFileSync(rexSettingsPath, 'utf-8'));
|
|
120
|
-
}
|
|
121
|
-
catch (e) {
|
|
122
|
-
throw new Error(`settings.json REX illisible: ${e.message}`);
|
|
123
|
-
}
|
|
124
|
-
const merged = deepMerge(existing, rex);
|
|
125
|
-
if (rex.hooks) {
|
|
126
|
-
merged.hooks = { ...existing.hooks };
|
|
127
|
-
for (const hookType of Object.keys(rex.hooks)) {
|
|
128
|
-
if (Array.isArray(rex.hooks[hookType])) {
|
|
129
|
-
merged.hooks[hookType] = mergeArraysByMatcher(existing.hooks?.[hookType] || [], rex.hooks[hookType]);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
if (!merged.mcpServers)
|
|
134
|
-
merged.mcpServers = {};
|
|
135
|
-
merged.mcpServers['rex-memory'] = {
|
|
136
|
-
command: 'node',
|
|
137
|
-
args: [join(REX_MEMORY_DIR, 'dist', 'server.js')]
|
|
138
|
-
};
|
|
139
|
-
return merged;
|
|
140
|
-
}
|
|
141
|
-
// ─── Install steps ────────────────────────────────────────────────────────────
|
|
142
|
-
function installOllama(os) {
|
|
143
|
-
const ollamaPath = safeWhich('ollama', '');
|
|
144
|
-
if (!ollamaPath) {
|
|
145
|
-
warn('Ollama non installé — embeddings désactivés');
|
|
146
|
-
info(' brew install ollama && ollama pull nomic-embed-text');
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
ok(`Ollama détecté (${ollamaPath})`);
|
|
150
|
-
if (os !== 'darwin') {
|
|
151
|
-
// Linux: systemd user service
|
|
152
|
-
info('Linux: démarrer Ollama manuellement ou via systemd');
|
|
153
|
-
info(' ollama serve &');
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
const launchAgentsDir = join(HOME, 'Library', 'LaunchAgents');
|
|
157
|
-
if (!ensureDir(launchAgentsDir))
|
|
158
|
-
return;
|
|
159
|
-
const logsDir = join(REX_MEMORY_DIR, 'logs');
|
|
160
|
-
if (!ensureDir(logsDir))
|
|
161
|
-
return;
|
|
162
|
-
const plistPath = join(launchAgentsDir, 'com.rex.ollama.plist');
|
|
163
|
-
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
164
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
165
|
-
<plist version="1.0">
|
|
166
|
-
<dict>
|
|
167
|
-
<key>Label</key>
|
|
168
|
-
<string>com.rex.ollama</string>
|
|
169
|
-
<key>ProgramArguments</key>
|
|
170
|
-
<array>
|
|
171
|
-
<string>${ollamaPath}</string>
|
|
172
|
-
<string>serve</string>
|
|
173
|
-
</array>
|
|
174
|
-
<key>EnvironmentVariables</key>
|
|
175
|
-
<dict>
|
|
176
|
-
<key>HOME</key>
|
|
177
|
-
<string>${HOME}</string>
|
|
178
|
-
<key>OLLAMA_HOST</key>
|
|
179
|
-
<string>127.0.0.1:11434</string>
|
|
180
|
-
</dict>
|
|
181
|
-
<key>RunAtLoad</key>
|
|
182
|
-
<true/>
|
|
183
|
-
<key>KeepAlive</key>
|
|
184
|
-
<true/>
|
|
185
|
-
<key>StandardOutPath</key>
|
|
186
|
-
<string>${join(logsDir, 'ollama.log')}</string>
|
|
187
|
-
<key>StandardErrorPath</key>
|
|
188
|
-
<string>${join(logsDir, 'ollama-error.log')}</string>
|
|
189
|
-
<key>ThrottleInterval</key>
|
|
190
|
-
<integer>10</integer>
|
|
191
|
-
</dict>
|
|
192
|
-
</plist>`;
|
|
193
|
-
if (!safeWriteFile(plistPath, plist))
|
|
194
|
-
return;
|
|
195
|
-
try {
|
|
196
|
-
loadPlist(plistPath);
|
|
197
|
-
ok('LaunchAgent Ollama installé (keepalive au login)');
|
|
198
|
-
}
|
|
199
|
-
catch (e) {
|
|
200
|
-
warn(`LaunchAgent Ollama créé — chargement manuel: launchctl load "${plistPath}"`);
|
|
201
|
-
info(` Erreur: ${e.message}`);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
function installMemoryIngest(os) {
|
|
205
|
-
const ingestScript = join(REX_MEMORY_DIR, 'dist', 'ingest.js');
|
|
206
|
-
if (!existsSync(ingestScript)) {
|
|
207
|
-
warn('ingest.js non trouvé — ingest auto ignoré (rex-memory non compilé ?)');
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
const logsDir = join(REX_MEMORY_DIR, 'logs');
|
|
211
|
-
if (!ensureDir(logsDir))
|
|
212
|
-
return;
|
|
213
|
-
const nodePath = safeWhich('node', process.execPath);
|
|
214
|
-
if (os === 'darwin') {
|
|
215
|
-
const launchAgentsDir = join(HOME, 'Library', 'LaunchAgents');
|
|
216
|
-
if (!ensureDir(launchAgentsDir))
|
|
217
|
-
return;
|
|
218
|
-
const plistPath = join(launchAgentsDir, 'com.rex.memory-ingest.plist');
|
|
219
|
-
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
220
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
221
|
-
<plist version="1.0">
|
|
222
|
-
<dict>
|
|
223
|
-
<key>Label</key>
|
|
224
|
-
<string>com.rex.memory-ingest</string>
|
|
225
|
-
<key>ProgramArguments</key>
|
|
226
|
-
<array>
|
|
227
|
-
<string>${nodePath}</string>
|
|
228
|
-
<string>${ingestScript}</string>
|
|
229
|
-
</array>
|
|
230
|
-
<key>EnvironmentVariables</key>
|
|
231
|
-
<dict>
|
|
232
|
-
<key>HOME</key>
|
|
233
|
-
<string>${HOME}</string>
|
|
234
|
-
<key>OLLAMA_URL</key>
|
|
235
|
-
<string>http://localhost:11434</string>
|
|
236
|
-
</dict>
|
|
237
|
-
<key>StartInterval</key>
|
|
238
|
-
<integer>21600</integer>
|
|
239
|
-
<key>StandardOutPath</key>
|
|
240
|
-
<string>${join(logsDir, 'ingest.log')}</string>
|
|
241
|
-
<key>StandardErrorPath</key>
|
|
242
|
-
<string>${join(logsDir, 'ingest-error.log')}</string>
|
|
243
|
-
<key>RunAtLoad</key>
|
|
244
|
-
<false/>
|
|
245
|
-
</dict>
|
|
246
|
-
</plist>`;
|
|
247
|
-
if (!safeWriteFile(plistPath, plist))
|
|
248
|
-
return;
|
|
249
|
-
try {
|
|
250
|
-
loadPlist(plistPath);
|
|
251
|
-
ok('LaunchAgent ingest mémoire installé (toutes les 6h)');
|
|
252
|
-
}
|
|
253
|
-
catch (e) {
|
|
254
|
-
warn(`LaunchAgent ingest créé — chargement manuel: launchctl load "${plistPath}"`);
|
|
255
|
-
info(` Erreur: ${e.message}`);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
// Linux: write to temp file then pipe to crontab (avoids quoting hell)
|
|
260
|
-
try {
|
|
261
|
-
const existing = (() => {
|
|
262
|
-
try {
|
|
263
|
-
return execSync('crontab -l', { stdio: 'pipe' }).toString();
|
|
264
|
-
}
|
|
265
|
-
catch {
|
|
266
|
-
return '';
|
|
267
|
-
}
|
|
268
|
-
})();
|
|
269
|
-
if (existing.includes('rex-memory')) {
|
|
270
|
-
ok('Cron ingest mémoire déjà configuré');
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
const cronLine = `0 */6 * * * ${nodePath} ${ingestScript} >> ${join(logsDir, 'ingest.log')} 2>&1`;
|
|
274
|
-
const tmpFile = `/tmp/rex-cron-${Date.now()}`;
|
|
275
|
-
writeFileSync(tmpFile, existing.trim() + '\n' + cronLine + '\n');
|
|
276
|
-
execSync(`crontab ${tmpFile}`, { stdio: 'pipe' });
|
|
277
|
-
execSync(`rm ${tmpFile}`, { stdio: 'pipe' });
|
|
278
|
-
ok('Cron ingest mémoire installé (toutes les 6h)');
|
|
279
|
-
}
|
|
280
|
-
catch (e) {
|
|
281
|
-
warn('Impossible de configurer le cron — ajouter manuellement :');
|
|
282
|
-
info(` 0 */6 * * * ${nodePath} ${ingestScript}`);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
function installTmuxHook() {
|
|
287
|
-
try {
|
|
288
|
-
execSync('which tmux', { stdio: 'pipe' });
|
|
289
|
-
}
|
|
290
|
-
catch {
|
|
291
|
-
warn('tmux non installé — auto-start ignoré');
|
|
292
|
-
info(' brew install tmux');
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
const shell = process.env.SHELL || '';
|
|
296
|
-
const candidates = [
|
|
297
|
-
shell.includes('zsh') ? join(HOME, '.zshrc') : null,
|
|
298
|
-
shell.includes('bash') ? join(HOME, '.bashrc') : null,
|
|
299
|
-
existsSync(join(HOME, '.zshrc')) ? join(HOME, '.zshrc') : null,
|
|
300
|
-
existsSync(join(HOME, '.bashrc')) ? join(HOME, '.bashrc') : null,
|
|
301
|
-
].filter(Boolean);
|
|
302
|
-
const rcFile = candidates.find(f => existsSync(f)) ?? null;
|
|
303
|
-
if (!rcFile) {
|
|
304
|
-
warn('Shell RC introuvable — tmux auto-start ignoré');
|
|
305
|
-
info(' Ajouter manuellement dans ~/.zshrc ou ~/.bashrc');
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
const existing = safeReadFile(rcFile);
|
|
309
|
-
if (existing === null) {
|
|
310
|
-
warn(`Impossible de lire ${rcFile} — tmux auto-start ignoré`);
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
if (existing.includes('REX — Auto-start tmux')) {
|
|
314
|
-
ok('tmux auto-start déjà configuré');
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
// Backup before modifying shell RC
|
|
318
|
-
const b = backup(rcFile);
|
|
319
|
-
if (b)
|
|
320
|
-
info(`${rcFile.replace(HOME, '~')} sauvegardé → ${b.replace(HOME, '~')}`);
|
|
321
|
-
const hook = `
|
|
322
|
-
# REX — Auto-start tmux
|
|
323
|
-
if command -v tmux &>/dev/null && [ -z "$TMUX" ] && [ "$TERM_PROGRAM" != "vscode" ] && [ "$TERM_PROGRAM" != "cursor" ]; then
|
|
324
|
-
tmux attach -t main 2>/dev/null || tmux new-session -s main
|
|
325
|
-
fi
|
|
326
|
-
`;
|
|
327
|
-
if (!safeWriteFile(rcFile, existing + hook)) {
|
|
328
|
-
warn('Écriture .zshrc échouée — tmux auto-start ignoré');
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
ok(`tmux auto-start ajouté dans ${rcFile.replace(HOME, '~')}`);
|
|
332
|
-
}
|
|
333
|
-
// ─── Main install ─────────────────────────────────────────────────────────────
|
|
334
|
-
function main() {
|
|
335
|
-
console.log(`\n${BOLD}${CYAN} ╔══════════════════════════════╗${RESET}`);
|
|
336
|
-
console.log(`${BOLD}${CYAN} ║ REX — Setup & Install ║${RESET}`);
|
|
337
|
-
console.log(`${BOLD}${CYAN} ╚══════════════════════════════╝${RESET}\n`);
|
|
338
|
-
const os = platform();
|
|
339
|
-
if (os !== 'darwin' && os !== 'linux') {
|
|
340
|
-
fail(`OS non supporté : ${os}. REX supporte macOS et Linux.`);
|
|
341
|
-
process.exit(1);
|
|
342
|
-
}
|
|
343
|
-
info(`OS détecté : ${os === 'darwin' ? 'macOS' : 'Linux'}`);
|
|
344
|
-
// 1. ~/.claude/
|
|
345
|
-
if (!ensureDir(CLAUDE_DIR))
|
|
346
|
-
process.exit(1);
|
|
347
|
-
ok('~/.claude/ prêt');
|
|
348
|
-
// 2. dotfiles → ~/.claude/
|
|
349
|
-
const dotfilesDir = join(PKG_DIR, 'dotfiles');
|
|
350
|
-
for (const dir of ['rules', 'skills', 'docs', 'commands', 'templates', 'agents']) {
|
|
351
|
-
const src = join(dotfilesDir, dir);
|
|
352
|
-
if (!existsSync(src))
|
|
353
|
-
continue;
|
|
354
|
-
try {
|
|
355
|
-
cpSync(src, join(CLAUDE_DIR, dir), { recursive: true, force: true });
|
|
356
|
-
ok(`Copié dotfiles/${dir}/ → ~/.claude/${dir}/`);
|
|
357
|
-
}
|
|
358
|
-
catch (e) {
|
|
359
|
-
warn(`Copie dotfiles/${dir} échouée: ${e.message?.split('\n')[0]}`);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
// CLAUDE.md
|
|
363
|
-
const claudeMdSrc = join(dotfilesDir, 'CLAUDE.md');
|
|
364
|
-
if (existsSync(claudeMdSrc)) {
|
|
365
|
-
const claudeMdDest = join(CLAUDE_DIR, 'CLAUDE.md');
|
|
366
|
-
const b = backup(claudeMdDest);
|
|
367
|
-
if (b)
|
|
368
|
-
warn(`CLAUDE.md existant sauvegardé → ${b}`);
|
|
369
|
-
try {
|
|
370
|
-
copyFileSync(claudeMdSrc, claudeMdDest);
|
|
371
|
-
ok('Copié CLAUDE.md → ~/.claude/CLAUDE.md');
|
|
372
|
-
}
|
|
373
|
-
catch (e) {
|
|
374
|
-
warn(`Copie CLAUDE.md échouée: ${e.message?.split('\n')[0]}`);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
// 3. settings.json
|
|
378
|
-
const settingsPath = join(CLAUDE_DIR, 'settings.json');
|
|
379
|
-
const rexSettingsPath = join(dotfilesDir, 'settings.json');
|
|
380
|
-
if (existsSync(rexSettingsPath)) {
|
|
381
|
-
try {
|
|
382
|
-
const merged = mergeSettings(settingsPath, rexSettingsPath);
|
|
383
|
-
if (!safeWriteFile(settingsPath, JSON.stringify(merged, null, 2) + '\n')) {
|
|
384
|
-
warn('settings.json non mis à jour');
|
|
385
|
-
}
|
|
386
|
-
else {
|
|
387
|
-
ok('Mergé settings.json (hooks, plugins, env, mcpServers)');
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
catch (e) {
|
|
391
|
-
warn(`Merge settings.json échoué: ${e.message?.split('\n')[0]}`);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
// 4. memory/ → ~/.rex-memory/
|
|
395
|
-
const memorySrc = join(PKG_DIR, 'memory');
|
|
396
|
-
if (existsSync(memorySrc)) {
|
|
397
|
-
if (!ensureDir(REX_MEMORY_DIR) || !ensureDir(join(REX_MEMORY_DIR, 'db'))) {
|
|
398
|
-
warn('Création ~/.rex-memory/ échouée — MCP memory ignoré');
|
|
399
|
-
}
|
|
400
|
-
else {
|
|
401
|
-
for (const sub of ['src', 'package.json', 'package-lock.json', 'tsconfig.json']) {
|
|
402
|
-
const s = join(memorySrc, sub);
|
|
403
|
-
if (!existsSync(s))
|
|
404
|
-
continue;
|
|
405
|
-
try {
|
|
406
|
-
cpSync(s, join(REX_MEMORY_DIR, sub), { recursive: true, force: true });
|
|
407
|
-
}
|
|
408
|
-
catch (e) {
|
|
409
|
-
warn(`Copie memory/${sub} échouée: ${e.message?.split('\n')[0]}`);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
ok('Copié memory/ → ~/.rex-memory/');
|
|
413
|
-
// 5. npm install + build
|
|
414
|
-
info('Installation des dépendances rex-memory...');
|
|
415
|
-
try {
|
|
416
|
-
execSync('npm install --production=false', { cwd: REX_MEMORY_DIR, stdio: 'pipe' });
|
|
417
|
-
execSync('npm run build', { cwd: REX_MEMORY_DIR, stdio: 'pipe' });
|
|
418
|
-
ok('rex-memory compilé avec succès');
|
|
419
|
-
}
|
|
420
|
-
catch (e) {
|
|
421
|
-
fail(`Erreur compilation rex-memory: ${e.message?.split('\n')[0]}`);
|
|
422
|
-
info(' cd ~/.rex-memory && npm install && npm run build');
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
// 6. tmux config
|
|
427
|
-
const tmuxSrc = join(PKG_DIR, 'tmux', '.tmux.conf');
|
|
428
|
-
if (existsSync(tmuxSrc)) {
|
|
429
|
-
const tmuxDest = join(HOME, '.tmux.conf');
|
|
430
|
-
const b = backup(tmuxDest);
|
|
431
|
-
if (b)
|
|
432
|
-
warn(`~/.tmux.conf sauvegardé → ${b}`);
|
|
433
|
-
try {
|
|
434
|
-
copyFileSync(tmuxSrc, tmuxDest);
|
|
435
|
-
ok('Copié tmux/.tmux.conf → ~/.tmux.conf');
|
|
436
|
-
}
|
|
437
|
-
catch (e) {
|
|
438
|
-
warn(`Copie .tmux.conf échouée: ${e.message?.split('\n')[0]}`);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
// 7. Hammerspoon (macOS only)
|
|
442
|
-
if (os === 'darwin') {
|
|
443
|
-
const activitySrc = join(PKG_DIR, 'activity');
|
|
444
|
-
if (existsSync(activitySrc)) {
|
|
445
|
-
const hsDir = join(HOME, '.hammerspoon');
|
|
446
|
-
if (existsSync(hsDir)) {
|
|
447
|
-
try {
|
|
448
|
-
cpSync(activitySrc, hsDir, { recursive: true, force: true });
|
|
449
|
-
ok('Copié activity/ → ~/.hammerspoon/');
|
|
450
|
-
}
|
|
451
|
-
catch (e) {
|
|
452
|
-
warn(`Copie Hammerspoon échouée: ${e.message?.split('\n')[0]}`);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
else {
|
|
456
|
-
warn('Hammerspoon non installé — activity logger ignoré');
|
|
457
|
-
info(' brew install --cask hammerspoon');
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
// 8. Ollama keepalive
|
|
462
|
-
installOllama(os);
|
|
463
|
-
// 9. Ingest mémoire (toutes les 6h)
|
|
464
|
-
installMemoryIngest(os);
|
|
465
|
-
// 10. tmux auto-start
|
|
466
|
-
installTmuxHook();
|
|
467
|
-
console.log(`\n${BOLD}${GREEN} ══════════════════════════════${RESET}`);
|
|
468
|
-
console.log(`${BOLD}${GREEN} REX installé avec succès !${RESET}`);
|
|
469
|
-
console.log(`${DIM} ~/.claude/ → rules, skills, docs, commands${RESET}`);
|
|
470
|
-
console.log(`${DIM} ~/.rex-memory/ → MCP memory server${RESET}`);
|
|
471
|
-
console.log(`${DIM} ~/.tmux.conf → tmux config${RESET}`);
|
|
472
|
-
console.log(`${DIM} Ollama → keepalive au login${RESET}`);
|
|
473
|
-
console.log(`${DIM} Ingest mémoire → toutes les 6h${RESET}`);
|
|
474
|
-
console.log(`${DIM} tmux → auto-attach au démarrage terminal${RESET}`);
|
|
475
|
-
console.log(`${BOLD}${GREEN} ══════════════════════════════${RESET}\n`);
|
|
476
|
-
}
|
|
477
|
-
// ─── Other commands ───────────────────────────────────────────────────────────
|
|
478
|
-
function update() {
|
|
479
|
-
console.log(`\n${BOLD}${CYAN} ╔══════════════════════════════╗${RESET}`);
|
|
480
|
-
console.log(`${BOLD}${CYAN} ║ REX — Update ║${RESET}`);
|
|
481
|
-
console.log(`${BOLD}${CYAN} ╚══════════════════════════════╝${RESET}\n`);
|
|
482
|
-
info('Mise à jour de rex-claude depuis npm...');
|
|
483
|
-
try {
|
|
484
|
-
execSync('npm install -g rex-claude@latest', { stdio: 'inherit' });
|
|
485
|
-
ok('rex-claude mis à jour');
|
|
486
|
-
info('Relance "rex" pour appliquer les changements');
|
|
487
|
-
}
|
|
488
|
-
catch (e) {
|
|
489
|
-
fail(`Échec de la mise à jour: ${e.message?.split('\n')[0]}`);
|
|
490
|
-
process.exit(1);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
// ─── Routing ──────────────────────────────────────────────────────────────────
|
|
494
|
-
const cmd = process.argv[2];
|
|
495
|
-
if (cmd === 'update') {
|
|
496
|
-
update();
|
|
497
|
-
}
|
|
498
|
-
else if (cmd === 'doctor' || cmd === 'setup') {
|
|
499
|
-
fail(`"rex ${cmd}" arrive bientôt — utilise "rex" pour l'instant`);
|
|
500
|
-
process.exit(1);
|
|
501
|
-
}
|
|
502
|
-
else {
|
|
503
|
-
main();
|
|
504
|
-
}
|
package/dotfiles/CLAUDE.md
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
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
|
|
@@ -1,8 +0,0 @@
|
|
|
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
|
package/dotfiles/commands/doc.md
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
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
|
|
@@ -1,15 +0,0 @@
|
|
|
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
|
|
@@ -1,11 +0,0 @@
|
|
|
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
|