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.
- package/README.md +74 -92
- package/package.json +11 -7
- package/packages/@monomind/cli/.claude/helpers/control-start.cjs +13 -20
- package/packages/@monomind/cli/.claude/helpers/event-logger.cjs +109 -6
- package/packages/@monomind/cli/.claude/helpers/graphify-freshen.cjs +19 -17
- package/packages/@monomind/cli/.claude/helpers/handlers/agent-start-handler.cjs +60 -28
- package/packages/@monomind/cli/.claude/helpers/handlers/capture-handler.cjs +99 -28
- package/packages/@monomind/cli/.claude/helpers/handlers/edit-handler.cjs +27 -9
- package/packages/@monomind/cli/.claude/helpers/handlers/gates-handler.cjs +18 -9
- package/packages/@monomind/cli/.claude/helpers/handlers/loops-status-handler.cjs +1 -1
- package/packages/@monomind/cli/.claude/helpers/handlers/route-handler.cjs +33 -4
- package/packages/@monomind/cli/.claude/helpers/handlers/session-handler.cjs +75 -45
- package/packages/@monomind/cli/.claude/helpers/handlers/session-restore-handler.cjs +0 -12
- package/packages/@monomind/cli/.claude/helpers/handlers/task-handler.cjs +53 -22
- package/packages/@monomind/cli/.claude/helpers/hook-handler.cjs +75 -4
- package/packages/@monomind/cli/.claude/helpers/intelligence.cjs +94 -15
- package/packages/@monomind/cli/.claude/helpers/session.cjs +6 -1
- package/packages/@monomind/cli/.claude/helpers/statusline.cjs +10 -10
- package/packages/@monomind/cli/.claude/helpers/token-tracker.cjs +3 -3
- package/packages/@monomind/cli/.claude/helpers/utils/agent-registrations.cjs +41 -0
- package/packages/@monomind/cli/.claude/helpers/utils/fs-helpers.cjs +183 -0
- package/packages/@monomind/cli/.claude/helpers/utils/micro-agents.cjs +10 -6
- package/packages/@monomind/cli/.claude/helpers/utils/monograph.cjs +73 -2
- package/packages/@monomind/cli/.claude/helpers/utils/telemetry.cjs +35 -19
- package/packages/@monomind/cli/README.md +74 -92
- package/packages/@monomind/cli/bin/cli.js +10 -1
- package/packages/@monomind/cli/dist/src/browser/workflow/store.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/commands/agent-ops.js +9 -15
- package/packages/@monomind/cli/dist/src/commands/browse.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/commands/cleanup.js +20 -4
- package/packages/@monomind/cli/dist/src/commands/completions.js +66 -261
- package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.js +5 -0
- package/packages/@monomind/cli/dist/src/commands/doctor.js +7 -2
- package/packages/@monomind/cli/dist/src/commands/hooks-workers.js +7 -0
- package/packages/@monomind/cli/dist/src/commands/index.js +3 -0
- package/packages/@monomind/cli/dist/src/commands/mcp.js +11 -6
- package/packages/@monomind/cli/dist/src/commands/memory-admin.js +12 -6
- package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +6 -13
- package/packages/@monomind/cli/dist/src/commands/monograph.js +3 -3
- package/packages/@monomind/cli/dist/src/commands/org.d.ts +3 -1
- package/packages/@monomind/cli/dist/src/commands/org.js +321 -127
- package/packages/@monomind/cli/dist/src/commands/platforms.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/commands/start.js +142 -37
- package/packages/@monomind/cli/dist/src/commands/tokens.js +21 -3
- package/packages/@monomind/cli/dist/src/config-adapter.js +37 -17
- package/packages/@monomind/cli/dist/src/index.js +64 -28
- package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +44 -11
- package/packages/@monomind/cli/dist/src/init/executor.js +93 -55
- package/packages/@monomind/cli/dist/src/mcp-client.d.ts +8 -3
- package/packages/@monomind/cli/dist/src/mcp-client.js +37 -3
- package/packages/@monomind/cli/dist/src/mcp-server.js +8 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.d.ts +20 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +10 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/auto-install.d.ts +8 -8
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/types.d.ts +31 -31
- package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +30 -8
- package/packages/@monomind/cli/dist/src/mcp-tools/graphify-tools.js +11 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +56 -35
- package/packages/@monomind/cli/dist/src/mcp-tools/knowledge-tools.js +9 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +56 -14
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +88 -41
- package/packages/@monomind/cli/dist/src/mcp-tools/quality/coverage-analysis/prioritize-gaps.d.ts +36 -36
- package/packages/@monomind/cli/dist/src/mcp-tools/quality/security-compliance/detect-secrets.d.ts +4 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +6 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +4 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/types.d.ts +20 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/types.js +36 -1
- package/packages/@monomind/cli/dist/src/memory/ewc-consolidation.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/memory/hnsw-operations.d.ts +13 -1
- package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +66 -14
- package/packages/@monomind/cli/dist/src/memory/intelligence.js +99 -3
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +20 -0
- package/packages/@monomind/cli/dist/src/memory/sona-optimizer.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/orgrt/broker.d.ts +26 -0
- package/packages/@monomind/cli/dist/src/orgrt/broker.js +73 -0
- package/packages/@monomind/cli/dist/src/orgrt/bus.d.ts +5 -1
- package/packages/@monomind/cli/dist/src/orgrt/bus.js +5 -1
- package/packages/@monomind/cli/dist/src/orgrt/daemon.d.ts +46 -0
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +201 -18
- package/packages/@monomind/cli/dist/src/orgrt/forwarder.d.ts +10 -5
- package/packages/@monomind/cli/dist/src/orgrt/forwarder.js +138 -8
- package/packages/@monomind/cli/dist/src/orgrt/inbox.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/orgrt/inbox.js +56 -0
- package/packages/@monomind/cli/dist/src/orgrt/policy.d.ts +5 -1
- package/packages/@monomind/cli/dist/src/orgrt/policy.js +58 -5
- package/packages/@monomind/cli/dist/src/orgrt/provider.js +1 -0
- package/packages/@monomind/cli/dist/src/orgrt/scheduler.d.ts +13 -0
- package/packages/@monomind/cli/dist/src/orgrt/scheduler.js +48 -0
- package/packages/@monomind/cli/dist/src/orgrt/server.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/orgrt/server.js +39 -0
- package/packages/@monomind/cli/dist/src/orgrt/session.d.ts +10 -1
- package/packages/@monomind/cli/dist/src/orgrt/session.js +24 -1
- package/packages/@monomind/cli/dist/src/orgrt/test-loop.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/orgrt/test-loop.js +108 -0
- package/packages/@monomind/cli/dist/src/orgrt/types.d.ts +52 -52
- package/packages/@monomind/cli/dist/src/output.d.ts +29 -29
- package/packages/@monomind/cli/dist/src/output.js +22 -7
- package/packages/@monomind/cli/dist/src/parser.d.ts +32 -0
- package/packages/@monomind/cli/dist/src/parser.js +130 -5
- package/packages/@monomind/cli/dist/src/routing/embed-worker.js +13 -1
- package/packages/@monomind/cli/dist/src/routing/route-layer-factory.js +31 -0
- package/packages/@monomind/cli/dist/src/services/config-file-manager.d.ts +21 -0
- package/packages/@monomind/cli/dist/src/services/config-file-manager.js +60 -7
- package/packages/@monomind/cli/dist/src/services/crash-reporter.js +2 -1
- package/packages/@monomind/cli/dist/src/types.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +243 -49
- package/packages/@monomind/cli/dist/src/ui/orgs-files.js +192 -0
- package/packages/@monomind/cli/dist/src/ui/orgs.html +79 -11
- package/packages/@monomind/cli/dist/src/ui/server.mjs +346 -139
- package/packages/@monomind/cli/dist/src/utils/input-guards.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/utils/input-guards.js +39 -5
- 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
|
-
|
|
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 {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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.
|
|
51
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
123
|
-
|
|
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
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
|
|
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
|
-
|
|
140
|
-
|
|
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 [
|
|
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
|