monomind 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/package.json +9 -5
  2. package/packages/@monomind/cli/.claude/commands/mastermind/runorg.md +4 -0
  3. package/packages/@monomind/cli/.claude/helpers/control-start.cjs +50 -5
  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 +75 -18
  8. package/packages/@monomind/cli/.claude/helpers/handlers/edit-handler.cjs +27 -9
  9. package/packages/@monomind/cli/.claude/helpers/handlers/gates-handler.cjs +16 -7
  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 +4 -4
  12. package/packages/@monomind/cli/.claude/helpers/handlers/session-handler.cjs +40 -22
  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 +54 -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/telemetry.cjs +35 -19
  24. package/packages/@monomind/cli/bin/cli.js +10 -1
  25. package/packages/@monomind/cli/dist/src/commands/agent-ops.js +9 -15
  26. package/packages/@monomind/cli/dist/src/commands/cleanup.js +20 -4
  27. package/packages/@monomind/cli/dist/src/commands/completions.js +66 -261
  28. package/packages/@monomind/cli/dist/src/commands/doctor.js +7 -2
  29. package/packages/@monomind/cli/dist/src/commands/hooks-workers.js +7 -0
  30. package/packages/@monomind/cli/dist/src/commands/index.js +3 -0
  31. package/packages/@monomind/cli/dist/src/commands/mcp.js +11 -6
  32. package/packages/@monomind/cli/dist/src/commands/memory-admin.js +12 -6
  33. package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +6 -13
  34. package/packages/@monomind/cli/dist/src/commands/monograph.js +3 -3
  35. package/packages/@monomind/cli/dist/src/commands/org.d.ts +3 -1
  36. package/packages/@monomind/cli/dist/src/commands/org.js +306 -129
  37. package/packages/@monomind/cli/dist/src/commands/start.js +142 -37
  38. package/packages/@monomind/cli/dist/src/commands/tokens.js +21 -3
  39. package/packages/@monomind/cli/dist/src/config-adapter.js +37 -17
  40. package/packages/@monomind/cli/dist/src/index.js +64 -28
  41. package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +44 -11
  42. package/packages/@monomind/cli/dist/src/init/executor.js +63 -49
  43. package/packages/@monomind/cli/dist/src/mcp-client.d.ts +8 -3
  44. package/packages/@monomind/cli/dist/src/mcp-client.js +37 -3
  45. package/packages/@monomind/cli/dist/src/mcp-server.js +8 -2
  46. package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.d.ts +20 -0
  47. package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +10 -5
  48. package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +30 -8
  49. package/packages/@monomind/cli/dist/src/mcp-tools/graphify-tools.js +11 -2
  50. package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +51 -34
  51. package/packages/@monomind/cli/dist/src/mcp-tools/knowledge-tools.js +9 -1
  52. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +56 -14
  53. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +65 -43
  54. package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +6 -4
  55. package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +4 -4
  56. package/packages/@monomind/cli/dist/src/mcp-tools/types.d.ts +20 -0
  57. package/packages/@monomind/cli/dist/src/mcp-tools/types.js +36 -1
  58. package/packages/@monomind/cli/dist/src/memory/hnsw-operations.d.ts +13 -1
  59. package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +66 -14
  60. package/packages/@monomind/cli/dist/src/memory/intelligence.js +99 -3
  61. package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +7 -0
  62. package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +20 -0
  63. package/packages/@monomind/cli/dist/src/orgrt/broker.d.ts +24 -0
  64. package/packages/@monomind/cli/dist/src/orgrt/broker.js +68 -0
  65. package/packages/@monomind/cli/dist/src/orgrt/daemon.d.ts +30 -0
  66. package/packages/@monomind/cli/dist/src/orgrt/daemon.js +96 -7
  67. package/packages/@monomind/cli/dist/src/orgrt/forwarder.d.ts +10 -5
  68. package/packages/@monomind/cli/dist/src/orgrt/forwarder.js +105 -8
  69. package/packages/@monomind/cli/dist/src/orgrt/live.html +56 -0
  70. package/packages/@monomind/cli/dist/src/orgrt/provider.js +1 -0
  71. package/packages/@monomind/cli/dist/src/orgrt/scheduler.d.ts +13 -0
  72. package/packages/@monomind/cli/dist/src/orgrt/scheduler.js +48 -0
  73. package/packages/@monomind/cli/dist/src/orgrt/server.d.ts +8 -0
  74. package/packages/@monomind/cli/dist/src/orgrt/server.js +72 -0
  75. package/packages/@monomind/cli/dist/src/orgrt/test-loop.d.ts +12 -0
  76. package/packages/@monomind/cli/dist/src/orgrt/test-loop.js +108 -0
  77. package/packages/@monomind/cli/dist/src/output.js +12 -5
  78. package/packages/@monomind/cli/dist/src/parser.d.ts +32 -0
  79. package/packages/@monomind/cli/dist/src/parser.js +130 -5
  80. package/packages/@monomind/cli/dist/src/routing/embed-worker.js +13 -1
  81. package/packages/@monomind/cli/dist/src/routing/route-layer-factory.js +31 -0
  82. package/packages/@monomind/cli/dist/src/services/config-file-manager.d.ts +21 -0
  83. package/packages/@monomind/cli/dist/src/services/config-file-manager.js +60 -7
  84. package/packages/@monomind/cli/dist/src/services/crash-reporter.js +2 -1
  85. package/packages/@monomind/cli/dist/src/ui/dashboard.html +40 -40
  86. package/packages/@monomind/cli/dist/src/ui/orgs.html +110 -11
  87. package/packages/@monomind/cli/dist/src/ui/server.mjs +304 -133
  88. package/packages/@monomind/cli/dist/src/utils/input-guards.d.ts +9 -0
  89. package/packages/@monomind/cli/dist/src/utils/input-guards.js +39 -5
  90. package/packages/@monomind/cli/package.json +6 -2
