@jossuealcala/madre 0.3.2 → 0.4.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 (75) hide show
  1. package/CHANGELOG.md +425 -0
  2. package/CONTRIBUTING.md +6 -1
  3. package/README.md +67 -183
  4. package/SECURITY.md +2 -1
  5. package/bin/madre.mjs +56 -13
  6. package/docs/INTERNALS.md +16 -0
  7. package/docs/REFERENCE.md +249 -0
  8. package/docs/SDK.md +121 -0
  9. package/docs/room.png +0 -0
  10. package/docs/sdk/hello-module.mjs +51 -0
  11. package/package.json +9 -1
  12. package/public/app.js +3880 -849
  13. package/public/es.js +2050 -0
  14. package/public/i18n.js +66 -0
  15. package/public/index.html +95 -14
  16. package/public/inquiry.js +220 -0
  17. package/public/resay.js +77 -0
  18. package/public/styles.css +626 -68
  19. package/public/troubleshooting.js +173 -51
  20. package/src/adapters/claude.mjs +13 -6
  21. package/src/adapters/codex.mjs +17 -13
  22. package/src/adapters/gemini.mjs +27 -16
  23. package/src/adapters/opencode.mjs +16 -12
  24. package/src/adapters/process.mjs +17 -5
  25. package/src/asking.mjs +128 -0
  26. package/src/auth-probe.mjs +58 -1
  27. package/src/capabilities.mjs +4 -3
  28. package/src/chats.mjs +193 -0
  29. package/src/checkpoint.mjs +1 -1
  30. package/src/cold.mjs +56 -0
  31. package/src/commands.mjs +31 -3
  32. package/src/conversation-context.mjs +35 -3
  33. package/src/credentials.mjs +145 -0
  34. package/src/dataset.mjs +56 -4
  35. package/src/distiller.mjs +12 -5
  36. package/src/event-store.mjs +14 -8
  37. package/src/exam.mjs +240 -0
  38. package/src/extensions.mjs +3 -2
  39. package/src/eyecat-watch.mjs +100 -0
  40. package/src/eyecat.mjs +169 -0
  41. package/src/i18n.mjs +47 -0
  42. package/src/image-studio.mjs +2 -0
  43. package/src/launch.mjs +61 -0
  44. package/src/lease.mjs +5 -3
  45. package/src/maturity.mjs +94 -0
  46. package/src/mcp/image-server.mjs +12 -1
  47. package/src/mcp/memory-server.mjs +1 -1
  48. package/src/memory.mjs +325 -17
  49. package/src/modules/ahp.mjs +9 -7
  50. package/src/modules/ash.mjs +36 -0
  51. package/src/modules/git-pulse.mjs +6 -4
  52. package/src/modules/helpers.mjs +31 -0
  53. package/src/modules/image-studio.mjs +9 -4
  54. package/src/modules/index.mjs +143 -5
  55. package/src/modules/ollama.mjs +66 -10
  56. package/src/modules/playwright.mjs +90 -0
  57. package/src/modules/ripley.mjs +5 -3
  58. package/src/modules/sdk.mjs +104 -2
  59. package/src/modules/updates.mjs +81 -0
  60. package/src/ollama.mjs +5 -2
  61. package/src/outbound.mjs +292 -0
  62. package/src/privacy.mjs +54 -7
  63. package/src/room/context.mjs +4 -4
  64. package/src/room/control.mjs +4 -4
  65. package/src/room/economy.mjs +161 -0
  66. package/src/room/prompt.mjs +118 -43
  67. package/src/room.mjs +465 -58
  68. package/src/runtime-detection.mjs +27 -8
  69. package/src/server.mjs +724 -68
  70. package/src/setup.mjs +1 -1
  71. package/src/updates.mjs +17 -2
  72. package/src/usage-sentinel.mjs +13 -8
  73. package/src/verdict.mjs +74 -0
  74. package/src/ashcode.mjs +0 -64
  75. package/src/modules/ashcode.mjs +0 -28
@@ -1,5 +1,6 @@
1
1
  import { constants } from 'node:fs';
2
2
  import { access } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
3
4
  import { delimiter, join } from 'node:path';
4
5
  import { execFile } from 'node:child_process';
5
6
  import { promisify } from 'node:util';
@@ -46,13 +47,24 @@ async function executable(path) {
46
47
  }
47
48
  }
48
49
 
49
- export async function findExecutable(candidates, envPath = process.env.PATH ?? '') {
50
+ // On Windows a CLI on PATH is `name.cmd`, `name.exe` or `name.ps1`: npm installs a .cmd shim.
51
+ // PATHEXT is the system's own list of what counts as runnable, and the file has no execute bit.
52
+ const extensions = (env = process.env) => (process.platform === 'win32'
53
+ ? (env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD').split(';').filter(Boolean).map((extension) => extension.toLowerCase())
54
+ : ['']);
55
+
56
+ export async function findExecutable(candidates, envPath = process.env.PATH ?? '', env = process.env) {
57
+ const suffixes = extensions(env);
58
+ const runnable = async (path) => (process.platform === 'win32' ? access(path).then(() => true, () => false) : executable(path));
50
59
  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;
60
+ if (candidate.includes('/') || candidate.includes('\\')) {
61
+ for (const suffix of candidate.includes('.') ? [''] : suffixes) if (await runnable(`${candidate}${suffix}`)) return `${candidate}${suffix}`;
62
+ continue;
63
+ }
64
+ for (const directory of envPath.split(delimiter).filter(Boolean)) {
65
+ for (const suffix of suffixes) {
66
+ const path = join(directory, `${candidate}${suffix}`);
67
+ if (await runnable(path)) return path;
56
68
  }
57
69
  }
58
70
  }
@@ -68,9 +80,16 @@ async function readVersion(path, args) {
68
80
  }
69
81
  }
70
82
 
71
- export async function detectAgents() {
83
+ // Where MADRE puts a CLI when the system folders are not writable: its own prefix, no
84
+ // administrator, nothing outside ~/.pulse. Detection looks here as well as along PATH, so an
85
+ // agent installed this way is found without the human touching their PATH.
86
+ export const toolsPrefix = (env = process.env) => join(env.PULSE_HOME ?? join(homedir(), '.pulse'), 'tools');
87
+ export const toolsBin = (env = process.env) => (process.platform === 'win32' ? toolsPrefix(env) : join(toolsPrefix(env), 'bin'));
88
+
89
+ export async function detectAgents({ env = process.env } = {}) {
90
+ const searchPath = [env.PATH ?? '', toolsBin(env)].filter(Boolean).join(delimiter);
72
91
  return Promise.all(definitions.map(async (definition) => {
73
- const path = await findExecutable(definition.candidates);
92
+ const path = await findExecutable(definition.candidates, searchPath);
74
93
  const detected = Boolean(path);
75
94
  return {
76
95
  id: definition.id,