monomind 2.2.0 → 2.3.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.
- package/package.json +2 -2
- package/packages/@monomind/cli/.claude/commands/mastermind/createorg.md +24 -16
- package/packages/@monomind/cli/.claude/commands/mastermind/master.md +23 -7
- package/packages/@monomind/cli/.claude/commands/mastermind/runorg.md +3 -1
- package/packages/@monomind/cli/.claude/commands/mastermind/stoporg.md +6 -3
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/code-quality-reviewer-prompt.md +3 -3
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/createorg.md +19 -24
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/debug.md +49 -4
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/design.md +11 -10
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/final-reviewer-prompt.md +144 -0
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/implementer-prompt.md +9 -4
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/org-settings.md +20 -2
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/orgs.md +40 -19
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/orgstatus.md +90 -41
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/plan.md +19 -0
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/skill-builder.md +171 -0
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/spec-reviewer-prompt.md +19 -2
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/stoporg.md +7 -5
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/taskdev.md +83 -15
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/tdd.md +30 -0
- package/packages/@monomind/cli/dist/src/autopilot-state.js +6 -4
- package/packages/@monomind/cli/dist/src/browser/dashboard/server.js +4 -1
- package/packages/@monomind/cli/dist/src/capabilities/manager.js +3 -1
- package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +26 -0
- package/packages/@monomind/cli/dist/src/commands/agent-ops.js +28 -46
- package/packages/@monomind/cli/dist/src/commands/cleanup.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/commands/cleanup.js +120 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +13 -4
- package/packages/@monomind/cli/dist/src/commands/hooks-routing-commands.js +34 -18
- package/packages/@monomind/cli/dist/src/commands/hooks-workers.js +8 -2
- package/packages/@monomind/cli/dist/src/commands/hooks.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/mcp.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/memory-admin.js +7 -1
- package/packages/@monomind/cli/dist/src/commands/org-observe.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/commands/org-observe.js +295 -0
- package/packages/@monomind/cli/dist/src/commands/org.d.ts +9 -1
- package/packages/@monomind/cli/dist/src/commands/org.js +191 -9
- package/packages/@monomind/cli/dist/src/commands/performance.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/platforms.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/security-scan.js +8 -2
- package/packages/@monomind/cli/dist/src/commands/start.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/swarm.js +3 -2
- package/packages/@monomind/cli/dist/src/index.js +13 -3
- package/packages/@monomind/cli/dist/src/init/executor.js +19 -5
- package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.js +3 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +63 -20
- package/packages/@monomind/cli/dist/src/mcp-tools/claims-tools.js +3 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/config-tools.js +3 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/daa-tools.js +3 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/embeddings-tools.js +6 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +35 -10
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +24 -7
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-embedding.js +13 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-intelligence.js +9 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-routing.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-routing.js +112 -10
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-tools.js +2 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +8 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/performance-tools.js +3 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/session-tools.js +15 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +4 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +6 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/task-tools.d.ts +20 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/task-tools.js +38 -16
- package/packages/@monomind/cli/dist/src/mcp-tools/terminal-tools.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/terminal-tools.js +41 -24
- package/packages/@monomind/cli/dist/src/memory/ewc-consolidation.js +6 -2
- package/packages/@monomind/cli/dist/src/memory/intelligence.js +32 -10
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +16 -4
- package/packages/@monomind/cli/dist/src/memory/memory-crud.js +26 -2
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +3 -2
- package/packages/@monomind/cli/dist/src/memory/memory-migrations.js +2 -0
- package/packages/@monomind/cli/dist/src/monovector/command-outcomes.js +4 -1
- package/packages/@monomind/cli/dist/src/orgrt/bus.js +4 -1
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +74 -19
- package/packages/@monomind/cli/dist/src/orgrt/inbox.js +3 -1
- package/packages/@monomind/cli/dist/src/orgrt/reporting.d.ts +40 -0
- package/packages/@monomind/cli/dist/src/orgrt/reporting.js +127 -0
- package/packages/@monomind/cli/dist/src/orgrt/session.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/orgrt/session.js +10 -2
- package/packages/@monomind/cli/dist/src/orgrt/templates.d.ts +16 -0
- package/packages/@monomind/cli/dist/src/orgrt/templates.js +57 -0
- package/packages/@monomind/cli/dist/src/services/config-file-manager.js +10 -1
- package/packages/@monomind/cli/dist/src/transfer/storage/gcs.js +6 -2
- package/packages/@monomind/cli/dist/src/ui/orgs.html +32 -0
- package/packages/@monomind/cli/dist/src/ui/server.mjs +25 -0
- package/packages/@monomind/cli/dist/src/update/executor.js +3 -1
- package/packages/@monomind/cli/dist/src/update/rate-limiter.js +3 -1
- package/packages/@monomind/cli/package.json +2 -2
|
@@ -23,7 +23,9 @@ export function drainInbox(root, orgName) {
|
|
|
23
23
|
try {
|
|
24
24
|
renameSync(path, draining);
|
|
25
25
|
}
|
|
26
|
-
catch {
|
|
26
|
+
catch (e) {
|
|
27
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
28
|
+
console.error('[inbox] drainInbox rename failed:', e);
|
|
27
29
|
return [];
|
|
28
30
|
}
|
|
29
31
|
const raw = readFileSync(draining, 'utf8').trim();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { BusEvent } from './types.js';
|
|
2
|
+
export interface RoleStats {
|
|
3
|
+
messagesSent: number;
|
|
4
|
+
toolsAllowed: number;
|
|
5
|
+
toolsDenied: number;
|
|
6
|
+
tokens: number;
|
|
7
|
+
costUsd: number;
|
|
8
|
+
crashed: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface RunSummary {
|
|
11
|
+
org: string;
|
|
12
|
+
run: string;
|
|
13
|
+
startedAt: number | null;
|
|
14
|
+
endedAt: number | null;
|
|
15
|
+
durationMs: number | null;
|
|
16
|
+
events: number;
|
|
17
|
+
messages: number;
|
|
18
|
+
xorgMessages: number;
|
|
19
|
+
assets: string[];
|
|
20
|
+
crashes: string[];
|
|
21
|
+
outcome: {
|
|
22
|
+
status: string;
|
|
23
|
+
summary: string;
|
|
24
|
+
by: string;
|
|
25
|
+
} | null;
|
|
26
|
+
roles: Record<string, RoleStats>;
|
|
27
|
+
totalTokens: number;
|
|
28
|
+
totalCostUsd: number;
|
|
29
|
+
}
|
|
30
|
+
/** Aggregate one run's bus events into a summary. */
|
|
31
|
+
export declare function summarizeRun(events: BusEvent[]): RunSummary;
|
|
32
|
+
/** run directories for an org, newest first (by name — run-YYYYMMDDHHMMSS-xxxx sorts naturally). */
|
|
33
|
+
export declare function listRunDirs(cwd: string, org: string): string[];
|
|
34
|
+
export declare function readRunEvents(cwd: string, org: string, run: string): BusEvent[];
|
|
35
|
+
/** history.jsonl — one RunSummary line per completed run, appended by the daemon at stopOrg. */
|
|
36
|
+
export declare function historyFile(cwd: string, org: string): string;
|
|
37
|
+
export declare function readHistory(cwd: string, org: string): RunSummary[];
|
|
38
|
+
/** One bus event as a compact human-readable log line. */
|
|
39
|
+
export declare function formatEvent(e: BusEvent): string;
|
|
40
|
+
//# sourceMappingURL=reporting.d.ts.map
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// packages/@monomind/cli/src/orgrt/reporting.ts
|
|
2
|
+
// Read-side aggregation over an org run's bus.jsonl — powers `org report`,
|
|
3
|
+
// `org logs`, and the per-run summary line appended to <org>/history.jsonl.
|
|
4
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { ORG_DIR } from './types.js';
|
|
7
|
+
const roleStats = () => ({ messagesSent: 0, toolsAllowed: 0, toolsDenied: 0, tokens: 0, costUsd: 0, crashed: false });
|
|
8
|
+
/** Aggregate one run's bus events into a summary. */
|
|
9
|
+
export function summarizeRun(events) {
|
|
10
|
+
const s = {
|
|
11
|
+
org: events[0]?.org ?? '', run: events[0]?.run ?? '',
|
|
12
|
+
startedAt: events.length ? events[0].ts : null,
|
|
13
|
+
endedAt: events.length ? events[events.length - 1].ts : null,
|
|
14
|
+
durationMs: null, events: events.length,
|
|
15
|
+
messages: 0, xorgMessages: 0, assets: [], crashes: [], outcome: null,
|
|
16
|
+
roles: {}, totalTokens: 0, totalCostUsd: 0,
|
|
17
|
+
};
|
|
18
|
+
if (s.startedAt !== null && s.endedAt !== null)
|
|
19
|
+
s.durationMs = s.endedAt - s.startedAt;
|
|
20
|
+
const role = (id) => {
|
|
21
|
+
const key = id ?? '(system)';
|
|
22
|
+
return (s.roles[key] ??= roleStats());
|
|
23
|
+
};
|
|
24
|
+
for (const e of events) {
|
|
25
|
+
switch (e.type) {
|
|
26
|
+
case 'message':
|
|
27
|
+
s.messages++;
|
|
28
|
+
role(e.from).messagesSent++;
|
|
29
|
+
break;
|
|
30
|
+
case 'xorg':
|
|
31
|
+
s.xorgMessages++;
|
|
32
|
+
role(e.from?.includes(':') ? e.from.split(':')[1] : e.from).messagesSent++;
|
|
33
|
+
break;
|
|
34
|
+
case 'tool':
|
|
35
|
+
if (e.decision === 'deny')
|
|
36
|
+
role(e.from).toolsDenied++;
|
|
37
|
+
else
|
|
38
|
+
role(e.from).toolsAllowed++;
|
|
39
|
+
break;
|
|
40
|
+
case 'asset':
|
|
41
|
+
if (e.path && !s.assets.includes(e.path))
|
|
42
|
+
s.assets.push(e.path);
|
|
43
|
+
break;
|
|
44
|
+
case 'usage': {
|
|
45
|
+
const tokens = Number(e.data?.tokens ?? 0);
|
|
46
|
+
const cost = Number(e.data?.cost_usd ?? 0);
|
|
47
|
+
const r = role(e.from);
|
|
48
|
+
r.tokens += tokens;
|
|
49
|
+
s.totalTokens += tokens;
|
|
50
|
+
if (Number.isFinite(cost)) {
|
|
51
|
+
r.costUsd += cost;
|
|
52
|
+
s.totalCostUsd += cost;
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case 'audit':
|
|
57
|
+
if (e.reason === 'agent-session-crash' && e.from) {
|
|
58
|
+
s.crashes.push(e.from);
|
|
59
|
+
role(e.from).crashed = true;
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
case 'status': {
|
|
63
|
+
const d = e.data;
|
|
64
|
+
if (e.reason === 'org-complete' && d?.outcome)
|
|
65
|
+
s.outcome = { status: d.outcome, summary: d.summary ?? '', by: e.from ?? '' };
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return s;
|
|
71
|
+
}
|
|
72
|
+
/** run directories for an org, newest first (by name — run-YYYYMMDDHHMMSS-xxxx sorts naturally). */
|
|
73
|
+
export function listRunDirs(cwd, org) {
|
|
74
|
+
const base = join(cwd, ORG_DIR, org);
|
|
75
|
+
if (!existsSync(base))
|
|
76
|
+
return [];
|
|
77
|
+
return readdirSync(base).filter(d => d.startsWith('run-')).sort().reverse();
|
|
78
|
+
}
|
|
79
|
+
export function readRunEvents(cwd, org, run) {
|
|
80
|
+
const f = join(cwd, ORG_DIR, org, run, 'bus.jsonl');
|
|
81
|
+
if (!existsSync(f))
|
|
82
|
+
return [];
|
|
83
|
+
return readFileSync(f, 'utf8').split('\n').filter(Boolean)
|
|
84
|
+
.map(l => { try {
|
|
85
|
+
return JSON.parse(l);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
} })
|
|
90
|
+
.filter((e) => e !== null);
|
|
91
|
+
}
|
|
92
|
+
/** history.jsonl — one RunSummary line per completed run, appended by the daemon at stopOrg. */
|
|
93
|
+
export function historyFile(cwd, org) {
|
|
94
|
+
return join(cwd, ORG_DIR, org, 'history.jsonl');
|
|
95
|
+
}
|
|
96
|
+
export function readHistory(cwd, org) {
|
|
97
|
+
const f = historyFile(cwd, org);
|
|
98
|
+
if (!existsSync(f))
|
|
99
|
+
return [];
|
|
100
|
+
return readFileSync(f, 'utf8').split('\n').filter(Boolean)
|
|
101
|
+
.map(l => { try {
|
|
102
|
+
return JSON.parse(l);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return null;
|
|
106
|
+
} })
|
|
107
|
+
.filter((s) => s !== null);
|
|
108
|
+
}
|
|
109
|
+
/** One bus event as a compact human-readable log line. */
|
|
110
|
+
export function formatEvent(e) {
|
|
111
|
+
const t = new Date(e.ts).toISOString().slice(11, 19);
|
|
112
|
+
const from = e.from ?? '·';
|
|
113
|
+
switch (e.type) {
|
|
114
|
+
case 'message':
|
|
115
|
+
case 'xorg':
|
|
116
|
+
return `${t} ${e.type === 'xorg' ? '⇄' : '→'} ${from} → ${e.to}: [${e.subject ?? ''}] ${trim(e.msg)}`;
|
|
117
|
+
case 'chat': return `${t} 💬 ${from}: ${trim(e.msg)}`;
|
|
118
|
+
case 'tool': return `${t} 🔧 ${from} ${e.tool} ${e.decision === 'deny' ? `DENIED (${e.reason})` : 'ok'}`;
|
|
119
|
+
case 'asset': return `${t} 📄 ${from} wrote ${e.path}`;
|
|
120
|
+
case 'usage': return `${t} 🪙 ${from} +${e.data?.tokens ?? 0} tokens`;
|
|
121
|
+
case 'audit': return `${t} ⚠️ ${from} ${e.msg ?? e.reason ?? ''}`;
|
|
122
|
+
case 'question': return `${t} ❓ ${from}: ${trim(e.msg)}`;
|
|
123
|
+
default: return `${t} ▪ ${from} ${e.type}: ${trim(e.msg ?? '')}`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const trim = (s, n = 120) => !s ? '' : s.length > n ? `${s.slice(0, n - 1)}…` : s.replace(/\n/g, ' ');
|
|
127
|
+
//# sourceMappingURL=reporting.js.map
|
|
@@ -13,6 +13,8 @@ export interface SessionOpts {
|
|
|
13
13
|
cwd: string;
|
|
14
14
|
deliver: DeliverFn;
|
|
15
15
|
askHuman?: (role: string, question: string) => Promise<string>;
|
|
16
|
+
/** Coordinator-only: records the run's outcome (daemon persists it to run history). */
|
|
17
|
+
onComplete?: (role: string, outcome: 'achieved' | 'partial' | 'failed', summary: string) => void;
|
|
16
18
|
def?: OrgDef;
|
|
17
19
|
maxTurns?: number;
|
|
18
20
|
queryFn?: typeof query;
|
|
@@ -4,17 +4,20 @@ import { query, tool, createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk'
|
|
|
4
4
|
import { resolveProviderEnv } from './provider.js';
|
|
5
5
|
/** Role briefing given to each agent session (SDK systemPrompt option). */
|
|
6
6
|
export function buildRolePrompt(role, def, roster) {
|
|
7
|
+
const isCoordinator = role.reports_to == null;
|
|
7
8
|
return [
|
|
8
9
|
`You are agent "${role.id}" (${role.title || role.type}) in the org "${def.name}".`,
|
|
9
10
|
`Org goal: ${def.goal}`,
|
|
10
|
-
|
|
11
|
+
isCoordinator ? `You are the coordinator of this org.` : `You report to "${role.reports_to}".`,
|
|
11
12
|
role.responsibilities?.length ? `Your responsibilities:\n- ${role.responsibilities.join('\n- ')}` : '',
|
|
12
13
|
`## Communication protocol`,
|
|
13
14
|
`The ONLY way to communicate with other agents is the org_send tool.`,
|
|
14
15
|
`Roster: ${roster.join(', ')}. Address another org's agent as "<org-name>:<role-id>".`,
|
|
15
16
|
`If you need a human decision, call ask_human with your question, then end your turn — you'll receive the human's answer as a new message when it arrives. Do not call ask_human for anything you can resolve yourself.`,
|
|
16
17
|
`When you receive a message, act on it, then org_send your result to the requester.`,
|
|
17
|
-
|
|
18
|
+
isCoordinator
|
|
19
|
+
? `When the org's goal for this run is achieved (or clearly can't be), call org_complete exactly once with the outcome and a concise summary of what was done — it is recorded in the org's run history and briefed to the next run. Then end your turn.`
|
|
20
|
+
: `When your current work is complete and no reply is needed, end your turn without further tool calls.`,
|
|
18
21
|
].filter(Boolean).join('\n\n');
|
|
19
22
|
}
|
|
20
23
|
/**
|
|
@@ -44,10 +47,15 @@ export async function runAgentSession(opts) {
|
|
|
44
47
|
async function runOneSession(opts) {
|
|
45
48
|
const { org, role, bus, policy, mailbox, cwd, deliver } = opts;
|
|
46
49
|
const queryFn = opts.queryFn ?? query;
|
|
50
|
+
const isCoordinator = role.reports_to == null;
|
|
47
51
|
const orgServer = createSdkMcpServer({
|
|
48
52
|
name: 'org',
|
|
49
53
|
version: '1.0.0',
|
|
50
54
|
tools: [
|
|
55
|
+
...(isCoordinator && opts.onComplete ? [tool('org_complete', 'Record the outcome of this run. Call exactly once, when the goal is achieved or clearly cannot be. The outcome and summary are persisted to the org run history and briefed to the next run.', { outcome: z.enum(['achieved', 'partial', 'failed']), summary: z.string() }, async (args) => {
|
|
56
|
+
opts.onComplete(role.id, args.outcome, args.summary);
|
|
57
|
+
return { content: [{ type: 'text', text: `outcome "${args.outcome}" recorded` }] };
|
|
58
|
+
})] : []),
|
|
51
59
|
tool('org_send', 'Send a message to another agent (role id) or another org ("org:role"). This is the only inter-agent channel.', { to: z.string(), subject: z.string(), message: z.string() }, async (args) => {
|
|
52
60
|
const receipt = await deliver(role.id, args.to, args.subject, args.message);
|
|
53
61
|
return { content: [{ type: 'text', text: receipt }] };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type OrgDef } from './types.js';
|
|
2
|
+
interface TemplateRole {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
type: string;
|
|
6
|
+
reports_to: string | null;
|
|
7
|
+
responsibilities: string[];
|
|
8
|
+
}
|
|
9
|
+
interface Template {
|
|
10
|
+
goal: string;
|
|
11
|
+
roles: TemplateRole[];
|
|
12
|
+
}
|
|
13
|
+
export declare const ORG_TEMPLATES: Record<string, Template>;
|
|
14
|
+
export declare function buildFromTemplate(templateName: string, orgName: string, goal?: string): OrgDef | null;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// packages/@monomind/cli/src/orgrt/templates.ts
|
|
2
|
+
// Starter org configs for `monomind org create <name> --template <t>`.
|
|
3
|
+
// Each validates against OrgDefSchema; goal is a placeholder the user edits
|
|
4
|
+
// (or supplies via --goal).
|
|
5
|
+
import { OrgDefSchema } from './types.js';
|
|
6
|
+
export const ORG_TEMPLATES = {
|
|
7
|
+
'content-team': {
|
|
8
|
+
goal: 'Produce and publish high-quality content on a steady cadence',
|
|
9
|
+
roles: [
|
|
10
|
+
{ id: 'editor-in-chief', title: 'Editor in Chief', type: 'boss', reports_to: null,
|
|
11
|
+
responsibilities: ['Own the content calendar and quality bar', 'Assign pieces to the writer and route drafts to review', 'Approve final drafts for publishing'] },
|
|
12
|
+
{ id: 'writer', title: 'Staff Writer', type: 'specialist', reports_to: 'editor-in-chief',
|
|
13
|
+
responsibilities: ['Draft articles from assigned briefs', 'Incorporate review feedback', 'Hand finished drafts to the reviewer'] },
|
|
14
|
+
{ id: 'reviewer', title: 'Content Reviewer', type: 'reviewer', reports_to: 'editor-in-chief',
|
|
15
|
+
responsibilities: ['Review drafts for accuracy, clarity, and tone', 'Return actionable feedback to the writer', 'Flag anything needing human judgment via ask_human'] },
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
'dev-team': {
|
|
19
|
+
goal: 'Deliver features and fixes from a continuously groomed backlog',
|
|
20
|
+
roles: [
|
|
21
|
+
{ id: 'tech-lead', title: 'Tech Lead', type: 'boss', reports_to: null,
|
|
22
|
+
responsibilities: ['Break the goal into concrete tasks and assign them', 'Arbitrate design questions', 'Merge only work that passed review and tests'] },
|
|
23
|
+
{ id: 'developer', title: 'Developer', type: 'specialist', reports_to: 'tech-lead',
|
|
24
|
+
responsibilities: ['Implement assigned tasks with tests', 'Keep changes small and focused', 'Send diffs to the reviewer before reporting done'] },
|
|
25
|
+
{ id: 'code-reviewer', title: 'Code Reviewer', type: 'reviewer', reports_to: 'tech-lead',
|
|
26
|
+
responsibilities: ['Review diffs for correctness and maintainability', 'Verify claimed test results', 'Reject unverified work'] },
|
|
27
|
+
{ id: 'qa', title: 'QA Engineer', type: 'specialist', reports_to: 'tech-lead',
|
|
28
|
+
responsibilities: ['Exercise delivered features end-to-end', 'File precise reproduction steps for defects'] },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
'research-pod': {
|
|
32
|
+
goal: 'Produce a recurring intelligence brief on a defined topic',
|
|
33
|
+
roles: [
|
|
34
|
+
{ id: 'lead-analyst', title: 'Lead Analyst', type: 'boss', reports_to: null,
|
|
35
|
+
responsibilities: ['Define research questions for each cycle', 'Synthesize findings into the final brief', 'Decide what merits deeper follow-up'] },
|
|
36
|
+
{ id: 'researcher', title: 'Researcher', type: 'researcher', reports_to: 'lead-analyst',
|
|
37
|
+
responsibilities: ['Gather primary sources on assigned questions', 'Separate observed facts from inference', 'Deliver structured findings with citations'] },
|
|
38
|
+
{ id: 'fact-checker', title: 'Fact Checker', type: 'reviewer', reports_to: 'lead-analyst',
|
|
39
|
+
responsibilities: ['Verify claims in draft briefs against sources', 'Flag unverifiable claims for removal or hedging'] },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
export function buildFromTemplate(templateName, orgName, goal) {
|
|
44
|
+
const t = ORG_TEMPLATES[templateName];
|
|
45
|
+
if (!t)
|
|
46
|
+
return null;
|
|
47
|
+
// Parse through the schema so a template can never produce an unrunnable config.
|
|
48
|
+
return OrgDefSchema.parse({
|
|
49
|
+
name: orgName,
|
|
50
|
+
goal: goal ?? t.goal,
|
|
51
|
+
status: 'stopped',
|
|
52
|
+
schedule: null,
|
|
53
|
+
run_config: { max_concurrent_agents: 4, budget_tokens: 1_000_000, memory_namespace: `org:${orgName}`, max_turns_per_message: 30 },
|
|
54
|
+
roles: t.roles,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -194,7 +194,16 @@ export class ConfigFileManager {
|
|
|
194
194
|
const parsed = JSON.parse(fs.readFileSync(targetPath, 'utf-8'));
|
|
195
195
|
onDisk = sanitizeConfigObject(parsed);
|
|
196
196
|
}
|
|
197
|
-
catch {
|
|
197
|
+
catch (e) {
|
|
198
|
+
// The file exists but failed to parse — do NOT fall through to
|
|
199
|
+
// cloneDefaultConfig() and write that back. This config can hold
|
|
200
|
+
// provider API keys (see this method's doc comment above); silently
|
|
201
|
+
// "recovering" a momentarily-corrupt file by overwriting it with
|
|
202
|
+
// defaults-plus-the-new-key discards every real credential in it.
|
|
203
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
204
|
+
console.error('[config-file-manager] failed to parse existing config file:', e);
|
|
205
|
+
throw new Error(`Config file exists but is unreadable/corrupt: ${targetPath}. Refusing to overwrite it — fix or remove the file manually, then retry.`);
|
|
206
|
+
}
|
|
198
207
|
}
|
|
199
208
|
setNestedValue(onDisk, key, sanitisedValue);
|
|
200
209
|
this.writeAtomic(targetPath, onDisk);
|
|
@@ -257,7 +257,9 @@ export async function listGCSObjects(prefix, config) {
|
|
|
257
257
|
updated: obj.updated || new Date().toISOString(),
|
|
258
258
|
}));
|
|
259
259
|
}
|
|
260
|
-
catch {
|
|
260
|
+
catch (e) {
|
|
261
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
262
|
+
console.error('[GCS] listGCSObjects failed:', e);
|
|
261
263
|
return [];
|
|
262
264
|
}
|
|
263
265
|
}
|
|
@@ -275,7 +277,9 @@ export async function deleteFromGCS(uri, config) {
|
|
|
275
277
|
execFileSync('gcloud', rmArgs, { encoding: 'utf-8', stdio: 'pipe' });
|
|
276
278
|
return true;
|
|
277
279
|
}
|
|
278
|
-
catch {
|
|
280
|
+
catch (e) {
|
|
281
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
282
|
+
console.error('[GCS] deleteFromGCS failed:', e);
|
|
279
283
|
return false;
|
|
280
284
|
}
|
|
281
285
|
}
|
|
@@ -975,6 +975,7 @@ html, body {
|
|
|
975
975
|
<div id="health-pane">
|
|
976
976
|
<div id="health-grid" class="health-grid"></div>
|
|
977
977
|
<div id="health-detail"></div>
|
|
978
|
+
<div id="health-history"></div>
|
|
978
979
|
</div>
|
|
979
980
|
</div>
|
|
980
981
|
|
|
@@ -1759,6 +1760,37 @@ function renderHealth() {
|
|
|
1759
1760
|
HEALTH DATA UNAVAILABLE
|
|
1760
1761
|
</div>`;
|
|
1761
1762
|
}
|
|
1763
|
+
|
|
1764
|
+
loadRunHistory();
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
// Run history (history.jsonl, written by the daemon at each stopOrg): outcome,
|
|
1768
|
+
// duration, tokens per completed run — rendered under the health grid.
|
|
1769
|
+
async function loadRunHistory() {
|
|
1770
|
+
const el = document.getElementById('health-history');
|
|
1771
|
+
if (!el || !selectedOrg) return;
|
|
1772
|
+
try {
|
|
1773
|
+
const listOrg = allOrgs.find(o => o.name === selectedOrg) || {};
|
|
1774
|
+
const r = await fetch(`/api/orgs/${encodeURIComponent(selectedOrg)}/history?dir=${encodeURIComponent(listOrg.projectDir || '')}`);
|
|
1775
|
+
if (!r.ok) { el.innerHTML = ''; return; }
|
|
1776
|
+
const runs = (await r.json()).runs || [];
|
|
1777
|
+
if (!runs.length) { el.innerHTML = ''; return; }
|
|
1778
|
+
const outcomeColor = s => s === 'achieved' ? 'var(--teal)' : s === 'partial' ? 'var(--amber, #d90)' : 'var(--red)';
|
|
1779
|
+
el.innerHTML = `
|
|
1780
|
+
<div style="margin-top:16px">
|
|
1781
|
+
<div style="font-size:8px;letter-spacing:2px;color:var(--dim);margin-bottom:8px">RUN HISTORY (${runs.length})</div>
|
|
1782
|
+
${runs.slice(0, 10).map(h => `
|
|
1783
|
+
<div style="font-size:9px;padding:5px 0;border-bottom:1px solid oklch(50% 0 0 / 0.1);display:flex;gap:10px;align-items:baseline">
|
|
1784
|
+
<span style="color:var(--dim);white-space:nowrap">${esc(h.run || '?')}</span>
|
|
1785
|
+
<span style="color:var(--muted);white-space:nowrap">${h.durationMs != null ? Math.round(h.durationMs / 1000) + 's' : '—'} · ${h.totalTokens || 0} tok</span>
|
|
1786
|
+
${h.outcome
|
|
1787
|
+
? `<span style="color:${outcomeColor(h.outcome.status)}">${esc(h.outcome.status.toUpperCase())}</span>
|
|
1788
|
+
<span style="color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(h.outcome.summary || '')}</span>`
|
|
1789
|
+
: `<span style="color:var(--dim)">NO OUTCOME</span>`}
|
|
1790
|
+
${h.crashes && h.crashes.length ? `<span style="color:var(--red)">✗ ${h.crashes.length} crash</span>` : ''}
|
|
1791
|
+
</div>`).join('')}
|
|
1792
|
+
</div>`;
|
|
1793
|
+
} catch (_) { el.innerHTML = ''; }
|
|
1762
1794
|
}
|
|
1763
1795
|
|
|
1764
1796
|
// ── CHAT TAB ──────────────────────────────────────────────────────
|
|
@@ -5863,6 +5863,31 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5863
5863
|
return;
|
|
5864
5864
|
}
|
|
5865
5865
|
|
|
5866
|
+
// GET /api/orgs/:name/history — per-run outcome summaries (history.jsonl, newest first)
|
|
5867
|
+
if (req.method === 'GET' && /^\/api\/orgs\/[^/]+\/history$/.test(url)) {
|
|
5868
|
+
try {
|
|
5869
|
+
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
5870
|
+
const _histQs = new URL(req.url, 'http://localhost').searchParams;
|
|
5871
|
+
const root = path.resolve(_histQs.get('dir') || projectDir || process.cwd());
|
|
5872
|
+
if (!orgName || orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
5873
|
+
res.writeHead(400); res.end('{"error":"invalid org name"}'); return;
|
|
5874
|
+
}
|
|
5875
|
+
const monoDir = _getGitMonomindDir(root) || path.join(root, '.monomind');
|
|
5876
|
+
const histFile = path.join(monoDir, 'orgs', orgName, 'history.jsonl');
|
|
5877
|
+
let runs = [];
|
|
5878
|
+
if (fs.existsSync(histFile)) {
|
|
5879
|
+
runs = fs.readFileSync(histFile, 'utf8').split('\n').filter(l => l.trim())
|
|
5880
|
+
.map(l => { try { return JSON.parse(l); } catch(_) { return null; } })
|
|
5881
|
+
.filter(Boolean).reverse().slice(0, 50);
|
|
5882
|
+
}
|
|
5883
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5884
|
+
res.end(JSON.stringify({ runs }));
|
|
5885
|
+
} catch(err) {
|
|
5886
|
+
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
5887
|
+
}
|
|
5888
|
+
return;
|
|
5889
|
+
}
|
|
5890
|
+
|
|
5866
5891
|
// GET /api/mastermind/metrics — aggregate system metrics from token-summary and swarm-activity
|
|
5867
5892
|
if (req.method === 'GET' && url === '/api/mastermind/metrics') {
|
|
5868
5893
|
try {
|
|
@@ -70,8 +70,10 @@ export function loadState() {
|
|
|
70
70
|
return state;
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
catch {
|
|
73
|
+
catch (e) {
|
|
74
74
|
// Corrupted file, reset
|
|
75
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
76
|
+
console.error('[rate-limiter] update-state.json read/parse failed:', e);
|
|
75
77
|
}
|
|
76
78
|
return getDefaultState();
|
|
77
79
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monoes/monomindcli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI engine for Monomind — an open-source MCP server that extends Claude Code with a codebase knowledge graph (tree-sitter + SQLite), persistent memory, multi-agent task coordination, and session hooks. MIT licensed, fully local.",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -119,4 +119,4 @@
|
|
|
119
119
|
"overrides": {
|
|
120
120
|
"onnxruntime-node": ">=1.27.0"
|
|
121
121
|
}
|
|
122
|
-
}
|
|
122
|
+
}
|