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
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/init.ts
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
5
|
+
import { join, dirname } from "path";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
var COLORS = {
|
|
9
|
+
reset: "\x1B[0m",
|
|
10
|
+
green: "\x1B[32m",
|
|
11
|
+
yellow: "\x1B[33m",
|
|
12
|
+
red: "\x1B[31m",
|
|
13
|
+
bold: "\x1B[1m",
|
|
14
|
+
dim: "\x1B[2m",
|
|
15
|
+
cyan: "\x1B[36m"
|
|
16
|
+
};
|
|
17
|
+
function log(icon, msg) {
|
|
18
|
+
console.log(` ${icon} ${msg}`);
|
|
19
|
+
}
|
|
20
|
+
function ok(msg) {
|
|
21
|
+
log(`${COLORS.green}\u2713${COLORS.reset}`, msg);
|
|
22
|
+
}
|
|
23
|
+
function skip(msg) {
|
|
24
|
+
log(`${COLORS.yellow}\u2192${COLORS.reset}`, `${COLORS.dim}${msg}${COLORS.reset}`);
|
|
25
|
+
}
|
|
26
|
+
function info(msg) {
|
|
27
|
+
log(`${COLORS.cyan}\u2139${COLORS.reset}`, msg);
|
|
28
|
+
}
|
|
29
|
+
function readJson(path) {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function writeJson(path, data) {
|
|
37
|
+
writeFileSync(path, JSON.stringify(data, null, 2) + "\n");
|
|
38
|
+
}
|
|
39
|
+
function ensureDir(dir) {
|
|
40
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
async function init() {
|
|
43
|
+
const claudeDir = join(homedir(), ".claude");
|
|
44
|
+
const line = "\u2550".repeat(45);
|
|
45
|
+
console.log(`
|
|
46
|
+
${line}`);
|
|
47
|
+
console.log(`${COLORS.bold} REX INIT \u2014 Setup${COLORS.reset}`);
|
|
48
|
+
console.log(`${line}
|
|
49
|
+
`);
|
|
50
|
+
let memoryServerPath = null;
|
|
51
|
+
{
|
|
52
|
+
const thisDir2 = new URL(".", import.meta.url).pathname;
|
|
53
|
+
const candidates = [
|
|
54
|
+
join(thisDir2, "..", "..", "memory", "src", "server.ts"),
|
|
55
|
+
join(homedir(), ".rex-memory", "src", "server.ts")
|
|
56
|
+
];
|
|
57
|
+
for (const c of candidates) {
|
|
58
|
+
if (existsSync(c)) {
|
|
59
|
+
memoryServerPath = c;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const settingsPath = join(claudeDir, "settings.json");
|
|
65
|
+
ensureDir(claudeDir);
|
|
66
|
+
const settings = readJson(settingsPath) ?? {};
|
|
67
|
+
if (!settings.mcpServers) settings.mcpServers = {};
|
|
68
|
+
if (settings.mcpServers["rex-memory"]) {
|
|
69
|
+
skip("MCP server rex-memory already configured");
|
|
70
|
+
} else if (memoryServerPath) {
|
|
71
|
+
const serverDir = join(memoryServerPath, "..", "..");
|
|
72
|
+
settings.mcpServers["rex-memory"] = {
|
|
73
|
+
command: "npx",
|
|
74
|
+
args: ["tsx", memoryServerPath],
|
|
75
|
+
cwd: serverDir
|
|
76
|
+
};
|
|
77
|
+
writeJson(settingsPath, settings);
|
|
78
|
+
ok("MCP server rex-memory configured");
|
|
79
|
+
} else {
|
|
80
|
+
info("Memory package not found \u2014 install @rex/memory or run from monorepo");
|
|
81
|
+
}
|
|
82
|
+
if (!settings.hooks) settings.hooks = {};
|
|
83
|
+
const hasIngestHook = settings.hooks.SessionEnd?.some?.(
|
|
84
|
+
(h) => h.hooks?.some?.((hh) => hh.command?.includes("rex") && hh.command?.includes("ingest"))
|
|
85
|
+
);
|
|
86
|
+
if (hasIngestHook) {
|
|
87
|
+
skip("Auto-ingest hook (SessionEnd) already configured");
|
|
88
|
+
} else {
|
|
89
|
+
if (!settings.hooks.SessionEnd) settings.hooks.SessionEnd = [];
|
|
90
|
+
settings.hooks.SessionEnd.push({
|
|
91
|
+
hooks: [{
|
|
92
|
+
type: "command",
|
|
93
|
+
command: "npx rex-cli ingest 2>/dev/null &",
|
|
94
|
+
timeout: 5
|
|
95
|
+
}]
|
|
96
|
+
});
|
|
97
|
+
ok("Auto-ingest hook configured (SessionEnd)");
|
|
98
|
+
}
|
|
99
|
+
const hasContextHook = settings.hooks.SessionStart?.some?.(
|
|
100
|
+
(h) => h.hooks?.some?.((hh) => hh.command?.includes("rex-context"))
|
|
101
|
+
);
|
|
102
|
+
if (hasContextHook) {
|
|
103
|
+
skip("Context injection hook (SessionStart) already configured");
|
|
104
|
+
} else {
|
|
105
|
+
const contextScript = join(claudeDir, "rex-context.sh");
|
|
106
|
+
if (!existsSync(contextScript)) {
|
|
107
|
+
writeFileSync(contextScript, `#!/bin/bash
|
|
108
|
+
# REX Context Injection \u2014 runs at session start
|
|
109
|
+
# Outputs relevant memory context to CLAUDE_ENV_FILE
|
|
110
|
+
|
|
111
|
+
if [ -z "$CLAUDE_ENV_FILE" ]; then
|
|
112
|
+
exit 0
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
# Quick check if rex-memory MCP is available
|
|
116
|
+
if command -v npx &>/dev/null; then
|
|
117
|
+
# Context will be loaded via MCP rex_context tool
|
|
118
|
+
# This hook just ensures the env is ready
|
|
119
|
+
echo "REX_MEMORY_AVAILABLE=true" >> "$CLAUDE_ENV_FILE"
|
|
120
|
+
fi
|
|
121
|
+
`, { mode: 493 });
|
|
122
|
+
}
|
|
123
|
+
if (!settings.hooks.SessionStart) settings.hooks.SessionStart = [];
|
|
124
|
+
settings.hooks.SessionStart.push({
|
|
125
|
+
hooks: [{
|
|
126
|
+
type: "command",
|
|
127
|
+
command: `bash ${contextScript}`,
|
|
128
|
+
timeout: 5
|
|
129
|
+
}]
|
|
130
|
+
});
|
|
131
|
+
ok("Context injection hook configured (SessionStart)");
|
|
132
|
+
}
|
|
133
|
+
const guardsDir = join(claudeDir, "rex-guards");
|
|
134
|
+
ensureDir(guardsDir);
|
|
135
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
136
|
+
const srcGuardsDir = join(thisDir, "guards");
|
|
137
|
+
const GUARDS = [
|
|
138
|
+
{
|
|
139
|
+
file: "completion-guard.sh",
|
|
140
|
+
event: "Stop",
|
|
141
|
+
desc: "Completion verifier (prevents 70% problem)",
|
|
142
|
+
matcher: void 0
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
file: "dangerous-cmd-guard.sh",
|
|
146
|
+
event: "PreToolUse",
|
|
147
|
+
desc: "Dangerous command blocker",
|
|
148
|
+
matcher: "Bash"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
file: "test-protect-guard.sh",
|
|
152
|
+
event: "PostToolUse",
|
|
153
|
+
desc: "Test assertion protector",
|
|
154
|
+
matcher: "Edit|Write"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
file: "session-summary.sh",
|
|
158
|
+
event: "Stop",
|
|
159
|
+
desc: "Auto session summary",
|
|
160
|
+
matcher: void 0
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
file: "ui-checklist-guard.sh",
|
|
164
|
+
event: "PostToolUse",
|
|
165
|
+
desc: "UI states checklist (loading/error/empty)",
|
|
166
|
+
matcher: "Edit|Write"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
file: "scope-guard.sh",
|
|
170
|
+
event: "PostToolUse",
|
|
171
|
+
desc: "Scope creep detector",
|
|
172
|
+
matcher: "Edit|Write"
|
|
173
|
+
}
|
|
174
|
+
];
|
|
175
|
+
let guardsInstalled = 0;
|
|
176
|
+
for (const guard of GUARDS) {
|
|
177
|
+
const destPath = join(guardsDir, guard.file);
|
|
178
|
+
const srcPath = join(srcGuardsDir, guard.file);
|
|
179
|
+
if (existsSync(srcPath)) {
|
|
180
|
+
writeFileSync(destPath, readFileSync(srcPath, "utf-8"), { mode: 493 });
|
|
181
|
+
} else if (!existsSync(destPath)) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
const event = guard.event;
|
|
185
|
+
if (!settings.hooks[event]) settings.hooks[event] = [];
|
|
186
|
+
const alreadyInstalled = settings.hooks[event].some(
|
|
187
|
+
(h) => h.hooks?.some?.((hh) => hh.command?.includes(guard.file))
|
|
188
|
+
);
|
|
189
|
+
if (!alreadyInstalled) {
|
|
190
|
+
const hookEntry = {
|
|
191
|
+
hooks: [{
|
|
192
|
+
type: "command",
|
|
193
|
+
command: `bash ${destPath}`,
|
|
194
|
+
timeout: 10
|
|
195
|
+
}]
|
|
196
|
+
};
|
|
197
|
+
if (guard.matcher) hookEntry.matcher = guard.matcher;
|
|
198
|
+
settings.hooks[event].push(hookEntry);
|
|
199
|
+
guardsInstalled++;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (guardsInstalled > 0) {
|
|
203
|
+
ok(`${guardsInstalled} REX guards installed (completion, safety, UI, scope)`);
|
|
204
|
+
} else {
|
|
205
|
+
skip("All REX guards already installed");
|
|
206
|
+
}
|
|
207
|
+
let ollamaOk = false;
|
|
208
|
+
try {
|
|
209
|
+
const res = await fetch("http://localhost:11434/api/tags");
|
|
210
|
+
ollamaOk = res.ok;
|
|
211
|
+
} catch {
|
|
212
|
+
}
|
|
213
|
+
if (ollamaOk) {
|
|
214
|
+
ok("Ollama running");
|
|
215
|
+
try {
|
|
216
|
+
const res = await fetch("http://localhost:11434/api/tags");
|
|
217
|
+
const data = await res.json();
|
|
218
|
+
const hasNomic = data.models?.some((m) => m.name?.includes("nomic-embed-text"));
|
|
219
|
+
if (hasNomic) {
|
|
220
|
+
ok("nomic-embed-text model available");
|
|
221
|
+
} else {
|
|
222
|
+
info("Pull embedding model: ollama pull nomic-embed-text");
|
|
223
|
+
}
|
|
224
|
+
} catch {
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
info("Ollama not running \u2014 needed for memory/RAG. Install: https://ollama.ai");
|
|
228
|
+
}
|
|
229
|
+
writeJson(settingsPath, settings);
|
|
230
|
+
console.log(`
|
|
231
|
+
${COLORS.dim}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${COLORS.reset}`);
|
|
232
|
+
console.log(`
|
|
233
|
+
${COLORS.bold} REX initialized!${COLORS.reset}`);
|
|
234
|
+
console.log(`
|
|
235
|
+
Next steps:`);
|
|
236
|
+
if (!ollamaOk) {
|
|
237
|
+
console.log(` 1. Install Ollama: ${COLORS.cyan}https://ollama.ai${COLORS.reset}`);
|
|
238
|
+
console.log(` 2. Pull model: ${COLORS.cyan}ollama pull nomic-embed-text${COLORS.reset}`);
|
|
239
|
+
console.log(` 3. Ingest history: ${COLORS.cyan}rex ingest${COLORS.reset}`);
|
|
240
|
+
} else {
|
|
241
|
+
console.log(` 1. Ingest session history: ${COLORS.cyan}rex ingest${COLORS.reset}`);
|
|
242
|
+
}
|
|
243
|
+
console.log(` \u2022 Run ${COLORS.cyan}rex doctor${COLORS.reset} to verify setup`);
|
|
244
|
+
console.log();
|
|
245
|
+
}
|
|
246
|
+
export {
|
|
247
|
+
init
|
|
248
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/optimize.ts
|
|
4
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
var COLORS = {
|
|
8
|
+
reset: "\x1B[0m",
|
|
9
|
+
green: "\x1B[32m",
|
|
10
|
+
yellow: "\x1B[33m",
|
|
11
|
+
red: "\x1B[31m",
|
|
12
|
+
bold: "\x1B[1m",
|
|
13
|
+
dim: "\x1B[2m",
|
|
14
|
+
cyan: "\x1B[36m"
|
|
15
|
+
};
|
|
16
|
+
var OLLAMA_URL = process.env.OLLAMA_URL || "http://localhost:11434";
|
|
17
|
+
var PREFERRED_MODELS = ["deepseek-r1:8b", "qwen2.5:1.5b", "llama3.2", "mistral"];
|
|
18
|
+
async function detectModel() {
|
|
19
|
+
if (process.env.REX_OPTIMIZE_MODEL) return process.env.REX_OPTIMIZE_MODEL;
|
|
20
|
+
try {
|
|
21
|
+
const res = await fetch(`${OLLAMA_URL}/api/tags`);
|
|
22
|
+
const data = await res.json();
|
|
23
|
+
const available = data.models.map((m) => m.name);
|
|
24
|
+
for (const pref of PREFERRED_MODELS) {
|
|
25
|
+
if (available.some((a) => a.includes(pref.split(":")[0]))) {
|
|
26
|
+
return available.find((a) => a.includes(pref.split(":")[0]));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return available.find((a) => !a.includes("embed")) || available[0];
|
|
30
|
+
} catch {
|
|
31
|
+
return "llama3.2";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function llm(prompt, system, model) {
|
|
35
|
+
const res = await fetch(`${OLLAMA_URL}/api/generate`, {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: { "Content-Type": "application/json" },
|
|
38
|
+
body: JSON.stringify({
|
|
39
|
+
model: model || "qwen2.5:1.5b",
|
|
40
|
+
prompt,
|
|
41
|
+
system,
|
|
42
|
+
stream: false
|
|
43
|
+
})
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok) throw new Error(`Ollama generate failed: ${res.status}`);
|
|
46
|
+
const data = await res.json();
|
|
47
|
+
return data.response;
|
|
48
|
+
}
|
|
49
|
+
async function optimize() {
|
|
50
|
+
const line = "\u2550".repeat(45);
|
|
51
|
+
console.log(`
|
|
52
|
+
${line}`);
|
|
53
|
+
console.log(`${COLORS.bold} REX OPTIMIZE${COLORS.reset}`);
|
|
54
|
+
console.log(`${line}
|
|
55
|
+
`);
|
|
56
|
+
try {
|
|
57
|
+
const res = await fetch(`${OLLAMA_URL}/api/tags`);
|
|
58
|
+
if (!res.ok) throw new Error();
|
|
59
|
+
} catch {
|
|
60
|
+
console.error(`${COLORS.red}Ollama not running.${COLORS.reset} Start it: ollama serve`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const cwd = process.cwd();
|
|
64
|
+
const projectClaudeMd = join(cwd, "CLAUDE.md");
|
|
65
|
+
const globalClaudeMd = join(homedir(), ".claude", "CLAUDE.md");
|
|
66
|
+
const target = existsSync(projectClaudeMd) ? projectClaudeMd : globalClaudeMd;
|
|
67
|
+
if (!existsSync(target)) {
|
|
68
|
+
console.error(`${COLORS.red}No CLAUDE.md found.${COLORS.reset}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
const content = readFileSync(target, "utf-8");
|
|
72
|
+
const lines = content.split("\n").length;
|
|
73
|
+
const chars = content.length;
|
|
74
|
+
const tokens = Math.ceil(chars / 4);
|
|
75
|
+
console.log(` ${COLORS.cyan}Target:${COLORS.reset} ${target}`);
|
|
76
|
+
console.log(` ${COLORS.cyan}Size:${COLORS.reset} ${lines} lines, ~${tokens} tokens`);
|
|
77
|
+
console.log();
|
|
78
|
+
const model = await detectModel();
|
|
79
|
+
console.log(` ${COLORS.dim}Analyzing with ${model}...${COLORS.reset}`);
|
|
80
|
+
const analysis = await llm(
|
|
81
|
+
`Analyze this CLAUDE.md file and provide specific suggestions to reduce its token count while keeping all important instructions. Focus on:
|
|
82
|
+
1. Redundant or duplicate instructions
|
|
83
|
+
2. Overly verbose sections that could be shortened
|
|
84
|
+
3. Content that could be moved to separate files and @imported
|
|
85
|
+
4. Dead or outdated references
|
|
86
|
+
|
|
87
|
+
CLAUDE.md content:
|
|
88
|
+
---
|
|
89
|
+
${content.slice(0, 6e3)}
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
Provide a concise analysis with specific, actionable suggestions. Format each suggestion as:
|
|
93
|
+
- [SECTION] What to change and why (estimated savings: N tokens)`,
|
|
94
|
+
"You are a technical editor that optimizes AI instruction files. Be direct and specific. Output only the analysis, no preamble.",
|
|
95
|
+
model
|
|
96
|
+
);
|
|
97
|
+
console.log(`
|
|
98
|
+
${COLORS.bold} Analysis:${COLORS.reset}
|
|
99
|
+
`);
|
|
100
|
+
for (const line2 of analysis.split("\n")) {
|
|
101
|
+
console.log(` ${line2}`);
|
|
102
|
+
}
|
|
103
|
+
console.log(`
|
|
104
|
+
${COLORS.dim}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${COLORS.reset}`);
|
|
105
|
+
console.log(`
|
|
106
|
+
${COLORS.dim}Tip: Run ${COLORS.cyan}rex optimize --apply${COLORS.reset}${COLORS.dim} to auto-apply suggestions${COLORS.reset}`);
|
|
107
|
+
console.log();
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
optimize
|
|
111
|
+
};
|
package/package.json
CHANGED
|
@@ -1,39 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rex-claude",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Claude Code sous steroides — guards, health checks, memory RAG",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"rex": "dist/
|
|
7
|
+
"rex": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"dist
|
|
11
|
-
"dotfiles/",
|
|
12
|
-
"memory/src/",
|
|
13
|
-
"memory/package.json",
|
|
14
|
-
"memory/package-lock.json",
|
|
15
|
-
"memory/tsconfig.json",
|
|
16
|
-
"activity/",
|
|
17
|
-
"tmux/"
|
|
10
|
+
"dist"
|
|
18
11
|
],
|
|
19
12
|
"scripts": {
|
|
20
|
-
"build": "
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
"publishConfig": {
|
|
24
|
-
"access": "public"
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"dev": "tsup --watch",
|
|
15
|
+
"postinstall": "node dist/index.js init 2>/dev/null || true"
|
|
25
16
|
},
|
|
26
17
|
"keywords": [
|
|
27
|
-
"claude-code",
|
|
28
18
|
"claude",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
19
|
+
"claude-code",
|
|
20
|
+
"llm",
|
|
21
|
+
"developer-tools",
|
|
22
|
+
"productivity",
|
|
23
|
+
"ai-assistant",
|
|
24
|
+
"guards",
|
|
25
|
+
"hooks"
|
|
32
26
|
],
|
|
33
27
|
"author": "Kevin <kevin@dstudio.company>",
|
|
34
28
|
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/Keiy78120/rex"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/Keiy78120/rex#readme",
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20"
|
|
36
|
+
},
|
|
35
37
|
"devDependencies": {
|
|
36
|
-
"@
|
|
37
|
-
"
|
|
38
|
+
"@rex/core": "workspace:*",
|
|
39
|
+
"tsup": "^8.0.0",
|
|
40
|
+
"typescript": "^5.7.0",
|
|
41
|
+
"@types/node": "^22.0.0"
|
|
38
42
|
}
|
|
39
43
|
}
|
package/README.md
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
# REX — Senior Dev Companion
|
|
2
|
-
|
|
3
|
-
Config unifiée Claude Code + MCP memory server + activity logger pour Kevin (D-Studio).
|
|
4
|
-
|
|
5
|
-
## Architecture
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
rex/
|
|
9
|
-
├── dotfiles/ # Claude Code config (symlinked → ~/.claude/)
|
|
10
|
-
│ ├── CLAUDE.md # Instructions globales REX
|
|
11
|
-
│ ├── settings.json # MCP servers, hooks, plugins
|
|
12
|
-
│ ├── commands/ # Slash commands custom
|
|
13
|
-
│ ├── rules/ # Règles auto-chargées au boot (7 fichiers)
|
|
14
|
-
│ ├── skills/ # Skills on-demand (8 skills)
|
|
15
|
-
│ ├── agents/ # Vide — migré vers skills
|
|
16
|
-
│ ├── docs/ # Cache docs frameworks (chargé on-demand)
|
|
17
|
-
│ └── templates/ # Templates de projets
|
|
18
|
-
├── memory/ # MCP Server REX-Memory
|
|
19
|
-
│ ├── src/
|
|
20
|
-
│ │ ├── server.ts # 3 tools MCP : rex_search, rex_learn, rex_context
|
|
21
|
-
│ │ ├── ingest.ts # Parse sessions JSONL → SQLite + embeddings
|
|
22
|
-
│ │ ├── embed.ts # Embeddings via Ollama (qwen3-embedding:4b)
|
|
23
|
-
│ │ └── search.ts # Recherche sémantique sqlite-vec
|
|
24
|
-
│ ├── db/ # SQLite DB (gitignored)
|
|
25
|
-
│ ├── package.json
|
|
26
|
-
│ └── tsconfig.json
|
|
27
|
-
├── activity/ # Hammerspoon activity logger
|
|
28
|
-
│ ├── init.lua # Log app switches → JSONL
|
|
29
|
-
│ └── config.lua # Config (chemin de log, intervalle)
|
|
30
|
-
├── install.sh # Setup complet en 1 commande
|
|
31
|
-
├── package.json
|
|
32
|
-
└── .gitignore
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Installation
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
cd ~/Documents/Developer/_config/rex
|
|
39
|
-
./install.sh
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Le script :
|
|
43
|
-
1. Crée les symlinks `dotfiles/*` → `~/.claude/`
|
|
44
|
-
2. `npm install` dans memory/
|
|
45
|
-
3. Build le MCP server (`npm run build`)
|
|
46
|
-
4. Copie la config Hammerspoon
|
|
47
|
-
5. Enregistre le MCP server dans `~/.claude/settings.json`
|
|
48
|
-
|
|
49
|
-
## MCP Server — REX Memory
|
|
50
|
-
|
|
51
|
-
### Tools disponibles
|
|
52
|
-
|
|
53
|
-
| Tool | Usage |
|
|
54
|
-
|------|-------|
|
|
55
|
-
| `rex_search(query)` | Recherche sémantique dans les sessions passées et faits mémorisés |
|
|
56
|
-
| `rex_learn(fact, category)` | Mémorise un pattern, debug insight, ou préférence |
|
|
57
|
-
| `rex_context(project_path)` | Retourne le contexte pertinent pour le projet courant |
|
|
58
|
-
|
|
59
|
-
### Stack technique
|
|
60
|
-
|
|
61
|
-
- **DB** : SQLite + [sqlite-vec](https://github.com/asg017/sqlite-vec) pour la recherche vectorielle
|
|
62
|
-
- **Embeddings** : Ollama local avec `qwen3-embedding:4b` (2560 dimensions)
|
|
63
|
-
- **Ingestion** : parse les JSONL de `~/.claude/projects/`, extrait messages + tool_use + metadata
|
|
64
|
-
- **Fix notable** : `CAST(? AS INTEGER)` pour contourner un bug sqlite-vec avec les BigInt rowid
|
|
65
|
-
|
|
66
|
-
### Ingestion manuelle
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
cd memory && npm run ingest
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Ingestion automatique
|
|
73
|
-
|
|
74
|
-
Un LaunchAgent macOS tourne toutes les heures :
|
|
75
|
-
- Fichier : `~/Library/LaunchAgents/com.dstudio.rex-ingest.plist`
|
|
76
|
-
- Logs : `/tmp/rex-ingest.log`
|
|
77
|
-
- RunAtLoad : oui (se lance au démarrage du Mac)
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
# Vérifier le status
|
|
81
|
-
launchctl list | grep rex
|
|
82
|
-
|
|
83
|
-
# Forcer une exécution
|
|
84
|
-
launchctl kickstart gui/$(id -u)/com.dstudio.rex-ingest
|
|
85
|
-
|
|
86
|
-
# Voir les logs
|
|
87
|
-
tail -f /tmp/rex-ingest.log
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## Activity Logger (Hammerspoon)
|
|
91
|
-
|
|
92
|
-
Log les changements d'app active en JSONL :
|
|
93
|
-
- **Quoi** : app name, durée, timestamps
|
|
94
|
-
- **Pas de keylogger** (privacy)
|
|
95
|
-
- **Fichier** : `rex/activity/activity.jsonl`
|
|
96
|
-
- **Chargé via** : `~/.hammerspoon/init.lua` → `rex/activity/init.lua`
|
|
97
|
-
|
|
98
|
-
## Skills (chargés on-demand)
|
|
99
|
-
|
|
100
|
-
| Skill | Description |
|
|
101
|
-
|-------|-------------|
|
|
102
|
-
| `rex-boot` | Briefing de session — auto-détecte projet, git, PRs, demande l'objectif |
|
|
103
|
-
| `context-loader` | Charge docs + CLAUDE.md + mémoire REX avant de bosser |
|
|
104
|
-
| `debug-assist` | Debugging systématique — parse erreur, cherche dans mémoire, root cause |
|
|
105
|
-
| `token-guard` | Audit du contexte — fichiers redondants, sorties trop longues, suggestions /compact |
|
|
106
|
-
| `project-init` | Init un nouveau projet avec CLAUDE.md, git, docs cache |
|
|
107
|
-
| `build-validate` | Vérifie build, lint, tests, dev server — reporte sans modifier |
|
|
108
|
-
| `code-review` | Review de code : logique, sécu, perf, TypeScript strictness |
|
|
109
|
-
| `one-shot` | Génère un projet complet Next.js + Shadcn en une passe |
|
|
110
|
-
|
|
111
|
-
## Rules (chargées au boot, ~370 lignes total)
|
|
112
|
-
|
|
113
|
-
| Fichier | Contenu |
|
|
114
|
-
|---------|---------|
|
|
115
|
-
| `defensive-engineering.md` | Scale, pagination, rate limits, error handling |
|
|
116
|
-
| `api-design.md` | REST conventions, response envelopes, status codes |
|
|
117
|
-
| `frontend.md` | Loading/empty/error states, SSR, hydration, forms, a11y |
|
|
118
|
-
| `security.md` | OWASP, secrets, SQL injection, XSS, CORS, auth |
|
|
119
|
-
| `testing.md` | Test discipline, build verification, mocking |
|
|
120
|
-
| `git-workflow.md` | Commit conventions, branching, PR process |
|
|
121
|
-
| `never-assume.md` | Règles anti-erreurs, alternatives obligatoires |
|
|
122
|
-
| `docs-first.md` | Documentation-first, cache local, Context7 |
|
|
123
|
-
|
|
124
|
-
## Docs Cache (~/.claude/docs/)
|
|
125
|
-
|
|
126
|
-
Fichiers de patterns/gotchas pré-chargés (lus on-demand uniquement) :
|
|
127
|
-
|
|
128
|
-
- `nextjs.md` — App Router, SSR, caching, middleware
|
|
129
|
-
- `react.md` — Hooks, patterns, performance
|
|
130
|
-
- `cloudflare.md` — Workers, D1, KV, limites
|
|
131
|
-
- `telegram-bot.md` — Bot API, rate limits, webhooks
|
|
132
|
-
- `tailwind.md` — Classes utilitaires, responsive, dark mode
|
|
133
|
-
|
|
134
|
-
## Plugins Claude Code
|
|
135
|
-
|
|
136
|
-
| Plugin | Status | Notes |
|
|
137
|
-
|--------|--------|-------|
|
|
138
|
-
| Playwright | Actif | Browser automation, tests E2E |
|
|
139
|
-
| Frontend Design | Actif | UI/design quality |
|
|
140
|
-
| Figma | **Désactivé** | Réactiver manuellement quand besoin (auth pénible) |
|
|
141
|
-
| Superpowers | Actif | Capacités étendues |
|
|
142
|
-
| Trail of Bits (x4) | Actif | Sécurité : static analysis, semgrep, audit, insecure defaults |
|
|
143
|
-
| Vercel | Désactivé | Pas utilisé |
|
|
144
|
-
| Ralph Loop | Désactivé | Pas utilisé |
|
|
145
|
-
|
|
146
|
-
Pour activer/désactiver : éditer `~/.claude/settings.json` > `enabledPlugins`.
|
|
147
|
-
|
|
148
|
-
## Services auto-start au boot Mac
|
|
149
|
-
|
|
150
|
-
| Service | Méthode | Vérification |
|
|
151
|
-
|---------|---------|-------------|
|
|
152
|
-
| Ollama | Login Items macOS | `pgrep ollama` |
|
|
153
|
-
| Hammerspoon | Login Items macOS | Icône menubar |
|
|
154
|
-
| REX Ingest | LaunchAgent (hourly) | `launchctl list \| grep rex` |
|
|
155
|
-
| MCP Server | Claude Code (auto) | Démarre avec Claude |
|
|
156
|
-
|
|
157
|
-
## Historique des décisions
|
|
158
|
-
|
|
159
|
-
1. **agents → skills** : les agents se chargent entièrement au boot (~500 tokens chacun), les skills ne chargent que les métadonnées (~700 tokens pour les 8). Économie significative.
|
|
160
|
-
2. **docs on-demand** : les fichiers `~/.claude/docs/` ne sont jamais lus au boot, seulement quand le framework est pertinent pour la tâche en cours.
|
|
161
|
-
3. **sqlite-vec CAST workaround** : le rowid en BigInt de `better-sqlite3` est rejeté par sqlite-vec. Fix : `CAST(? AS INTEGER)` dans le SQL.
|
|
162
|
-
4. **Figma désactivé par défaut** : re-auth fréquente, activé manuellement quand besoin.
|
|
163
|
-
5. **qwen3-embedding:4b** : modèle d'embedding local via Ollama, 2560 dimensions, déjà installé.
|