monomind 2.0.3 → 2.1.3

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 (113) hide show
  1. package/README.md +74 -92
  2. package/package.json +11 -7
  3. package/packages/@monomind/cli/.claude/helpers/control-start.cjs +13 -20
  4. package/packages/@monomind/cli/.claude/helpers/event-logger.cjs +109 -6
  5. package/packages/@monomind/cli/.claude/helpers/graphify-freshen.cjs +19 -17
  6. package/packages/@monomind/cli/.claude/helpers/handlers/agent-start-handler.cjs +60 -28
  7. package/packages/@monomind/cli/.claude/helpers/handlers/capture-handler.cjs +99 -28
  8. package/packages/@monomind/cli/.claude/helpers/handlers/edit-handler.cjs +27 -9
  9. package/packages/@monomind/cli/.claude/helpers/handlers/gates-handler.cjs +18 -9
  10. package/packages/@monomind/cli/.claude/helpers/handlers/loops-status-handler.cjs +1 -1
  11. package/packages/@monomind/cli/.claude/helpers/handlers/route-handler.cjs +33 -4
  12. package/packages/@monomind/cli/.claude/helpers/handlers/session-handler.cjs +75 -45
  13. package/packages/@monomind/cli/.claude/helpers/handlers/session-restore-handler.cjs +0 -12
  14. package/packages/@monomind/cli/.claude/helpers/handlers/task-handler.cjs +53 -22
  15. package/packages/@monomind/cli/.claude/helpers/hook-handler.cjs +75 -4
  16. package/packages/@monomind/cli/.claude/helpers/intelligence.cjs +94 -15
  17. package/packages/@monomind/cli/.claude/helpers/session.cjs +6 -1
  18. package/packages/@monomind/cli/.claude/helpers/statusline.cjs +10 -10
  19. package/packages/@monomind/cli/.claude/helpers/token-tracker.cjs +3 -3
  20. package/packages/@monomind/cli/.claude/helpers/utils/agent-registrations.cjs +41 -0
  21. package/packages/@monomind/cli/.claude/helpers/utils/fs-helpers.cjs +183 -0
  22. package/packages/@monomind/cli/.claude/helpers/utils/micro-agents.cjs +10 -6
  23. package/packages/@monomind/cli/.claude/helpers/utils/monograph.cjs +73 -2
  24. package/packages/@monomind/cli/.claude/helpers/utils/telemetry.cjs +35 -19
  25. package/packages/@monomind/cli/README.md +74 -92
  26. package/packages/@monomind/cli/bin/cli.js +10 -1
  27. package/packages/@monomind/cli/dist/src/browser/workflow/store.d.ts +2 -2
  28. package/packages/@monomind/cli/dist/src/commands/agent-ops.js +9 -15
  29. package/packages/@monomind/cli/dist/src/commands/browse.d.ts +1 -1
  30. package/packages/@monomind/cli/dist/src/commands/cleanup.js +20 -4
  31. package/packages/@monomind/cli/dist/src/commands/completions.js +66 -261
  32. package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.js +5 -0
  33. package/packages/@monomind/cli/dist/src/commands/doctor.js +7 -2
  34. package/packages/@monomind/cli/dist/src/commands/hooks-workers.js +7 -0
  35. package/packages/@monomind/cli/dist/src/commands/index.js +3 -0
  36. package/packages/@monomind/cli/dist/src/commands/mcp.js +11 -6
  37. package/packages/@monomind/cli/dist/src/commands/memory-admin.js +12 -6
  38. package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +6 -13
  39. package/packages/@monomind/cli/dist/src/commands/monograph.js +3 -3
  40. package/packages/@monomind/cli/dist/src/commands/org.d.ts +3 -1
  41. package/packages/@monomind/cli/dist/src/commands/org.js +321 -127
  42. package/packages/@monomind/cli/dist/src/commands/platforms.d.ts +1 -1
  43. package/packages/@monomind/cli/dist/src/commands/start.js +142 -37
  44. package/packages/@monomind/cli/dist/src/commands/tokens.js +21 -3
  45. package/packages/@monomind/cli/dist/src/config-adapter.js +37 -17
  46. package/packages/@monomind/cli/dist/src/index.js +64 -28
  47. package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +44 -11
  48. package/packages/@monomind/cli/dist/src/init/executor.js +93 -55
  49. package/packages/@monomind/cli/dist/src/mcp-client.d.ts +8 -3
  50. package/packages/@monomind/cli/dist/src/mcp-client.js +37 -3
  51. package/packages/@monomind/cli/dist/src/mcp-server.js +8 -2
  52. package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.d.ts +20 -0
  53. package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +10 -5
  54. package/packages/@monomind/cli/dist/src/mcp-tools/auto-install.d.ts +8 -8
  55. package/packages/@monomind/cli/dist/src/mcp-tools/coherence/types.d.ts +31 -31
  56. package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +30 -8
  57. package/packages/@monomind/cli/dist/src/mcp-tools/graphify-tools.js +11 -2
  58. package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +56 -35
  59. package/packages/@monomind/cli/dist/src/mcp-tools/knowledge-tools.js +9 -1
  60. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +56 -14
  61. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +88 -41
  62. package/packages/@monomind/cli/dist/src/mcp-tools/quality/coverage-analysis/prioritize-gaps.d.ts +36 -36
  63. package/packages/@monomind/cli/dist/src/mcp-tools/quality/security-compliance/detect-secrets.d.ts +4 -4
  64. package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +6 -4
  65. package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +4 -4
  66. package/packages/@monomind/cli/dist/src/mcp-tools/types.d.ts +20 -0
  67. package/packages/@monomind/cli/dist/src/mcp-tools/types.js +36 -1
  68. package/packages/@monomind/cli/dist/src/memory/ewc-consolidation.d.ts +12 -0
  69. package/packages/@monomind/cli/dist/src/memory/hnsw-operations.d.ts +13 -1
  70. package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +66 -14
  71. package/packages/@monomind/cli/dist/src/memory/intelligence.js +99 -3
  72. package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +7 -0
  73. package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +20 -0
  74. package/packages/@monomind/cli/dist/src/memory/sona-optimizer.d.ts +12 -0
  75. package/packages/@monomind/cli/dist/src/orgrt/broker.d.ts +26 -0
  76. package/packages/@monomind/cli/dist/src/orgrt/broker.js +73 -0
  77. package/packages/@monomind/cli/dist/src/orgrt/bus.d.ts +5 -1
  78. package/packages/@monomind/cli/dist/src/orgrt/bus.js +5 -1
  79. package/packages/@monomind/cli/dist/src/orgrt/daemon.d.ts +46 -0
  80. package/packages/@monomind/cli/dist/src/orgrt/daemon.js +201 -18
  81. package/packages/@monomind/cli/dist/src/orgrt/forwarder.d.ts +10 -5
  82. package/packages/@monomind/cli/dist/src/orgrt/forwarder.js +138 -8
  83. package/packages/@monomind/cli/dist/src/orgrt/inbox.d.ts +11 -0
  84. package/packages/@monomind/cli/dist/src/orgrt/inbox.js +56 -0
  85. package/packages/@monomind/cli/dist/src/orgrt/policy.d.ts +5 -1
  86. package/packages/@monomind/cli/dist/src/orgrt/policy.js +58 -5
  87. package/packages/@monomind/cli/dist/src/orgrt/provider.js +1 -0
  88. package/packages/@monomind/cli/dist/src/orgrt/scheduler.d.ts +13 -0
  89. package/packages/@monomind/cli/dist/src/orgrt/scheduler.js +48 -0
  90. package/packages/@monomind/cli/dist/src/orgrt/server.d.ts +9 -0
  91. package/packages/@monomind/cli/dist/src/orgrt/server.js +39 -0
  92. package/packages/@monomind/cli/dist/src/orgrt/session.d.ts +10 -1
  93. package/packages/@monomind/cli/dist/src/orgrt/session.js +24 -1
  94. package/packages/@monomind/cli/dist/src/orgrt/test-loop.d.ts +12 -0
  95. package/packages/@monomind/cli/dist/src/orgrt/test-loop.js +108 -0
  96. package/packages/@monomind/cli/dist/src/orgrt/types.d.ts +52 -52
  97. package/packages/@monomind/cli/dist/src/output.d.ts +29 -29
  98. package/packages/@monomind/cli/dist/src/output.js +22 -7
  99. package/packages/@monomind/cli/dist/src/parser.d.ts +32 -0
  100. package/packages/@monomind/cli/dist/src/parser.js +130 -5
  101. package/packages/@monomind/cli/dist/src/routing/embed-worker.js +13 -1
  102. package/packages/@monomind/cli/dist/src/routing/route-layer-factory.js +31 -0
  103. package/packages/@monomind/cli/dist/src/services/config-file-manager.d.ts +21 -0
  104. package/packages/@monomind/cli/dist/src/services/config-file-manager.js +60 -7
  105. package/packages/@monomind/cli/dist/src/services/crash-reporter.js +2 -1
  106. package/packages/@monomind/cli/dist/src/types.d.ts +2 -2
  107. package/packages/@monomind/cli/dist/src/ui/dashboard.html +243 -49
  108. package/packages/@monomind/cli/dist/src/ui/orgs-files.js +192 -0
  109. package/packages/@monomind/cli/dist/src/ui/orgs.html +79 -11
  110. package/packages/@monomind/cli/dist/src/ui/server.mjs +346 -139
  111. package/packages/@monomind/cli/dist/src/utils/input-guards.d.ts +9 -0
  112. package/packages/@monomind/cli/dist/src/utils/input-guards.js +39 -5
  113. package/packages/@monomind/cli/package.json +15 -15
