monomind 1.14.3 → 1.14.5
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/.claude/commands/mastermind/runorg.md +4 -2
- package/.claude/skills/mastermind/_repeat.md +9 -6
- package/.claude/skills/mastermind/runorg.md +4 -4
- package/package.json +2 -2
- package/packages/@monomind/cli/dist/src/commands/org.js +107 -86
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +91 -20
- package/packages/@monomind/cli/package.json +2 -2
|
@@ -93,7 +93,8 @@ curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
|
93
93
|
-d "$(jq -cn \
|
|
94
94
|
--arg session "$session_id" \
|
|
95
95
|
--arg org "$org_name" \
|
|
96
|
-
|
|
96
|
+
--arg proj "$(pwd)" \
|
|
97
|
+
'{type:"domain:dispatch",session:$session,domain:"ops",cmd:("Starting org "+$org+" as persistent daemon"),project:$proj,ts:(now*1000|floor)}')" || true
|
|
97
98
|
```
|
|
98
99
|
|
|
99
100
|
Invoke `Skill("mastermind:runorg")` passing: brain_context, org_name: `$org_name`, session_id: `$session_id`, task: task_override, caller: "command".
|
|
@@ -105,7 +106,8 @@ curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
|
105
106
|
-d "$(jq -cn \
|
|
106
107
|
--arg session "$session_id" \
|
|
107
108
|
--arg status "<status>" \
|
|
108
|
-
|
|
109
|
+
--arg proj "$(pwd)" \
|
|
110
|
+
'{type:"session:complete",session:$session,domain:"ops",status:$status,domains:["ops"],project:$proj,ts:(now*1000|floor)}')" || true
|
|
109
111
|
```
|
|
110
112
|
|
|
111
113
|
Follow _protocol.md Brain Write Procedure for domain `ops`.
|
|
@@ -44,10 +44,11 @@ Set `TILLEND_EMPTY=true` **only if BOTH are zero for this round**.
|
|
|
44
44
|
```
|
|
45
45
|
- Emit dashboard event (non-fatal if control server is not running):
|
|
46
46
|
```bash
|
|
47
|
-
|
|
47
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
48
|
+
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
48
49
|
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
49
50
|
-H "Content-Type: application/json" \
|
|
50
|
-
-d "{\"type\":\"loop:complete\",\"loopId\":\"${LOOP_ID}\",\"command\":\"/<command>\",\"mode\":\"tillend\",\"ranReps\":<current_rep>,\"reason\":\"empty-round\",\"ts\":$(date +%s)000}" || true
|
|
51
|
+
-d "{\"type\":\"loop:complete\",\"loopId\":\"${LOOP_ID}\",\"command\":\"/<command>\",\"mode\":\"tillend\",\"ranReps\":<current_rep>,\"reason\":\"empty-round\",\"project\":\"${REPO_ROOT}\",\"ts\":$(date +%s)000}" || true
|
|
51
52
|
```
|
|
52
53
|
- Run: `rm -f ".monomind/loops/${LOOP_ID}.json"`
|
|
53
54
|
- **END**.
|
|
@@ -73,10 +74,11 @@ If `current_rep >= repeat_count`:
|
|
|
73
74
|
- Output: `[repeat] All <repeat_count> runs of /<command> complete.`
|
|
74
75
|
- Emit dashboard event:
|
|
75
76
|
```bash
|
|
76
|
-
|
|
77
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
78
|
+
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
77
79
|
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
78
80
|
-H "Content-Type: application/json" \
|
|
79
|
-
-d "{\"type\":\"loop:complete\",\"loopId\":\"${LOOP_ID}\",\"command\":\"/<command>\",\"ranReps\":<repeat_count>,\"ts\":$(date +%s)000}" || true
|
|
81
|
+
-d "{\"type\":\"loop:complete\",\"loopId\":\"${LOOP_ID}\",\"command\":\"/<command>\",\"ranReps\":<repeat_count>,\"project\":\"${REPO_ROOT}\",\"ts\":$(date +%s)000}" || true
|
|
80
82
|
```
|
|
81
83
|
- Run: `rm -f ".monomind/loops/${LOOP_ID}.json"`
|
|
82
84
|
- **END**.
|
|
@@ -120,10 +122,11 @@ EOF
|
|
|
120
122
|
|
|
121
123
|
Emit `loop:tick`:
|
|
122
124
|
```bash
|
|
123
|
-
|
|
125
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
126
|
+
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
124
127
|
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
125
128
|
-H "Content-Type: application/json" \
|
|
126
|
-
-d "{\"type\":\"loop:tick\",\"loopId\":\"${LOOP_ID}\",\"command\":\"/<command>\",\"completedRep\":<current_rep>,\"nextRep\":<next_rep>,\"nextAt\":${NEXT_AT},\"ts\":$(date +%s)000}" || true
|
|
129
|
+
-d "{\"type\":\"loop:tick\",\"loopId\":\"${LOOP_ID}\",\"command\":\"/<command>\",\"completedRep\":<current_rep>,\"nextRep\":<next_rep>,\"nextAt\":${NEXT_AT},\"project\":\"${REPO_ROOT}\",\"ts\":$(date +%s)000}" || true
|
|
127
130
|
```
|
|
128
131
|
|
|
129
132
|
**Call `ScheduleWakeup` now** — this is a mandatory tool call:
|
|
@@ -501,13 +501,13 @@ OPERATING LOOP:
|
|
|
501
501
|
# On start — announce you are working:
|
|
502
502
|
curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
|
|
503
503
|
-d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" \
|
|
504
|
-
--arg from "<role_id>" --arg msg "Starting: <card title>" \
|
|
505
|
-
'{type:"org:comms",session:$s,org:$o,runId:$rid,from:$from,to:"boss",msg:$msg,ts:(now*1000|floor)}')" || true
|
|
504
|
+
--arg from "<role_id>" --arg msg "Starting: <card title>" --arg p "$REPO_ROOT" \
|
|
505
|
+
'{type:"org:comms",session:$s,org:$o,runId:$rid,from:$from,to:"boss",msg:$msg,project:$p,ts:(now*1000|floor)}')" || true
|
|
506
506
|
# On completion — report back:
|
|
507
507
|
curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
|
|
508
508
|
-d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" \
|
|
509
|
-
--arg from "<role_id>" --arg msg "Completed: <one-sentence summary of output>" \
|
|
510
|
-
'{type:"org:comms",session:$s,org:$o,runId:$rid,from:$from,to:"boss",msg:$msg,ts:(now*1000|floor)}')" || true
|
|
509
|
+
--arg from "<role_id>" --arg msg "Completed: <one-sentence summary of output>" --arg p "$REPO_ROOT" \
|
|
510
|
+
'{type:"org:comms",session:$s,org:$o,runId:$rid,from:$from,to:"boss",msg:$msg,project:$p,ts:(now*1000|floor)}')" || true
|
|
511
511
|
|
|
512
512
|
Fill in the literal values for orgName="${orgName}", runId="${runId}", sessionId="${sessionId}" — embed them
|
|
513
513
|
directly in the prompt string so the specialist doesn't need to resolve them.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "monomind",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.5",
|
|
4
4
|
"description": "Monomind - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -147,4 +147,4 @@
|
|
|
147
147
|
"access": "public",
|
|
148
148
|
"tag": "latest"
|
|
149
149
|
}
|
|
150
|
-
}
|
|
150
|
+
}
|
|
@@ -1,93 +1,114 @@
|
|
|
1
1
|
import { output } from '../output.js';
|
|
2
2
|
import { existsSync, unlinkSync, rmSync, readdirSync } from 'fs';
|
|
3
3
|
import { join, resolve } from 'path';
|
|
4
|
-
|
|
5
4
|
const orgCommand = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
output.info(' delete <name> Delete an org and all its data');
|
|
25
|
-
return { success: true };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (sub === 'list') {
|
|
29
|
-
const cwd = context?.projectPath || process.cwd();
|
|
30
|
-
const orgsDir = join(cwd, '.monomind', 'orgs');
|
|
31
|
-
if (!existsSync(orgsDir)) {
|
|
32
|
-
output.info('No orgs directory found. Create an org first with /mastermind:createorg');
|
|
33
|
-
return { success: true };
|
|
34
|
-
}
|
|
35
|
-
const ignore = ['-state','-goals','-threads','-activity','-approvals','-members','-secrets','-budgets','-routines','-issues','-projects','-workspaces','-worktrees','-environments','-plugins','-adapters','-join-requests','-bootstrap','-project-workspaces','-approval-comments','-skills'];
|
|
36
|
-
const configs = readdirSync(orgsDir)
|
|
37
|
-
.filter(f => f.endsWith('.json') && !ignore.some(s => f.includes(s)));
|
|
38
|
-
if (!configs.length) { output.info('No orgs found.'); return { success: true }; }
|
|
39
|
-
output.info(`Found ${configs.length} org(s):`);
|
|
40
|
-
for (const f of configs) output.info(` • ${f.replace('.json', '')}`);
|
|
41
|
-
return { success: true };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (sub === 'delete') {
|
|
45
|
-
const orgName = args[1];
|
|
46
|
-
if (!orgName) {
|
|
47
|
-
output.error('Usage: monomind org delete <name>');
|
|
48
|
-
return { success: false, error: 'org name required' };
|
|
49
|
-
}
|
|
50
|
-
if (!/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
51
|
-
output.error(`Invalid org name: ${orgName}`);
|
|
52
|
-
return { success: false, error: 'invalid org name' };
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const confirmed = args.includes('--yes') || args.includes('-y');
|
|
56
|
-
if (!confirmed) {
|
|
57
|
-
output.warn(`This will permanently delete org "${orgName}" and all its data.`);
|
|
58
|
-
output.warn('Pass --yes to confirm.');
|
|
59
|
-
return { success: false, error: 'confirmation required' };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const cwd = resolve(context?.projectPath || process.cwd());
|
|
63
|
-
const orgsDir = join(cwd, '.monomind', 'orgs');
|
|
64
|
-
const configFile = join(orgsDir, `${orgName}.json`);
|
|
65
|
-
if (!existsSync(configFile)) {
|
|
66
|
-
output.error(`Org not found: ${orgName}`);
|
|
67
|
-
return { success: false, error: 'org not found' };
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const suffixes = ['','-state','-goals','-routines','-approvals','-activity','-issues','-members','-projects','-workspaces','-worktrees','-environments','-plugins','-adapters','-budgets','-threads','-secrets','-join-requests','-bootstrap','-project-workspaces','-approval-comments','-skills'];
|
|
71
|
-
let removed = 0;
|
|
72
|
-
for (const suf of suffixes) {
|
|
73
|
-
for (const ext of ['.json', '.jsonl']) {
|
|
74
|
-
const f = join(orgsDir, `${orgName}${suf}${ext}`);
|
|
75
|
-
try { if (existsSync(f)) { unlinkSync(f); removed++; } } catch (_) {}
|
|
5
|
+
name: 'org',
|
|
6
|
+
description: 'Manage monomind organisations',
|
|
7
|
+
subcommands: ['delete', 'list'],
|
|
8
|
+
usage: 'monomind org <subcommand> [options]',
|
|
9
|
+
examples: [
|
|
10
|
+
'monomind org list',
|
|
11
|
+
'monomind org delete my-org',
|
|
12
|
+
'monomind org delete my-org --yes',
|
|
13
|
+
],
|
|
14
|
+
async execute(args, context) {
|
|
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
|
+
return { success: true };
|
|
76
23
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
24
|
+
if (sub === 'list') {
|
|
25
|
+
const cwd = context.projectPath || process.cwd();
|
|
26
|
+
const orgsDir = join(cwd, '.monomind', 'orgs');
|
|
27
|
+
if (!existsSync(orgsDir)) {
|
|
28
|
+
output.info('No orgs directory found. Create an org first with /mastermind:createorg');
|
|
29
|
+
return { success: true };
|
|
30
|
+
}
|
|
31
|
+
const configs = readdirSync(orgsDir)
|
|
32
|
+
.filter(f => f.endsWith('.json') && !f.includes('-state') && !f.includes('-goals')
|
|
33
|
+
&& !f.includes('-threads') && !f.includes('-activity') && !f.includes('-approvals')
|
|
34
|
+
&& !f.includes('-members') && !f.includes('-secrets') && !f.includes('-budgets'));
|
|
35
|
+
if (!configs.length) {
|
|
36
|
+
output.info('No orgs found.');
|
|
37
|
+
return { success: true };
|
|
38
|
+
}
|
|
39
|
+
output.info(`Found ${configs.length} org(s):`);
|
|
40
|
+
for (const f of configs)
|
|
41
|
+
output.info(` • ${f.replace('.json', '')}`);
|
|
42
|
+
return { success: true };
|
|
43
|
+
}
|
|
44
|
+
if (sub === 'delete') {
|
|
45
|
+
const orgName = args[1];
|
|
46
|
+
if (!orgName) {
|
|
47
|
+
output.error('Usage: monomind org delete <name>');
|
|
48
|
+
return { success: false, error: 'org name required' };
|
|
49
|
+
}
|
|
50
|
+
if (!/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
51
|
+
output.error(`Invalid org name: ${orgName}`);
|
|
52
|
+
return { success: false, error: 'invalid org name' };
|
|
53
|
+
}
|
|
54
|
+
const confirmed = args.includes('--yes') || args.includes('-y');
|
|
55
|
+
if (!confirmed) {
|
|
56
|
+
output.warn(`This will permanently delete org "${orgName}" and all its data.`);
|
|
57
|
+
output.warn('Pass --yes to confirm.');
|
|
58
|
+
return { success: false, error: 'confirmation required' };
|
|
59
|
+
}
|
|
60
|
+
const cwd = resolve(context.projectPath || process.cwd());
|
|
61
|
+
const orgsDir = join(cwd, '.monomind', 'orgs');
|
|
62
|
+
const configFile = join(orgsDir, `${orgName}.json`);
|
|
63
|
+
if (!existsSync(configFile)) {
|
|
64
|
+
output.error(`Org not found: ${orgName}`);
|
|
65
|
+
return { success: false, error: 'org not found' };
|
|
66
|
+
}
|
|
67
|
+
const suffixes = ['', '-state', '-goals', '-routines', '-approvals', '-activity',
|
|
68
|
+
'-issues', '-members', '-projects', '-workspaces', '-worktrees', '-environments',
|
|
69
|
+
'-plugins', '-adapters', '-budgets', '-threads', '-secrets', '-join-requests',
|
|
70
|
+
'-bootstrap', '-project-workspaces', '-approval-comments', '-skills'];
|
|
71
|
+
let removed = 0;
|
|
72
|
+
for (const suf of suffixes) {
|
|
73
|
+
for (const ext of ['.json', '.jsonl']) {
|
|
74
|
+
const f = join(orgsDir, `${orgName}${suf}${ext}`);
|
|
75
|
+
try {
|
|
76
|
+
if (existsSync(f)) {
|
|
77
|
+
unlinkSync(f);
|
|
78
|
+
removed++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (_) { }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Remove stop file
|
|
85
|
+
try {
|
|
86
|
+
unlinkSync(join(orgsDir, '.stops', `${orgName}.stop`));
|
|
87
|
+
}
|
|
88
|
+
catch (_) { }
|
|
89
|
+
// Remove org subdirectory
|
|
90
|
+
const orgSubDir = join(orgsDir, orgName);
|
|
91
|
+
try {
|
|
92
|
+
if (existsSync(orgSubDir))
|
|
93
|
+
rmSync(orgSubDir, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
catch (_) { }
|
|
96
|
+
// Remove loop prompt file
|
|
97
|
+
try {
|
|
98
|
+
unlinkSync(join(cwd, '.monomind', 'loops', `${orgName}.md`));
|
|
99
|
+
}
|
|
100
|
+
catch (_) { }
|
|
101
|
+
// Remove run prompt file
|
|
102
|
+
try {
|
|
103
|
+
unlinkSync(join(cwd, '.monomind', 'orgs', `${orgName}-run.md`));
|
|
104
|
+
}
|
|
105
|
+
catch (_) { }
|
|
106
|
+
output.success(`Org "${orgName}" deleted (${removed} file(s) removed).`);
|
|
107
|
+
return { success: true };
|
|
108
|
+
}
|
|
109
|
+
output.error(`Unknown subcommand: ${sub}. Run "monomind org help" for usage.`);
|
|
110
|
+
return { success: false, error: `unknown subcommand: ${sub}` };
|
|
111
|
+
},
|
|
91
112
|
};
|
|
92
|
-
|
|
93
113
|
export default orgCommand;
|
|
114
|
+
//# sourceMappingURL=org.js.map
|
|
@@ -544,10 +544,58 @@ const monographReportTool = {
|
|
|
544
544
|
}
|
|
545
545
|
},
|
|
546
546
|
};
|
|
547
|
+
// ── Shared staleness helper ───────────────────────────────────────────────────
|
|
548
|
+
/** Guard against concurrent background buildAsync calls on the same DB. */
|
|
549
|
+
let _buildInProgress = false;
|
|
550
|
+
/**
|
|
551
|
+
* Compute how many commits the index is behind HEAD.
|
|
552
|
+
* Returns { commitsBehind, lastCommit } — or null if the index has never been
|
|
553
|
+
* built or git is unavailable.
|
|
554
|
+
*/
|
|
555
|
+
async function computeCommitsBehind(repoPath) {
|
|
556
|
+
const { openDb, closeDb } = await import('@monoes/monograph');
|
|
557
|
+
const { execSync } = await import('child_process');
|
|
558
|
+
const db = openDb(join(repoPath, '.monomind', 'monograph.db'));
|
|
559
|
+
try {
|
|
560
|
+
const meta = db.prepare("SELECT value FROM index_meta WHERE key = 'last_commit_hash'").get() ?? db.prepare("SELECT value FROM index_meta WHERE key = 'lastCommit'").get();
|
|
561
|
+
const lastCommit = meta?.value ?? null;
|
|
562
|
+
if (!lastCommit || !/^[0-9a-f]{7,40}$/i.test(lastCommit))
|
|
563
|
+
return null;
|
|
564
|
+
try {
|
|
565
|
+
const out = execSync(`git rev-list --count ${lastCommit}..HEAD`, {
|
|
566
|
+
cwd: repoPath, encoding: 'utf-8',
|
|
567
|
+
}).trim();
|
|
568
|
+
return { commitsBehind: parseInt(out, 10), lastCommit };
|
|
569
|
+
}
|
|
570
|
+
catch {
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
finally {
|
|
575
|
+
closeDb(db);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Fire-and-forget background rebuild. Uses a module-level guard so concurrent
|
|
580
|
+
* MCP tool calls (e.g. repeated monograph_suggest_auto) don't pile up builds.
|
|
581
|
+
* threshold: minimum commitsBehind to trigger (default 1, Task 2 uses 10).
|
|
582
|
+
*/
|
|
583
|
+
function triggerBackgroundBuildIfNeeded(repoPath, commitsBehind, threshold = 1) {
|
|
584
|
+
if (commitsBehind < threshold)
|
|
585
|
+
return false;
|
|
586
|
+
if (_buildInProgress)
|
|
587
|
+
return false;
|
|
588
|
+
_buildInProgress = true;
|
|
589
|
+
void import('@monoes/monograph')
|
|
590
|
+
.then(({ buildAsync }) => buildAsync(repoPath, { codeOnly: true }))
|
|
591
|
+
.catch(() => { })
|
|
592
|
+
.finally(() => { _buildInProgress = false; });
|
|
593
|
+
return true;
|
|
594
|
+
}
|
|
547
595
|
// ── monograph_staleness ───────────────────────────────────────────────────────
|
|
548
596
|
const monographStalenessTool = {
|
|
549
597
|
name: 'monograph_staleness',
|
|
550
|
-
description: 'Git staleness detection: compares the commit hash at last index build against current HEAD.
|
|
598
|
+
description: 'Git staleness detection: compares the commit hash at last index build against current HEAD. When the index is more than 10 commits behind HEAD it automatically triggers a background rebuild. Returns { commitsBehind, status, triggered }.',
|
|
551
599
|
inputSchema: {
|
|
552
600
|
type: 'object',
|
|
553
601
|
properties: {
|
|
@@ -555,26 +603,16 @@ const monographStalenessTool = {
|
|
|
555
603
|
},
|
|
556
604
|
},
|
|
557
605
|
handler: async (input) => {
|
|
558
|
-
const { getMonographStaleness } = await import('@monoes/monograph');
|
|
559
606
|
const repoPath = input.path ?? getProjectCwd();
|
|
560
|
-
const
|
|
561
|
-
if (!
|
|
562
|
-
return text(
|
|
563
|
-
}
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
lines.push(`Stale since: ${r.staleSince}`);
|
|
570
|
-
if (r.changedSince.length > 0) {
|
|
571
|
-
const shown = r.changedSince.slice(0, 10);
|
|
572
|
-
const more = r.changedSince.length - shown.length;
|
|
573
|
-
lines.push(`Changed files (${r.changedSince.length}):${shown.map(f => `\n ${f}`).join('')}${more > 0 ? `\n … ${more} more` : ''}`);
|
|
574
|
-
}
|
|
575
|
-
if (r.isStale)
|
|
576
|
-
lines.push('Action: run monograph_build to re-index');
|
|
577
|
-
return text(lines.join('\n'));
|
|
607
|
+
const result = await computeCommitsBehind(repoPath);
|
|
608
|
+
if (!result) {
|
|
609
|
+
return text(JSON.stringify({ commitsBehind: 0, status: 'unknown', triggered: false }));
|
|
610
|
+
}
|
|
611
|
+
const { commitsBehind } = result;
|
|
612
|
+
const AUTO_BUILD_THRESHOLD = 10;
|
|
613
|
+
const triggered = triggerBackgroundBuildIfNeeded(repoPath, commitsBehind, AUTO_BUILD_THRESHOLD + 1);
|
|
614
|
+
const status = triggered ? 'building' : commitsBehind === 0 ? 'fresh' : 'stale';
|
|
615
|
+
return text(JSON.stringify({ commitsBehind, status, triggered }));
|
|
578
616
|
},
|
|
579
617
|
};
|
|
580
618
|
// ── monograph_snapshot ────────────────────────────────────────────────────────
|
|
@@ -1735,6 +1773,38 @@ const monographNeighborsTool = {
|
|
|
1735
1773
|
}
|
|
1736
1774
|
},
|
|
1737
1775
|
};
|
|
1776
|
+
// ── monograph_suggest_auto ────────────────────────────────────────────────────
|
|
1777
|
+
const monographSuggestAutoTool = {
|
|
1778
|
+
name: 'monograph_suggest_auto',
|
|
1779
|
+
description: 'Like monograph_suggest but health-aware: checks staleness first and triggers a background rebuild when the index is behind HEAD before returning suggestions. Result includes a _staleness annotation.',
|
|
1780
|
+
inputSchema: {
|
|
1781
|
+
type: 'object',
|
|
1782
|
+
properties: {
|
|
1783
|
+
task: { type: 'string', description: 'Optional task description for task-relevance scoring' },
|
|
1784
|
+
limit: { type: 'number', description: 'Max questions (default 10)' },
|
|
1785
|
+
},
|
|
1786
|
+
},
|
|
1787
|
+
handler: async (input) => {
|
|
1788
|
+
const repoPath = getProjectCwd();
|
|
1789
|
+
// Check staleness and trigger rebuild if needed (threshold: any staleness).
|
|
1790
|
+
const stalenessResult = await computeCommitsBehind(repoPath);
|
|
1791
|
+
const commitsBehind = stalenessResult?.commitsBehind ?? 0;
|
|
1792
|
+
const triggered = triggerBackgroundBuildIfNeeded(repoPath, commitsBehind, 1);
|
|
1793
|
+
const stalenessStatus = triggered ? 'building' : commitsBehind === 0 ? 'fresh' : 'stale';
|
|
1794
|
+
// Delegate to the base suggest tool — no logic duplication.
|
|
1795
|
+
const baseResult = await monographSuggestTool.handler(input);
|
|
1796
|
+
// Append staleness annotation to the text content.
|
|
1797
|
+
const stalenessAnnotation = `\n_staleness: ${JSON.stringify({ commitsBehind, status: stalenessStatus, triggered })}`;
|
|
1798
|
+
if (baseResult && Array.isArray(baseResult.content)) {
|
|
1799
|
+
const content = baseResult.content;
|
|
1800
|
+
if (content.length > 0 && content[0].type === 'text') {
|
|
1801
|
+
content[0].text += stalenessAnnotation;
|
|
1802
|
+
}
|
|
1803
|
+
return baseResult;
|
|
1804
|
+
}
|
|
1805
|
+
return baseResult;
|
|
1806
|
+
},
|
|
1807
|
+
};
|
|
1738
1808
|
// ── Export all tools ──────────────────────────────────────────────────────────
|
|
1739
1809
|
export const monographTools = [
|
|
1740
1810
|
monographBuildTool,
|
|
@@ -1747,6 +1817,7 @@ export const monographTools = [
|
|
|
1747
1817
|
monographCommunityTool,
|
|
1748
1818
|
monographSurprisesTool,
|
|
1749
1819
|
monographSuggestTool,
|
|
1820
|
+
monographSuggestAutoTool,
|
|
1750
1821
|
monographVisualizeTool,
|
|
1751
1822
|
monographWatchTool,
|
|
1752
1823
|
monographWatchStopTool,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monoes/monomindcli",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Monomind CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -104,4 +104,4 @@
|
|
|
104
104
|
"access": "public",
|
|
105
105
|
"tag": "latest"
|
|
106
106
|
}
|
|
107
|
-
}
|
|
107
|
+
}
|