rex-claude 4.0.0 → 6.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.
Files changed (38) hide show
  1. package/dist/agents-JIZXXASP.js +853 -0
  2. package/dist/app-3VWDSH5F.js +248 -0
  3. package/dist/audio-US2J627E.js +196 -0
  4. package/dist/audit-ZVTGE4L4.js +8 -0
  5. package/dist/call-AQZ3Z5SE.js +143 -0
  6. package/dist/chunk-5ND7JYY3.js +62 -0
  7. package/dist/chunk-6SRV2I2H.js +56 -0
  8. package/dist/{setup-AO3MW46W.js → chunk-A7ZLQUOX.js} +93 -16
  9. package/dist/chunk-E5UYN3W7.js +105 -0
  10. package/dist/chunk-HAHJD3QH.js +147 -0
  11. package/dist/{init-DLFEGD6O.js → chunk-KR7ISYZH.js} +328 -29
  12. package/dist/chunk-LTOM55UV.js +154 -0
  13. package/dist/chunk-PDX44BCA.js +11 -0
  14. package/dist/chunk-PPGYFMU5.js +67 -0
  15. package/dist/{chunk-7AGI43F5.js → chunk-WBMVBMWB.js} +4 -2
  16. package/dist/{context-FN5O5YBI.js → context-XNCG2M5Q.js} +2 -1
  17. package/dist/daemon-5KNSNFTD.js +208 -0
  18. package/dist/gateway-YLP66MCQ.js +2273 -0
  19. package/dist/hammerspoon/rex-call-watcher.lua +186 -0
  20. package/dist/index.js +309 -15
  21. package/dist/init-RDZFIBLA.js +30 -0
  22. package/dist/install-63JBDPRU.js +41 -0
  23. package/dist/{llm-YRORUH7E.js → llm-RALIPIMI.js} +2 -1
  24. package/dist/mcp_registry-DX4GGSP6.js +514 -0
  25. package/dist/migrate-GDO37TI5.js +87 -0
  26. package/dist/{optimize-UKMAGQQE.js → optimize-5TE5RKZV.js} +2 -1
  27. package/dist/paths-4SECM6E6.js +38 -0
  28. package/dist/preload-I3MYBVNU.js +78 -0
  29. package/dist/projects-V6TSLO7E.js +17 -0
  30. package/dist/{prune-2PPIVDXK.js → prune-B7F5B5OF.js} +2 -1
  31. package/dist/recategorize-YXYIMQLZ.js +155 -0
  32. package/dist/router-2JD34COX.js +12 -0
  33. package/dist/self-improve-YK7RCYF4.js +197 -0
  34. package/dist/setup-KNDTVFO6.js +8 -0
  35. package/dist/skills-AIWFY5NH.js +374 -0
  36. package/dist/voice-RITC3EVC.js +248 -0
  37. package/package.json +12 -3
  38. package/dist/gateway-EKMU5D7J.js +0 -784
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ findProject
4
+ } from "./chunk-LTOM55UV.js";
5
+ import "./chunk-PPGYFMU5.js";
6
+ import {
7
+ createLogger
8
+ } from "./chunk-5ND7JYY3.js";
9
+ import {
10
+ MEMORY_DB_PATH
11
+ } from "./chunk-6SRV2I2H.js";
12
+ import "./chunk-PDX44BCA.js";
13
+
14
+ // src/preload.ts
15
+ import Database from "better-sqlite3";
16
+ import * as sqliteVec from "sqlite-vec";
17
+ import { existsSync } from "fs";
18
+ var log = createLogger("preload");
19
+ var MAX_TOKENS = 200;
20
+ async function preload(cwd) {
21
+ if (!existsSync(MEMORY_DB_PATH)) {
22
+ log.debug("No memory DB found, skipping preload");
23
+ return "";
24
+ }
25
+ const project = findProject(cwd);
26
+ const sections = [];
27
+ let db;
28
+ try {
29
+ db = new Database(MEMORY_DB_PATH, { readonly: true });
30
+ sqliteVec.load(db);
31
+ db.pragma("journal_mode = WAL");
32
+ } catch (e) {
33
+ log.error(`Failed to open memory DB: ${e.message?.slice(0, 100)}`);
34
+ return "";
35
+ }
36
+ try {
37
+ if (project) {
38
+ const recent = db.prepare(
39
+ "SELECT summary, category FROM memories WHERE content LIKE ? AND category != 'session' AND summary IS NOT NULL ORDER BY created_at DESC LIMIT 3"
40
+ ).all(`%${project.name}%`);
41
+ if (recent.length) {
42
+ sections.push(`[REX Context] Project: ${project.name} | ${project.stack.join(", ")}`);
43
+ sections.push(`Last: ${recent[0].summary.slice(0, 80)}`);
44
+ }
45
+ }
46
+ const lessons = db.prepare(
47
+ "SELECT summary FROM memories WHERE category = 'lesson' AND summary IS NOT NULL ORDER BY created_at DESC LIMIT 3"
48
+ ).all();
49
+ if (lessons.length) {
50
+ sections.push("Lessons:");
51
+ for (const l of lessons) {
52
+ sections.push(` - ${l.summary.slice(0, 100)}`);
53
+ }
54
+ }
55
+ if (project) {
56
+ const patterns = db.prepare(
57
+ "SELECT summary FROM memories WHERE category = 'pattern' AND summary IS NOT NULL AND content LIKE ? ORDER BY created_at DESC LIMIT 2"
58
+ ).all(`%${project.name}%`);
59
+ if (patterns.length) {
60
+ sections.push("Patterns:");
61
+ for (const p of patterns) {
62
+ sections.push(` - ${p.summary.slice(0, 100)}`);
63
+ }
64
+ }
65
+ }
66
+ } finally {
67
+ db.close();
68
+ }
69
+ const output = sections.join("\n");
70
+ log.info(`Preloaded ${sections.length} sections for ${project?.name || cwd} (${output.length} chars)`);
71
+ if (output.length > MAX_TOKENS * 4) {
72
+ return output.slice(0, MAX_TOKENS * 4);
73
+ }
74
+ return output;
75
+ }
76
+ export {
77
+ preload
78
+ };
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ findProject,
4
+ loadProjectIndex,
5
+ saveProjectIndex,
6
+ scanProjects
7
+ } from "./chunk-LTOM55UV.js";
8
+ import "./chunk-PPGYFMU5.js";
9
+ import "./chunk-5ND7JYY3.js";
10
+ import "./chunk-6SRV2I2H.js";
11
+ import "./chunk-PDX44BCA.js";
12
+ export {
13
+ findProject,
14
+ loadProjectIndex,
15
+ saveProjectIndex,
16
+ scanProjects
17
+ };
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import "./chunk-PDX44BCA.js";
2
3
 
