@monoes/monomindcli 2.16.12 → 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 (217) 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-scratch.d.ts +43 -0
  14. package/dist/src/commands/cleanup-scratch.d.ts.map +1 -0
  15. package/dist/src/commands/cleanup-scratch.js +125 -0
  16. package/dist/src/commands/cleanup-scratch.js.map +1 -0
  17. package/dist/src/commands/cleanup.d.ts +2 -39
  18. package/dist/src/commands/cleanup.d.ts.map +1 -1
  19. package/dist/src/commands/cleanup.js +5 -247
  20. package/dist/src/commands/cleanup.js.map +1 -1
  21. package/dist/src/commands/doctor-gitignore-checks.d.ts +10 -0
  22. package/dist/src/commands/doctor-gitignore-checks.d.ts.map +1 -0
  23. package/dist/src/commands/doctor-gitignore-checks.js +141 -0
  24. package/dist/src/commands/doctor-gitignore-checks.js.map +1 -0
  25. package/dist/src/commands/doctor-helpers-checks.d.ts +10 -0
  26. package/dist/src/commands/doctor-helpers-checks.d.ts.map +1 -0
  27. package/dist/src/commands/doctor-helpers-checks.js +292 -0
  28. package/dist/src/commands/doctor-helpers-checks.js.map +1 -0
  29. package/dist/src/commands/doctor-memory-checks.d.ts +47 -0
  30. package/dist/src/commands/doctor-memory-checks.d.ts.map +1 -0
  31. package/dist/src/commands/doctor-memory-checks.js +298 -0
  32. package/dist/src/commands/doctor-memory-checks.js.map +1 -0
  33. package/dist/src/commands/doctor-monograph-checks.d.ts +9 -0
  34. package/dist/src/commands/doctor-monograph-checks.d.ts.map +1 -0
  35. package/dist/src/commands/doctor-monograph-checks.js +331 -0
  36. package/dist/src/commands/doctor-monograph-checks.js.map +1 -0
  37. package/dist/src/commands/doctor-project-checks.d.ts +7 -66
  38. package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
  39. package/dist/src/commands/doctor-project-checks.js +10 -1331
  40. package/dist/src/commands/doctor-project-checks.js.map +1 -1
  41. package/dist/src/commands/doctor-routing-checks.d.ts +10 -0
  42. package/dist/src/commands/doctor-routing-checks.d.ts.map +1 -0
  43. package/dist/src/commands/doctor-routing-checks.js +194 -0
  44. package/dist/src/commands/doctor-routing-checks.js.map +1 -0
  45. package/dist/src/commands/doctor-worker-checks.d.ts +17 -0
  46. package/dist/src/commands/doctor-worker-checks.d.ts.map +1 -0
  47. package/dist/src/commands/doctor-worker-checks.js +122 -0
  48. package/dist/src/commands/doctor-worker-checks.js.map +1 -0
  49. package/dist/src/commands/hooks-routing-commands.js +1 -1
  50. package/dist/src/commands/hooks-routing-commands.js.map +1 -1
  51. package/dist/src/commands/init.d.ts.map +1 -1
  52. package/dist/src/commands/init.js +6 -15
  53. package/dist/src/commands/init.js.map +1 -1
  54. package/dist/src/index.js +2 -2
  55. package/dist/src/index.js.map +1 -1
  56. package/dist/src/init/never-commit.d.ts +3 -3
  57. package/dist/src/init/types.d.ts +1 -1
  58. package/dist/src/init/types.js +1 -1
  59. package/dist/src/knowledge/document-store.js +1 -1
  60. package/dist/src/knowledge/document-store.js.map +1 -1
  61. package/dist/src/mcp-tools/hooks-edit-command.d.ts +10 -0
  62. package/dist/src/mcp-tools/hooks-edit-command.d.ts.map +1 -0
  63. package/dist/src/mcp-tools/hooks-edit-command.js +247 -0
  64. package/dist/src/mcp-tools/hooks-edit-command.js.map +1 -0
  65. package/dist/src/mcp-tools/hooks-learning.d.ts +9 -0
  66. package/dist/src/mcp-tools/hooks-learning.d.ts.map +1 -0
  67. package/dist/src/mcp-tools/hooks-learning.js +408 -0
  68. package/dist/src/mcp-tools/hooks-learning.js.map +1 -0
  69. package/dist/src/mcp-tools/hooks-metrics-list.d.ts +8 -0
  70. package/dist/src/mcp-tools/hooks-metrics-list.d.ts.map +1 -0
  71. package/dist/src/mcp-tools/hooks-metrics-list.js +205 -0
  72. package/dist/src/mcp-tools/hooks-metrics-list.js.map +1 -0
  73. package/dist/src/mcp-tools/hooks-route.d.ts +12 -0
  74. package/dist/src/mcp-tools/hooks-route.d.ts.map +1 -0
  75. package/dist/src/mcp-tools/hooks-route.js +251 -0
  76. package/dist/src/mcp-tools/hooks-route.js.map +1 -0
  77. package/dist/src/mcp-tools/hooks-routing.d.ts +6 -27
  78. package/dist/src/mcp-tools/hooks-routing.d.ts.map +1 -1
  79. package/dist/src/mcp-tools/hooks-routing.js +6 -1727
  80. package/dist/src/mcp-tools/hooks-routing.js.map +1 -1
  81. package/dist/src/mcp-tools/hooks-session.d.ts +9 -0
  82. package/dist/src/mcp-tools/hooks-session.d.ts.map +1 -0
  83. package/dist/src/mcp-tools/hooks-session.js +243 -0
  84. package/dist/src/mcp-tools/hooks-session.js.map +1 -0
  85. package/dist/src/mcp-tools/hooks-task.d.ts +17 -0
  86. package/dist/src/mcp-tools/hooks-task.d.ts.map +1 -0
  87. package/dist/src/mcp-tools/hooks-task.js +427 -0
  88. package/dist/src/mcp-tools/hooks-task.js.map +1 -0
  89. package/dist/src/mcp-tools/hooks-tools.d.ts +2 -1
  90. package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
  91. package/dist/src/mcp-tools/hooks-tools.js +2 -1
  92. package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
  93. package/dist/src/mcp-tools/memory-tools.js +1 -1
  94. package/dist/src/mcp-tools/memory-tools.js.map +1 -1
  95. package/dist/src/memory/bm25-index.d.ts +2 -2
  96. package/dist/src/memory/bm25-index.js +2 -2
  97. package/dist/src/memory/embedding-operations.js +2 -2
  98. package/dist/src/memory/embedding-operations.js.map +1 -1
  99. package/dist/src/memory/entity-name-key.d.ts +1 -1
  100. package/dist/src/memory/entity-name-key.js +1 -1
  101. package/dist/src/memory/hnsw-operations.d.ts +1 -1
  102. package/dist/src/memory/hnsw-operations.js +1 -1
  103. package/dist/src/memory/memory-bridge-backend.d.ts +43 -0
  104. package/dist/src/memory/memory-bridge-backend.d.ts.map +1 -0
  105. package/dist/src/memory/memory-bridge-backend.js +363 -0
  106. package/dist/src/memory/memory-bridge-backend.js.map +1 -0
  107. package/dist/src/memory/memory-bridge-core.d.ts +42 -0
  108. package/dist/src/memory/memory-bridge-core.d.ts.map +1 -0
  109. package/dist/src/memory/memory-bridge-core.js +126 -0
  110. package/dist/src/memory/memory-bridge-core.js.map +1 -0
  111. package/dist/src/memory/memory-bridge-entries.d.ts +131 -0
  112. package/dist/src/memory/memory-bridge-entries.d.ts.map +1 -0
  113. package/dist/src/memory/memory-bridge-entries.js +227 -0
  114. package/dist/src/memory/memory-bridge-entries.js.map +1 -0
  115. package/dist/src/memory/memory-bridge-feedback.d.ts +60 -0
  116. package/dist/src/memory/memory-bridge-feedback.d.ts.map +1 -0
  117. package/dist/src/memory/memory-bridge-feedback.js +118 -0
  118. package/dist/src/memory/memory-bridge-feedback.js.map +1 -0
  119. package/dist/src/memory/memory-bridge-paths.d.ts +123 -0
  120. package/dist/src/memory/memory-bridge-paths.d.ts.map +1 -0
  121. package/dist/src/memory/memory-bridge-paths.js +335 -0
  122. package/dist/src/memory/memory-bridge-paths.js.map +1 -0
  123. package/dist/src/memory/memory-bridge-search.d.ts +48 -0
  124. package/dist/src/memory/memory-bridge-search.d.ts.map +1 -0
  125. package/dist/src/memory/memory-bridge-search.js +383 -0
  126. package/dist/src/memory/memory-bridge-search.js.map +1 -0
  127. package/dist/src/memory/memory-bridge-sessions.d.ts +38 -0
  128. package/dist/src/memory/memory-bridge-sessions.d.ts.map +1 -0
  129. package/dist/src/memory/memory-bridge-sessions.js +109 -0
  130. package/dist/src/memory/memory-bridge-sessions.js.map +1 -0
  131. package/dist/src/memory/memory-bridge-store.d.ts +58 -0
  132. package/dist/src/memory/memory-bridge-store.d.ts.map +1 -0
  133. package/dist/src/memory/memory-bridge-store.js +201 -0
  134. package/dist/src/memory/memory-bridge-store.js.map +1 -0
  135. package/dist/src/memory/memory-bridge.d.ts +10 -438
  136. package/dist/src/memory/memory-bridge.d.ts.map +1 -1
  137. package/dist/src/memory/memory-bridge.js +14 -1787
  138. package/dist/src/memory/memory-bridge.js.map +1 -1
  139. package/dist/src/memory/memory-kg-claims.d.ts +52 -0
  140. package/dist/src/memory/memory-kg-claims.d.ts.map +1 -0
  141. package/dist/src/memory/memory-kg-claims.js +99 -0
  142. package/dist/src/memory/memory-kg-claims.js.map +1 -0
  143. package/dist/src/memory/memory-kg-index.d.ts +86 -0
  144. package/dist/src/memory/memory-kg-index.d.ts.map +1 -0
  145. package/dist/src/memory/memory-kg-index.js +256 -0
  146. package/dist/src/memory/memory-kg-index.js.map +1 -0
  147. package/dist/src/memory/memory-kg-ingest.d.ts +44 -0
  148. package/dist/src/memory/memory-kg-ingest.d.ts.map +1 -0
  149. package/dist/src/memory/memory-kg-ingest.js +361 -0
  150. package/dist/src/memory/memory-kg-ingest.js.map +1 -0
  151. package/dist/src/memory/memory-kg-model.d.ts +168 -0
  152. package/dist/src/memory/memory-kg-model.d.ts.map +1 -0
  153. package/dist/src/memory/memory-kg-model.js +158 -0
  154. package/dist/src/memory/memory-kg-model.js.map +1 -0
  155. package/dist/src/memory/memory-kg-names.d.ts +64 -0
  156. package/dist/src/memory/memory-kg-names.d.ts.map +1 -0
  157. package/dist/src/memory/memory-kg-names.js +187 -0
  158. package/dist/src/memory/memory-kg-names.js.map +1 -0
  159. package/dist/src/memory/memory-kg-promote.d.ts +79 -0
  160. package/dist/src/memory/memory-kg-promote.d.ts.map +1 -0
  161. package/dist/src/memory/memory-kg-promote.js +248 -0
  162. package/dist/src/memory/memory-kg-promote.js.map +1 -0
  163. package/dist/src/memory/memory-kg-rebuild.d.ts +77 -0
  164. package/dist/src/memory/memory-kg-rebuild.d.ts.map +1 -0
  165. package/dist/src/memory/memory-kg-rebuild.js +299 -0
  166. package/dist/src/memory/memory-kg-rebuild.js.map +1 -0
  167. package/dist/src/memory/memory-kg-rollback.d.ts +47 -0
  168. package/dist/src/memory/memory-kg-rollback.d.ts.map +1 -0
  169. package/dist/src/memory/memory-kg-rollback.js +193 -0
  170. package/dist/src/memory/memory-kg-rollback.js.map +1 -0
  171. package/dist/src/memory/memory-kg-rules.d.ts +64 -0
  172. package/dist/src/memory/memory-kg-rules.d.ts.map +1 -0
  173. package/dist/src/memory/memory-kg-rules.js +251 -0
  174. package/dist/src/memory/memory-kg-rules.js.map +1 -0
  175. package/dist/src/memory/memory-kg-scan.d.ts +118 -0
  176. package/dist/src/memory/memory-kg-scan.d.ts.map +1 -0
  177. package/dist/src/memory/memory-kg-scan.js +209 -0
  178. package/dist/src/memory/memory-kg-scan.js.map +1 -0
  179. package/dist/src/memory/memory-kg-search.d.ts +80 -0
  180. package/dist/src/memory/memory-kg-search.d.ts.map +1 -0
  181. package/dist/src/memory/memory-kg-search.js +239 -0
  182. package/dist/src/memory/memory-kg-search.js.map +1 -0
  183. package/dist/src/memory/memory-kg.d.ts +19 -547
  184. package/dist/src/memory/memory-kg.d.ts.map +1 -1
  185. package/dist/src/memory/memory-kg.js +10 -2389
  186. package/dist/src/memory/memory-kg.js.map +1 -1
  187. package/dist/src/ui/org-hil.mjs +1 -1
  188. package/dist/src/ui/routes-org-agents.mjs +318 -0
  189. package/dist/src/ui/routes-org-approvals.mjs +133 -0
  190. package/dist/src/ui/routes-org-config.mjs +459 -0
  191. package/dist/src/ui/routes-org-control.mjs +300 -0
  192. package/dist/src/ui/routes-org-files.mjs +172 -0
  193. package/dist/src/ui/routes-org-helpers.mjs +84 -0
  194. package/dist/src/ui/routes-org-knowledge.mjs +262 -0
  195. package/dist/src/ui/routes-org-lifecycle.mjs +224 -0
  196. package/dist/src/ui/routes-org-live.mjs +278 -0
  197. package/dist/src/ui/routes-org-mastermind.mjs +346 -0
  198. package/dist/src/ui/routes-org-planning.mjs +398 -0
  199. package/dist/src/ui/routes-org-runs.mjs +430 -0
  200. package/dist/src/ui/routes-org-status.mjs +342 -0
  201. package/dist/src/ui/routes-org-workflows.mjs +212 -0
  202. package/dist/src/ui/routes-org.mjs +26 -3847
  203. package/dist/src/ui/server.mjs +1 -1
  204. package/dist/src/utils/native-error.d.ts +1 -1
  205. package/dist/src/utils/native-error.js +1 -1
  206. package/dist/src/utils/paths.d.ts +2 -1
  207. package/dist/src/utils/paths.d.ts.map +1 -1
  208. package/dist/src/utils/paths.js +2 -1
  209. package/dist/src/utils/paths.js.map +1 -1
  210. package/dist/tsconfig.tsbuildinfo +1 -1
  211. package/org-skills/adversarial-reviewer/SKILL.md +1 -1
  212. package/org-skills/board-deck-builder/SKILL.md +1 -1
  213. package/org-skills/code-reviewer/SKILL.md +1 -1
  214. package/org-skills/debugging-wizard/SKILL.md +1 -1
  215. package/org-skills/scientific-thinking-literature-review/SKILL.md +1 -1
  216. package/org-skills/scientific-thinking-scholar-evaluation/SKILL.md +1 -1
  217. package/package.json +8 -8
