@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,342 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import * as orgRuntime from './org-runtime.mjs';
4
+ import { approvalsOrEmpty, hilRespond, hilRoot, readHilBody } from './routes-org-helpers.mjs';
5
+
6
+ // Org dashboard routes: search, health, plugins, runtime, live config and agent stats.
7
+ // Registered, in order, by handleOrgRoutes in routes-org.mjs.
8
+ export async function handleOrgStatusRoutes(req, res, url, corsOrigin, ctx) {
9
+ // GET /api/org/:name/search?q=<query> — fuzzy search across org data
10
+ if (req.method === 'GET' && /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/search(\?.*)?$/i.test(url)) {
11
+ try {
12
+ const urlObj = new URL(`http://x${req.url}`);
13
+ const orgName = decodeURIComponent(urlObj.pathname.split('/')[3]);
14
+ if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
15
+ res.writeHead(400);
16
+ res.end('{}');
17
+ return true;
18
+ }
19
+ const q = (urlObj.searchParams.get('q') || '').toLowerCase().trim();
20
+ if (!q || q.length < 2) {
21
+ res.writeHead(200, {
22
+ 'Content-Type': 'application/json',
23
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
24
+ });
25
+ res.end('{"hits":[]}');
26
+ return true;
27
+ }
28
+
29
+ const d = path.resolve(urlObj.searchParams.get('dir') || ctx.projectDir || process.cwd());
30
+ const orgsDir = path.join(d, '.monomind', 'orgs');
31
+ const readJ = (f) => {
32
+ try {
33
+ return JSON.parse(fs.readFileSync(f, 'utf8'));
34
+ } catch (_) {
35
+ return null;
36
+ }
37
+ };
38
+
39
+ const hits = [];
40
+ const match = (str) => str?.toLowerCase().includes(q);
41
+
42
+ // Agents
43
+ const config = readJ(path.join(orgsDir, `${orgName}.json`));
44
+ for (const role of config?.roles || []) {
45
+ if (
46
+ match(role.id) ||
47
+ match(role.title) ||
48
+ (role.responsibilities || []).some((r) => match(r))
49
+ ) {
50
+ hits.push({ type: 'agent', id: role.id, title: role.title, meta: role.agent_type });
51
+ }
52
+ }
53
+
54
+ // Goals
55
+ const goals = readJ(path.join(orgsDir, `${orgName}-goals.json`));
56
+ for (const g of goals?.goals || []) {
57
+ if (match(g.title) || match(g.text) || match(g.goal) || match(g.description)) {
58
+ hits.push({
59
+ type: 'goal',
60
+ id: g.id,
61
+ title: g.title || g.text || g.goal,
62
+ meta: g.status || 'open',
63
+ });
64
+ }
65
+ }
66
+
67
+ // Routines
68
+ const routines = readJ(path.join(orgsDir, `${orgName}-routines.json`));
69
+ for (const r of routines?.routines || []) {
70
+ if (match(r.name) || match(r.description)) {
71
+ hits.push({ type: 'routine', id: r.name, title: r.name, meta: r.schedule || '' });
72
+ }
73
+ }
74
+
75
+ // Approvals
76
+ for (const a of approvalsOrEmpty(orgsDir, orgName)) {
77
+ if (match(a.action) || match(a.roleId)) {
78
+ hits.push({
79
+ type: 'approval',
80
+ id: a.id,
81
+ title: `${a.roleId}: ${a.action}`,
82
+ meta: a.status,
83
+ });
84
+ }
85
+ }
86
+
87
+ // Projects
88
+ const projects = readJ(path.join(orgsDir, `${orgName}-projects.json`));
89
+ for (const p of projects?.projects || []) {
90
+ if (match(p.name) || match(p.description)) {
91
+ hits.push({
92
+ type: 'project',
93
+ id: p.id || p.name,
94
+ title: p.name,
95
+ meta: p.status || 'active',
96
+ });
97
+ }
98
+ }
99
+
100
+ // Issues
101
+ const issuesData = readJ(path.join(orgsDir, `${orgName}-issues.json`));
102
+ for (const i of issuesData?.issues || []) {
103
+ if (match(i.title) || match(i.description) || match(i.slug)) {
104
+ hits.push({
105
+ type: 'issue',
106
+ id: i.id || i.slug,
107
+ title: i.title || i.slug,
108
+ meta: i.status || 'open',
109
+ });
110
+ }
111
+ }
112
+
113
+ // Recent activity events
114
+ const eventsFile = path.join(d, 'data', 'mastermind-events.jsonl');
115
+ if (fs.existsSync(eventsFile)) {
116
+ const lines = fs.readFileSync(eventsFile, 'utf8').split('\n').filter(Boolean).slice(-500);
117
+ for (const l of lines) {
118
+ try {
119
+ const e = JSON.parse(l);
120
+ if (e.org === orgName && match(JSON.stringify(e))) {
121
+ hits.push({
122
+ type: 'event',
123
+ id: String(e.ts),
124
+ title: e.type,
125
+ meta: e.role || e.task || '',
126
+ });
127
+ if (hits.length >= 50) break;
128
+ }
129
+ } catch (_) {}
130
+ }
131
+ }
132
+
133
+ res.writeHead(200, {
134
+ 'Content-Type': 'application/json',
135
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
136
+ });
137
+ res.end(JSON.stringify({ q, hits: hits.slice(0, 50) }));
138
+ } catch (_) {
139
+ res.writeHead(500);
140
+ res.end('{}');
141
+ }
142
+ return true;
143
+ }
144
+
145
+ // GET /api/org/:name/health — aggregate org health, from the same runtime
146
+ // sources as /runtime: live role sessions, the task DAG, this run's usage on
147
+ // the budget's enforcement basis, and how the last 7 days of runs ended.
148
+ if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/health$/i)) {
149
+ const orgName = decodeURIComponent(url.split('/')[3]);
150
+ return hilRespond(res, corsOrigin, async () => {
151
+ const v = await orgRuntime.runtimeView(hilRoot(req, ctx), orgName);
152
+ if (!v) throw Object.assign(new Error(`org "${orgName}" has no definition`), { status: 404 });
153
+ const running = v.roles.filter((r) => r.status === 'running').length;
154
+ const openTasks = v.tasks.filter((t) =>
155
+ ['pending', 'ready', 'blocked'].includes(t.status),
156
+ ).length;
157
+ const activeTasks = v.tasks.filter((t) => t.status === 'running').length;
158
+ const weekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
159
+ // A resumed run keeps its run id and appends a history line per stop:
160
+ // count each run once, by its latest line (history is newest first).
161
+ const seenRuns = new Set();
162
+ const week = v.history.filter(
163
+ (h) => (h.endedAt || 0) >= weekAgo && !seenRuns.has(h.run) && seenRuns.add(h.run),
164
+ );
165
+ // Only a boss's gate-checked org_complete with 'achieved' counts as success.
166
+ const achieved = week.filter(
167
+ (h) => h.closedBy === 'org-complete' && h.outcome?.status === 'achieved',
168
+ ).length;
169
+ const { used, tokens } = v.budget;
170
+ return {
171
+ agents_running: running,
172
+ agents_idle: v.roles.length - running,
173
+ agents_active: running,
174
+ open_issues: openTasks,
175
+ in_progress_issues: activeTasks,
176
+ tasks_pending: openTasks + activeTasks,
177
+ budget_used_tokens: used,
178
+ budget_max_tokens: tokens,
179
+ budget_basis: v.budget.basis,
180
+ budget_used_pct: tokens && used !== null ? Math.round((used / tokens) * 100) : null,
181
+ run_success_rate_7d: week.length ? Math.round((achieved / week.length) * 100) : null,
182
+ total_runs_7d: week.length,
183
+ errors: [...v.problems, ...(v.liveError ? [`daemon: ${v.liveError}`] : [])],
184
+ };
185
+ });
186
+ }
187
+
188
+ // GET /api/org/:name/plugins — plugins from registry filtered/merged with org overrides
189
+ if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/plugins$/i)) {
190
+ try {
191
+ const orgName = decodeURIComponent(url.split('/')[3]);
192
+ if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
193
+ res.writeHead(400);
194
+ res.end('Invalid org name');
195
+ return true;
196
+ }
197
+ const _pluginsQs = new URL(req.url, 'http://localhost').searchParams;
198
+ const base = path.join(
199
+ path.resolve(_pluginsQs.get('dir') || ctx.projectDir || process.cwd()),
200
+ '.monomind',
201
+ );
202
+ let plugins = [];
203
+ try {
204
+ const reg = JSON.parse(
205
+ fs.readFileSync(path.join(base, 'plugins', 'registry.json'), 'utf8'),
206
+ );
207
+ plugins = reg.plugins || [];
208
+ // Strip sensitive config fields from output
209
+ plugins = plugins.map((p) => {
210
+ const safe = { ...p };
211
+ if (safe.config) {
212
+ safe.config = Object.fromEntries(
213
+ Object.entries(safe.config).map(([k, v]) =>
214
+ /key|token|secret|password|api/i.test(k) ? [k, '***'] : [k, v],
215
+ ),
216
+ );
217
+ }
218
+ return safe;
219
+ });
220
+ } catch (_) {
221
+ /* no global registry */
222
+ }
223
+ // Merge org-level overrides
224
+ try {
225
+ const orgPlugins = JSON.parse(
226
+ fs.readFileSync(path.join(base, 'orgs', `${orgName}-plugins.json`), 'utf8'),
227
+ );
228
+ const overrideMap = {};
229
+ (orgPlugins.plugins || []).forEach((p) => {
230
+ overrideMap[p.id] = p;
231
+ });
232
+ if (Object.keys(overrideMap).length) {
233
+ plugins = plugins.map((p) =>
234
+ overrideMap[p.id] ? { ...p, ...overrideMap[p.id], _orgOverride: true } : p,
235
+ );
236
+ }
237
+ } catch (_) {
238
+ /* no org-level overrides */
239
+ }
240
+ res.writeHead(200, { 'Content-Type': 'application/json' });
241
+ res.end(JSON.stringify({ plugins }));
242
+ } catch (_) {
243
+ res.writeHead(500);
244
+ res.end('{}');
245
+ }
246
+ return true;
247
+ }
248
+
249
+ // GET /api/org/:name/runtime — what the org is doing now, from the runtime's
250
+ // own sources (live daemon status, run bus, idle watchdog, history).
251
+ if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/runtime$/i)) {
252
+ const orgName = decodeURIComponent(url.split('/')[3]);
253
+ return hilRespond(res, corsOrigin, async () => {
254
+ const view = await orgRuntime.runtimeView(hilRoot(req, ctx), orgName);
255
+ if (!view)
256
+ throw Object.assign(new Error(`org "${orgName}" has no definition`), { status: 404 });
257
+ return view;
258
+ });
259
+ }
260
+
261
+ // POST /api/org/:name/config — edit the org definition's goal, schedule,
262
+ // run_config and default cost tier; refused unless the result passes the
263
+ // same OrgDefSchema parse `org run` does.
264
+ if (req.method === 'POST' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/config$/i)) {
265
+ const orgName = decodeURIComponent(url.split('/')[3]);
266
+ const root = hilRoot(req, ctx);
267
+ return hilRespond(res, corsOrigin, async () => {
268
+ const body = await readHilBody(req);
269
+ if (!body || typeof body !== 'object')
270
+ throw Object.assign(new Error('JSON body required'), { status: 400 });
271
+ try {
272
+ return { ok: true, def: orgRuntime.patchOrgConfig(root, orgName, body) };
273
+ } catch (err) {
274
+ if (err instanceof orgRuntime.ConfigRejected)
275
+ throw Object.assign(new Error(err.message), { status: 400 });
276
+ throw err;
277
+ }
278
+ });
279
+ }
280
+
281
+ // GET /api/org/:name/agents — agents from roles + merged heartbeat state
282
+ if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/agents$/i)) {
283
+ try {
284
+ const orgName = decodeURIComponent(url.split('/')[3]);
285
+ if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
286
+ res.writeHead(400);
287
+ res.end('Invalid org name');
288
+ return true;
289
+ }
290
+ const _agentsQs = new URL(req.url, 'http://localhost').searchParams;
291
+ const d = path.resolve(_agentsQs.get('dir') || ctx.projectDir || process.cwd());
292
+ const base = path.join(d, '.monomind', 'orgs');
293
+ const readJsonSafe = (f) => {
294
+ try {
295
+ return JSON.parse(fs.readFileSync(f, 'utf8'));
296
+ } catch (_) {
297
+ return null;
298
+ }
299
+ };
300
+ const config = readJsonSafe(path.join(base, `${orgName}.json`)) || {};
301
+ const stateData = readJsonSafe(path.join(base, `${orgName}-state.json`)) || {};
302
+ const agentState =
303
+ stateData.agents || stateData.roles
304
+ ? stateData.agents || Object.fromEntries((stateData.roles || []).map((r) => [r.id, r]))
305
+ : {};
306
+ const roles = config.roles || [];
307
+ const agents = roles.map((r) => {
308
+ const s = agentState[r.id] || {};
309
+ // Runtime and model the role actually runs with (cost tiers included).
310
+ const m = orgRuntime.describeRoleModel(r, config);
311
+ return {
312
+ id: r.id,
313
+ title: r.title || r.id,
314
+ adapterType: m.runtime,
315
+ adapterModel: m.model,
316
+ effort: m.effort,
317
+ tier: m.tier,
318
+ governance: r.governance || null,
319
+ reportsTo: r.reports_to || null,
320
+ status: s.status || 'idle',
321
+ lastHeartbeat: s.last_heartbeat || s.lastHeartbeat || null,
322
+ tokensIn: s.tokens_in || 0,
323
+ tokensOut: s.tokens_out || 0,
324
+ cacheRead: s.cache_read_tokens || 0,
325
+ cacheCreation: s.cache_creation_tokens || 0,
326
+ tokensUsed: s.tokens_used || 0,
327
+ costUsd: s.total_cost_usd || 0,
328
+ };
329
+ });
330
+ res.writeHead(200, {
331
+ 'Content-Type': 'application/json',
332
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
333
+ });
334
+ res.end(JSON.stringify({ agents }));
335
+ } catch (_) {
336
+ res.writeHead(500);
337
+ res.end('{"agents":[]}');
338
+ }
339
+ return true;
340
+ }
341
+ return false;
342
+ }
@@ -0,0 +1,212 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+
5
+ // Org dashboard routes: metrics, playbooks, workflow defs, coverage and workflow runs.
6
+ // Registered, in order, by handleOrgRoutes in routes-org.mjs.
7
+ export async function handleOrgWorkflowRoutes(req, res, url, corsOrigin, ctx) {
8
+ // GET /api/mastermind/metrics — aggregate system metrics from token-summary and monoswarm-activity
9
+ if (req.method === 'GET' && url === '/api/mastermind/metrics') {
10
+ try {
11
+ const base = path.join(ctx.projectDir || process.cwd(), '.monomind', 'metrics');
12
+ let tokens = {},
13
+ monoswarm = {},
14
+ events = [];
15
+ try {
16
+ tokens = JSON.parse(fs.readFileSync(path.join(base, 'token-summary.json'), 'utf8'));
17
+ } catch (_) {}
18
+ try {
19
+ monoswarm = JSON.parse(fs.readFileSync(path.join(base, 'monoswarm-activity.json'), 'utf8'));
20
+ } catch (_) {}
21
+ try {
22
+ const evPath = path.join(
23
+ ctx.projectDir || process.cwd(),
24
+ 'data',
25
+ 'mastermind-events.jsonl',
26
+ );
27
+ const lines = fs
28
+ .readFileSync(evPath, 'utf8')
29
+ .split('\n')
30
+ .filter((l) => l.trim())
31
+ .slice(-20);
32
+ events = lines
33
+ .map((l) => {
34
+ try {
35
+ return JSON.parse(l);
36
+ } catch (_) {
37
+ return null;
38
+ }
39
+ })
40
+ .filter(Boolean);
41
+ } catch (_) {}
42
+ res.writeHead(200, { 'Content-Type': 'application/json' });
43
+ res.end(JSON.stringify({ tokens, monoswarm, recentEvents: events }));
44
+ } catch (_) {
45
+ res.writeHead(500);
46
+ res.end('{"tokens":{},"monoswarm":{},"recentEvents":[]}');
47
+ }
48
+ return true;
49
+ }
50
+
51
+ // ------------------------------------------------- GET /api/playbooks
52
+ // List playbook definitions from <dir>/.monomind/playbooks/*.json.
53
+ // (Previously only POST was registered, so GET /api/playbooks?dir=... fell
54
+ // through to the 404 handler.)
55
+ if (req.method === 'GET' && url === '/api/playbooks') {
56
+ try {
57
+ const qp = new URL(req.url, 'http://localhost').searchParams;
58
+ const dir = qp.get('dir') || ctx.projectDir || process.cwd();
59
+ const playbookDir = path.join(path.resolve(dir), '.monomind', 'playbooks');
60
+ const result = [];
61
+ if (fs.existsSync(playbookDir)) {
62
+ const files = fs
63
+ .readdirSync(playbookDir)
64
+ .filter((f) => f.endsWith('.json') && !f.startsWith('._'));
65
+ for (const file of files) {
66
+ try {
67
+ const fpath = path.join(playbookDir, file);
68
+ const def = JSON.parse(fs.readFileSync(fpath, 'utf8'));
69
+ const stat = fs.statSync(fpath);
70
+ result.push({
71
+ ...def,
72
+ id: def.id || file.replace('.json', ''),
73
+ file,
74
+ modifiedAt: stat.mtimeMs,
75
+ });
76
+ } catch (_) {}
77
+ }
78
+ }
79
+ res.writeHead(200, {
80
+ 'Content-Type': 'application/json',
81
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
82
+ 'Cache-Control': 'no-cache',
83
+ });
84
+ res.end(JSON.stringify(result));
85
+ } catch (e) {
86
+ res.writeHead(500, { 'Content-Type': 'application/json' });
87
+ res.end(JSON.stringify({ error: e.message }));
88
+ }
89
+ return true;
90
+ }
91
+
92
+ // ------------------------------------------------- POST /api/playbooks
93
+ // Save a playbook definition to .monomind/playbooks/<id>.json
94
+ if (req.method === 'POST' && url === '/api/playbooks') {
95
+ try {
96
+ let body = '';
97
+ await new Promise((resolve, reject) => {
98
+ req.on('data', (d) => {
99
+ body += d;
100
+ });
101
+ req.on('end', resolve);
102
+ req.on('error', reject);
103
+ });
104
+ const pb = JSON.parse(body);
105
+ if (!pb.id || !pb.name) {
106
+ res.writeHead(400, { 'Content-Type': 'application/json' });
107
+ res.end(JSON.stringify({ error: 'id and name are required' }));
108
+ return true;
109
+ }
110
+ const dir = ctx.projectDir || process.cwd();
111
+ const playbookDir = path.join(dir, '.monomind', 'playbooks');
112
+ fs.mkdirSync(playbookDir, { recursive: true });
113
+ const filePath = path.join(playbookDir, `${pb.id}.json`);
114
+ fs.writeFileSync(filePath, JSON.stringify(pb, null, 2));
115
+ res.writeHead(200, { 'Content-Type': 'application/json' });
116
+ res.end(JSON.stringify({ ok: true, id: pb.id, file: `${pb.id}.json` }));
117
+ } catch (e) {
118
+ res.writeHead(500);
119
+ res.end(JSON.stringify({ error: e.message }));
120
+ }
121
+ return true;
122
+ }
123
+
124
+ // ------------------------------------------------- GET /api/workflow-defs
125
+ if (req.method === 'GET' && url === '/api/workflow-defs') {
126
+ try {
127
+ const qp = new URL(req.url, 'http://x').searchParams;
128
+ const dir = qp.get('dir') || ctx.projectDir || process.cwd();
129
+ const playbookDir = path.join(dir, '.monomind', 'playbooks');
130
+ const result = [];
131
+ if (fs.existsSync(playbookDir)) {
132
+ const files = fs.readdirSync(playbookDir).filter((f) => f.endsWith('.json'));
133
+ for (const file of files) {
134
+ try {
135
+ const fpath = path.join(playbookDir, file);
136
+ const stat = fs.statSync(fpath);
137
+ const def = JSON.parse(fs.readFileSync(fpath, 'utf8'));
138
+ const params = (def.params || []).map((p) =>
139
+ typeof p === 'string' ? p : p.name || p.key || '',
140
+ );
141
+ result.push({
142
+ id: def.id || file.replace('.json', ''),
143
+ name: def.name || file.replace('.json', ''),
144
+ description: def.description || null,
145
+ file,
146
+ nodeCount: Array.isArray(def.nodes) ? def.nodes.length : 0,
147
+ params,
148
+ modifiedAt: stat.mtimeMs,
149
+ });
150
+ } catch (_) {}
151
+ }
152
+ }
153
+ res.writeHead(200, { 'Content-Type': 'application/json' });
154
+ res.end(JSON.stringify(result));
155
+ } catch (e) {
156
+ res.writeHead(500);
157
+ res.end(JSON.stringify({ error: e.message }));
158
+ }
159
+ return true;
160
+ }
161
+
162
+ // ------------------------------------------------- GET /api/org-coverage
163
+ // Audit coverage report written by the audit loop to .monomind/audit/coverage.json
164
+ if (req.method === 'GET' && url === '/api/org-coverage') {
165
+ try {
166
+ const qp = new URL(req.url, 'http://localhost').searchParams;
167
+ const dir = path.resolve(qp.get('dir') || ctx.projectDir || process.cwd());
168
+ const covPath = path.join(dir, '.monomind', 'audit', 'coverage.json');
169
+ if (!fs.existsSync(covPath)) {
170
+ res.writeHead(404, {
171
+ 'Content-Type': 'application/json',
172
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
173
+ });
174
+ res.end('{}');
175
+ return true;
176
+ }
177
+ const coverage = JSON.parse(fs.readFileSync(covPath, 'utf8'));
178
+ res.writeHead(200, {
179
+ 'Content-Type': 'application/json',
180
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
181
+ 'Cache-Control': 'no-cache',
182
+ });
183
+ res.end(JSON.stringify(coverage));
184
+ } catch (e) {
185
+ res.writeHead(500, { 'Content-Type': 'application/json' });
186
+ res.end(JSON.stringify({ error: e.message }));
187
+ }
188
+ return true;
189
+ }
190
+
191
+ // ------------------------------------------------- GET /api/workflow-runs
192
+ if (req.method === 'GET' && url === '/api/workflow-runs') {
193
+ // Reads from ~/.monomind/browse-runs.json written by the monobrowse dashboard server.
194
+ try {
195
+ const runsFile = path.join(os.homedir(), '.monomind', 'browse-runs.json');
196
+ if (fs.existsSync(runsFile)) {
197
+ const raw = fs.readFileSync(runsFile, 'utf-8');
198
+ const runs = JSON.parse(raw);
199
+ res.writeHead(200, { 'Content-Type': 'application/json' });
200
+ res.end(JSON.stringify(Array.isArray(runs) ? runs : []));
201
+ } else {
202
+ res.writeHead(200, { 'Content-Type': 'application/json' });
203
+ res.end('[]');
204
+ }
205
+ } catch {
206
+ res.writeHead(200, { 'Content-Type': 'application/json' });
207
+ res.end('[]');
208
+ }
209
+ return true;
210
+ }
211
+ return false;
212
+ }