@jossuealcala/madre 0.2.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 (47) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +5 -0
  4. package/README.md +281 -0
  5. package/bin/madre.mjs +134 -0
  6. package/docs/madre-banner.svg +25 -0
  7. package/package.json +63 -0
  8. package/public/app.js +4031 -0
  9. package/public/brands.js +59 -0
  10. package/public/index.html +231 -0
  11. package/public/styles.css +976 -0
  12. package/public/troubleshooting.js +460 -0
  13. package/src/adapters/claude.mjs +105 -0
  14. package/src/adapters/codex.mjs +84 -0
  15. package/src/adapters/gemini.mjs +335 -0
  16. package/src/adapters/opencode.mjs +148 -0
  17. package/src/adapters/process.mjs +132 -0
  18. package/src/ashcode.mjs +64 -0
  19. package/src/auth-probe.mjs +145 -0
  20. package/src/capabilities.mjs +126 -0
  21. package/src/checkpoint.mjs +106 -0
  22. package/src/cli-args.mjs +38 -0
  23. package/src/commands.mjs +98 -0
  24. package/src/config.mjs +59 -0
  25. package/src/conversation-context.mjs +59 -0
  26. package/src/directives.mjs +70 -0
  27. package/src/distiller.mjs +78 -0
  28. package/src/embeddings.mjs +80 -0
  29. package/src/event-store.mjs +152 -0
  30. package/src/extensions.mjs +263 -0
  31. package/src/files.mjs +177 -0
  32. package/src/image-studio.mjs +25 -0
  33. package/src/lease.mjs +83 -0
  34. package/src/mcp/image-server.mjs +169 -0
  35. package/src/mcp/memory-server.mjs +221 -0
  36. package/src/memory-tools.mjs +34 -0
  37. package/src/memory.mjs +543 -0
  38. package/src/models.mjs +78 -0
  39. package/src/mother.mjs +163 -0
  40. package/src/quota-monitor.mjs +119 -0
  41. package/src/quota-sources.mjs +161 -0
  42. package/src/room.mjs +1159 -0
  43. package/src/router.mjs +13 -0
  44. package/src/runtime-detection.mjs +85 -0
  45. package/src/server.mjs +693 -0
  46. package/src/setup.mjs +192 -0
  47. package/src/usage-sentinel.mjs +65 -0
package/src/router.mjs ADDED
@@ -0,0 +1,13 @@
1
+ const supported = new Set(['codex', 'claude', 'gemini', 'opencode']);
2
+
3
+ export function parseMessage(text, fallbackTarget = null) {
4
+ const value = String(text ?? '').trim();
5
+ const match = value.match(/^@([a-z0-9_-]+)\b\s*/i);
6
+ const mentioned = match?.[1]?.toLowerCase() ?? null;
7
+ const target = mentioned && supported.has(mentioned) ? mentioned : fallbackTarget;
8
+ return {
9
+ text: match && target === mentioned ? value.slice(match[0].length).trim() : value,
10
+ target,
11
+ mentioned,
12
+ };
13
+ }
@@ -0,0 +1,85 @@
1
+ import { constants } from 'node:fs';
2
+ import { access } from 'node:fs/promises';
3
+ import { delimiter, join } from 'node:path';
4
+ import { execFile } from 'node:child_process';
5
+ import { promisify } from 'node:util';
6
+
7
+ const execFileAsync = promisify(execFile);
8
+
9
+ const definitions = [
10
+ {
11
+ id: 'codex',
12
+ label: 'Codex',
13
+ candidates: ['codex', '/Applications/ChatGPT.app/Contents/Resources/codex'],
14
+ versionArgs: ['--version'],
15
+ adapter: 'codex-readonly',
16
+ },
17
+ {
18
+ id: 'claude',
19
+ label: 'Claude',
20
+ candidates: ['claude', '/opt/homebrew/bin/claude'],
21
+ versionArgs: ['--version'],
22
+ adapter: 'claude-readonly',
23
+ },
24
+ {
25
+ id: 'gemini',
26
+ label: 'Gemini',
27
+ candidates: ['gemini', '/opt/homebrew/bin/gemini'],
28
+ versionArgs: ['--version'],
29
+ adapter: 'gemini-readonly',
30
+ },
31
+ {
32
+ id: 'opencode',
33
+ label: 'OpenCode',
34
+ candidates: ['opencode', '/opt/homebrew/bin/opencode'],
35
+ versionArgs: ['--version'],
36
+ adapter: 'opencode-readonly',
37
+ },
38
+ ];
39
+
40
+ async function executable(path) {
41
+ try {
42
+ await access(path, constants.X_OK);
43
+ return true;
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+
49
+ export async function findExecutable(candidates, envPath = process.env.PATH ?? '') {
50
+ for (const candidate of candidates) {
51
+ if (candidate.includes('/') && await executable(candidate)) return candidate;
52
+ if (!candidate.includes('/')) {
53
+ for (const directory of envPath.split(delimiter).filter(Boolean)) {
54
+ const path = join(directory, candidate);
55
+ if (await executable(path)) return path;
56
+ }
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+
62
+ async function readVersion(path, args) {
63
+ try {
64
+ const { stdout, stderr } = await execFileAsync(path, args, { timeout: 3000 });
65
+ return (stdout || stderr).trim().split('\n')[0] || null;
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+
71
+ export async function detectAgents() {
72
+ return Promise.all(definitions.map(async (definition) => {
73
+ const path = await findExecutable(definition.candidates);
74
+ const detected = Boolean(path);
75
+ return {
76
+ id: definition.id,
77
+ label: definition.label,
78
+ detected,
79
+ ready: detected && Boolean(definition.adapter),
80
+ adapter: definition.adapter ?? null,
81
+ path,
82
+ version: path ? await readVersion(path, definition.versionArgs) : null,
83
+ };
84
+ }));
85
+ }