@monoes/monomindcli 2.16.11 → 2.16.13

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 (225) hide show
  1. package/.claude/agents/core/coder.md +1 -1
  2. package/.claude/agents/engineering/engineering-devops-automator.md +1 -1
  3. package/.claude/agents/github/repo-architect.md +1 -1
  4. package/.claude/agents/testing/production-validator.md +1 -1
  5. package/.claude/agents/testing/testing-tool-evaluator.md +1 -1
  6. package/.claude/commands/github/pr-manager.md +1 -1
  7. package/.claude/helpers/pick-rank.cjs +34 -4
  8. package/.claude/skills/monolean-audit/SKILL.md +1 -1
  9. package/dist/src/commands/cleanup-data.d.ts +30 -0
  10. package/dist/src/commands/cleanup-data.d.ts.map +1 -0
  11. package/dist/src/commands/cleanup-data.js +138 -0
  12. package/dist/src/commands/cleanup-data.js.map +1 -0
  13. package/dist/src/commands/cleanup-origin.d.ts +24 -0
  14. package/dist/src/commands/cleanup-origin.d.ts.map +1 -0
  15. package/dist/src/commands/cleanup-origin.js +67 -0
  16. package/dist/src/commands/cleanup-origin.js.map +1 -0
  17. package/dist/src/commands/cleanup-scratch.d.ts +43 -0
  18. package/dist/src/commands/cleanup-scratch.d.ts.map +1 -0
  19. package/dist/src/commands/cleanup-scratch.js +125 -0
  20. package/dist/src/commands/cleanup-scratch.js.map +1 -0
  21. package/dist/src/commands/cleanup.d.ts +2 -39
  22. package/dist/src/commands/cleanup.d.ts.map +1 -1
  23. package/dist/src/commands/cleanup.js +9 -296
  24. package/dist/src/commands/cleanup.js.map +1 -1
  25. package/dist/src/commands/doctor-gitignore-checks.d.ts +10 -0
  26. package/dist/src/commands/doctor-gitignore-checks.d.ts.map +1 -0
  27. package/dist/src/commands/doctor-gitignore-checks.js +141 -0
  28. package/dist/src/commands/doctor-gitignore-checks.js.map +1 -0
  29. package/dist/src/commands/doctor-helpers-checks.d.ts +10 -0
  30. package/dist/src/commands/doctor-helpers-checks.d.ts.map +1 -0
  31. package/dist/src/commands/doctor-helpers-checks.js +292 -0
  32. package/dist/src/commands/doctor-helpers-checks.js.map +1 -0
  33. package/dist/src/commands/doctor-memory-checks.d.ts +47 -0
  34. package/dist/src/commands/doctor-memory-checks.d.ts.map +1 -0
  35. package/dist/src/commands/doctor-memory-checks.js +298 -0
  36. package/dist/src/commands/doctor-memory-checks.js.map +1 -0
  37. package/dist/src/commands/doctor-monograph-checks.d.ts +9 -0
  38. package/dist/src/commands/doctor-monograph-checks.d.ts.map +1 -0
  39. package/dist/src/commands/doctor-monograph-checks.js +331 -0
  40. package/dist/src/commands/doctor-monograph-checks.js.map +1 -0
  41. package/dist/src/commands/doctor-project-checks.d.ts +7 -66
  42. package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
  43. package/dist/src/commands/doctor-project-checks.js +10 -1331
  44. package/dist/src/commands/doctor-project-checks.js.map +1 -1
  45. package/dist/src/commands/doctor-routing-checks.d.ts +10 -0
  46. package/dist/src/commands/doctor-routing-checks.d.ts.map +1 -0
  47. package/dist/src/commands/doctor-routing-checks.js +194 -0
  48. package/dist/src/commands/doctor-routing-checks.js.map +1 -0
  49. package/dist/src/commands/doctor-worker-checks.d.ts +17 -0
  50. package/dist/src/commands/doctor-worker-checks.d.ts.map +1 -0
  51. package/dist/src/commands/doctor-worker-checks.js +122 -0
  52. package/dist/src/commands/doctor-worker-checks.js.map +1 -0
  53. package/dist/src/commands/hooks-routing-commands.js +1 -1
  54. package/dist/src/commands/hooks-routing-commands.js.map +1 -1
  55. package/dist/src/commands/init.d.ts.map +1 -1
  56. package/dist/src/commands/init.js +6 -15
  57. package/dist/src/commands/init.js.map +1 -1
  58. package/dist/src/index.js +2 -2
  59. package/dist/src/index.js.map +1 -1
  60. package/dist/src/init/never-commit.d.ts +3 -3
  61. package/dist/src/init/types.d.ts +1 -1
  62. package/dist/src/init/types.js +1 -1
  63. package/dist/src/knowledge/document-store.js +1 -1
  64. package/dist/src/knowledge/document-store.js.map +1 -1
  65. package/dist/src/mcp-tools/hooks-edit-command.d.ts +10 -0
  66. package/dist/src/mcp-tools/hooks-edit-command.d.ts.map +1 -0
  67. package/dist/src/mcp-tools/hooks-edit-command.js +247 -0
  68. package/dist/src/mcp-tools/hooks-edit-command.js.map +1 -0
  69. package/dist/src/mcp-tools/hooks-learning.d.ts +9 -0
  70. package/dist/src/mcp-tools/hooks-learning.d.ts.map +1 -0
  71. package/dist/src/mcp-tools/hooks-learning.js +408 -0
  72. package/dist/src/mcp-tools/hooks-learning.js.map +1 -0
  73. package/dist/src/mcp-tools/hooks-metrics-list.d.ts +8 -0
  74. package/dist/src/mcp-tools/hooks-metrics-list.d.ts.map +1 -0
  75. package/dist/src/mcp-tools/hooks-metrics-list.js +205 -0
  76. package/dist/src/mcp-tools/hooks-metrics-list.js.map +1 -0
  77. package/dist/src/mcp-tools/hooks-route.d.ts +12 -0
  78. package/dist/src/mcp-tools/hooks-route.d.ts.map +1 -0
  79. package/dist/src/mcp-tools/hooks-route.js +251 -0
  80. package/dist/src/mcp-tools/hooks-route.js.map +1 -0
  81. package/dist/src/mcp-tools/hooks-routing.d.ts +6 -27
  82. package/dist/src/mcp-tools/hooks-routing.d.ts.map +1 -1
  83. package/dist/src/mcp-tools/hooks-routing.js +6 -1727
  84. package/dist/src/mcp-tools/hooks-routing.js.map +1 -1
  85. package/dist/src/mcp-tools/hooks-session.d.ts +9 -0
  86. package/dist/src/mcp-tools/hooks-session.d.ts.map +1 -0
  87. package/dist/src/mcp-tools/hooks-session.js +243 -0
  88. package/dist/src/mcp-tools/hooks-session.js.map +1 -0
  89. package/dist/src/mcp-tools/hooks-task.d.ts +17 -0
  90. package/dist/src/mcp-tools/hooks-task.d.ts.map +1 -0
  91. package/dist/src/mcp-tools/hooks-task.js +427 -0
  92. package/dist/src/mcp-tools/hooks-task.js.map +1 -0
  93. package/dist/src/mcp-tools/hooks-tools.d.ts +2 -1
  94. package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
  95. package/dist/src/mcp-tools/hooks-tools.js +2 -1
  96. package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
  97. package/dist/src/mcp-tools/memory-tools.js +1 -1
  98. package/dist/src/mcp-tools/memory-tools.js.map +1 -1
  99. package/dist/src/memory/bm25-index.d.ts +2 -2
  100. package/dist/src/memory/bm25-index.js +2 -2
  101. package/dist/src/memory/embedding-operations.js +2 -2
  102. package/dist/src/memory/embedding-operations.js.map +1 -1
  103. package/dist/src/memory/entity-name-key.d.ts +1 -1
  104. package/dist/src/memory/entity-name-key.js +1 -1
  105. package/dist/src/memory/hnsw-operations.d.ts +1 -1
  106. package/dist/src/memory/hnsw-operations.js +1 -1
  107. package/dist/src/memory/memory-bridge-backend.d.ts +43 -0
  108. package/dist/src/memory/memory-bridge-backend.d.ts.map +1 -0
  109. package/dist/src/memory/memory-bridge-backend.js +363 -0
  110. package/dist/src/memory/memory-bridge-backend.js.map +1 -0
  111. package/dist/src/memory/memory-bridge-core.d.ts +42 -0
  112. package/dist/src/memory/memory-bridge-core.d.ts.map +1 -0
  113. package/dist/src/memory/memory-bridge-core.js +126 -0
  114. package/dist/src/memory/memory-bridge-core.js.map +1 -0
  115. package/dist/src/memory/memory-bridge-entries.d.ts +131 -0
  116. package/dist/src/memory/memory-bridge-entries.d.ts.map +1 -0
  117. package/dist/src/memory/memory-bridge-entries.js +227 -0
  118. package/dist/src/memory/memory-bridge-entries.js.map +1 -0
  119. package/dist/src/memory/memory-bridge-feedback.d.ts +60 -0
  120. package/dist/src/memory/memory-bridge-feedback.d.ts.map +1 -0
  121. package/dist/src/memory/memory-bridge-feedback.js +118 -0
  122. package/dist/src/memory/memory-bridge-feedback.js.map +1 -0
  123. package/dist/src/memory/memory-bridge-paths.d.ts +123 -0
  124. package/dist/src/memory/memory-bridge-paths.d.ts.map +1 -0
  125. package/dist/src/memory/memory-bridge-paths.js +335 -0
  126. package/dist/src/memory/memory-bridge-paths.js.map +1 -0
  127. package/dist/src/memory/memory-bridge-search.d.ts +48 -0
  128. package/dist/src/memory/memory-bridge-search.d.ts.map +1 -0
  129. package/dist/src/memory/memory-bridge-search.js +383 -0
  130. package/dist/src/memory/memory-bridge-search.js.map +1 -0
  131. package/dist/src/memory/memory-bridge-sessions.d.ts +38 -0
  132. package/dist/src/memory/memory-bridge-sessions.d.ts.map +1 -0
  133. package/dist/src/memory/memory-bridge-sessions.js +109 -0
  134. package/dist/src/memory/memory-bridge-sessions.js.map +1 -0
  135. package/dist/src/memory/memory-bridge-store.d.ts +58 -0
  136. package/dist/src/memory/memory-bridge-store.d.ts.map +1 -0
  137. package/dist/src/memory/memory-bridge-store.js +201 -0
  138. package/dist/src/memory/memory-bridge-store.js.map +1 -0
  139. package/dist/src/memory/memory-bridge.d.ts +10 -438
  140. package/dist/src/memory/memory-bridge.d.ts.map +1 -1
  141. package/dist/src/memory/memory-bridge.js +14 -1787
  142. package/dist/src/memory/memory-bridge.js.map +1 -1
  143. package/dist/src/memory/memory-kg-claims.d.ts +52 -0
  144. package/dist/src/memory/memory-kg-claims.d.ts.map +1 -0
  145. package/dist/src/memory/memory-kg-claims.js +99 -0
  146. package/dist/src/memory/memory-kg-claims.js.map +1 -0
  147. package/dist/src/memory/memory-kg-index.d.ts +86 -0
  148. package/dist/src/memory/memory-kg-index.d.ts.map +1 -0
  149. package/dist/src/memory/memory-kg-index.js +256 -0
  150. package/dist/src/memory/memory-kg-index.js.map +1 -0
  151. package/dist/src/memory/memory-kg-ingest.d.ts +44 -0
  152. package/dist/src/memory/memory-kg-ingest.d.ts.map +1 -0
  153. package/dist/src/memory/memory-kg-ingest.js +361 -0
  154. package/dist/src/memory/memory-kg-ingest.js.map +1 -0
  155. package/dist/src/memory/memory-kg-model.d.ts +168 -0
  156. package/dist/src/memory/memory-kg-model.d.ts.map +1 -0
  157. package/dist/src/memory/memory-kg-model.js +158 -0
  158. package/dist/src/memory/memory-kg-model.js.map +1 -0
  159. package/dist/src/memory/memory-kg-names.d.ts +64 -0
  160. package/dist/src/memory/memory-kg-names.d.ts.map +1 -0
  161. package/dist/src/memory/memory-kg-names.js +187 -0
  162. package/dist/src/memory/memory-kg-names.js.map +1 -0
  163. package/dist/src/memory/memory-kg-promote.d.ts +79 -0
  164. package/dist/src/memory/memory-kg-promote.d.ts.map +1 -0
  165. package/dist/src/memory/memory-kg-promote.js +248 -0
  166. package/dist/src/memory/memory-kg-promote.js.map +1 -0
  167. package/dist/src/memory/memory-kg-rebuild.d.ts +77 -0
  168. package/dist/src/memory/memory-kg-rebuild.d.ts.map +1 -0
  169. package/dist/src/memory/memory-kg-rebuild.js +299 -0
  170. package/dist/src/memory/memory-kg-rebuild.js.map +1 -0
  171. package/dist/src/memory/memory-kg-rollback.d.ts +47 -0
  172. package/dist/src/memory/memory-kg-rollback.d.ts.map +1 -0
  173. package/dist/src/memory/memory-kg-rollback.js +193 -0
  174. package/dist/src/memory/memory-kg-rollback.js.map +1 -0
  175. package/dist/src/memory/memory-kg-rules.d.ts +64 -0
  176. package/dist/src/memory/memory-kg-rules.d.ts.map +1 -0
  177. package/dist/src/memory/memory-kg-rules.js +251 -0
  178. package/dist/src/memory/memory-kg-rules.js.map +1 -0
  179. package/dist/src/memory/memory-kg-scan.d.ts +118 -0
  180. package/dist/src/memory/memory-kg-scan.d.ts.map +1 -0
  181. package/dist/src/memory/memory-kg-scan.js +209 -0
  182. package/dist/src/memory/memory-kg-scan.js.map +1 -0
  183. package/dist/src/memory/memory-kg-search.d.ts +80 -0
  184. package/dist/src/memory/memory-kg-search.d.ts.map +1 -0
  185. package/dist/src/memory/memory-kg-search.js +239 -0
  186. package/dist/src/memory/memory-kg-search.js.map +1 -0
  187. package/dist/src/memory/memory-kg.d.ts +19 -547
  188. package/dist/src/memory/memory-kg.d.ts.map +1 -1
  189. package/dist/src/memory/memory-kg.js +10 -2389
  190. package/dist/src/memory/memory-kg.js.map +1 -1
  191. package/dist/src/orgrt/sandbox-stubs.d.ts +4 -0
  192. package/dist/src/orgrt/sandbox-stubs.d.ts.map +1 -1
  193. package/dist/src/orgrt/sandbox-stubs.js +12 -2
  194. package/dist/src/orgrt/sandbox-stubs.js.map +1 -1
  195. package/dist/src/ui/org-hil.mjs +1 -1
  196. package/dist/src/ui/routes-org-agents.mjs +318 -0
  197. package/dist/src/ui/routes-org-approvals.mjs +133 -0
  198. package/dist/src/ui/routes-org-config.mjs +459 -0
  199. package/dist/src/ui/routes-org-control.mjs +300 -0
  200. package/dist/src/ui/routes-org-files.mjs +172 -0
  201. package/dist/src/ui/routes-org-helpers.mjs +84 -0
  202. package/dist/src/ui/routes-org-knowledge.mjs +262 -0
  203. package/dist/src/ui/routes-org-lifecycle.mjs +224 -0
  204. package/dist/src/ui/routes-org-live.mjs +278 -0
  205. package/dist/src/ui/routes-org-mastermind.mjs +346 -0
  206. package/dist/src/ui/routes-org-planning.mjs +398 -0
  207. package/dist/src/ui/routes-org-runs.mjs +430 -0
  208. package/dist/src/ui/routes-org-status.mjs +342 -0
  209. package/dist/src/ui/routes-org-workflows.mjs +212 -0
  210. package/dist/src/ui/routes-org.mjs +26 -3847
  211. package/dist/src/ui/server.mjs +1 -1
  212. package/dist/src/utils/native-error.d.ts +1 -1
  213. package/dist/src/utils/native-error.js +1 -1
  214. package/dist/src/utils/paths.d.ts +2 -1
  215. package/dist/src/utils/paths.d.ts.map +1 -1
  216. package/dist/src/utils/paths.js +2 -1
  217. package/dist/src/utils/paths.js.map +1 -1
  218. package/dist/tsconfig.tsbuildinfo +1 -1
  219. package/org-skills/adversarial-reviewer/SKILL.md +1 -1
  220. package/org-skills/board-deck-builder/SKILL.md +1 -1
  221. package/org-skills/code-reviewer/SKILL.md +1 -1
  222. package/org-skills/debugging-wizard/SKILL.md +1 -1
  223. package/org-skills/scientific-thinking-literature-review/SKILL.md +1 -1
  224. package/org-skills/scientific-thinking-scholar-evaluation/SKILL.md +1 -1
  225. package/package.json +8 -8