@@ -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,144 +1,321 @@
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
+ /** Config filenames for real orgs under orgsDir — excludes org-internal
23
+ * artifact files (state/goals/threads/etc) that share the .json extension. */
24
+ const ORG_ARTIFACT_SUFFIXES = ['-state', '-goals', '-threads', '-activity', '-approvals', '-members', '-secrets', '-budgets'];
25
+ function listOrgConfigFiles(orgsDir) {
26
+ return readdirSync(orgsDir)
27
+ .filter(f => f.endsWith('.json') && !ORG_ARTIFACT_SUFFIXES.some(suf => f.includes(suf)));
28
+ }
29
+ /** Remove a lingering stopfile so a fresh `org run` doesn't self-terminate. */
30
+ export const clearStopfile = (cwd, name) => {
31
+ rmSync(join(cwd, ORG_DIR, name, 'stop'), { force: true });
32
+ };
33
+ const runAction = async (ctx) => {
34
+ if (!ctx.args[0])
35
+ return { success: false, message: 'org name required: monomind org run <name> [--task "..."] [--serve] [--port N]' };
36
+ const validated = validateOrgName(ctx.args[0]);
37
+ if (!validated.ok)
38
+ return validated.result;
39
+ const name = validated.name;
40
+ const crossProcess = ctx.flags['crossProcess'] !== false;
41
+ const daemon = new OrgDaemon(ctx.cwd, { crossProcess });
42
+ let srv;
43
+ if (ctx.flags['serve'] !== false) {
44
+ const port = Number(ctx.flags['port'] ?? 4243);
45
+ srv = await startOrgServer(daemon, port);
46
+ log(output.info(`org live view: http://localhost:${srv.port}`));
47
+ if (crossProcess)
48
+ daemon.setInboxUrl(`http://127.0.0.1:${srv.port}`);
49
+ }
50
+ const running = await daemon.startOrg(name, ctx.flags['task']);
51
+ log(output.info(`org ${name} running (${running.def.roles.length} agents, run ${running.run}) — Ctrl-C or "monomind org stop ${name}" to stop`));
52
+ // stopfile poll lets `org stop` work from another terminal;
53
+ // clear any stale stopfile from a previous run before polling
54
+ clearStopfile(ctx.cwd, name);
55
+ const stopfile = join(ctx.cwd, ORG_DIR, name, 'stop');
56
+ await new Promise(resolvePromise => {
57
+ const iv = setInterval(() => { if (existsSync(stopfile)) {
58
+ clearInterval(iv);
59
+ resolvePromise();
60
+ } }, 2000);
61
+ process.once('SIGINT', () => { clearInterval(iv); resolvePromise(); });
62
+ process.once('SIGTERM', () => { clearInterval(iv); resolvePromise(); });
63
+ });
64
+ clearStopfile(ctx.cwd, name);
65
+ await daemon.stopAll();
66
+ srv?.close();
67
+ return { success: true, message: `org ${name} stopped` };
68
+ };
69
+ const stopAction = async (ctx) => {
70
+ const validated = validateOrgName(ctx.args[0]);
71
+ if (!validated.ok)
72
+ return validated.result;
73
+ const name = validated.name;
74
+ const { writeFileSync, mkdirSync } = await import('node:fs');
75
+ mkdirSync(join(ctx.cwd, ORG_DIR, name), { recursive: true });
76
+ writeFileSync(join(ctx.cwd, ORG_DIR, name, 'stop'), new Date().toISOString());
77
+ return { success: true, message: `stop requested for ${name} (daemon exits within 2s)` };
78
+ };
79
+ const statusAction = async (ctx) => {
80
+ const name = ctx.args[0];
81
+ const orgDir = join(ctx.cwd, ORG_DIR);
82
+ const targets = name ? [name] : (existsSync(orgDir)
83
+ ? listOrgConfigFiles(orgDir).map(f => f.replace(/\.json$/, ''))
84
+ : []);
85
+ for (const t of targets) {
86
+ const rt = join(orgDir, t, 'runtime.json');
87
+ let state = { status: 'never run' };
88
+ if (existsSync(rt)) {
39
89
  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 };
90
+ state = JSON.parse(readFileSync(rt, 'utf8'));
48
91
  }
49
92
  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' };
93
+ log(output.warning(`${t}: could not read runtime.json (${err instanceof Error ? err.message : 'corrupt/truncated file'})`));
94
+ continue;
52
95
  }