@@ -105,10 +105,10 @@ const buildCommand = {
105
105
  const progressLines = [];
106
106
  try {
107
107
  const { buildAsync } = await import('@monoes/monograph');
108
- // llmMaxSections is a UI variable only; not a valid BuildOptions key in @monoes/monograph@1.1.0
109
108
  await buildAsync(root, {
110
109
  codeOnly,
111
110
  force,
111
+ llmMaxSections,
112
112
  onProgress: (p) => {
113
113
  const msg = `[${p.phase}] ${p.message ?? ''}`;
114
114
  progressLines.push(msg);
@@ -200,10 +200,10 @@ const wikiCommand = {
200
200
  const progressLines = [];
201
201
  try {
202
202
  const { buildAsync } = await import('@monoes/monograph');
203
- // llmMaxSections is a UI variable only; not a valid BuildOptions key in @monoes/monograph@1.1.0
204
203
  await buildAsync(root, {
205
204
  codeOnly: false,
206
205
  force,
206
+ llmMaxSections,
207
207
  onProgress: (p) => {
208
208
  const msg = `[${p.phase}] ${p.message ?? ''}`;
209
209
  progressLines.push(msg);
@@ -408,7 +408,7 @@ const watchCommand = {
408
408
  const watcher = new MonographWatcher(root);
409
409
  watcher.on('monograph:updated', () => {
410
410
  output.writeln(output.dim(' [watch] File change detected, rebuilding…'));
411
- buildAsync(root, { codeOnly: false }).catch((err) => {
411
+ buildAsync(root, { codeOnly: false, llmMaxSections }).catch((err) => {
412
412
  output.writeln(output.dim(` [watch] Rebuild error: ${err.message}`));
413
413
  });
414
414
  });
@@ -1,4 +1,6 @@
1
1
  import type { Command } from '../types.js';
2
- declare const orgCommand: Command;
2
+ /** Remove a lingering stopfile so a fresh `org run` doesn't self-terminate. */
3
+ export declare const clearStopfile: (cwd: string, name: string) => void;
4
+ export declare const orgCommand: Command;
3
5
  export default orgCommand;
4
6
  //# sourceMappingURL=org.d.ts.map
@@ -1,143 +1,337 @@
1
+ // packages/@monomind/cli/src/commands/org.ts
2
+ import { readFileSync, existsSync, unlinkSync, rmSync, readdirSync } from 'node:fs';
3
+ import { join, resolve } from 'node:path';
1
4
  import { output } from '../output.js';
2
- import { existsSync, unlinkSync, rmSync, readdirSync, readFileSync } from 'fs';
3
- import { join, resolve } from 'path';
4
- const orgCommand = {
5
- name: 'org',
6
- description: 'Manage monomind organisations',
7
- examples: [
8
- { command: 'monomind org list', description: 'List all orgs in the project' },
9
- { command: 'monomind org delete my-org', description: 'Delete an org (with confirmation)' },
10
- { command: 'monomind org delete my-org --yes', description: 'Delete without confirmation' },
11
- { command: 'monomind org mark-complete my-org', description: 'Manually close a STALE run (crashed boss recovery)' },
12
- ],
13
- action: async (context) => {
14
- const args = context.args ?? [];
15
- const sub = args[0];
16
- if (!sub || sub === 'help') {
17
- output.info('Usage: monomind org <subcommand>');
18
- output.info('');
19
- output.info('Subcommands:');
20
- output.info(' list List all orgs in the current project');
21
- output.info(' delete <name> Delete an org and all its data');
22
- output.info(' mark-complete <name> Manually close a stale/crashed run');
23
- return { success: true };
24
- }
25
- if (sub === 'mark-complete') {
26
- const orgName = args[1];
27
- if (!orgName || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
28
- output.error('Usage: monomind org mark-complete <name>');
29
- return { success: false, message: 'valid org name required' };
30
- }
31
- const cwd = resolve(context.cwd || process.cwd());
32
- let ctrlUrl = 'http://localhost:4242';
33
- try {
34
- const ctl = JSON.parse(readFileSync(join(cwd, '.monomind', 'control.json'), 'utf8'));
35
- if (ctl.url)
36
- ctrlUrl = ctl.url;
37
- }
38
- catch { /* default */ }
5
+ import { OrgDaemon } from '../orgrt/daemon.js';
6
+ import { startOrgServer } from '../orgrt/server.js';
7
+ import { ORG_DIR } from '../orgrt/types.js';
8
+ const log = (text) => { console.log(text); };
9
+ /** Org names are used to build filesystem paths under .monomind/orgs — reject
10
+ * anything that isn't a plain identifier to prevent path traversal (e.g.
11
+ * `monomind org stop '../../../../tmp/x'`). */
12
+ const ORG_NAME_RE = /^[a-z0-9][a-z0-9_-]*$/i;
13
+ function validateOrgName(name) {
14
+ if (!name)
15
+ return { ok: false, result: { success: false, message: 'org name required' } };
16
+ if (!ORG_NAME_RE.test(name)) {
17
+ log(output.error(`Invalid org name: ${name}`));
18
+ return { ok: false, result: { success: false, message: 'invalid org name' } };
19
+ }
20
+ return { ok: true, name };
21
+ }
22
+ /** Suffixes of org-internal artifact files (state/goals/threads/etc) that
23
+ * share the `<org>.json`/`.jsonl` naming pattern with the org's own config
24
+ * file. Single source of truth for both listOrgConfigFiles() (which must
25
+ * exclude them when discovering real org configs) and deleteAction (which
26
+ * must remove all of them when deleting an org). */
27
+ const ORG_ARTIFACT_SUFFIXES = [
28
+ '-state', '-goals', '-threads', '-activity', '-approvals', '-members', '-secrets', '-budgets',
29
+ '-routines', '-issues', '-projects', '-workspaces', '-worktrees', '-environments',
30
+ '-plugins', '-adapters', '-join-requests', '-bootstrap', '-project-workspaces',
31
+ '-approval-comments', '-skills',
32
+ ];
33
+ function listOrgConfigFiles(orgsDir) {
34
+ return readdirSync(orgsDir)
35
+ .filter(f => f.endsWith('.json') && !f.startsWith('._') && !ORG_ARTIFACT_SUFFIXES.some(suf => f.includes(suf)));
36
+ }
37
+ /** Remove a lingering stopfile so a fresh `org run` doesn't self-terminate. */
38
+ export const clearStopfile = (cwd, name) => {
39
+ rmSync(join(cwd, ORG_DIR, name, 'stop'), { force: true });
40
+ };
41
+ const runAction = async (ctx) => {
42
+ if (!ctx.args[0])
43
+ return { success: false, message: 'org name required: monomind org run <name> [--task "..."]' };
44
+ const validated = validateOrgName(ctx.args[0]);
45
+ if (!validated.ok)
46
+ return validated.result;
47
+ const name = validated.name;
48
+ // A repeated --task flag is promoted to an array by the parser (deliberate,
49
+ // documented behavior elsewhere — repeats never silently drop a value); a
50
+ // plain `as string` cast would let that array flow straight into the org's
51
+ // goal and get stringified as "a,b" with no warning. Checked before any
52
+ // side effects (starting the xdeliver listener) run.
53
+ const taskFlag = ctx.flags['task'];
54
+ if (Array.isArray(taskFlag))
55
+ return { success: false, message: '--task was passed more than once — pass it exactly once' };
56
+ const crossProcess = ctx.flags['crossProcess'] !== false;
57
+ const daemon = new OrgDaemon(ctx.cwd, { crossProcess });
58
+ let srv;
59
+ if (crossProcess) {
60
+ srv = await startOrgServer(daemon, 0);
61
+ daemon.setInboxUrl(`http://127.0.0.1:${srv.port}`);
62
+ }
63
+ const running = await daemon.startOrg(name, taskFlag);
64
+ log(output.info(`org ${name} running (${running.def.roles.length} agents, run ${running.run}) — Ctrl-C or "monomind org stop ${name}" to stop`));
65
+ // stopfile poll lets `org stop` work from another terminal;
66
+ // clear any stale stopfile from a previous run before polling
67
+ clearStopfile(ctx.cwd, name);
68
+ const stopfile = join(ctx.cwd, ORG_DIR, name, 'stop');
69
+ await new Promise(resolvePromise => {
70
+ const iv = setInterval(() => { if (existsSync(stopfile)) {
71
+ clearInterval(iv);
72
+ resolvePromise();
73
+ } }, 2000);
74
+ process.once('SIGINT', () => { clearInterval(iv); resolvePromise(); });
75
+ process.once('SIGTERM', () => { clearInterval(iv); resolvePromise(); });
76
+ });
77
+ clearStopfile(ctx.cwd, name);
78
+ await daemon.stopAll();
79
+ srv?.close();
80
+ return { success: true, message: `org ${name} stopped` };
81
+ };
82
+ const stopAction = async (ctx) => {
83
+ const validated = validateOrgName(ctx.args[0]);
84
+ if (!validated.ok)
85
+ return validated.result;
86
+ const name = validated.name;
87
+ const { writeFileSync, mkdirSync } = await import('node:fs');
88
+ mkdirSync(join(ctx.cwd, ORG_DIR, name), { recursive: true });
89
+ writeFileSync(join(ctx.cwd, ORG_DIR, name, 'stop'), new Date().toISOString());
90
+ return { success: true, message: `stop requested for ${name} (daemon exits within 2s)` };
91
+ };
92
+ const statusAction = async (ctx) => {
93
+ const name = ctx.args[0];
94
+ const orgDir = join(ctx.cwd, ORG_DIR);
95
+ const targets = name ? [name] : (existsSync(orgDir)
96
+ ? listOrgConfigFiles(orgDir).map(f => f.replace(/\.json$/, ''))
97
+ : []);
98
+ for (const t of targets) {
99
+ const rt = join(orgDir, t, 'runtime.json');
100
+ let state = { status: 'never run' };
101
+ if (existsSync(rt)) {
39
102
  try {
40
- const res = await fetch(`${ctrlUrl}/api/orgs/${encodeURIComponent(orgName)}/mark-complete`, { method: 'POST' });
41
- const body = await res.json().catch(() => ({}));
42
- if (!res.ok) {
43
- output.error(`mark-complete failed (${res.status}): ${body.error || 'unknown error'}`);
44
- return { success: false, message: 'server rejected mark-complete' };
45
- }
46
- output.success(`Run marked complete for org "${orgName}"${body.runId ? ` (run ${body.runId})` : ''}.`);
47
- return { success: true };
103
+ state = JSON.parse(readFileSync(rt, 'utf8'));
48
104
  }
49
105
  catch (err) {
50
- output.error(`Dashboard server unreachable at ${ctrlUrl} is it running? (${err instanceof Error ? err.message : 'error'})`);
51
- return { success: false, message: 'server unreachable' };
106
+ log(output.warning(`${t}: could not read runtime.json (${err instanceof Error ? err.message : 'corrupt/truncated file'})`));
107
+ continue;
52
108
  }
53
109
  }
54
- if (sub === 'list') {
55
- const cwd = context.cwd || process.cwd();
56
- const orgsDir = join(cwd, '.monomind', 'orgs');
57
- if (!existsSync(orgsDir)) {
58
- output.info('No orgs directory found. Create an org first with /mastermind:createorg');
59
- return { success: true };
60
- }
61
- const configs = readdirSync(orgsDir)
62
- .filter(f => f.endsWith('.json') && !f.includes('-state') && !f.includes('-goals')
63
- && !f.includes('-threads') && !f.includes('-activity') && !f.includes('-approvals')
64
- && !f.includes('-members') && !f.includes('-secrets') && !f.includes('-budgets'));
65
- if (!configs.length) {
66
- output.info('No orgs found.');
67
- return { success: true };
68
- }
69
- output.info(`Found ${configs.length} org(s):`);
70
- for (const f of configs)
71
- output.info(` • ${f.replace('.json', '')}`);
72
- return { success: true };
110
+ log(output.info(`${t}: ${state.status}${state.run ? ` (run ${state.run}, pid ${state.pid})` : ''}`));
111
+ }
112
+ return { success: true };
113
+ };
114
+ const serveAction = async (ctx) => {
115
+ const crossProcess = ctx.flags['crossProcess'] !== false;
116
+ const daemon = new OrgDaemon(ctx.cwd, { crossProcess });
117
+ let srv;
118
+ if (crossProcess) {
119
+ srv = await startOrgServer(daemon, 0);
120
+ daemon.setInboxUrl(`http://127.0.0.1:${srv.port}`);
121
+ }
122
+ log(output.info('org daemon serving — Ctrl-C to stop'));
123
+ // schedule orgs whose definition declares an interval (e.g. "15m", "2h")
124
+ const { OrgScheduler, parseSchedule } = await import('../orgrt/scheduler.js');
125
+ const sched = new OrgScheduler(async (name, intervalMs) => {
126
+ try {
127
+ await daemon.startOrg(name);
128
+ // Scheduled iterations are time-bounded: agents' `done` promises only
129
+ // resolve after stopOrg closes the mailboxes, so waiting on them alone
130
+ // deadlocks. Race against a max-run timeout, then ALWAYS stopOrg
131
+ // (idempotent — it resolves `done` and flushes).
132
+ const org = daemon.getOrg(name);
133
+ const allDone = org
134
+ ? Promise.allSettled([...org.agents.values()].map(a => a.done))
135
+ : Promise.resolve([]);
136
+ const maxRun = org?.def?.run_config?.max_run;
137
+ const maxMs = parseSchedule(maxRun) ?? Math.min(intervalMs, 600_000); // cap: schedule interval or 10 min
138
+ let timer;
139
+ await Promise.race([allDone, new Promise(r => {
140
+ timer = setTimeout(r, maxMs);
141
+ timer.unref?.();
142
+ })]);
143
+ if (timer)
144
+ clearTimeout(timer);
73
145
  }
74
- if (sub === 'delete') {
75
- const orgName = args[1];
76
- if (!orgName) {
77
- output.error('Usage: monomind org delete <name>');
78
- return { success: false, message: 'org name required' };
79
- }
80
- if (!/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
81
- output.error(`Invalid org name: ${orgName}`);
82
- return { success: false, message: 'invalid org name' };
83
- }
84
- const confirmed = args.includes('--yes') || args.includes('-y');
85
- if (!confirmed) {
86
- output.warning(`This will permanently delete org "${orgName}" and all its data.`);
87
- output.warning('Pass --yes to confirm.');
88
- return { success: false, message: 'confirmation required' };
89
- }
90
- const cwd = resolve(context.cwd || process.cwd());
91
- const orgsDir = join(cwd, '.monomind', 'orgs');
92
- const configFile = join(orgsDir, `${orgName}.json`);
93
- if (!existsSync(configFile)) {
94
- output.error(`Org not found: ${orgName}`);
95
- return { success: false, message: 'org not found' };
96
- }
97
- const suffixes = ['', '-state', '-goals', '-routines', '-approvals', '-activity',
98
- '-issues', '-members', '-projects', '-workspaces', '-worktrees', '-environments',
99
- '-plugins', '-adapters', '-budgets', '-threads', '-secrets', '-join-requests',
100
- '-bootstrap', '-project-workspaces', '-approval-comments', '-skills'];
101
- let removed = 0;
102
- for (const suf of suffixes) {
103
- for (const ext of ['.json', '.jsonl']) {
104
- const f = join(orgsDir, `${orgName}${suf}${ext}`);
105
- try {
106
- if (existsSync(f)) {
107
- unlinkSync(f);
108
- removed++;
109
- }
110
- }
111
- catch (_) { }
112
- }
113
- }
114
- // Remove stop file
115
- try {
116
- unlinkSync(join(orgsDir, '.stops', `${orgName}.stop`));
117
- }
118
- catch (_) { }
119
- // Remove org subdirectory
120
- const orgSubDir = join(orgsDir, orgName);
146
+ catch (err) {
147
+ console.error(`org ${name}: scheduled run failed:`, err);
148
+ }
149
+ finally {
150
+ await daemon.stopOrg(name).catch(err => console.error(`org ${name}: stop failed:`, err));
151
+ }
152
+ });
153
+ const orgDir = join(ctx.cwd, ORG_DIR);
154
+ if (existsSync(orgDir)) {
155
+ for (const f of listOrgConfigFiles(orgDir)) {
121
156
  try {
122
- if (existsSync(orgSubDir))
123
- rmSync(orgSubDir, { recursive: true, force: true });
157
+ const def = JSON.parse(readFileSync(join(orgDir, f), 'utf8'));
158
+ const ms = parseSchedule(def.schedule);
159
+ if (ms) {
160
+ // register by filename stem — that's what startOrg loads
161
+ const stem = f.replace(/\.json$/, '');
162
+ if (def.name && def.name !== stem)
163
+ log(output.warning(`org file ${f}: def.name "${def.name}" differs from filename — scheduling as "${stem}"`));
164
+ sched.add(stem, ms);
165
+ log(output.info(`scheduled org ${stem} every ${Math.round(ms / 60_000)}m`));
166
+ }
124
167
  }
125
- catch (_) { }
126
- // Remove loop prompt file
127
- try {
128
- unlinkSync(join(cwd, '.monomind', 'loops', `${orgName}.md`));
168
+ catch (err) {
169
+ log(output.warning(`org file ${f}: could not parse — skipping (${err instanceof Error ? err.message : 'invalid JSON'})`));
129
170
  }
130
- catch (_) { }
131
- // Remove run prompt file
171
+ }
172
+ }
173
+ await new Promise(r => { process.once('SIGINT', () => r()); process.once('SIGTERM', () => r()); });
174
+ sched.stop();
175
+ await daemon.stopAll();
176
+ srv?.close();
177
+ return { success: true };
178
+ };
179
+ const testLoopAction = async (ctx) => {
180
+ // non-literal specifier: test-loop.ts lands in a later task; keeps tsc clean until then
181
+ const testLoopModule = '../orgrt/test-loop.js';
182
+ const { runTestLoop } = await import(testLoopModule);
183
+ const n = Number(ctx.flags['times'] ?? ctx.flags['n'] ?? 5);
184
+ const report = await runTestLoop(ctx.cwd, n);
185
+ log(output.info(report.summary));
186
+ return { success: report.failed === 0, message: report.summary };
187
+ };
188
+ // ---- legacy management subcommands (list / delete / mark-complete) ----
189
+ const listAction = async (ctx) => {
190
+ const orgsDir = join(ctx.cwd || process.cwd(), ORG_DIR);
191
+ if (!existsSync(orgsDir)) {
192
+ log(output.info('No orgs directory found. Create an org first with /mastermind:createorg'));
193
+ return { success: true };
194
+ }
195
+ const configs = listOrgConfigFiles(orgsDir);
196
+ if (!configs.length) {
197
+ log(output.info('No orgs found.'));
198
+ return { success: true };
199
+ }
200
+ log(output.info(`Found ${configs.length} org(s):`));
201
+ for (const f of configs)
202
+ log(output.info(` • ${f.replace('.json', '')}`));
203
+ return { success: true };
204
+ };
205
+ const deleteAction = async (ctx) => {
206
+ const orgName = ctx.args[0];
207
+ if (!orgName) {
208
+ log(output.error('Usage: monomind org delete <name>'));
209
+ return { success: false, message: 'org name required' };
210
+ }
211
+ if (!ORG_NAME_RE.test(orgName)) {
212
+ log(output.error(`Invalid org name: ${orgName}`));
213
+ return { success: false, message: 'invalid org name' };
214
+ }
215
+ const confirmed = ctx.flags['yes'] === true || ctx.args.includes('--yes') || ctx.args.includes('-y');
216
+ if (!confirmed) {
217
+ log(output.warning(`This will permanently delete org "${orgName}" and all its data.`));
218
+ log(output.warning('Pass --yes to confirm.'));
219
+ return { success: false, message: 'confirmation required' };
220
+ }
221
+ const cwd = resolve(ctx.cwd || process.cwd());
222
+ const orgsDir = join(cwd, ORG_DIR);
223
+ const configFile = join(orgsDir, `${orgName}.json`);
224
+ if (!existsSync(configFile)) {
225
+ log(output.error(`Org not found: ${orgName}`));
226
+ return { success: false, message: 'org not found' };
227
+ }
228
+ let removed = 0;
229
+ for (const suf of ['', ...ORG_ARTIFACT_SUFFIXES]) {
230
+ for (const ext of ['.json', '.jsonl']) {
231
+ const f = join(orgsDir, `${orgName}${suf}${ext}`);
132
232
  try {
133
- unlinkSync(join(cwd, '.monomind', 'orgs', `${orgName}-run.md`));
233
+ if (existsSync(f)) {
234
+ unlinkSync(f);
235
+ removed++;
236
+ }
134
237
  }
135
- catch (_) { }
136
- output.success(`Org "${orgName}" deleted (${removed} file(s) removed).`);
137
- return { success: true };
238
+ catch { /* ignore */ }
138
239
  }
139
- output.error(`Unknown subcommand: ${sub}. Run "monomind org help" for usage.`);
140
- return { success: false, message: `unknown subcommand: ${sub}` };
240
+ }
241
+ try {
242
+ unlinkSync(join(orgsDir, '.stops', `${orgName}.stop`));
243
+ }
244
+ catch { /* ignore */ }
245
+ const orgSubDir = join(orgsDir, orgName);
246
+ try {
247
+ if (existsSync(orgSubDir))
248
+ rmSync(orgSubDir, { recursive: true, force: true });
249
+ }
250
+ catch { /* ignore */ }
251
+ try {
252
+ unlinkSync(join(cwd, '.monomind', 'loops', `${orgName}.md`));
253
+ }
254
+ catch { /* ignore */ }
255
+ try {
256
+ unlinkSync(join(orgsDir, `${orgName}-run.md`));
257
+ }
258
+ catch { /* ignore */ }
259
+ log(output.success(`Org "${orgName}" deleted (${removed} file(s) removed).`));
260
+ return { success: true };
261
+ };
262
+ const markCompleteAction = async (ctx) => {
263
+ const orgName = ctx.args[0];
264
+ if (!orgName || !ORG_NAME_RE.test(orgName)) {
265
+ log(output.error('Usage: monomind org mark-complete <name>'));
266
+ return { success: false, message: 'valid org name required' };
267
+ }
268
+ const cwd = resolve(ctx.cwd || process.cwd());
269
+ let ctrlUrl = 'http://localhost:4242';
270
+ try {
271
+ const ctl = JSON.parse(readFileSync(join(cwd, '.monomind', 'control.json'), 'utf8'));
272
+ if (ctl.url)
273
+ ctrlUrl = ctl.url;
274
+ }
275
+ catch { /* default */ }
276
+ try {
277
+ const res = await fetch(`${ctrlUrl}/api/orgs/${encodeURIComponent(orgName)}/mark-complete`, { method: 'POST' });
278
+ const body = await res.json().catch(() => ({}));
279
+ if (!res.ok) {
280
+ log(output.error(`mark-complete failed (${res.status}): ${body.error || 'unknown error'}`));
281
+ return { success: false, message: 'server rejected mark-complete' };
282
+ }
283
+ const runId = body.runId;
284
+ log(output.success(`Run marked complete for org "${orgName}"${runId ? ` (run ${runId})` : ''}.`));
285
+ return { success: true };
286
+ }
287
+ catch (err) {
288
+ log(output.error(`Dashboard server unreachable at ${ctrlUrl} — is it running? (${err instanceof Error ? err.message : 'error'})`));
289
+ return { success: false, message: 'server unreachable' };
290
+ }
291
+ };
292
+ export const orgCommand = {
293
+ name: 'org',
294
+ description: 'SDK-based org runtime — run agent organizations as a controlled daemon',
295
+ subcommands: [
296
+ {
297
+ name: 'run', description: 'Start an org (foreground daemon)',
298
+ options: [
299
+ { name: 'task', description: 'Override the org goal for this run', type: 'string' },
300
+ { name: 'cross-process', description: 'Discover and message orgs hosted by other monomind processes on this machine (default true)', type: 'boolean', default: true },
301
+ ],
302
+ examples: [{ command: 'monomind org run growth --task "weekly report"', description: 'Run the growth org once with a task' }],
303
+ action: runAction,
304
+ },
305
+ { name: 'stop', description: 'Request a running org daemon to stop', action: stopAction },
306
+ { name: 'status', description: 'Show runtime state of orgs', action: statusAction },
307
+ {
308
+ name: 'serve', description: 'Start the daemon server only (hosts scheduled orgs)',
309
+ options: [
310
+ { name: 'cross-process', description: 'Discover and message orgs hosted by other monomind processes on this machine (default true)', type: 'boolean', default: true },
311
+ ],
312
+ action: serveAction,
313
+ },
314
+ {
315
+ name: 'test-loop', description: 'Run the org e2e verification loop N times',
316
+ options: [{ name: 'times', short: 'n', description: 'Iterations', type: 'number', default: 5 }],
317
+ action: testLoopAction,
318
+ },
319
+ { name: 'list', description: 'List all orgs in the current project', action: listAction },
320
+ {
321
+ name: 'delete', description: 'Delete an org and all its data',
322
+ options: [{ name: 'yes', short: 'y', description: 'Skip confirmation', type: 'boolean' }],
323
+ action: deleteAction,
324
+ },
325
+ { name: 'mark-complete', description: 'Manually close a stale/crashed run', action: markCompleteAction },
326
+ ],
327
+ examples: [{ command: 'monomind org run my-org', description: 'Run an org under full daemon control' }],
328
+ action: async () => {
329
+ // index.ts's dispatcher never prints result.message on a failed action —
330
+ // it only exits with result.exitCode — so this must log itself or bare
331
+ // `monomind org` exits silently with code 1 and zero output.
332
+ const message = 'usage: monomind org <run|stop|status|serve|test-loop|list|delete|mark-complete>';
333
+ log(output.error(message));
334
+ return { success: false, message };
141
335
  },
142
336
  };
143
337
  export default orgCommand;
@@ -5,7 +5,7 @@
5
5
  * github.com/monoes/monomind
6
6
  */
7
7
  import type { Command } from '../types.js';
8
- export declare const SUPPORTED_PLATFORMS: readonly ["claude", "gemini", "cursor", "vscode", "copilot", "opencode", "aider", "kiro", "trae", "claw", "droid", "antigravity", "hermes", "codex"];
8
+ export declare const SUPPORTED_PLATFORMS: readonly ['claude', 'gemini', 'cursor', 'vscode', 'copilot', 'opencode', 'aider', 'kiro', 'trae', 'claw', 'droid', 'antigravity', 'hermes', 'codex'];
9
9
  export type Platform = typeof SUPPORTED_PLATFORMS[number];
10
10
  export declare const platformsCommand: Command;
11
11
  //# sourceMappingURL=platforms.d.ts.map