@@ -0,0 +1,318 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ // Org dashboard routes: adapters, agent detail and agent avatar.
5
+ // Registered, in order, by handleOrgRoutes in routes-org.mjs.
6
+ export async function handleOrgAgentRoutes(req, res, url, corsOrigin, ctx) {
7
+ // GET /api/org/:name/adapters — org adapter registry
8
+ if (req.method === 'GET' && /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/adapters$/i.test(url)) {
9
+ try {
10
+ const parts = url.split('/');
11
+ const orgName = decodeURIComponent(parts[3]);
12
+ if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
13
+ res.writeHead(400);
14
+ res.end('{}');
15
+ return true;
16
+ }
17
+ const _adaptersQs = new URL(req.url, 'http://localhost').searchParams;
18
+ const _adaptersRoot = path.resolve(_adaptersQs.get('dir') || ctx.projectDir || process.cwd());
19
+ const d = ctx._resolveOrgProjectDir(orgName, _adaptersRoot) || _adaptersRoot;
20
+ const adaptersFile = path.join(d, '.monomind', 'orgs', `${orgName}-adapters.json`);
21
+ if (!fs.existsSync(adaptersFile)) {
22
+ // Return defaults derived from org config if available
23
+ const orgFile = path.join(d, '.monomind', 'orgs', `${orgName}.json`);
24
+ // claude-sonnet-5 = DEFAULT_CLAUDE_MODEL (src/orgrt/vercel-providers.ts); .mjs can't import TS.
25
+ let defaultAdapter = 'claude-sonnet-5';
26
+ try {
27
+ defaultAdapter =
28
+ JSON.parse(fs.readFileSync(orgFile, 'utf8'))?.run_config?.ceo_adapter || defaultAdapter;
29
+ } catch (_) {}
30
+ res.writeHead(200, {
31
+ 'Content-Type': 'application/json',
32
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
33
+ });
34
+ // This list mirrors the runtimes orgrt/daemon.ts actually registers
35
+ // (resolveRunner()'s if-chain + the ClaudeAgentRunner default it falls
36
+ // through to) rather than an independent, hand-maintained catalog — a
37
+ // standalone 'gemini-local' entry used to be advertised here even
38
+ // though no runner is ever registered for it (autoRuntimeFromProvider
39
+ // has no 'gemini' case, so a role requesting it silently falls back to
40
+ // Claude — see daemon.ts#startOrg's fail-fast warning). Gemini is only
41
+ // actually reachable via the 'vercel' runtime (vendor: 'google') or via
42
+ // 'antigravity' (Google-account CLI), both listed below. 'http' maps to
43
+ // provider.kind === 'base-url' (ANTHROPIC_BASE_URL + optional auth
44
+ // token) — a real, usable path, not disabled.
45
+ res.end(
46
+ JSON.stringify({
47
+ default_adapter: defaultAdapter,
48
+ adapters: [
49
+ {
50
+ type: 'claude-local',
51
+ label: 'Claude (local CLI)',
52
+ source: 'built-in',
53
+ disabled: false,
54
+ modelsCount: 3,
55
+ },
56
+ {
57
+ type: 'codex-local',
58
+ label: 'Codex CLI',
59
+ source: 'built-in',
60
+ disabled: false,
61
+ modelsCount: 1,
62
+ },
63
+ {
64
+ type: 'antigravity',
65
+ label: 'Antigravity (Google CLI)',
66
+ source: 'built-in',
67
+ disabled: false,
68
+ modelsCount: 1,
69
+ },
70
+ {
71
+ type: 'grok',
72
+ label: 'Grok CLI',
73
+ source: 'built-in',
74
+ disabled: false,
75
+ modelsCount: 1,
76
+ },
77
+ {
78
+ type: 'qwen',
79
+ label: 'Qwen CLI',
80
+ source: 'built-in',
81
+ disabled: false,
82
+ modelsCount: 1,
83
+ },
84
+ {
85
+ type: 'crush',
86
+ label: 'Crush CLI',
87
+ source: 'built-in',
88
+ disabled: false,
89
+ modelsCount: 1,
90
+ },
91
+ {
92
+ type: 'copilot',
93
+ label: 'GitHub Copilot CLI',
94
+ source: 'built-in',
95
+ disabled: false,
96
+ modelsCount: 1,
97
+ },
98
+ { type: 'pi', label: 'Pi CLI', source: 'built-in', disabled: false, modelsCount: 1 },
99
+ {
100
+ type: 'opencode',
101
+ label: 'OpenCode CLI',
102
+ source: 'built-in',
103
+ disabled: false,
104
+ modelsCount: 1,
105
+ },
106
+ {
107
+ type: 'kimicode',
108
+ label: 'KimiCode CLI',
109
+ source: 'built-in',
110
+ disabled: false,
111
+ modelsCount: 1,
112
+ },
113
+ {
114
+ type: 'vercel',
115
+ label: 'Vercel AI SDK (multi-vendor, incl. Gemini/OpenAI)',
116
+ source: 'built-in',
117
+ disabled: false,
118
+ modelsCount: 14,
119
+ },
120
+ {
121
+ type: 'http',
122
+ label: 'Custom HTTP (base-url provider)',
123
+ source: 'built-in',
124
+ disabled: false,
125
+ modelsCount: 0,
126
+ },
127
+ ],
128
+ }),
129
+ );
130
+ return true;
131
+ }
132
+ const data = JSON.parse(fs.readFileSync(adaptersFile, 'utf8'));
133
+ res.writeHead(200, {
134
+ 'Content-Type': 'application/json',
135
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
136
+ });
137
+ res.end(JSON.stringify(data));
138
+ } catch (_) {
139
+ res.writeHead(500);
140
+ res.end('{}');
141
+ }
142
+ return true;
143
+ }
144
+
145
+ // GET /api/org/:name/agent/:roleId — full agent detail: org role + .claude/agents definition
146
+ // (characteristics, responsibilities, instructions document)
147
+ if (
148
+ req.method === 'GET' &&
149
+ /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/agent\/[a-z0-9][a-z0-9_-]{0,63}$/i.test(url)
150
+ ) {
151
+ try {
152
+ const parts = url.split('/');
153
+ const orgName = decodeURIComponent(parts[3]);
154
+ const roleId = decodeURIComponent(parts[5]);
155
+ if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
156
+ res.writeHead(400);
157
+ res.end('{}');
158
+ return true;
159
+ }
160
+ if (roleId.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(roleId)) {
161
+ res.writeHead(400);
162
+ res.end('{}');
163
+ return true;
164
+ }
165
+ const _agentQs = new URL(req.url, 'http://localhost').searchParams;
166
+ const d = path.resolve(_agentQs.get('dir') || ctx.projectDir || process.cwd());
167
+ const orgFile = path.join(d, '.monomind', 'orgs', `${orgName}.json`);
168
+ if (!fs.existsSync(orgFile)) {
169
+ res.writeHead(404);
170
+ res.end('{}');
171
+ return true;
172
+ }
173
+ const config = JSON.parse(fs.readFileSync(orgFile, 'utf8'));
174
+ const role = (config.roles || []).find((r) => r.id === roleId);
175
+ if (!role) {
176
+ res.writeHead(404);
177
+ res.end('{}');
178
+ return true;
179
+ }
180
+
181
+ const agentType = String(role.agent_type || role.type || '').toLowerCase();
182
+ const wanted = [agentType, String(role.id).toLowerCase()].filter(Boolean);
183
+
184
+ // Find a matching agent definition under .claude/agents (recursive); match frontmatter name then filename.
185
+ const agentsDir = path.join(d, '.claude', 'agents');
186
+ let definition = { found: false };
187
+ if (wanted.length && fs.existsSync(agentsDir)) {
188
+ const stack = [agentsDir];
189
+ let nameMatch = null,
190
+ slugMatch = null;
191
+ while (stack.length && !nameMatch) {
192
+ const dir = stack.pop();
193
+ let entries = [];
194
+ try {
195
+ entries = fs.readdirSync(dir, { withFileTypes: true });
196
+ } catch (_) {
197
+ continue;
198
+ }
199
+ for (const e of entries) {
200
+ const full = path.join(dir, e.name);
201
+ if (e.isDirectory()) {
202
+ stack.push(full);
203
+ continue;
204
+ }
205
+ if (!e.name.endsWith('.md') || e.name.startsWith('_')) continue;
206
+ const slug = e.name.replace(/\.md$/, '').toLowerCase();
207
+ let raw = '';
208
+ try {
209
+ raw = fs.readFileSync(full, 'utf8');
210
+ } catch (_) {
211
+ continue;
212
+ }
213
+ const fmName = ((raw.match(/^name:\s*(.+)$/m) || [])[1] || '').trim().toLowerCase();
214
+ if (fmName && wanted.includes(fmName)) {
215
+ nameMatch = { full, raw };
216
+ break;
217
+ }
218
+ if (!slugMatch && wanted.includes(slug)) slugMatch = { full, raw };
219
+ }
220
+ }
221
+ const match = nameMatch || slugMatch;
222
+ if (match) {
223
+ definition = ctx.parseAgentDef(match.raw);
224
+ definition.found = true;
225
+ definition.file = path.relative(d, match.full);
226
+ }
227
+ }
228
+
229
+ res.writeHead(200, {
230
+ 'Content-Type': 'application/json',
231
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
232
+ 'Cache-Control': 'no-cache',
233
+ });
234
+ res.end(JSON.stringify({ role, definition }));
235
+ } catch (_) {
236
+ res.writeHead(500);
237
+ res.end('{}');
238
+ }
239
+ return true;
240
+ }
241
+
242
+ // POST /api/org/:name/agent/:roleId/avatar — set (or clear) a custom avatar for one
243
+ // role, stored inline as a data URL on the role itself so it travels with the org
244
+ // config export/import. Body: { avatarDataUrl } — null/omitted avatarDataUrl clears
245
+ // the custom avatar and reverts to the built-in library picture. `dir` (like every
246
+ // other dir-accepting route in this file, POST included — see /goals above) comes
247
+ // from the query string, not the body.
248
+ if (
249
+ req.method === 'POST' &&
250
+ /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/agent\/[a-z0-9][a-z0-9_-]{0,63}\/avatar$/i.test(url)
251
+ ) {
252
+ let body = '';
253
+ for await (const chunk of req) {
254
+ body += chunk;
255
+ if (body.length > 8388608) {
256
+ req.destroy();
257
+ break;
258
+ }
259
+ }
260
+ try {
261
+ const parts = url.split('/');
262
+ const orgName = decodeURIComponent(parts[3]);
263
+ const roleId = decodeURIComponent(parts[5]);
264
+ if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
265
+ res.writeHead(400);
266
+ res.end('{"ok":false,"error":"Invalid org name"}');
267
+ return true;
268
+ }
269
+ if (roleId.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(roleId)) {
270
+ res.writeHead(400);
271
+ res.end('{"ok":false,"error":"Invalid role id"}');
272
+ return true;
273
+ }
274
+ const parsed = JSON.parse(body);
275
+ const avatarDataUrl = parsed.avatarDataUrl;
276
+ if (
277
+ avatarDataUrl != null &&
278
+ (typeof avatarDataUrl !== 'string' ||
279
+ avatarDataUrl.length > 2_000_000 ||
280
+ !/^data:image\/(png|jpe?g|webp|gif);base64,[A-Za-z0-9+/=]+$/.test(avatarDataUrl))
281
+ ) {
282
+ res.writeHead(400);
283
+ res.end('{"ok":false,"error":"Invalid or oversized avatarDataUrl"}');
284
+ return true;
285
+ }
286
+ const _avatarQs = new URL(req.url, 'http://localhost').searchParams;
287
+ const d = path.resolve(_avatarQs.get('dir') || ctx.projectDir || process.cwd());
288
+ const orgFile = path.join(d, '.monomind', 'orgs', `${orgName}.json`);
289
+ if (!fs.existsSync(orgFile)) {
290
+ res.writeHead(404);
291
+ res.end('{"ok":false,"error":"org not found"}');
292
+ return true;
293
+ }
294
+ const config = JSON.parse(fs.readFileSync(orgFile, 'utf8'));
295
+ const role = (config.roles || []).find((r) => r.id === roleId);
296
+ if (!role) {
297
+ res.writeHead(404);
298
+ res.end('{"ok":false,"error":"role not found"}');
299
+ return true;
300
+ }
301
+ if (avatarDataUrl) role.avatar = avatarDataUrl;
302
+ else delete role.avatar;
303
+ const tmp = `${orgFile}.tmp`;
304
+ fs.writeFileSync(tmp, JSON.stringify(config, null, 2), 'utf-8');
305
+ fs.renameSync(tmp, orgFile);
306
+ res.writeHead(200, {
307
+ 'Content-Type': 'application/json',
308
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
309
+ });
310
+ res.end(JSON.stringify({ ok: true }));
311
+ } catch (e) {
312
+ res.writeHead(500);
313
+ res.end(JSON.stringify({ ok: false, error: String(e.message || e) }));
314
+ }
315
+ return true;
316
+ }
317
+ return false;
318
+ }
@@ -0,0 +1,133 @@
1
+ import path from 'node:path';
2
+ import * as hil from './org-hil.mjs';
3
+ import {
4
+ decodeHilSegment,
5
+ hilEvent,
6
+ hilRespond,
7
+ hilRoot,
8
+ readHilBody,
9
+ validOrgName,
10
+ } from './routes-org-helpers.mjs';
11
+
12
+ // Org dashboard routes: human-in-the-loop approvals, gates and questions.
13
+ // Registered, in order, by handleOrgRoutes in routes-org.mjs.
14
+ export async function handleOrgApprovalRoutes(req, res, url, corsOrigin, ctx) {
15
+ // GET /api/org/:name/approvals — the org's tool/action approvals
16
+ // (<org>/approvals.json, written by orgrt/approvals.ts), newest first.
17
+ if (
18
+ req.method === 'GET' &&
19
+ url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/approvals(\?.*)?$/i)
20
+ ) {
21
+ const orgName = decodeURIComponent(url.split('/')[3].split('?')[0]);
22
+ return hilRespond(res, corsOrigin, () => {
23
+ const approvals = hil.listApprovals(hilRoot(req, ctx), orgName);
24
+ return { approvals, pending: approvals.filter((a) => a.status === 'pending').length };
25
+ });
26
+ }
27
+
28
+ // GET /api/org/:name/gates — the org's decision gates (<org>/gates.json).
29
+ if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/gates(\?.*)?$/i)) {
30
+ const orgName = decodeURIComponent(url.split('/')[3].split('?')[0]);
31
+ return hilRespond(res, corsOrigin, () => {
32
+ const gates = hil.listGates(hilRoot(req, ctx), orgName);
33
+ return { gates, pending: gates.filter((g) => g.status === 'pending').length };
34
+ });
35
+ }
36
+
37
+ // GET /api/questions?dir= — ask_human questions (pending and answered) for
38
+ // every org in one project, each with its `blocking` flag.
39
+ if (req.method === 'GET' && (url === '/api/questions' || url.startsWith('/api/questions?'))) {
40
+ return hilRespond(res, corsOrigin, () => ({
41
+ questions: hil.pendingForProject(hilRoot(req, ctx)).questions,
42
+ }));
43
+ }
44
+
45
+ // GET /api/hil?dir= — everything in one project that waits on a human:
46
+ // questions, approvals and decision gates across all its orgs.
47
+ if (req.method === 'GET' && (url === '/api/hil' || url.startsWith('/api/hil?'))) {
48
+ return hilRespond(res, corsOrigin, () => hil.pendingForProject(hilRoot(req, ctx)));
49
+ }
50
+
51
+ // POST /api/questions/answer { dir, org, questionId, answer } — live to the
52
+ // hosting daemon (operator credential), else queued for the org's next run.
53
+ if (req.method === 'POST' && url === '/api/questions/answer') {
54
+ return hilRespond(res, corsOrigin, async () => {
55
+ const body = await readHilBody(req);
56
+ const { org, questionId, answer } = body || {};
57
+ if (!validOrgName(org) || !questionId || typeof answer !== 'string' || !answer.trim())
58
+ throw Object.assign(new Error('org, questionId and a non-empty answer are required'), {
59
+ status: 400,
60
+ });
61
+ const root = path.resolve(body.dir || ctx.projectDir || process.cwd());
62
+ const r = await hil.answerQuestion(root, org, String(questionId), answer.trim());
63
+ hilEvent(ctx, root, {
64
+ type: 'org:question-answered',
65
+ org,
66
+ role: r.role,
67
+ questionId,
68
+ delivery: r.delivery,
69
+ });
70
+ return { ok: true, ...r };
71
+ });
72
+ }
73
+
74
+ // POST /api/org/:name/gates/:id { approved: boolean, resolution? } — resolve
75
+ // a decision gate, live or (org not running) in gates.json.
76
+ if (
77
+ req.method === 'POST' &&
78
+ url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/gates\/[^/?]+(\?.*)?$/i)
79
+ ) {
80
+ const parts = url.split('?')[0].split('/');
81
+ const orgName = decodeURIComponent(parts[3]);
82
+ const root = hilRoot(req, ctx);
83
+ return hilRespond(res, corsOrigin, async () => {
84
+ const gateId = decodeHilSegment(parts[5]);
85
+ const body = await readHilBody(req);
86
+ const resolution =
87
+ typeof body?.resolution === 'string' && body.resolution.trim()
88
+ ? body.resolution.trim().slice(0, 4000)
89
+ : undefined;
90
+ if (typeof body?.approved !== 'boolean')
91
+ throw Object.assign(new Error('approved (boolean) is required'), { status: 400 });
92
+ const r = await hil.resolveGate(root, orgName, gateId, body.approved, resolution);
93
+ const status = body.approved ? 'approved' : 'rejected';
94
+ hilEvent(ctx, root, {
95
+ type: 'org:gate:resolved',
96
+ org: orgName,
97
+ gateId,
98
+ status,
99
+ delivery: r.delivery,
100
+ });
101
+ return { ok: true, status, ...r };
102
+ });
103
+ }
104
+
105
+ // POST /api/org/:name/approvals/:requestId { action: "approve" | "reject" } —
106
+ // resolve one tool/action approval, live or (org not running) in approvals.json.
107
+ if (
108
+ req.method === 'POST' &&
109
+ url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/approvals\/[^/?]+(\?.*)?$/i)
110
+ ) {
111
+ const parts = url.split('?')[0].split('/');
112
+ const orgName = decodeURIComponent(parts[3]);
113
+ const root = hilRoot(req, ctx);
114
+ return hilRespond(res, corsOrigin, async () => {
115
+ const requestId = decodeHilSegment(parts[5]);
116
+ const body = await readHilBody(req);
117
+ if (body?.action !== 'approve' && body?.action !== 'reject')
118
+ throw Object.assign(new Error('action must be approve or reject'), { status: 400 });
119
+ const approved = body.action === 'approve';
120
+ const r = await hil.resolveApproval(root, orgName, requestId, approved);
121
+ const status = approved ? 'approved' : 'denied';
122
+ hilEvent(ctx, root, {
123
+ type: 'org:approval:resolved',
124
+ org: orgName,
125
+ requestId,
126
+ status,
127
+ delivery: r.delivery,
128
+ });
129
+ return { ok: true, status, ...r };
130
+ });
131
+ }
132
+ return false;
133
+ }