@@ -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
+ }
@@ -0,0 +1,459 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { approvalsOrEmpty } from './routes-org-helpers.mjs';
4
+
5
+ // Org dashboard routes: org list, import, create, config and activity.
6
+ // Registered, in order, by handleOrgRoutes in routes-org.mjs.
7
+ export async function handleOrgConfigRoutes(req, res, url, corsOrigin, ctx) {
8
+ // ------------------------------------------------- Org management
9
+ // GET /api/orgs — list all saved org configs
10
+ if (req.method === 'GET' && url === '/api/orgs') {
11
+ try {
12
+ const _orgsQs = new URL(req.url, 'http://localhost').searchParams;
13
+ const _orgsExplicitDir = _orgsQs.get('dir');
14
+ const _orgsServerRoot = path.resolve(_orgsExplicitDir || ctx.projectDir || process.cwd());
15
+ // Collect project dirs to search: explicit dir + known-projects (like sessions API)
16
+ const _orgsProjDirs = new Set([_orgsServerRoot]);
17
+ if (!_orgsExplicitDir) {
18
+ try {
19
+ const _knownOrgsFile = path.join(_orgsServerRoot, 'data', 'known-projects.json');
20
+ if (fs.existsSync(_knownOrgsFile)) {
21
+ JSON.parse(fs.readFileSync(_knownOrgsFile, 'utf8')).forEach((p) =>
22
+ _orgsProjDirs.add(p),
23
+ );
24
+ }
25
+ } catch (_) {}
26
+ }
27
+ const _sidecarSuffixRe =
28
+ /-(approvals|state|activity|goals|routines|projects|members|issues|workspaces|worktrees|environments|plugins|adapters|bootstrap|threads|budgets|project-workspaces|approval-comments|secrets|join-requests|skills)\.json$/;
29
+ const _orgsSeen = new Set();
30
+ const orgs = [];
31
+ for (const _opd of _orgsProjDirs) {
32
+ const orgsDir = path.join(_opd, '.monomind', 'orgs');
33
+ if (!fs.existsSync(orgsDir)) continue;
34
+ const files = fs
35
+ .readdirSync(orgsDir)
36
+ .filter((f) => f.endsWith('.json') && !_sidecarSuffixRe.test(f));
37
+ for (const f of files) {
38
+ try {
39
+ const cfg = JSON.parse(fs.readFileSync(path.join(orgsDir, f), 'utf8'));
40
+ const _lOrgName = cfg.name || '';
41
+ if (!_lOrgName || _orgsSeen.has(_lOrgName)) continue;
42
+ _orgsSeen.add(_lOrgName);
43
+ const _rs = ctx._readRunState(_lOrgName, _opd);
44
+ const _ttl = Math.max((_rs?.checkpointInterval || 600000) * 2, 7200000);
45
+ const running =
46
+ (_rs?.status === 'running' && Date.now() - (_rs?.lastEventAt || 0) < _ttl) ||
47
+ ctx.activeOrgRuns.has(_lOrgName);
48
+ orgs.push({
49
+ name: cfg.name,
50
+ goal: cfg.goal,
51
+ roles: Array.isArray(cfg.roles) ? cfg.roles : [],
52
+ topology: cfg.topology,
53
+ created_at: cfg.created_at,
54
+ running,
55
+ status: cfg.status,
56
+ projectDir: _opd,
57
+ lastEventAt: _rs?.lastEventAt || null,
58
+ loop: cfg.loop
59
+ ? {
60
+ poll_interval_minutes: cfg.loop.poll_interval_minutes,
61
+ last_run: cfg.loop.last_run,
62
+ next_run: cfg.loop.next_run,
63
+ }
64
+ : undefined,
65
+ });
66
+ } catch (_) {}
67
+ }
68
+ }
69
+ res.writeHead(200, {
70
+ 'Content-Type': 'application/json',
71
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
72
+ });
73
+ res.end(JSON.stringify(orgs));
74
+ } catch (_) {
75
+ res.writeHead(500);
76
+ res.end('[]');
77
+ }
78
+ return true;
79
+ }
80
+
81
+ // POST /api/orgs/:name/import — import an org config by name (orgs.html upload flow)
82
+ if (req.method === 'POST' && /^\/api\/orgs\/[a-z0-9][a-z0-9_-]{0,63}\/import$/i.test(url)) {
83
+ let body = '';
84
+ req.on('data', (c) => {
85
+ body += c;
86
+ if (body.length > 2e6) req.destroy();
87
+ });
88
+ req.on('end', () => {
89
+ try {
90
+ const urlParts = url.split('/');
91
+ const orgName = decodeURIComponent(urlParts[3]);
92
+ if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
93
+ res.writeHead(400, { 'Content-Type': 'application/json' });
94
+ res.end(JSON.stringify({ error: 'Invalid org name' }));
95
+ return;
96
+ }
97
+ const cfg = JSON.parse(body);
98
+ const _importQs = new URL(req.url, 'http://localhost').searchParams;
99
+ const dir = path.resolve(_importQs.get('dir') || ctx.projectDir || process.cwd());
100
+ const orgsDir = path.join(dir, '.monomind', 'orgs');
101
+ fs.mkdirSync(orgsDir, { recursive: true });
102
+ const destFile = path.join(orgsDir, `${orgName}.json`);
103
+ fs.writeFileSync(destFile, JSON.stringify({ ...cfg, name: orgName }, null, 2), 'utf8');
104
+ res.writeHead(200, {
105
+ 'Content-Type': 'application/json',
106
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
107
+ });
108
+ res.end(JSON.stringify({ ok: true, name: orgName, file: destFile }));
109
+ } catch (e) {
110
+ res.writeHead(400, { 'Content-Type': 'application/json' });
111
+ res.end(JSON.stringify({ error: e.message }));
112
+ }
113
+ });
114
+ return true;
115
+ }
116
+
117
+ // POST /api/orgs — import / create org from JSON body
118
+ if (req.method === 'POST' && url === '/api/orgs') {
119
+ let body = '';
120
+ req.on('data', (c) => {
121
+ body += c;
122
+ if (body.length > 2e6) req.destroy();
123
+ });
124
+ req.on('end', () => {
125
+ try {
126
+ const cfg = JSON.parse(body);
127
+ const qs = new URL(req.url, 'http://localhost').searchParams;
128
+ const dir = qs.get('dir') || cfg.dir || ctx.projectDir || process.cwd();
129
+ const name = (cfg.name || '')
130
+ .toLowerCase()
131
+ .replace(/[^a-z0-9_-]/g, '-')
132
+ .replace(/^-+|-+$/g, '')
133
+ .slice(0, 64);
134
+ if (!name) {
135
+ res.writeHead(400, { 'Content-Type': 'application/json' });
136
+ res.end(JSON.stringify({ error: 'Invalid org name' }));
137
+ return;
138
+ }
139
+ const orgsDir = path.join(path.resolve(dir), '.monomind', 'orgs');
140
+ fs.mkdirSync(orgsDir, { recursive: true });
141
+ const destFile = path.join(orgsDir, `${name}.json`);
142
+ const cleanCfg = Object.fromEntries(
143
+ Object.entries({ ...cfg, name }).filter(([k]) => !k.startsWith('_')),
144
+ );
145
+ fs.writeFileSync(destFile, JSON.stringify(cleanCfg, null, 2), 'utf8');
146
+ res.writeHead(200, {
147
+ 'Content-Type': 'application/json',
148
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
149
+ });
150
+ res.end(JSON.stringify({ ok: true, name, file: destFile }));
151
+ } catch (e) {
152
+ res.writeHead(400, { 'Content-Type': 'application/json' });
153
+ res.end(JSON.stringify({ error: e.message }));
154
+ }
155
+ });
156
+ return true;
157
+ }
158
+
159
+ // GET /api/orgs/:name — get specific org config (exact path: /api/orgs/<slug>)
160
+ if (req.method === 'GET' && /^\/api\/orgs\/[a-z0-9][a-z0-9_-]{0,63}$/i.test(url)) {
161
+ try {
162
+ const orgName = decodeURIComponent(url.slice('/api/orgs/'.length));
163
+ if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
164
+ res.writeHead(400);
165
+ res.end('Invalid org name');
166
+ return true;
167
+ }
168
+ const _orgsOneQs = new URL(req.url, 'http://localhost').searchParams;
169
+ const _orgsOneRoot = path.resolve(_orgsOneQs.get('dir') || ctx.projectDir || process.cwd());
170
+ const _orgsOneProjDir = ctx._resolveOrgProjectDir(orgName, _orgsOneRoot) || _orgsOneRoot;
171
+ const f = path.join(_orgsOneProjDir, '.monomind', 'orgs', `${orgName}.json`);
172
+ if (!fs.existsSync(f)) {
173
+ res.writeHead(404);
174
+ res.end('{"error":"not found"}');
175
+ return true;
176
+ }
177
+ res.writeHead(200, {
178
+ 'Content-Type': 'application/json',
179
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
180
+ });
181
+ res.end(fs.readFileSync(f, 'utf8'));
182
+ } catch (_) {
183
+ res.writeHead(500);
184
+ res.end('{}');
185
+ }
186
+ return true;
187
+ }
188
+
189
+ // GET /api/org/:name — ORG ROOM: rich org data (config + state + tasks + routines + goals)
190
+ if (req.method === 'GET' && /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}$/i.test(url)) {
191
+ try {
192
+ const orgName = decodeURIComponent(url.slice('/api/org/'.length));
193
+ if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
194
+ res.writeHead(400);
195
+ res.end('Invalid org name');
196
+ return true;
197
+ }
198
+ const _orgQs = new URL(req.url, 'http://localhost').searchParams;
199
+ const _orgServerRoot = path.resolve(_orgQs.get('dir') || ctx.projectDir || process.cwd());
200
+ // Resolve which project dir actually has this org's config
201
+ const d = ctx._resolveOrgProjectDir(orgName, _orgServerRoot) || _orgServerRoot;
202
+ const orgsDir = path.join(d, '.monomind', 'orgs');
203
+
204
+ const readJsonSafe = (f) => {
205
+ try {
206
+ return JSON.parse(fs.readFileSync(f, 'utf8'));
207
+ } catch (_) {
208
+ return null;
209
+ }
210
+ };
211
+
212
+ const configFile = path.join(orgsDir, `${orgName}.json`);
213
+ if (!fs.existsSync(configFile)) {
214
+ res.writeHead(404);
215
+ res.end('{"error":"org not found"}');
216
+ return true;
217
+ }
218
+ const config = readJsonSafe(configFile);
219
+
220
+ const state = readJsonSafe(path.join(orgsDir, `${orgName}-state.json`)) || { agents: {} };
221
+ const goalsData = readJsonSafe(path.join(orgsDir, `${orgName}-goals.json`)) || { goals: [] };
222
+ const routinesData = readJsonSafe(path.join(orgsDir, `${orgName}-routines.json`)) || {
223
+ routines: [],
224
+ };
225
+ const approvalsData = { approvals: approvalsOrEmpty(orgsDir, orgName) };
226
+
227
+ // Check running status: stop file absence AND (in-memory ctx.activeOrgRuns OR state-file agents OR active loop file)
228
+ // Path must match what `org run`'s poll loop and `org serve`'s pollStopfiles()
229
+ // actually watch (.monomind/orgs/<name>/stop) — see the POST .../stop handler below.
230
+ const stopFile = path.join(orgsDir, orgName, 'stop');
231
+ const _loopsDir = path.join(d, '.monomind', 'loops');
232
+ const _loopRunning = (() => {
233
+ try {
234
+ if (!fs.existsSync(_loopsDir)) return false;
235
+ // Get the org's state file mtime to correlate with loop activity
236
+ const orgStateMtime = (() => {
237
+ try {
238
+ return fs.statSync(path.join(orgsDir, `${orgName}-state.json`)).mtimeMs;
239
+ } catch {
240
+ return 0;
241
+ }
242
+ })();
243
+ // Also check org's most recent run file mtime
244
+ const orgRunsDir = path.join(
245
+ ctx._getGitMonomindDir(d) || path.join(d, '.monomind'),
246
+ 'orgs',
247
+ orgName,
248
+ 'runs',
249
+ );
250
+ const orgLastRunMtime = (() => {
251
+ try {
252
+ if (!fs.existsSync(orgRunsDir)) return 0;
253
+ const runFiles = fs
254
+ .readdirSync(orgRunsDir)
255
+ .filter((f) => f.endsWith('.jsonl') && !f.startsWith('._'));
256
+ if (!runFiles.length) return 0;
257
+ return Math.max(
258
+ ...runFiles.map((f) => {
259
+ try {
260
+ return fs.statSync(path.join(orgRunsDir, f)).mtimeMs;
261
+ } catch {
262
+ return 0;
263
+ }
264
+ }),
265
+ );
266
+ } catch {
267
+ return 0;
268
+ }
269
+ })();
270
+ const orgLastActivity = Math.max(orgStateMtime, orgLastRunMtime);
271
+ return fs.readdirSync(_loopsDir).some((f) => {
272
+ if (!f.endsWith('.json') || f.endsWith('.stop')) return false;
273
+ try {
274
+ const lp = JSON.parse(fs.readFileSync(path.join(_loopsDir, f), 'utf8'));
275
+ if (!lp.command?.includes('runorg')) return false;
276
+ if (!['running', 'paused'].includes(lp.status)) return false;
277
+ // Primary match: explicit orgName field (written by runorg command since v1.14.2)
278
+ if (lp.orgName === orgName) return true;
279
+ // Fallback: org name in prompt (early loop files that preserved --org flag)
280
+ if ((lp.prompt || '').includes(orgName)) return true;
281
+ // Heuristic: if loop's lastRunAt is within 3x wait interval of org's last activity
282
+ const waitMs = (lp.wait || 60) * 3 * 1000;
283
+ return (
284
+ orgLastActivity > 0 && Math.abs(orgLastActivity - (lp.lastRunAt || 0)) < waitMs
285
+ );
286
+ } catch {
287
+ return false;
288
+ }
289
+ });
290
+ } catch {
291
+ return false;
292
+ }
293
+ })();
294
+ const _runstateData = ctx._readRunState(orgName, d);
295
+ const _runstateTtl = Math.max((_runstateData?.checkpointInterval || 600000) * 2, 7200000);
296
+ const _runstateAlive =
297
+ _runstateData?.status === 'running' &&
298
+ Date.now() - (_runstateData?.lastEventAt || 0) < _runstateTtl;
299
+ const running =
300
+ !fs.existsSync(stopFile) &&
301
+ (_runstateAlive || ctx.activeOrgRuns.has(orgName) || _loopRunning);
302
+
303
+ // Read real tasks from the task store and group by status column
304
+ const taskStoreData = readJsonSafe(path.join(d, '.monomind', 'tasks', 'store.json'));
305
+ const allTasks = taskStoreData ? Object.values(taskStoreData.tasks || {}) : [];
306
+ const tasks = {
307
+ todo: allTasks
308
+ .filter((t) => t.status === 'pending')
309
+ .map((t) => ({
310
+ id: t.taskId,
311
+ description: t.description,
312
+ status: 'todo',
313
+ ts: t.createdAt,
314
+ })),
315
+ doing: allTasks
316
+ .filter((t) => t.status === 'in_progress')
317
+ .map((t) => ({
318
+ id: t.taskId,
319
+ description: t.description,
320
+ status: 'doing',
321
+ ts: t.startedAt || t.createdAt,
322
+ })),
323
+ done: allTasks
324
+ .filter(
325
+ (t) => t.status === 'completed' || t.status === 'failed' || t.status === 'cancelled',
326
+ )
327
+ .map((t) => ({
328
+ id: t.taskId,
329
+ description: t.description,
330
+ status: t.status,
331
+ ts: t.completedAt || t.createdAt,
332
+ })),
333
+ };
334
+
335
+ const result = {
336
+ config,
337
+ state,
338
+ goals: goalsData.goals,
339
+ routines: routinesData.routines,
340
+ approvals: approvalsData.approvals,
341
+ running,
342
+ tasks,
343
+ runId: _runstateData?.runId || null,
344
+ lastEventAt: _runstateData?.lastEventAt || null,
345
+ agentStates: _runstateData?.agentStates || {},
346
+ };
347
+
348
+ res.writeHead(200, {
349
+ 'Content-Type': 'application/json',
350
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
351
+ });
352
+ res.end(JSON.stringify(result));
353
+ } catch (_) {
354
+ res.writeHead(500);
355
+ res.end('{}');
356
+ }
357
+ return true;
358
+ }
359
+
360
+ // GET /api/org/:name/activity — recent org events from mastermind-events.jsonl
361
+ if (req.method === 'GET' && /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/activity$/i.test(url)) {
362
+ try {
363
+ const parts = url.split('/');
364
+ const orgName = decodeURIComponent(parts[3]);
365
+ if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
366
+ res.writeHead(400);
367
+ res.end('[]');
368
+ return true;
369
+ }
370
+ const _actQs = new URL(req.url, 'http://localhost').searchParams;
371
+ const _actServerRoot = path.resolve(_actQs.get('dir') || ctx.projectDir || process.cwd());
372
+ const d = ctx._resolveOrgProjectDir(orgName, _actServerRoot) || _actServerRoot;
373
+ const orgsDir = path.join(d, '.monomind', 'orgs');
374
+ const readJ = (f) => {
375
+ try {
376
+ return JSON.parse(fs.readFileSync(f, 'utf8'));
377
+ } catch (_) {
378
+ return null;
379
+ }
380
+ };
381
+ const events = [];
382
+
383
+ // 1) Global mastermind events that EXPLICITLY belong to this org (strict — no untagged leak)
384
+ const eventsFile = path.join(d, 'data', 'mastermind-events.jsonl');
385
+ if (fs.existsSync(eventsFile)) {
386
+ const lines = fs.readFileSync(eventsFile, 'utf8').split('\n').filter(Boolean);
387
+ for (const l of lines.slice(-1000)) {
388
+ try {
389
+ const e = JSON.parse(l);
390
+ if (e && e.org === orgName) events.push(e);
391
+ } catch (_) {}
392
+ }
393
+ }
394
+
395
+ // 2) Synthesize an org-scoped timeline from this org's own records (real data, distinct per org)
396
+ const cfg = readJ(path.join(orgsDir, `${orgName}.json`));
397
+ if (cfg) {
398
+ const createdMs = cfg.created_at ? Date.parse(cfg.created_at) : null;
399
+ if (createdMs)
400
+ events.push({
401
+ type: 'org:create',
402
+ ts: createdMs,
403
+ msg: String(cfg.goal || 'Org created').slice(0, 80),
404
+ });
405
+ // Roles are defined atomically at org creation — there is no per-role
406
+ // timestamp in the config, so every role:defined event uses the org's
407
+ // real created_at instead of a fabricated per-index offset.
408
+ (cfg.roles || []).forEach((r) => {
409
+ events.push({
410
+ type: 'role:defined',
411
+ ts: createdMs,
412
+ role: r.title || r.id,
413
+ msg: r.agent_type || '',
414
+ });
415
+ });
416
+ }
417
+ const goals = readJ(path.join(orgsDir, `${orgName}-goals.json`));
418
+ (goals?.goals || []).forEach((g) =>
419
+ events.push({
420
+ type: 'goal',
421
+ ts: Date.parse(g.created_at || g.updated_at || '') || null,
422
+ role: g.status || '',
423
+ msg: String(g.text || g.title || g.goal || '').slice(0, 80),
424
+ }),
425
+ );
426
+ approvalsOrEmpty(orgsDir, orgName).forEach((a) => {
427
+ events.push({
428
+ type: 'approval',
429
+ ts: a.ts,
430
+ role: a.roleId || '',
431
+ msg: `${a.action} — ${a.status}`.slice(0, 80),
432
+ });
433
+ });
434
+ const state = readJ(path.join(orgsDir, `${orgName}-state.json`));
435
+ if (state?.agents) {
436
+ for (const [aid, a] of Object.entries(state.agents)) {
437
+ const raw = a.lastHeartbeat || a.last_seen || a.updated_at || null;
438
+ const ts = typeof raw === 'number' ? raw : raw ? Date.parse(raw) : null;
439
+ events.push({ type: 'org:heartbeat', ts, agent: aid, msg: a.status || '' });
440
+ }
441
+ }
442
+
443
+ const out = events
444
+ .filter((e) => e?.ts)
445
+ .sort((a, b) => b.ts - a.ts)
446
+ .slice(0, 100);
447
+ res.writeHead(200, {
448
+ 'Content-Type': 'application/json',
449
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
450
+ });
451
+ res.end(JSON.stringify(out));
452
+ } catch (_) {
453
+ res.writeHead(500);
454
+ res.end('[]');
455
+ }
456
+ return true;
457
+ }
458
+ return false;
459
+ }