53
96
  }
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 };
97
+ log(output.info(`${t}: ${state.status}${state.run ? ` (run ${state.run}, pid ${state.pid})` : ''}`));
98
+ }
99
+ return { success: true };
100
+ };
101
+ const serveAction = async (ctx) => {
102
+ const crossProcess = ctx.flags['crossProcess'] !== false;
103
+ const daemon = new OrgDaemon(ctx.cwd, { crossProcess });
104
+ const srv = await startOrgServer(daemon, Number(ctx.flags['port'] ?? 4243));
105
+ log(output.info(`org daemon serving on http://localhost:${srv.port} Ctrl-C to stop`));
106
+ if (crossProcess)
107
+ daemon.setInboxUrl(`http://127.0.0.1:${srv.port}`);
108
+ // schedule orgs whose definition declares an interval (e.g. "15m", "2h")
109
+ const { OrgScheduler, parseSchedule } = await import('../orgrt/scheduler.js');
110
+ const sched = new OrgScheduler(async (name, intervalMs) => {
111
+ try {
112
+ await daemon.startOrg(name);
113
+ // Scheduled iterations are time-bounded: agents' `done` promises only
114
+ // resolve after stopOrg closes the mailboxes, so waiting on them alone
115
+ // deadlocks. Race against a max-run timeout, then ALWAYS stopOrg
116
+ // (idempotent — it resolves `done` and flushes).
117
+ const org = daemon.getOrg(name);
118
+ const allDone = org
119
+ ? Promise.allSettled([...org.agents.values()].map(a => a.done))
120
+ : Promise.resolve([]);
121
+ const maxRun = org?.def?.run_config?.max_run;
122
+ const maxMs = parseSchedule(maxRun) ?? Math.min(intervalMs, 600_000); // cap: schedule interval or 10 min
123
+ let timer;
124
+ await Promise.race([allDone, new Promise(r => {
125
+ timer = setTimeout(r, maxMs);
126
+ timer.unref?.();
127
+ })]);
128
+ if (timer)
129
+ clearTimeout(timer);
73
130
  }
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);
121
- try {
122
- if (existsSync(orgSubDir))
123
- rmSync(orgSubDir, { recursive: true, force: true });
124
- }
125
- catch (_) { }
126
- // Remove loop prompt file
131
+ catch (err) {
132
+ console.error(`org ${name}: scheduled run failed:`, err);
133
+ }
134
+ finally {
135
+ await daemon.stopOrg(name).catch(err => console.error(`org ${name}: stop failed:`, err));
136
+ }
137
+ });
138
+ const orgDir = join(ctx.cwd, ORG_DIR);
139
+ if (existsSync(orgDir)) {
140
+ for (const f of readdirSync(orgDir).filter(f => f.endsWith('.json'))) {
127
141
  try {
128
- unlinkSync(join(cwd, '.monomind', 'loops', `${orgName}.md`));
142
+ const def = JSON.parse(readFileSync(join(orgDir, f), 'utf8'));
143
+ const ms = parseSchedule(def.schedule);
144
+ if (ms) {
145
+ // register by filename stem — that's what startOrg loads
146
+ const stem = f.replace(/\.json$/, '');
147
+ if (def.name && def.name !== stem)
148
+ log(output.warning(`org file ${f}: def.name "${def.name}" differs from filename — scheduling as "${stem}"`));
149
+ sched.add(stem, ms);
150
+ log(output.info(`scheduled org ${stem} every ${Math.round(ms / 60_000)}m`));
151
+ }
129
152
  }
130
- catch (_) { }
131
- // Remove run prompt file
153
+ catch { /* skip unparseable org file */ }
154
+ }
155
+ }
156
+ await new Promise(r => { process.once('SIGINT', () => r()); process.once('SIGTERM', () => r()); });
157
+ sched.stop();
158
+ await daemon.stopAll();
159
+ srv.close();
160
+ return { success: true };
161
+ };
162
+ const testLoopAction = async (ctx) => {
163
+ // non-literal specifier: test-loop.ts lands in a later task; keeps tsc clean until then
164
+ const testLoopModule = '../orgrt/test-loop.js';
165
+ const { runTestLoop } = await import(testLoopModule);
166
+ const n = Number(ctx.flags['times'] ?? ctx.flags['n'] ?? 5);
167
+ const report = await runTestLoop(ctx.cwd, n);
168
+ log(output.info(report.summary));
169
+ return { success: report.failed === 0, message: report.summary };
170
+ };
171
+ // ---- legacy management subcommands (list / delete / mark-complete) ----
172
+ const listAction = async (ctx) => {
173
+ const orgsDir = join(ctx.cwd || process.cwd(), ORG_DIR);
174
+ if (!existsSync(orgsDir)) {
175
+ log(output.info('No orgs directory found. Create an org first with /mastermind:createorg'));
176
+ return { success: true };
177
+ }
178
+ const configs = listOrgConfigFiles(orgsDir);
179
+ if (!configs.length) {
180
+ log(output.info('No orgs found.'));
181
+ return { success: true };
182
+ }
183
+ log(output.info(`Found ${configs.length} org(s):`));
184
+ for (const f of configs)
185
+ log(output.info(` • ${f.replace('.json', '')}`));
186
+ return { success: true };
187
+ };
188
+ const deleteAction = async (ctx) => {
189
+ const orgName = ctx.args[0];
190
+ if (!orgName) {
191
+ log(output.error('Usage: monomind org delete <name>'));
192
+ return { success: false, message: 'org name required' };
193
+ }
194
+ if (!/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
195
+ log(output.error(`Invalid org name: ${orgName}`));
196
+ return { success: false, message: 'invalid org name' };
197
+ }
198
+ const confirmed = ctx.flags['yes'] === true || ctx.args.includes('--yes') || ctx.args.includes('-y');
199
+ if (!confirmed) {
200
+ log(output.warning(`This will permanently delete org "${orgName}" and all its data.`));
201
+ log(output.warning('Pass --yes to confirm.'));
202
+ return { success: false, message: 'confirmation required' };
203
+ }
204
+ const cwd = resolve(ctx.cwd || process.cwd());
205
+ const orgsDir = join(cwd, ORG_DIR);
206
+ const configFile = join(orgsDir, `${orgName}.json`);
207
+ if (!existsSync(configFile)) {
208
+ log(output.error(`Org not found: ${orgName}`));
209
+ return { success: false, message: 'org not found' };
210
+ }
211
+ const suffixes = ['', '-state', '-goals', '-routines', '-approvals', '-activity',
212
+ '-issues', '-members', '-projects', '-workspaces', '-worktrees', '-environments',
213
+ '-plugins', '-adapters', '-budgets', '-threads', '-secrets', '-join-requests',
214
+ '-bootstrap', '-project-workspaces', '-approval-comments', '-skills'];
215
+ let removed = 0;
216
+ for (const suf of suffixes) {
217
+ for (const ext of ['.json', '.jsonl']) {
218
+ const f = join(orgsDir, `${orgName}${suf}${ext}`);
132
219
  try {
133
- unlinkSync(join(cwd, '.monomind', 'orgs', `${orgName}-run.md`));
220
+ if (existsSync(f)) {
221
+ unlinkSync(f);
222
+ removed++;
223
+ }
134
224
  }
135
- catch (_) { }
136
- output.success(`Org "${orgName}" deleted (${removed} file(s) removed).`);
137
- return { success: true };
225
+ catch { /* ignore */ }
138
226
  }
139
- output.error(`Unknown subcommand: ${sub}. Run "monomind org help" for usage.`);
140
- return { success: false, message: `unknown subcommand: ${sub}` };
141
- },
227
+ }
228
+ try {
229
+ unlinkSync(join(orgsDir, '.stops', `${orgName}.stop`));
230
+ }
231
+ catch { /* ignore */ }
232
+ const orgSubDir = join(orgsDir, orgName);
233
+ try {
234
+ if (existsSync(orgSubDir))
235
+ rmSync(orgSubDir, { recursive: true, force: true });
236
+ }
237
+ catch { /* ignore */ }
238
+ try {
239
+ unlinkSync(join(cwd, '.monomind', 'loops', `${orgName}.md`));
240
+ }
241
+ catch { /* ignore */ }
242
+ try {
243
+ unlinkSync(join(orgsDir, `${orgName}-run.md`));
244
+ }
245
+ catch { /* ignore */ }
246
+ log(output.success(`Org "${orgName}" deleted (${removed} file(s) removed).`));
247
+ return { success: true };
248
+ };
249
+ const markCompleteAction = async (ctx) => {
250
+ const orgName = ctx.args[0];
251
+ if (!orgName || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
252
+ log(output.error('Usage: monomind org mark-complete <name>'));
253
+ return { success: false, message: 'valid org name required' };
254
+ }
255
+ const cwd = resolve(ctx.cwd || process.cwd());
256
+ let ctrlUrl = 'http://localhost:4242';
257
+ try {
258
+ const ctl = JSON.parse(readFileSync(join(cwd, '.monomind', 'control.json'), 'utf8'));
259
+ if (ctl.url)
260
+ ctrlUrl = ctl.url;
261
+ }
262
+ catch { /* default */ }
263
+ try {
264
+ const res = await fetch(`${ctrlUrl}/api/orgs/${encodeURIComponent(orgName)}/mark-complete`, { method: 'POST' });
265
+ const body = await res.json().catch(() => ({}));
266
+ if (!res.ok) {
267
+ log(output.error(`mark-complete failed (${res.status}): ${body.error || 'unknown error'}`));
268
+ return { success: false, message: 'server rejected mark-complete' };
269
+ }
270
+ const runId = body.runId;
271
+ log(output.success(`Run marked complete for org "${orgName}"${runId ? ` (run ${runId})` : ''}.`));
272
+ return { success: true };
273
+ }
274
+ catch (err) {
275
+ log(output.error(`Dashboard server unreachable at ${ctrlUrl} — is it running? (${err instanceof Error ? err.message : 'error'})`));
276
+ return { success: false, message: 'server unreachable' };
277
+ }
278
+ };
279
+ export const orgCommand = {
280
+ name: 'org',
281
+ description: 'SDK-based org runtime — run agent organizations as a controlled daemon',
282
+ subcommands: [
283
+ {
284
+ name: 'run', description: 'Start an org (foreground daemon)',
285
+ options: [
286
+ { name: 'task', description: 'Override the org goal for this run', type: 'string' },
287
+ { name: 'serve', description: 'Serve the live dashboard (default true)', type: 'boolean', default: true },
288
+ { name: 'port', description: 'Live dashboard port', type: 'number', default: 4243 },
289
+ { name: 'cross-process', description: 'Discover and message orgs hosted by other monomind processes on this machine (default true)', type: 'boolean', default: true },
290
+ ],
291
+ examples: [{ command: 'monomind org run growth --task "weekly report"', description: 'Run the growth org once with a task' }],
292
+ action: runAction,
293
+ },
294
+ { name: 'stop', description: 'Request a running org daemon to stop', action: stopAction },
295
+ { name: 'status', description: 'Show runtime state of orgs', action: statusAction },
296
+ {
297
+ name: 'serve', description: 'Start the daemon server only (hosts scheduled orgs)',
298
+ options: [
299
+ { name: 'port', description: 'Port', type: 'number', default: 4243 },
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
+ action: serveAction,
303
+ },
304
+ {
305
+ name: 'test-loop', description: 'Run the org e2e verification loop N times',
306
+ options: [{ name: 'times', short: 'n', description: 'Iterations', type: 'number', default: 5 }],
307
+ action: testLoopAction,
308
+ },
309
+ { name: 'list', description: 'List all orgs in the current project', action: listAction },
310
+ {
311
+ name: 'delete', description: 'Delete an org and all its data',
312
+ options: [{ name: 'yes', short: 'y', description: 'Skip confirmation', type: 'boolean' }],
313
+ action: deleteAction,
314
+ },
315
+ { name: 'mark-complete', description: 'Manually close a stale/crashed run', action: markCompleteAction },
316
+ ],
317
+ examples: [{ command: 'monomind org run my-org', description: 'Run an org under full daemon control' }],
318
+ action: async () => ({ success: false, message: 'usage: monomind org <run|stop|status|serve|test-loop|list|delete|mark-complete>' }),
142
319
  };
143
320
  export default orgCommand;
144
321
  //# sourceMappingURL=org.js.map