@monoes/monomindcli 1.12.0 → 1.14.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/.claude/agents/generated/churn-analyst.md +53 -0
- package/.claude/agents/generated/code-reviewer.md +55 -0
- package/.claude/agents/generated/code-validator.md +57 -0
- package/.claude/agents/generated/complexity-scanner.md +56 -0
- package/.claude/agents/generated/devbot-orchestrator.md +58 -0
- package/.claude/agents/generated/devbot-planner.md +63 -0
- package/.claude/agents/generated/impact-assessor.md +54 -0
- package/.claude/commands/mastermind/master.md +88 -24
- package/.claude/helpers/control-start.cjs +60 -1
- package/.claude/helpers/event-logger.cjs +43 -2
- package/.claude/helpers/handlers/capture-handler.cjs +336 -0
- package/.claude/helpers/handlers/route-handler.cjs +20 -13
- package/.claude/helpers/handlers/session-restore-handler.cjs +14 -8
- package/.claude/helpers/hook-handler.cjs +57 -1
- package/.claude/helpers/intelligence.cjs +129 -57
- package/.claude/helpers/memory-palace.cjs +461 -0
- package/.claude/helpers/memory.cjs +134 -15
- package/.claude/helpers/metrics-db.mjs +87 -0
- package/.claude/helpers/router.cjs +296 -41
- package/.claude/helpers/session.cjs +107 -32
- package/.claude/helpers/statusline.cjs +138 -2
- package/.claude/helpers/toggle-statusline.cjs +73 -0
- package/.claude/helpers/token-tracker.cjs +934 -0
- package/.claude/helpers/utils/monograph.cjs +39 -4
- package/.claude/helpers/utils/telemetry.cjs +3 -3
- package/.claude/skills/mastermind/createorg.md +227 -16
- package/.claude/skills/mastermind/idea.md +15 -3
- package/.claude/skills/mastermind/runorg.md +2 -1
- package/dist/src/commands/doctor.d.ts.map +1 -1
- package/dist/src/commands/doctor.js +96 -4
- package/dist/src/commands/doctor.js.map +1 -1
- package/dist/src/commands/index.js +2 -0
- package/dist/src/commands/org.d.ts +4 -0
- package/dist/src/commands/org.d.ts.map +1 -0
- package/dist/src/commands/org.js +93 -0
- package/dist/src/commands/org.js.map +1 -0
- package/dist/src/mcp-tools/memory-tools.js +6 -6
- package/dist/src/mcp-tools/memory-tools.js.map +1 -1
- package/dist/src/mcp-tools/monograph-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/monograph-tools.js +329 -37
- package/dist/src/mcp-tools/monograph-tools.js.map +1 -1
- package/dist/src/mcp-tools/session-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/session-tools.js +9 -10
- package/dist/src/mcp-tools/session-tools.js.map +1 -1
- package/dist/src/mcp-tools/task-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/task-tools.js +7 -8
- package/dist/src/mcp-tools/task-tools.js.map +1 -1
- package/dist/src/mcp-tools/types.d.ts +1 -0
- package/dist/src/mcp-tools/types.d.ts.map +1 -1
- package/dist/src/mcp-tools/types.js +49 -0
- package/dist/src/mcp-tools/types.js.map +1 -1
- package/dist/src/services/worker-daemon.d.ts.map +1 -1
- package/dist/src/services/worker-daemon.js +295 -5
- package/dist/src/services/worker-daemon.js.map +1 -1
- package/dist/src/transfer/serialization/cfp.js +1 -1
- package/dist/src/transfer/serialization/cfp.js.map +1 -1
- package/dist/src/ui/dashboard.html +2235 -178
- package/dist/src/ui/orgs.html +1 -0
- package/dist/src/ui/server.mjs +532 -133
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* metrics-db.mjs — Lightweight metrics aggregation helper.
|
|
4
|
+
* Reads .monomind/metrics/*.json files and produces summary output.
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* sync — aggregate metrics files → JSON summary (default)
|
|
8
|
+
* status — print current metrics status as JSON
|
|
9
|
+
* export — export metrics to timestamped file
|
|
10
|
+
* (other) — print usage hint, exit 0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import fs from 'fs';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
|
|
17
|
+
const CWD = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
18
|
+
const METRICS_DIR = path.join(CWD, '.monomind', 'metrics');
|
|
19
|
+
|
|
20
|
+
function safeRead(filePath) {
|
|
21
|
+
try {
|
|
22
|
+
if (!fs.existsSync(filePath)) return null;
|
|
23
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
24
|
+
} catch (_) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function aggregate() {
|
|
30
|
+
const recentEdits = safeRead(path.join(METRICS_DIR, 'recent-edits.json'));
|
|
31
|
+
const toolCalls = safeRead(path.join(METRICS_DIR, 'tool-calls.json'));
|
|
32
|
+
const hookLatency = safeRead(path.join(METRICS_DIR, 'hook-latency.json'));
|
|
33
|
+
const tokenSummary = safeRead(path.join(METRICS_DIR, 'token-summary.json'));
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
aggregatedAt: new Date().toISOString(),
|
|
37
|
+
recentEdits: recentEdits ? (recentEdits.edits || []).length : 0,
|
|
38
|
+
toolCalls: toolCalls ? Object.keys(toolCalls.calls || {}).length : 0,
|
|
39
|
+
hookLatency: hookLatency ? Object.keys(hookLatency).filter(k => k !== 'lastUpdated').length : 0,
|
|
40
|
+
tokenSummary: tokenSummary ? { todayCost: tokenSummary.todayCost, monthCost: tokenSummary.monthCost } : null,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function cmdSync() {
|
|
45
|
+
const summary = aggregate();
|
|
46
|
+
process.stdout.write(JSON.stringify(summary) + '\n');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function cmdStatus() {
|
|
51
|
+
const summary = aggregate();
|
|
52
|
+
process.stdout.write(JSON.stringify({ status: 'ok', metrics: summary }) + '\n');
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function cmdExport() {
|
|
57
|
+
const summary = aggregate();
|
|
58
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
59
|
+
const exportPath = path.join(METRICS_DIR, `export-${ts}.json`);
|
|
60
|
+
try {
|
|
61
|
+
fs.mkdirSync(METRICS_DIR, { recursive: true });
|
|
62
|
+
fs.writeFileSync(exportPath, JSON.stringify(summary, null, 2));
|
|
63
|
+
process.stdout.write(`Exported metrics to ${exportPath}\n`);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
process.stdout.write(`Exported metrics (in-memory only): ${JSON.stringify(summary)}\n`);
|
|
66
|
+
}
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function cmdUsage(unknownCmd) {
|
|
71
|
+
process.stdout.write(
|
|
72
|
+
`Usage: metrics-db.mjs [sync|status|export]\n` +
|
|
73
|
+
(unknownCmd ? ` Unknown command: ${unknownCmd}\n` : '')
|
|
74
|
+
);
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ── main ──────────────────────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
const cmd = process.argv[2] || 'sync';
|
|
81
|
+
|
|
82
|
+
switch (cmd) {
|
|
83
|
+
case 'sync': cmdSync(); break;
|
|
84
|
+
case 'status': cmdStatus(); break;
|
|
85
|
+
case 'export': cmdExport(); break;
|
|
86
|
+
default: cmdUsage(cmd); break;
|
|
87
|
+
}
|
|
@@ -2,62 +2,234 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Keyword-based task router for hook-handler.cjs
|
|
4
4
|
* Returns: { agent, agentSlug, confidence, reason, semanticRouting, specificAgents, skillMatches, extrasMatches }
|
|
5
|
+
*
|
|
6
|
+
* Exports:
|
|
7
|
+
* routeTask(prompt) → routing result object
|
|
8
|
+
* routeTaskSemantic(prompt) → alias for routeTask
|
|
9
|
+
* matchSkills(prompt, topN) → array of { skill, invoke, score }
|
|
10
|
+
* matchExtras(prompt, topN) → array of { slug, name, category, score }
|
|
11
|
+
* buildCategoryList() → array of { name, count, examples }
|
|
12
|
+
* getAgentsInCategory(cat) → array of { slug, name }
|
|
13
|
+
* AGENT_CAPABILITIES → { [slug]: string[] }
|
|
14
|
+
* TASK_PATTERNS → { [agentSlug]: RegExp }
|
|
5
15
|
*/
|
|
6
16
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
// ── Dev patterns (slug-correct) ─────────────────────────────────────────────
|
|
18
|
+
// TASK_PATTERNS: { keywords: agentSlug } — exported for introspection
|
|
19
|
+
// Values are agent slugs; keys describe what keywords trigger that agent.
|
|
20
|
+
|
|
21
|
+
var TASK_PATTERNS = {
|
|
22
|
+
'test|spec|coverage|vitest|jest|mocha|e2e': 'tester',
|
|
23
|
+
'review|audit|code quality|lint|refactor|cleanup': 'reviewer',
|
|
24
|
+
'architect|system design|ADR|bounded context|architecture': 'architect',
|
|
25
|
+
'security|vulnerability|CVE|injection|XSS|CSRF|OWASP': 'security-engineer',
|
|
26
|
+
'deploy|CI/CD|docker|kubernetes|infra|devops|helm|terraform': 'devops',
|
|
27
|
+
'document|readme|docs|api reference|jsdoc': 'technical-writer',
|
|
28
|
+
'research|investigate|explore|analyze|survey|compare': 'researcher',
|
|
29
|
+
'plan|roadmap|prioritize|breakdown|estimate': 'planner',
|
|
30
|
+
'mobile|ios|android|react native|flutter': 'mobile-dev',
|
|
31
|
+
'ml|machine learning|neural network|model training': 'ai-engineer',
|
|
32
|
+
'api|rest|graphql|endpoint|http|websocket|grpc|optimize': 'backend-dev',
|
|
33
|
+
'ui|frontend|react|vue|component|css|layout|style': 'frontend-dev',
|
|
34
|
+
'bug|fix|error|feature|implement|build|create|develop': 'coder',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Internal routing patterns (regex per slug for routeTask)
|
|
38
|
+
var _ROUTING_PATTERNS = {
|
|
39
|
+
'tester': /\b(test|tests|spec|coverage|vitest|jest|mocha|e2e)\b/i,
|
|
40
|
+
'reviewer': /\b(review|audit|code quality|lint|smell|refactor|clean up|cleanup)\b/i,
|
|
41
|
+
'architect': /\b(architect|system design|ADR|domain|bounded context|microservice|architecture)\b/i,
|
|
42
|
+
'security-engineer': /\b(security|vulnerability|CVE|injection|XSS|CSRF|OWASP)\b/i,
|
|
43
|
+
'devops': /\b(deploy|CI\/CD|docker|kubernetes|infra|devops|helm|terraform)\b/i,
|
|
44
|
+
'technical-writer': /\b(document|readme|docs|api reference|jsdoc|write up)\b/i,
|
|
45
|
+
'researcher': /\b(research|investigate|explore|analyze|survey|compare)\b/i,
|
|
46
|
+
'planner': /\b(plan|roadmap|prioritize|breakdown|estimate)\b/i,
|
|
47
|
+
'mobile-dev': /\b(mobile|ios|android|react native|flutter)\b/i,
|
|
48
|
+
'ai-engineer': /\b(ml|machine learning|neural network|model training|inference)\b/i,
|
|
49
|
+
'backend-dev': /\b(api|rest|graphql|endpoint|http|websocket|grpc|optimize)\b/i,
|
|
50
|
+
'frontend-dev': /\b(ui|frontend|react|vue|component|css|layout|style)\b/i,
|
|
51
|
+
'coder': /\b(bug|fix|error|exception|crash|broken|fail|regression|feature|implement|add|build|create|develop|new|memory|vector|embedding|hook|swarm|agent|mcp|cli|routing|monomind)\b/i,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
var TASK_CONFIDENCES = {
|
|
55
|
+
'tester': 0.85,
|
|
56
|
+
'reviewer': 0.82,
|
|
57
|
+
'architect': 0.85,
|
|
58
|
+
'security-engineer': 0.90,
|
|
59
|
+
'devops': 0.85,
|
|
60
|
+
'technical-writer': 0.82,
|
|
61
|
+
'researcher': 0.78,
|
|
62
|
+
'planner': 0.80,
|
|
63
|
+
'mobile-dev': 0.88,
|
|
64
|
+
'ai-engineer': 0.85,
|
|
65
|
+
'backend-dev': 0.80,
|
|
66
|
+
'frontend-dev': 0.80,
|
|
67
|
+
'coder': 0.80,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
var TASK_AGENTS = {
|
|
71
|
+
'tester': 'Tester',
|
|
72
|
+
'reviewer': 'Reviewer',
|
|
73
|
+
'architect': 'Architect',
|
|
74
|
+
'security-engineer': 'Security Engineer',
|
|
75
|
+
'devops': 'DevOps',
|
|
76
|
+
'technical-writer': 'Technical Writer',
|
|
77
|
+
'researcher': 'Researcher',
|
|
78
|
+
'planner': 'Planner',
|
|
79
|
+
'mobile-dev': 'Mobile Developer',
|
|
80
|
+
'ai-engineer': 'AI Engineer',
|
|
81
|
+
'backend-dev': 'Backend Developer',
|
|
82
|
+
'frontend-dev': 'Frontend Developer',
|
|
83
|
+
'coder': 'Coder',
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Priority order: higher-priority slugs checked first
|
|
87
|
+
var DEV_PRIORITY = [
|
|
88
|
+
'tester', 'reviewer', 'architect', 'security-engineer', 'devops',
|
|
89
|
+
'mobile-dev', 'ai-engineer', 'frontend-dev', 'backend-dev',
|
|
90
|
+
'researcher', 'planner', 'technical-writer', 'coder',
|
|
25
91
|
];
|
|
26
92
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
93
|
+
// ── AGENT_CAPABILITIES ────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
var AGENT_CAPABILITIES = {
|
|
96
|
+
'coder': ['implement', 'fix', 'build', 'develop', 'create'],
|
|
97
|
+
'tester': ['test', 'spec', 'coverage', 'vitest', 'jest', 'e2e'],
|
|
98
|
+
'reviewer': ['review', 'audit', 'refactor', 'code quality', 'lint'],
|
|
99
|
+
'researcher': ['research', 'investigate', 'analyze', 'survey', 'compare'],
|
|
100
|
+
'architect': ['design', 'architecture', 'ADR', 'domain', 'microservice'],
|
|
101
|
+
'planner': ['plan', 'roadmap', 'strategy', 'prioritize', 'estimate'],
|
|
102
|
+
'security-engineer': ['security', 'vulnerability', 'CVE', 'XSS', 'CSRF'],
|
|
103
|
+
'backend-dev': ['api', 'rest', 'graphql', 'endpoint', 'http', 'grpc'],
|
|
104
|
+
'frontend-dev': ['ui', 'frontend', 'react', 'css', 'component', 'layout'],
|
|
105
|
+
'devops': ['deploy', 'docker', 'kubernetes', 'CI/CD', 'terraform'],
|
|
106
|
+
'mobile-dev': ['mobile', 'ios', 'android', 'react native', 'flutter'],
|
|
107
|
+
'ai-engineer': ['ml', 'machine learning', 'model', 'neural', 'inference'],
|
|
108
|
+
'technical-writer': ['docs', 'readme', 'document', 'jsdoc', 'api reference'],
|
|
36
109
|
};
|
|
37
110
|
|
|
111
|
+
// ── Non-dev domain agent registry ─────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
var DOMAIN_AGENTS = [
|
|
114
|
+
// Marketing
|
|
115
|
+
{ slug: 'content-strategist', name: 'Content Strategist', category: 'marketing',
|
|
116
|
+
keywords: /\b(content|brand|blogging|copywriting|content strategy)\b/i },
|
|
117
|
+
{ slug: 'seo-specialist', name: 'SEO Specialist', category: 'marketing',
|
|
118
|
+
keywords: /\b(seo|search engine|keyword research|backlink|organic traffic)\b/i },
|
|
119
|
+
{ slug: 'social-media-manager', name: 'Social Media Manager', category: 'marketing',
|
|
120
|
+
keywords: /\b(social media|instagram|tiktok|twitter|linkedin|facebook|campaign)\b/i },
|
|
121
|
+
{ slug: 'marketing-analyst', name: 'Marketing Analyst', category: 'marketing',
|
|
122
|
+
keywords: /\b(marketing|advertising|analytics|conversion|funnel|cpm|cpa)\b/i },
|
|
123
|
+
|
|
124
|
+
// Sales
|
|
125
|
+
{ slug: 'sales-strategist', name: 'Sales Strategist', category: 'sales',
|
|
126
|
+
keywords: /\b(sales|crm|lead generation|prospect|quota|sales revenue)\b/i },
|
|
127
|
+
{ slug: 'account-manager', name: 'Account Manager', category: 'sales',
|
|
128
|
+
keywords: /\b(account management|client relationship|upsell|renewal|b2b)\b/i },
|
|
129
|
+
|
|
130
|
+
// Academic
|
|
131
|
+
{ slug: 'academic-researcher', name: 'Academic Researcher', category: 'academic',
|
|
132
|
+
keywords: /\b(anthropolog|ethnograph|kinship|cultural ritual|qualitative study|thesis|dissertation|peer review|academic)\b/i },
|
|
133
|
+
{ slug: 'data-scientist', name: 'Data Scientist', category: 'academic',
|
|
134
|
+
keywords: /\b(statistical analysis|regression|hypothesis|p-value|dataset|R studio)\b/i },
|
|
135
|
+
|
|
136
|
+
// Game development
|
|
137
|
+
{ slug: 'game-developer', name: 'Game Developer', category: 'game-development',
|
|
138
|
+
keywords: /\b(unity|unreal|godot|game engine|shader|sprite|tilemap|game jam)\b/i },
|
|
139
|
+
{ slug: 'game-designer', name: 'Game Designer', category: 'game-development',
|
|
140
|
+
keywords: /\b(game design|game mechanic|level design|player experience|narrative design)\b/i },
|
|
141
|
+
|
|
142
|
+
// Legal / Finance
|
|
143
|
+
{ slug: 'legal-advisor', name: 'Legal Advisor', category: 'legal',
|
|
144
|
+
keywords: /\b(legal|contract|compliance|regulation|gdpr|liability|intellectual property)\b/i },
|
|
145
|
+
{ slug: 'financial-analyst', name: 'Financial Analyst', category: 'finance',
|
|
146
|
+
keywords: /\b(finance|investment|portfolio|valuation|balance sheet|ROI|P&L)\b/i },
|
|
147
|
+
|
|
148
|
+
// HR / Operations
|
|
149
|
+
{ slug: 'hr-specialist', name: 'HR Specialist', category: 'hr',
|
|
150
|
+
keywords: /\b(hiring|recruitment|onboarding|performance review|employee|hr|human resources)\b/i },
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
// Categories that are opt-in (only returned when keywords match)
|
|
154
|
+
var OPT_IN_CATEGORIES = new Set(['academic', 'game-development', 'legal', 'finance', 'hr']);
|
|
155
|
+
|
|
156
|
+
// Marketing is also opt-in — keywords must match
|
|
157
|
+
var MARKETING_OPTIN = /\b(marketing|seo|social media|advertising|campaign|content strategy|tiktok|instagram|brand)\b/i;
|
|
158
|
+
|
|
159
|
+
// ── Skills registry ────────────────────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
var SKILLS = [
|
|
162
|
+
{ skill: 'mastermind', invoke: '/mastermind', description: 'Select swarm topology', keywords: /\b(swarm|topology|hive|mastermind|multi.agent)\b/i },
|
|
163
|
+
{ skill: 'monodesign', invoke: '/monodesign', description: 'Frontend design and UI', keywords: /\b(design|ui|ux|component|visual|layout|css|theme)\b/i },
|
|
164
|
+
{ skill: 'monomotion', invoke: '/monomotion', description: 'Web animations and motion', keywords: /\b(animate|animation|motion|gsap|transition|scroll)\b/i },
|
|
165
|
+
{ skill: 'graphify', invoke: '/graphify', description: 'Input to knowledge graph', keywords: /\b(graph|knowledge|monograph|node|edge|visualize)\b/i },
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
// ── Utilities ─────────────────────────────────────────────────────────────────
|
|
169
|
+
|
|
38
170
|
var MAX_PROMPT = 2000;
|
|
39
171
|
|
|
172
|
+
// ── routeTask ─────────────────────────────────────────────────────────────────
|
|
173
|
+
|
|
40
174
|
function routeTask(prompt) {
|
|
41
|
-
|
|
175
|
+
// Empty / null → confidence 0
|
|
176
|
+
if (!prompt || typeof prompt !== 'string' || prompt.trim() === '') {
|
|
177
|
+
return {
|
|
178
|
+
agent: 'coder',
|
|
179
|
+
agentSlug: 'coder',
|
|
180
|
+
confidence: 0,
|
|
181
|
+
reason: 'Default routing — empty input',
|
|
182
|
+
semanticRouting: false,
|
|
183
|
+
specificAgents: [],
|
|
184
|
+
skillMatches: [],
|
|
185
|
+
extrasMatches: [],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
42
189
|
var safePrompt = prompt.slice(0, MAX_PROMPT);
|
|
43
190
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
191
|
+
// Check non-dev domain agents first (only if opt-in keywords match)
|
|
192
|
+
var extras = matchExtras(safePrompt);
|
|
193
|
+
if (extras.length > 0) {
|
|
194
|
+
var topExtra = extras[0];
|
|
195
|
+
return {
|
|
196
|
+
agent: topExtra.name,
|
|
197
|
+
agentSlug: topExtra.slug,
|
|
198
|
+
confidence: 0.80,
|
|
199
|
+
reason: 'Domain: ' + topExtra.category,
|
|
200
|
+
semanticRouting: false,
|
|
201
|
+
specificAgents: extras,
|
|
202
|
+
skillMatches: [],
|
|
203
|
+
extrasMatches: extras,
|
|
204
|
+
};
|
|
52
205
|
}
|
|
53
206
|
|
|
54
|
-
|
|
207
|
+
// Check dev patterns in priority order
|
|
208
|
+
for (var i = 0; i < DEV_PRIORITY.length; i++) {
|
|
209
|
+
var slug = DEV_PRIORITY[i];
|
|
210
|
+
var pattern = _ROUTING_PATTERNS[slug];
|
|
211
|
+
if (pattern && pattern.test(safePrompt)) {
|
|
212
|
+
var confidence = TASK_CONFIDENCES[slug] || 0.75;
|
|
213
|
+
var skills = matchSkills(safePrompt);
|
|
214
|
+
return {
|
|
215
|
+
agent: TASK_AGENTS[slug],
|
|
216
|
+
agentSlug: slug,
|
|
217
|
+
confidence: confidence,
|
|
218
|
+
reason: ('Keyword match: ' + slug).slice(0, 80),
|
|
219
|
+
semanticRouting: false,
|
|
220
|
+
specificAgents: [{ slug: slug, name: TASK_AGENTS[slug], confidence: confidence }],
|
|
221
|
+
skillMatches: skills,
|
|
222
|
+
extrasMatches: [],
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
55
226
|
|
|
227
|
+
// No match → default
|
|
56
228
|
return {
|
|
57
|
-
agent:
|
|
58
|
-
agentSlug:
|
|
59
|
-
confidence:
|
|
60
|
-
reason:
|
|
229
|
+
agent: 'coder',
|
|
230
|
+
agentSlug: 'coder',
|
|
231
|
+
confidence: 0.5,
|
|
232
|
+
reason: 'Default routing — no strong keyword match',
|
|
61
233
|
semanticRouting: false,
|
|
62
234
|
specificAgents: [],
|
|
63
235
|
skillMatches: [],
|
|
@@ -65,7 +237,90 @@ function routeTask(prompt) {
|
|
|
65
237
|
};
|
|
66
238
|
}
|
|
67
239
|
|
|
240
|
+
// ── matchSkills ────────────────────────────────────────────────────────────────
|
|
241
|
+
|
|
242
|
+
function matchSkills(prompt, topN) {
|
|
243
|
+
if (!prompt || typeof prompt !== 'string') return [];
|
|
244
|
+
topN = topN || 5;
|
|
245
|
+
var safePrompt = prompt.slice(0, MAX_PROMPT);
|
|
246
|
+
|
|
247
|
+
var scored = [];
|
|
248
|
+
for (var i = 0; i < SKILLS.length; i++) {
|
|
249
|
+
var s = SKILLS[i];
|
|
250
|
+
if (s.keywords.test(safePrompt)) {
|
|
251
|
+
scored.push({ skill: s.skill, invoke: s.invoke, description: s.description, score: 1.0 });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
scored.sort(function(a, b) { return b.score - a.score; });
|
|
256
|
+
return scored.slice(0, topN);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// ── matchExtras ────────────────────────────────────────────────────────────────
|
|
260
|
+
|
|
261
|
+
function matchExtras(prompt, topN) {
|
|
262
|
+
if (!prompt || typeof prompt !== 'string') return [];
|
|
263
|
+
topN = topN || 8;
|
|
264
|
+
var safePrompt = prompt.slice(0, MAX_PROMPT);
|
|
265
|
+
|
|
266
|
+
var scored = [];
|
|
267
|
+
for (var i = 0; i < DOMAIN_AGENTS.length; i++) {
|
|
268
|
+
var a = DOMAIN_AGENTS[i];
|
|
269
|
+
if (!a.keywords.test(safePrompt)) continue;
|
|
270
|
+
|
|
271
|
+
// Opt-in categories: only include if keywords explicitly match
|
|
272
|
+
if (OPT_IN_CATEGORIES.has(a.category)) {
|
|
273
|
+
scored.push({ slug: a.slug, name: a.name, category: a.category, score: 1.0 });
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Marketing is opt-in too
|
|
278
|
+
if (a.category === 'marketing') {
|
|
279
|
+
if (MARKETING_OPTIN.test(safePrompt)) {
|
|
280
|
+
scored.push({ slug: a.slug, name: a.name, category: a.category, score: 1.0 });
|
|
281
|
+
}
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Sales: only include when sales keywords match
|
|
286
|
+
scored.push({ slug: a.slug, name: a.name, category: a.category, score: 1.0 });
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
scored.sort(function(a, b) { return b.score - a.score; });
|
|
290
|
+
return scored.slice(0, topN);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ── buildCategoryList ─────────────────────────────────────────────────────────
|
|
294
|
+
|
|
295
|
+
function buildCategoryList() {
|
|
296
|
+
var catMap = {};
|
|
297
|
+
for (var i = 0; i < DOMAIN_AGENTS.length; i++) {
|
|
298
|
+
var a = DOMAIN_AGENTS[i];
|
|
299
|
+
if (!catMap[a.category]) catMap[a.category] = { name: a.category, count: 0, examples: [] };
|
|
300
|
+
catMap[a.category].count++;
|
|
301
|
+
if (catMap[a.category].examples.length < 3) catMap[a.category].examples.push(a.slug);
|
|
302
|
+
}
|
|
303
|
+
return Object.values(catMap);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ── getAgentsInCategory ────────────────────────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
function getAgentsInCategory(category) {
|
|
309
|
+
if (!category || typeof category !== 'string') return [];
|
|
310
|
+
return DOMAIN_AGENTS
|
|
311
|
+
.filter(function(a) { return a.category === category; })
|
|
312
|
+
.map(function(a) { return { slug: a.slug, name: a.name }; });
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ── exports ────────────────────────────────────────────────────────────────────
|
|
316
|
+
|
|
68
317
|
module.exports = {
|
|
69
318
|
routeTask: routeTask,
|
|
70
319
|
routeTaskSemantic: routeTask,
|
|
320
|
+
matchSkills: matchSkills,
|
|
321
|
+
matchExtras: matchExtras,
|
|
322
|
+
buildCategoryList: buildCategoryList,
|
|
323
|
+
getAgentsInCategory: getAgentsInCategory,
|
|
324
|
+
AGENT_CAPABILITIES: AGENT_CAPABILITIES,
|
|
325
|
+
TASK_PATTERNS: TASK_PATTERNS,
|
|
71
326
|
};
|
|
@@ -1,65 +1,140 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/**
|
|
3
3
|
* Session state management for hook-handler.cjs
|
|
4
|
-
* Persists session data to .monomind/
|
|
4
|
+
* Persists session data to .git/monomind/sessions/current.json (branch-agnostic,
|
|
5
|
+
* shared across git worktrees). Falls back to .monomind/sessions/ if not in a git repo.
|
|
6
|
+
*
|
|
7
|
+
* API: start(), restore(), end(), status(), metric(key), update(patch)
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
10
|
const path = require('path');
|
|
8
11
|
const fs = require('fs');
|
|
9
12
|
|
|
10
13
|
const CWD = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
11
|
-
|
|
14
|
+
|
|
15
|
+
function getMonoDir(workDir) {
|
|
16
|
+
try {
|
|
17
|
+
const gitEntry = path.join(workDir, '.git');
|
|
18
|
+
const st = fs.statSync(gitEntry);
|
|
19
|
+
if (st.isDirectory()) return path.join(gitEntry, 'monomind');
|
|
20
|
+
if (st.isFile()) {
|
|
21
|
+
const m = fs.readFileSync(gitEntry, 'utf8').match(/^gitdir:\s*(.+)/m);
|
|
22
|
+
if (m) {
|
|
23
|
+
const worktreeDir = path.resolve(workDir, m[1].trim());
|
|
24
|
+
return path.join(path.dirname(path.dirname(worktreeDir)), 'monomind');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} catch {}
|
|
28
|
+
return path.join(workDir, '.monomind');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const SESSIONS_DIR = path.join(getMonoDir(CWD), 'sessions');
|
|
32
|
+
const CURRENT_FILE = path.join(SESSIONS_DIR, 'current.json');
|
|
33
|
+
|
|
34
|
+
var KNOWN_METRICS = new Set(['edits', 'commands', 'tasks', 'errors']);
|
|
12
35
|
|
|
13
36
|
function ensureDir() {
|
|
14
|
-
try { fs.mkdirSync(
|
|
37
|
+
try { fs.mkdirSync(SESSIONS_DIR, { recursive: true }); } catch (_) {}
|
|
15
38
|
}
|
|
16
39
|
|
|
17
|
-
function
|
|
40
|
+
function readCurrent() {
|
|
18
41
|
try {
|
|
19
|
-
if (!fs.existsSync(
|
|
20
|
-
var st = fs.statSync(
|
|
21
|
-
if (st.size > 1 * 1024 * 1024) return null;
|
|
22
|
-
var raw = fs.readFileSync(
|
|
42
|
+
if (!fs.existsSync(CURRENT_FILE)) return null;
|
|
43
|
+
var st = fs.statSync(CURRENT_FILE);
|
|
44
|
+
if (st.size > 1 * 1024 * 1024) return null;
|
|
45
|
+
var raw = fs.readFileSync(CURRENT_FILE, 'utf-8');
|
|
23
46
|
var data = JSON.parse(raw);
|
|
24
|
-
if (!data || !data.
|
|
25
|
-
console.log('[OK] Session restored: ' + data.sessionId);
|
|
47
|
+
if (!data || !data.id) return null;
|
|
26
48
|
return data;
|
|
27
49
|
} catch (_) {
|
|
28
50
|
return null;
|
|
29
51
|
}
|
|
30
52
|
}
|
|
31
53
|
|
|
32
|
-
function
|
|
54
|
+
function writeCurrent(data) {
|
|
33
55
|
ensureDir();
|
|
34
|
-
var sessionId = 'session-' + Date.now();
|
|
35
|
-
var data = { sessionId: sessionId, startedAt: new Date().toISOString(), editCount: 0, taskCount: 0 };
|
|
36
56
|
try {
|
|
37
|
-
fs.writeFileSync(
|
|
38
|
-
console.log('[OK] Session started: ' + sessionId);
|
|
57
|
+
fs.writeFileSync(CURRENT_FILE, JSON.stringify(data, null, 2), 'utf-8');
|
|
39
58
|
} catch (_) {}
|
|
40
|
-
return data;
|
|
41
59
|
}
|
|
42
60
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
61
|
+
// ── start ──────────────────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
function start() {
|
|
64
|
+
var sessionId = 'session-' + Date.now();
|
|
65
|
+
var sess = {
|
|
66
|
+
id: sessionId,
|
|
67
|
+
startedAt: new Date().toISOString(),
|
|
68
|
+
context: {},
|
|
69
|
+
metrics: { edits: 0, commands: 0, tasks: 0, errors: 0 },
|
|
70
|
+
};
|
|
71
|
+
writeCurrent(sess);
|
|
72
|
+
return sess;
|
|
53
73
|
}
|
|
54
74
|
|
|
75
|
+
// ── restore ────────────────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
function restore() {
|
|
78
|
+
var data = readCurrent();
|
|
79
|
+
if (!data) return null;
|
|
80
|
+
data.restoredAt = new Date().toISOString();
|
|
81
|
+
writeCurrent(data);
|
|
82
|
+
return data;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── end ────────────────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
55
87
|
function end() {
|
|
88
|
+
var data = readCurrent();
|
|
89
|
+
if (!data) return null;
|
|
90
|
+
|
|
91
|
+
var startTs = new Date(data.startedAt).getTime();
|
|
92
|
+
var endTs = Date.now();
|
|
93
|
+
var duration = endTs - startTs;
|
|
94
|
+
|
|
95
|
+
var archived = Object.assign({}, data, {
|
|
96
|
+
endedAt: new Date(endTs).toISOString(),
|
|
97
|
+
duration: duration,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Archive to <session-id>.json
|
|
56
101
|
try {
|
|
57
|
-
var
|
|
58
|
-
|
|
59
|
-
update({ endedAt: new Date().toISOString() });
|
|
60
|
-
console.log('[OK] Session ended: ' + data.sessionId);
|
|
61
|
-
}
|
|
102
|
+
var archivePath = path.join(SESSIONS_DIR, data.id + '.json');
|
|
103
|
+
fs.writeFileSync(archivePath, JSON.stringify(archived, null, 2), 'utf-8');
|
|
62
104
|
} catch (_) {}
|
|
105
|
+
|
|
106
|
+
// Remove current.json
|
|
107
|
+
try { fs.unlinkSync(CURRENT_FILE); } catch (_) {}
|
|
108
|
+
|
|
109
|
+
return archived;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── status ─────────────────────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
function status() {
|
|
115
|
+
return readCurrent();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── metric ─────────────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
function metric(key) {
|
|
121
|
+
var data = readCurrent();
|
|
122
|
+
if (!data) return null;
|
|
123
|
+
if (!KNOWN_METRICS.has(key)) return data;
|
|
124
|
+
|
|
125
|
+
data.metrics[key] = (data.metrics[key] || 0) + 1;
|
|
126
|
+
writeCurrent(data);
|
|
127
|
+
return data;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── update ─────────────────────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
function update(patch) {
|
|
133
|
+
var existing = readCurrent();
|
|
134
|
+
if (!existing) return null;
|
|
135
|
+
var merged = Object.assign({}, existing, patch, { updatedAt: new Date().toISOString() });
|
|
136
|
+
writeCurrent(merged);
|
|
137
|
+
return merged;
|
|
63
138
|
}
|
|
64
139
|
|
|
65
|
-
module.exports = { restore,
|
|
140
|
+
module.exports = { start, restore, end, status, metric, update };
|