3
4
  // src/prune.ts
4
5
  import { existsSync, statSync } from "fs";
@@ -49,7 +50,7 @@ ${line}`);
49
50
  }
50
51
  try {
51
52
  Database = (await import(join(memDir, "node_modules", "better-sqlite3", "lib", "index.js"))).default;
52
- sqliteVec = await import(join(memDir, "node_modules", "sqlite-vec", "src", "index.js"));
53
+ sqliteVec = await import(join(memDir, "node_modules", "sqlite-vec", "index.mjs"));
53
54
  } catch (err) {
54
55
  console.log(` ${COLORS.red}Cannot load database modules:${COLORS.reset} ${err.message}
55
56
  `);
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ pickModel
4
+ } from "./chunk-E5UYN3W7.js";
5
+ import {
6
+ loadConfig
7
+ } from "./chunk-PPGYFMU5.js";
8
+ import {
9
+ createLogger
10
+ } from "./chunk-5ND7JYY3.js";
11
+ import {
12
+ MEMORY_DB_PATH
13
+ } from "./chunk-6SRV2I2H.js";
14
+ import {
15
+ llm
16
+ } from "./chunk-WBMVBMWB.js";
17
+ import "./chunk-PDX44BCA.js";
18
+
19
+ // src/recategorize.ts
20
+ import Database from "better-sqlite3";
21
+ import * as sqliteVec from "sqlite-vec";
22
+ var log = createLogger("recategorize");
23
+ var OLLAMA_URL = process.env.OLLAMA_URL || "http://localhost:11434";
24
+ var VALID_CATEGORIES = ["debug", "fix", "pattern", "lesson", "architecture", "config", "project", "reference", "session"];
25
+ var CLASSIFY_PROMPT = (content) => `Classify this developer memory chunk. Output ONLY valid JSON, no markdown.
26
+
27
+ Categories: debug, fix, pattern, lesson, architecture, config, project, reference, session
28
+
29
+ - debug: debugging an issue, tracing errors
30
+ - fix: applying a fix or patch, solution found
31
+ - pattern: reusable code patterns, techniques
32
+ - lesson: lessons learned, mistakes to avoid
33
+ - architecture: system design, structure decisions
34
+ - config: configuration changes, setup steps
35
+ - project: project overview, stack, status
36
+ - reference: API docs, external knowledge, lib behavior
37
+ - session: general content (default fallback)
38
+
39
+ Content:
40
+ ${content.slice(0, 1500)}
41
+
42
+ JSON output: {"category": "<one of the above>", "summary": "<1-2 sentence summary>"}`;
43
+ async function classifyChunk(content, routing, claudeFallback) {
44
+ const prompt = CLASSIFY_PROMPT(content);
45
+ if (routing !== "claude-only") {
46
+ try {
47
+ const model = await pickModel("categorize");
48
+ const res = await fetch(`${OLLAMA_URL}/api/generate`, {
49
+ method: "POST",
50
+ headers: { "Content-Type": "application/json" },
51
+ body: JSON.stringify({ model, prompt, stream: false, options: { temperature: 0.3, num_ctx: 4096 } }),
52
+ signal: AbortSignal.timeout(15e3)
53
+ });
54
+ if (res.ok) {
55
+ const data = await res.json();
56
+ const parsed = parseJsonResponse(data.response);
57
+ if (parsed) return parsed;
58
+ }
59
+ } catch {
60
+ }
61
+ }
62
+ if (routing !== "ollama-only") {
63
+ try {
64
+ const result = await llm(prompt, void 0, claudeFallback);
65
+ return parseJsonResponse(result);
66
+ } catch {
67
+ }
68
+ }
69
+ return null;
70
+ }
71
+ function parseJsonResponse(raw) {
72
+ let parsed = null;
73
+ try {
74
+ parsed = JSON.parse(raw);
75
+ } catch {
76
+ }
77
+ if (!parsed) {
78
+ const fence = raw.match(/```(?:json)?\s*([\s\S]*?)```/);
79
+ if (fence) {
80
+ try {
81
+ parsed = JSON.parse(fence[1].trim());
82
+ } catch {
83
+ }
84
+ }
85
+ }
86
+ if (!parsed) {
87
+ const brace = raw.match(/\{[\s\S]*\}/);
88
+ if (brace) {
89
+ try {
90
+ parsed = JSON.parse(brace[0]);
91
+ } catch {
92
+ }
93
+ }
94
+ }
95
+ if (!parsed) return null;
96
+ const category = VALID_CATEGORIES.includes(parsed.category) ? parsed.category : "session";
97
+ const summary = typeof parsed.summary === "string" && parsed.summary.length > 5 ? parsed.summary : null;
98
+ if (!summary) return null;
99
+ return { category, summary };
100
+ }
101
+ async function recategorize(options = {}) {
102
+ const COLORS = { green: "\x1B[32m", yellow: "\x1B[33m", red: "\x1B[31m", cyan: "\x1B[36m", dim: "\x1B[2m", reset: "\x1B[0m", bold: "\x1B[1m" };
103
+ const config = loadConfig();
104
+ const batchSize = options.batch ?? 50;
105
+ const db = new Database(MEMORY_DB_PATH);
106
+ sqliteVec.load(db);
107
+ db.pragma("journal_mode = WAL");
108
+ const rows = db.prepare(
109
+ "SELECT id, content FROM memories WHERE category IN ('session', 'general') OR needs_reprocess = 1 LIMIT ?"
110
+ ).all(batchSize);
111
+ log.info(`Starting recategorize: ${rows.length} memories to process (batch: ${batchSize})`);
112
+ console.log(`
113
+ ${COLORS.bold}REX Recategorize${COLORS.reset}`);
114
+ console.log(`${COLORS.dim}Found ${rows.length} memories to process (batch: ${batchSize})${COLORS.reset}
115
+ `);
116
+ if (rows.length === 0) {
117
+ console.log(`${COLORS.green}All memories are already categorized.${COLORS.reset}`);
118
+ db.close();
119
+ return;
120
+ }
121
+ if (options.dryRun) {
122
+ console.log(`${COLORS.yellow}[dry-run] Would process ${rows.length} memories. Nothing saved.${COLORS.reset}`);
123
+ db.close();
124
+ return;
125
+ }
126
+ const update = db.prepare("UPDATE memories SET category = ?, summary = ?, needs_reprocess = 0 WHERE id = ?");
127
+ let processed = 0;
128
+ let failed = 0;
129
+ const stats = {};
130
+ for (const row of rows) {
131
+ const result = await classifyChunk(row.content, config.llm.routing, config.llm.claudeFallback);
132
+ if (result) {
133
+ update.run(result.category, result.summary, row.id);
134
+ stats[result.category] = (stats[result.category] || 0) + 1;
135
+ processed++;
136
+ process.stdout.write(`\r ${COLORS.cyan}${processed}/${rows.length}${COLORS.reset} processed`);
137
+ } else {
138
+ log.warn(`Failed to classify memory id=${row.id}, flagged for retry`);
139
+ db.prepare("UPDATE memories SET needs_reprocess = 1 WHERE id = ?").run(row.id);
140
+ failed++;
141
+ }
142
+ }
143
+ log.info(`Done: ${processed} categorized, ${failed} failed`);
144
+ console.log(`
145
+
146
+ ${COLORS.green}Done:${COLORS.reset} ${processed} categorized, ${failed} failed (flagged for retry)
147
+ `);
148
+ for (const [cat, count] of Object.entries(stats).sort((a, b) => b[1] - a[1])) {
149
+ console.log(` ${cat.padEnd(15)} ${count}`);
150
+ }
151
+ db.close();
152
+ }
153
+ export {
154
+ recategorize
155
+ };
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getRouterSnapshot,
4
+ pickModel,
5
+ showModelRouter
6
+ } from "./chunk-E5UYN3W7.js";
7
+ import "./chunk-PDX44BCA.js";
8
+ export {
9
+ getRouterSnapshot,
10
+ pickModel,
11
+ showModelRouter
12
+ };
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ pickModel
4
+ } from "./chunk-E5UYN3W7.js";
5
+ import {
6
+ loadConfig
7
+ } from "./chunk-PPGYFMU5.js";
8
+ import {
9
+ createLogger
10
+ } from "./chunk-5ND7JYY3.js";
11
+ import {
12
+ MEMORY_DB_PATH,
13
+ SELF_IMPROVEMENT_DIR
14
+ } from "./chunk-6SRV2I2H.js";
15
+ import {
16
+ llm
17
+ } from "./chunk-WBMVBMWB.js";
18
+ import "./chunk-PDX44BCA.js";
19
+
20
+ // src/self-improve.ts
21
+ import { readFileSync, writeFileSync, existsSync } from "fs";
22
+ import { join } from "path";
23
+ import Database from "better-sqlite3";
24
+ import * as sqliteVec from "sqlite-vec";
25
+ var log = createLogger("self-improve");
26
+ function loadLessons() {
27
+ const path = join(SELF_IMPROVEMENT_DIR, "lessons.json");
28
+ if (!existsSync(path)) return [];
29
+ try {
30
+ return JSON.parse(readFileSync(path, "utf-8"));
31
+ } catch {
32
+ return [];
33
+ }
34
+ }
35
+ function saveLessons(lessons) {
36
+ writeFileSync(join(SELF_IMPROVEMENT_DIR, "lessons.json"), JSON.stringify(lessons, null, 2));
37
+ }
38
+ function loadErrorPatterns() {
39
+ const path = join(SELF_IMPROVEMENT_DIR, "error-patterns.json");
40
+ if (!existsSync(path)) return [];
41
+ try {
42
+ return JSON.parse(readFileSync(path, "utf-8"));
43
+ } catch {
44
+ return [];
45
+ }
46
+ }
47
+ function saveErrorPatterns(patterns) {
48
+ writeFileSync(join(SELF_IMPROVEMENT_DIR, "error-patterns.json"), JSON.stringify(patterns, null, 2));
49
+ }
50
+ async function selfReview() {
51
+ const COLORS = { green: "\x1B[32m", yellow: "\x1B[33m", dim: "\x1B[2m", reset: "\x1B[0m", bold: "\x1B[1m" };
52
+ const config = loadConfig();
53
+ if (!config.selfImprovement.enabled) {
54
+ log.info("Self-improvement disabled in config");
55
+ console.log(`${COLORS.dim}Self-improvement disabled in config.${COLORS.reset}`);
56
+ return { newLessons: 0, ruleCandidates: 0 };
57
+ }
58
+ if (!existsSync(MEMORY_DB_PATH)) {
59
+ log.warn(`No memory DB found at ${MEMORY_DB_PATH}`);
60
+ console.log(`${COLORS.yellow}No memory DB found at ${MEMORY_DB_PATH}${COLORS.reset}`);
61
+ return { newLessons: 0, ruleCandidates: 0 };
62
+ }
63
+ console.log(`
64
+ ${COLORS.bold}REX Self-Review${COLORS.reset}
65
+ `);
66
+ const db = new Database(MEMORY_DB_PATH, { readonly: true });
67
+ sqliteVec.load(db);
68
+ db.pragma("journal_mode = WAL");
69
+ const recentLessons = db.prepare(
70
+ "SELECT id, summary, content, created_at FROM memories WHERE category = 'lesson' AND summary IS NOT NULL ORDER BY created_at DESC LIMIT 20"
71
+ ).all();
72
+ const existingLessons = loadLessons();
73
+ const existingTexts = new Set(existingLessons.map((l) => l.text.toLowerCase().trim()));
74
+ let newCount = 0;
75
+ for (const mem of recentLessons) {
76
+ const text = mem.summary.trim();
77
+ if (existingTexts.has(text.toLowerCase())) {
78
+ const existing = existingLessons.find((l) => l.text.toLowerCase().trim() === text.toLowerCase());
79
+ if (existing) {
80
+ existing.occurrences++;
81
+ existing.lastSeen = mem.created_at;
82
+ }
83
+ continue;
84
+ }
85
+ existingLessons.push({
86
+ id: `lesson-${Date.now()}-${mem.id}`,
87
+ text,
88
+ category: "lesson",
89
+ occurrences: 1,
90
+ firstSeen: mem.created_at,
91
+ lastSeen: mem.created_at,
92
+ promoted: false,
93
+ dismissed: false
94
+ });
95
+ newCount++;
96
+ }
97
+ saveLessons(existingLessons);
98
+ log.info(`Lessons: ${existingLessons.length} total, ${newCount} new`);
99
+ console.log(` Lessons: ${existingLessons.length} total, ${newCount} new`);
100
+ const errorMemories = db.prepare(
101
+ "SELECT summary FROM memories WHERE category IN ('debug', 'fix') AND summary IS NOT NULL ORDER BY created_at DESC LIMIT 50"
102
+ ).all();
103
+ const patterns = loadErrorPatterns();
104
+ let ruleCandidates = 0;
105
+ if (errorMemories.length >= 5) {
106
+ const errorSummaries = errorMemories.map((m) => m.summary).join("\n");
107
+ try {
108
+ const model = await pickModel("reason");
109
+ console.log(` Analyzing ${errorMemories.length} error/fix memories with ${model}...`);
110
+ const analysis = await llm(
111
+ `Analyze these error/fix summaries for recurring patterns. Output JSON array of patterns:
112
+
113
+ ${errorSummaries.slice(0, 3e3)}
114
+
115
+ JSON: [{"pattern": "description", "count": estimated_occurrences, "suggestedRule": "rule text"}]`,
116
+ "You are a code pattern analyzer. Output ONLY valid JSON.",
117
+ model
118
+ );
119
+ let parsed = [];
120
+ try {
121
+ parsed = JSON.parse(analysis);
122
+ } catch {
123
+ const brace = analysis.match(/\[[\s\S]*\]/);
124
+ if (brace) {
125
+ try {
126
+ parsed = JSON.parse(brace[0]);
127
+ } catch {
128
+ }
129
+ }
130
+ }
131
+ for (const p of parsed) {
132
+ if (p.count >= config.selfImprovement.ruleThreshold) {
133
+ const exists = patterns.find((ep) => ep.pattern === p.pattern);
134
+ if (!exists) {
135
+ patterns.push({
136
+ pattern: p.pattern,
137
+ count: p.count,
138
+ firstSeen: (/* @__PURE__ */ new Date()).toISOString(),
139
+ lastSeen: (/* @__PURE__ */ new Date()).toISOString(),
140
+ suggestedRule: p.suggestedRule
141
+ });
142
+ ruleCandidates++;
143
+ }
144
+ }
145
+ }
146
+ } catch (e) {
147
+ log.warn(`Pattern analysis skipped: ${e.message?.slice(0, 100)}`);
148
+ console.log(` ${COLORS.yellow}Pattern analysis skipped (LLM unavailable)${COLORS.reset}`);
149
+ }
150
+ } else {
151
+ console.log(` ${COLORS.dim}Not enough error memories for pattern analysis (need 5+, have ${errorMemories.length})${COLORS.reset}`);
152
+ }
153
+ saveErrorPatterns(patterns);
154
+ const activePatterns = patterns.filter((p) => p.count >= config.selfImprovement.ruleThreshold);
155
+ if (activePatterns.length) {
156
+ const md = `# Rule Candidates
157
+
158
+ These patterns were detected ${config.selfImprovement.ruleThreshold}+ times. Review and approve with \`rex promote-rule <index>\`.
159
+
160
+ ` + activePatterns.map((p, i) => `## ${i + 1}. ${p.pattern}
161
+
162
+ - Count: ${p.count}
163
+ - First seen: ${p.firstSeen}
164
+ - Suggested rule: ${p.suggestedRule || "N/A"}
165
+ `).join("\n");
166
+ writeFileSync(join(SELF_IMPROVEMENT_DIR, "rule-candidates.md"), md);
167
+ console.log(` Rule candidates: ${activePatterns.length} (see ~/.claude/rex/self-improvement/rule-candidates.md)`);
168
+ }
169
+ db.close();
170
+ log.info(`Self-review done: ${newCount} new lessons, ${ruleCandidates} rule candidates`);
171
+ console.log(`
172
+ ${COLORS.green}Done.${COLORS.reset} ${newCount} new lessons, ${ruleCandidates} rule candidates
173
+ `);
174
+ return { newLessons: newCount, ruleCandidates };
175
+ }
176
+ async function promoteRule(index) {
177
+ const patterns = loadErrorPatterns();
178
+ const active = patterns.filter((p) => p.count >= loadConfig().selfImprovement.ruleThreshold);
179
+ if (index < 1 || index > active.length) return false;
180
+ const pattern = active[index - 1];
181
+ if (!pattern.suggestedRule) return false;
182
+ const HOME = process.env.HOME || "~";
183
+ const rulesDir = join(HOME, ".claude", "rules");
184
+ const ruleName = pattern.pattern.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 40);
185
+ const rulePath = join(rulesDir, `${ruleName}.md`);
186
+ writeFileSync(rulePath, `# ${pattern.pattern}
187
+
188
+ ${pattern.suggestedRule}
189
+
190
+ <!-- Auto-generated by REX self-improvement on ${(/* @__PURE__ */ new Date()).toISOString()} -->
191
+ `);
192
+ return true;
193
+ }
194
+ export {
195
+ promoteRule,
196
+ selfReview
197
+ };
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ setup
4
+ } from "./chunk-A7ZLQUOX.js";
5
+ import "./chunk-PDX44BCA.js";
6
+ export {
7
+ setup
8
+ };