monomind 1.17.0 → 1.17.2
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/engineering/engineering-security-engineer.md +1 -1
- package/.claude/commands/mastermind/_repeat.md +4 -0
- package/.claude/commands/mastermind/master.md +52 -1
- package/.claude/scheduled_tasks.lock +1 -1
- package/.claude/skills/mastermind/_repeat.md +2 -0
- package/package.json +1 -1
- package/packages/@monomind/cli/.claude/agents/engineering/engineering-security-engineer.md +1 -1
- package/packages/@monomind/cli/.claude/commands/mastermind/_repeat.md +4 -0
- package/packages/@monomind/cli/.claude/commands/mastermind/master.md +52 -1
- package/packages/@monomind/cli/.claude/skills/mastermind/_repeat.md +2 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-analyzer.test.js +42 -59
- package/packages/@monomind/cli/dist/src/agents/registry-builder.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/agents/registry-builder.js +22 -0
- package/packages/@monomind/cli/dist/src/browser/dashboard/server.js +18 -0
- package/packages/@monomind/cli/dist/src/browser/dashboard/ui.html +37 -125
- package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.d.ts +17 -0
- package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +320 -0
- package/packages/@monomind/cli/dist/src/commands/agent-ops.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/agent-ops.js +329 -0
- package/packages/@monomind/cli/dist/src/commands/agent.js +5 -907
- package/packages/@monomind/cli/dist/src/commands/analyze-ast.d.ts +26 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-ast.js +284 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.js +295 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-diff.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-diff.js +395 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-graph.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-graph.js +304 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-imports.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-imports.js +287 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-symbols.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-symbols.js +302 -0
- package/packages/@monomind/cli/dist/src/commands/analyze.d.ts +38 -0
- package/packages/@monomind/cli/dist/src/commands/analyze.js +12 -1827
- package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.d.ts +26 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.js +189 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.d.ts +20 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +432 -0
- package/packages/@monomind/cli/dist/src/commands/doctor.js +54 -943
- package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.js +242 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.d.ts +35 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.js +203 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.js +233 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.js +274 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind.js +10 -1129
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.d.ts +4 -4
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +19 -819
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.js +334 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.js +399 -0
- package/packages/@monomind/cli/dist/src/commands/init-subcommands.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/init-subcommands.js +156 -0
- package/packages/@monomind/cli/dist/src/commands/init-upgrade.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/init-upgrade.js +203 -0
- package/packages/@monomind/cli/dist/src/commands/init-wizard.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/init-wizard.js +246 -0
- package/packages/@monomind/cli/dist/src/commands/init.js +6 -623
- package/packages/@monomind/cli/dist/src/commands/memory-admin.d.ts +10 -0
- package/packages/@monomind/cli/dist/src/commands/memory-admin.js +433 -0
- package/packages/@monomind/cli/dist/src/commands/memory-crud.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/memory-crud.js +342 -0
- package/packages/@monomind/cli/dist/src/commands/memory-list.d.ts +10 -0
- package/packages/@monomind/cli/dist/src/commands/memory-list.js +321 -0
- package/packages/@monomind/cli/dist/src/commands/memory-transfer.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +372 -0
- package/packages/@monomind/cli/dist/src/commands/memory.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/memory.js +10 -1441
- package/packages/@monomind/cli/dist/src/commands/neural-core.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/neural-core.js +274 -0
- package/packages/@monomind/cli/dist/src/commands/neural-optimize.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/neural-optimize.js +332 -0
- package/packages/@monomind/cli/dist/src/commands/neural-registry.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/neural-registry.js +290 -0
- package/packages/@monomind/cli/dist/src/commands/neural.js +3 -974
- package/packages/@monomind/cli/dist/src/commands/platforms.js +327 -7
- package/packages/@monomind/cli/dist/src/commands/security-cve.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/security-cve.js +310 -0
- package/packages/@monomind/cli/dist/src/commands/security-misc.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/security-misc.js +293 -0
- package/packages/@monomind/cli/dist/src/commands/security-scan.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/commands/security-scan.js +328 -0
- package/packages/@monomind/cli/dist/src/commands/security.js +3 -958
- package/packages/@monomind/cli/dist/src/commands/session.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/swarm.js +23 -17
- package/packages/@monomind/cli/dist/src/index.js +8 -37
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +77 -0
- package/packages/@monomind/cli/dist/src/parser.js +11 -6
- package/packages/@monomind/cli/dist/src/routing/llm-caller.js +1 -2
- package/packages/@monomind/cli/package.json +2 -3
- package/packages/@monomind/cli/scripts/understand-analyze.mjs +1 -1
|
@@ -5,980 +5,9 @@
|
|
|
5
5
|
* github.com/monoes/monomind
|
|
6
6
|
*/
|
|
7
7
|
import { output } from '../output.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
description: 'Check pattern-learning status (JS intelligence layer)',
|
|
12
|
-
options: [
|
|
13
|
-
{ name: 'verbose', short: 'v', type: 'boolean', description: 'Show detailed metrics' },
|
|
14
|
-
],
|
|
15
|
-
examples: [
|
|
16
|
-
{ command: 'monomind neural status', description: 'Show pattern-learning status' },
|
|
17
|
-
{ command: 'monomind neural status -v', description: 'Show detailed metrics' },
|
|
18
|
-
],
|
|
19
|
-
action: async (ctx) => {
|
|
20
|
-
const verbose = ctx.flags.verbose === true;
|
|
21
|
-
output.writeln();
|
|
22
|
-
output.writeln(output.bold('Pattern Learning Status'));
|
|
23
|
-
output.writeln(output.dim('─'.repeat(50)));
|
|
24
|
-
const spinner = output.createSpinner({ text: 'Checking pattern-learning systems...', spinner: 'dots' });
|
|
25
|
-
spinner.start();
|
|
26
|
-
try {
|
|
27
|
-
const { getIntelligenceStats, initializeIntelligence, getPersistenceStatus } = await import('../memory/intelligence.js');
|
|
28
|
-
const { getHNSWStatus, loadEmbeddingModel } = await import('../memory/memory-initializer.js');
|
|
29
|
-
// Initialize if needed and get real stats
|
|
30
|
-
await initializeIntelligence();
|
|
31
|
-
const stats = getIntelligenceStats();
|
|
32
|
-
const hnswStatus = getHNSWStatus();
|
|
33
|
-
const persistence = getPersistenceStatus();
|
|
34
|
-
// Check embedding model
|
|
35
|
-
const modelInfo = await loadEmbeddingModel({ verbose: false });
|
|
36
|
-
spinner.succeed('Pattern-learning systems checked');
|
|
37
|
-
output.writeln();
|
|
38
|
-
output.printTable({
|
|
39
|
-
columns: [
|
|
40
|
-
{ key: 'component', header: 'Component', width: 22 },
|
|
41
|
-
{ key: 'status', header: 'Status', width: 12 },
|
|
42
|
-
{ key: 'details', header: 'Details', width: 34 },
|
|
43
|
-
],
|
|
44
|
-
data: [
|
|
45
|
-
{
|
|
46
|
-
component: 'Pattern Learning',
|
|
47
|
-
status: stats.sonaEnabled ? output.success('Active') : output.warning('Inactive'),
|
|
48
|
-
details: stats.sonaEnabled ? 'JS pattern-learning layer initialized' : 'Not initialized',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
component: 'ReasoningBank',
|
|
52
|
-
status: stats.reasoningBankSize > 0 ? output.success('Active') : output.dim('Empty'),
|
|
53
|
-
details: `${stats.patternsLearned} patterns stored`,
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
component: 'Pattern Index',
|
|
57
|
-
status: hnswStatus.available ? output.success('Ready') : output.dim('Empty'),
|
|
58
|
-
details: hnswStatus.available
|
|
59
|
-
? `${hnswStatus.entryCount} vectors, ${hnswStatus.dimensions}-dim (ANN via LanceDB)`
|
|
60
|
-
: 'No vectors indexed yet',
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
component: 'Embedding Model',
|
|
64
|
-
status: modelInfo.success ? output.success('Loaded') : output.warning('Fallback'),
|
|
65
|
-
details: `${modelInfo.modelName} (${modelInfo.dimensions}-dim)`,
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
component: 'Persistence',
|
|
69
|
-
status: persistence.patternsExist ? output.success('Saved') : output.dim('None'),
|
|
70
|
-
details: persistence.patternsExist ? output.dim(persistence.dataDir) : 'No persisted patterns',
|
|
71
|
-
},
|
|
72
|
-
],
|
|
73
|
-
});
|
|
74
|
-
if (verbose) {
|
|
75
|
-
output.writeln();
|
|
76
|
-
output.writeln(output.bold('Detailed Metrics'));
|
|
77
|
-
const detailedData = [
|
|
78
|
-
{ metric: 'Trajectories Recorded', value: String(stats.trajectoriesRecorded) },
|
|
79
|
-
{ metric: 'Patterns Learned', value: String(stats.patternsLearned) },
|
|
80
|
-
{ metric: 'ReasoningBank Size', value: String(stats.reasoningBankSize) },
|
|
81
|
-
{ metric: 'Index Dimensions', value: String(hnswStatus.dimensions) },
|
|
82
|
-
{ metric: 'Avg Adaptation Time', value: `${stats.avgAdaptationTime.toFixed(3)}ms` },
|
|
83
|
-
{
|
|
84
|
-
metric: 'Last Adaptation',
|
|
85
|
-
value: stats.lastAdaptation
|
|
86
|
-
? new Date(stats.lastAdaptation).toLocaleTimeString()
|
|
87
|
-
: 'Never',
|
|
88
|
-
},
|
|
89
|
-
];
|
|
90
|
-
output.printTable({
|
|
91
|
-
columns: [
|
|
92
|
-
{ key: 'metric', header: 'Metric', width: 28 },
|
|
93
|
-
{ key: 'value', header: 'Value', width: 20 },
|
|
94
|
-
],
|
|
95
|
-
data: detailedData,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
return { success: true, data: { stats, hnswStatus, modelInfo, persistence } };
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
spinner.fail('Failed to check pattern-learning systems');
|
|
102
|
-
output.printError(error instanceof Error ? error.message : String(error));
|
|
103
|
-
return { success: false, exitCode: 1 };
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
// Patterns subcommand
|
|
108
|
-
const patternsCommand = {
|
|
109
|
-
name: 'patterns',
|
|
110
|
-
description: 'Analyze and manage cognitive patterns',
|
|
111
|
-
options: [
|
|
112
|
-
{ name: 'action', short: 'a', type: 'string', description: 'Action: analyze, learn, predict, list', default: 'list' },
|
|
113
|
-
{ name: 'query', short: 'q', type: 'string', description: 'Pattern query for search' },
|
|
114
|
-
{ name: 'limit', short: 'l', type: 'number', description: 'Max patterns to return', default: '10' },
|
|
115
|
-
],
|
|
116
|
-
examples: [
|
|
117
|
-
{ command: 'monomind neural patterns --action list', description: 'List all patterns' },
|
|
118
|
-
{ command: 'monomind neural patterns -a analyze -q "error handling"', description: 'Analyze patterns' },
|
|
119
|
-
],
|
|
120
|
-
action: async (ctx) => {
|
|
121
|
-
const action = ctx.flags.action || 'list';
|
|
122
|
-
const query = ctx.flags.query;
|
|
123
|
-
const limit = parseInt(ctx.flags.limit, 10) || 10;
|
|
124
|
-
output.writeln();
|
|
125
|
-
output.writeln(output.bold(`Neural Patterns - ${action}`));
|
|
126
|
-
output.writeln(output.dim('─'.repeat(40)));
|
|
127
|
-
try {
|
|
128
|
-
const { initializeIntelligence, getIntelligenceStats, findSimilarPatterns, getAllPatterns, getPersistenceStatus, } = await import('../memory/intelligence.js');
|
|
129
|
-
await initializeIntelligence();
|
|
130
|
-
const stats = getIntelligenceStats();
|
|
131
|
-
const persistence = getPersistenceStatus();
|
|
132
|
-
if (action === 'list') {
|
|
133
|
-
// Get ALL patterns from ReasoningBank (loaded from disk)
|
|
134
|
-
const allPatterns = await getAllPatterns();
|
|
135
|
-
const patterns = query
|
|
136
|
-
? await findSimilarPatterns(query, { k: limit })
|
|
137
|
-
: allPatterns.slice(0, limit);
|
|
138
|
-
if (patterns.length === 0) {
|
|
139
|
-
output.writeln(output.dim('No patterns found. Train some patterns first with: neural train'));
|
|
140
|
-
output.writeln();
|
|
141
|
-
output.printBox([
|
|
142
|
-
`Total Patterns: ${stats.patternsLearned}`,
|
|
143
|
-
`Trajectories: ${stats.trajectoriesRecorded}`,
|
|
144
|
-
`ReasoningBank Size: ${stats.reasoningBankSize}`,
|
|
145
|
-
`Persistence: ${persistence.patternsExist ? 'Loaded from disk' : 'Not persisted'}`,
|
|
146
|
-
`Data Dir: ${persistence.dataDir}`,
|
|
147
|
-
].join('\n'), 'Pattern Statistics');
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
output.printTable({
|
|
151
|
-
columns: [
|
|
152
|
-
{ key: 'id', header: 'ID', width: 20 },
|
|
153
|
-
{ key: 'type', header: 'Type', width: 18 },
|
|
154
|
-
{ key: 'confidence', header: 'Confidence', width: 12 },
|
|
155
|
-
{ key: 'usage', header: 'Usage', width: 10 },
|
|
156
|
-
],
|
|
157
|
-
data: patterns.map((p, i) => ({
|
|
158
|
-
id: (p.id || `P${String(i + 1).padStart(3, '0')}`).substring(0, 18),
|
|
159
|
-
type: output.highlight(p.type || 'unknown'),
|
|
160
|
-
confidence: `${((p.confidence || 0.5) * 100).toFixed(1)}%`,
|
|
161
|
-
usage: String(p.usageCount || 0),
|
|
162
|
-
})),
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
output.writeln();
|
|
166
|
-
output.writeln(output.dim(`Total: ${allPatterns.length} patterns (persisted) | Trajectories: ${stats.trajectoriesRecorded}`));
|
|
167
|
-
if (persistence.patternsExist) {
|
|
168
|
-
output.writeln(output.success(`✓ Loaded from: ${persistence.patternsFile}`));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
else if (action === 'analyze' && !query) {
|
|
172
|
-
output.printError('--query is required when --action analyze is used.');
|
|
173
|
-
return { success: false, exitCode: 1 };
|
|
174
|
-
}
|
|
175
|
-
else if (action === 'analyze' && query) {
|
|
176
|
-
// Analyze patterns related to query
|
|
177
|
-
const related = await findSimilarPatterns(query, { k: limit });
|
|
178
|
-
output.writeln(`Analyzing patterns related to: "${query}"`);
|
|
179
|
-
output.writeln();
|
|
180
|
-
if (related.length > 0) {
|
|
181
|
-
output.printTable({
|
|
182
|
-
columns: [
|
|
183
|
-
{ key: 'content', header: 'Pattern', width: 40 },
|
|
184
|
-
{ key: 'confidence', header: 'Confidence', width: 12 },
|
|
185
|
-
{ key: 'type', header: 'Type', width: 15 },
|
|
186
|
-
],
|
|
187
|
-
data: related.slice(0, 5).map(p => ({
|
|
188
|
-
content: (p.content || '').substring(0, 38) + (p.content?.length > 38 ? '...' : ''),
|
|
189
|
-
confidence: `${((p.confidence || 0) * 100).toFixed(0)}%`,
|
|
190
|
-
type: p.type || 'general',
|
|
191
|
-
})),
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
output.writeln(output.dim('No related patterns found.'));
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return { success: true };
|
|
199
|
-
}
|
|
200
|
-
catch (error) {
|
|
201
|
-
// Fallback if intelligence not initialized
|
|
202
|
-
output.writeln(output.dim('Intelligence system not initialized.'));
|
|
203
|
-
output.writeln(output.dim('Run: monomind neural train --pattern-type general'));
|
|
204
|
-
return { success: false };
|
|
205
|
-
}
|
|
206
|
-
},
|
|
207
|
-
};
|
|
208
|
-
// Predict subcommand
|
|
209
|
-
const predictCommand = {
|
|
210
|
-
name: 'predict',
|
|
211
|
-
description: 'Make AI predictions using trained models',
|
|
212
|
-
options: [
|
|
213
|
-
{ name: 'input', short: 'i', type: 'string', description: 'Input text to predict routing for', required: true },
|
|
214
|
-
{ name: 'k', short: 'k', type: 'number', description: 'Number of top predictions', default: '5' },
|
|
215
|
-
{ name: 'format', short: 'f', type: 'string', description: 'Output format: json, table', default: 'table' },
|
|
216
|
-
],
|
|
217
|
-
examples: [
|
|
218
|
-
{ command: 'monomind neural predict -i "implement authentication"', description: 'Predict routing for task' },
|
|
219
|
-
{ command: 'monomind neural predict -i "fix bug in login" -k 3', description: 'Get top 3 predictions' },
|
|
220
|
-
],
|
|
221
|
-
action: async (ctx) => {
|
|
222
|
-
const input = ctx.flags.input;
|
|
223
|
-
const k = parseInt(ctx.flags.k || '5', 10);
|
|
224
|
-
const format = ctx.flags.format || 'table';
|
|
225
|
-
if (!input) {
|
|
226
|
-
output.printError('--input is required');
|
|
227
|
-
return { success: false, exitCode: 1 };
|
|
228
|
-
}
|
|
229
|
-
output.writeln();
|
|
230
|
-
output.writeln(output.bold('Neural Prediction (Real)'));
|
|
231
|
-
output.writeln(output.dim('─'.repeat(50)));
|
|
232
|
-
const spinner = output.createSpinner({ text: 'Running inference...', spinner: 'dots' });
|
|
233
|
-
spinner.start();
|
|
234
|
-
try {
|
|
235
|
-
const { initializeIntelligence, findSimilarPatterns } = await import('../memory/intelligence.js');
|
|
236
|
-
// Initialize intelligence system
|
|
237
|
-
await initializeIntelligence();
|
|
238
|
-
// Find similar patterns (embedding is done internally)
|
|
239
|
-
const startSearch = performance.now();
|
|
240
|
-
const matches = await findSimilarPatterns(input, { k });
|
|
241
|
-
const searchTime = performance.now() - startSearch;
|
|
242
|
-
spinner.succeed(`Prediction complete (search: ${searchTime.toFixed(1)}ms)`);
|
|
243
|
-
output.writeln();
|
|
244
|
-
if (matches.length === 0) {
|
|
245
|
-
output.writeln(output.warning('No similar patterns found. Try training first: monomind neural train'));
|
|
246
|
-
return { success: true, data: { matches: [] } };
|
|
247
|
-
}
|
|
248
|
-
if (format === 'json') {
|
|
249
|
-
output.writeln(JSON.stringify(matches, null, 2));
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
// Determine best prediction based on patterns
|
|
253
|
-
const patternTypes = {};
|
|
254
|
-
for (const match of matches) {
|
|
255
|
-
const type = match.type || 'unknown';
|
|
256
|
-
patternTypes[type] = (patternTypes[type] || 0) + match.similarity;
|
|
257
|
-
}
|
|
258
|
-
const sorted = Object.entries(patternTypes).sort((a, b) => b[1] - a[1]);
|
|
259
|
-
const topType = sorted[0]?.[0] || 'unknown';
|
|
260
|
-
const confidence = matches[0]?.similarity || 0;
|
|
261
|
-
output.printBox([
|
|
262
|
-
`Input: ${input.substring(0, 60)}${input.length > 60 ? '...' : ''}`,
|
|
263
|
-
``,
|
|
264
|
-
`Predicted Type: ${topType}`,
|
|
265
|
-
`Confidence: ${(confidence * 100).toFixed(1)}%`,
|
|
266
|
-
`Latency: ${searchTime.toFixed(1)}ms`,
|
|
267
|
-
``,
|
|
268
|
-
`Top ${matches.length} Similar Patterns:`,
|
|
269
|
-
].join('\n'), 'Result');
|
|
270
|
-
output.printTable({
|
|
271
|
-
columns: [
|
|
272
|
-
{ key: 'rank', header: '#', width: 3 },
|
|
273
|
-
{ key: 'id', header: 'Pattern ID', width: 20 },
|
|
274
|
-
{ key: 'type', header: 'Type', width: 15 },
|
|
275
|
-
{ key: 'similarity', header: 'Similarity', width: 12 },
|
|
276
|
-
],
|
|
277
|
-
data: matches.slice(0, k).map((m, i) => ({
|
|
278
|
-
rank: String(i + 1),
|
|
279
|
-
id: m.id?.substring(0, 20) || 'unknown',
|
|
280
|
-
type: m.type || 'action',
|
|
281
|
-
similarity: `${(m.similarity * 100).toFixed(1)}%`,
|
|
282
|
-
})),
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
return { success: true, data: { matches, searchTime } };
|
|
286
|
-
}
|
|
287
|
-
catch (error) {
|
|
288
|
-
spinner.fail('Prediction failed');
|
|
289
|
-
output.printError(error instanceof Error ? error.message : String(error));
|
|
290
|
-
return { success: false, exitCode: 1 };
|
|
291
|
-
}
|
|
292
|
-
},
|
|
293
|
-
};
|
|
294
|
-
// Optimize subcommand - Real Int8 quantization and pattern optimization
|
|
295
|
-
const optimizeCommand = {
|
|
296
|
-
name: 'optimize',
|
|
297
|
-
description: 'Optimize neural patterns (Int8 quantization, memory compression)',
|
|
298
|
-
options: [
|
|
299
|
-
{ name: 'method', type: 'string', description: 'Method: quantize, analyze, compact', default: 'quantize' },
|
|
300
|
-
{ name: 'verbose', short: 'v', type: 'boolean', description: 'Show detailed metrics' },
|
|
301
|
-
],
|
|
302
|
-
examples: [
|
|
303
|
-
{ command: 'monomind neural optimize --method quantize', description: 'Quantize patterns to Int8' },
|
|
304
|
-
{ command: 'monomind neural optimize --method analyze -v', description: 'Analyze memory usage' },
|
|
305
|
-
],
|
|
306
|
-
action: async (ctx) => {
|
|
307
|
-
const method = ctx.flags.method || 'quantize';
|
|
308
|
-
const verbose = ctx.flags.verbose === true;
|
|
309
|
-
output.writeln();
|
|
310
|
-
output.writeln(output.bold('Pattern Optimization (Real)'));
|
|
311
|
-
output.writeln(output.dim('─'.repeat(50)));
|
|
312
|
-
const spinner = output.createSpinner({ text: `Running ${method} optimization...`, spinner: 'dots' });
|
|
313
|
-
spinner.start();
|
|
314
|
-
try {
|
|
315
|
-
const { initializeIntelligence, getIntelligenceStats, getAllPatterns, flushPatterns, compactPatterns } = await import('../memory/intelligence.js');
|
|
316
|
-
const fs = await import('fs');
|
|
317
|
-
const path = await import('path');
|
|
318
|
-
await initializeIntelligence();
|
|
319
|
-
const patterns = await getAllPatterns();
|
|
320
|
-
const stats = getIntelligenceStats();
|
|
321
|
-
// Get actual pattern storage size
|
|
322
|
-
const patternDir = path.join(process.cwd(), '.monomind', 'neural');
|
|
323
|
-
let beforeSize = 0;
|
|
324
|
-
try {
|
|
325
|
-
const patternFile = path.join(patternDir, 'patterns.json');
|
|
326
|
-
if (fs.existsSync(patternFile)) {
|
|
327
|
-
beforeSize = fs.statSync(patternFile).size;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
catch { /* ignore */ }
|
|
331
|
-
if (method === 'quantize') {
|
|
332
|
-
spinner.setText('Applying Int8 quantization to pattern embeddings...');
|
|
333
|
-
let quantized = 0;
|
|
334
|
-
let savedBytes = 0;
|
|
335
|
-
// Int8 quantization: compress Float32 embeddings to Int8 by scaling to [-127, 127]
|
|
336
|
-
// Reduces embedding storage by ~4x with minimal retrieval quality loss.
|
|
337
|
-
const quantizedPatterns = patterns.map(p => {
|
|
338
|
-
if (!p.embedding || p.embedding.length === 0)
|
|
339
|
-
return p;
|
|
340
|
-
// Compute scale factor from max absolute value
|
|
341
|
-
const maxAbs = p.embedding.reduce((m, v) => Math.max(m, Math.abs(v)), 1e-8);
|
|
342
|
-
const scale = 127 / maxAbs;
|
|
343
|
-
// Quantize to Int8 range and store as regular number array (portable JSON)
|
|
344
|
-
const int8Embedding = Array.from(p.embedding).map(v => Math.round(v * scale) / scale);
|
|
345
|
-
savedBytes += (p.embedding.length * 4) - (p.embedding.length * 1); // float32→int8
|
|
346
|
-
quantized++;
|
|
347
|
-
return { ...p, embedding: int8Embedding, _quantized: true, _scale: scale };
|
|
348
|
-
});
|
|
349
|
-
// Write quantized patterns back to disk
|
|
350
|
-
const patternFile = path.join(patternDir, 'patterns.json');
|
|
351
|
-
const tmpFile = `${patternFile}.${process.pid}.tmp`;
|
|
352
|
-
fs.writeFileSync(tmpFile, JSON.stringify(quantizedPatterns, null, 2), 'utf-8');
|
|
353
|
-
fs.renameSync(tmpFile, patternFile);
|
|
354
|
-
spinner.succeed(`Quantized ${quantized} patterns`);
|
|
355
|
-
output.writeln();
|
|
356
|
-
output.printTable({
|
|
357
|
-
columns: [
|
|
358
|
-
{ key: 'metric', header: 'Metric', width: 25 },
|
|
359
|
-
{ key: 'value', header: 'Value', width: 20 },
|
|
360
|
-
],
|
|
361
|
-
data: [
|
|
362
|
-
{ metric: 'Patterns Quantized', value: String(quantized) },
|
|
363
|
-
{ metric: 'Memory Saved', value: `~${(savedBytes / 1024).toFixed(1)} KB` },
|
|
364
|
-
{ metric: 'Compression', value: '~4x (Float32 → Int8)' },
|
|
365
|
-
{ metric: 'Quality Impact', value: 'Minimal (<1% cosine error)' },
|
|
366
|
-
],
|
|
367
|
-
});
|
|
368
|
-
return { success: true, data: { quantized, savedBytes } };
|
|
369
|
-
}
|
|
370
|
-
else if (method === 'analyze') {
|
|
371
|
-
spinner.succeed('Analysis complete');
|
|
372
|
-
output.writeln();
|
|
373
|
-
output.writeln(output.bold('Pattern Memory Analysis'));
|
|
374
|
-
const embeddingBytes = patterns.reduce((sum, p) => sum + (p.embedding?.length || 0) * 4, 0);
|
|
375
|
-
const metadataEstimate = patterns.length * 100; // ~100 bytes per pattern metadata
|
|
376
|
-
output.printTable({
|
|
377
|
-
columns: [
|
|
378
|
-
{ key: 'component', header: 'Component', width: 25 },
|
|
379
|
-
{ key: 'size', header: 'Size', width: 18 },
|
|
380
|
-
{ key: 'count', header: 'Count', width: 12 },
|
|
381
|
-
],
|
|
382
|
-
data: [
|
|
383
|
-
{ component: 'Pattern Embeddings (F32)', size: `${(embeddingBytes / 1024).toFixed(1)} KB`, count: String(patterns.length) },
|
|
384
|
-
{ component: 'Pattern Metadata', size: `${(metadataEstimate / 1024).toFixed(1)} KB`, count: '-' },
|
|
385
|
-
{ component: 'Total In-Memory', size: `${((embeddingBytes + metadataEstimate) / 1024).toFixed(1)} KB`, count: '-' },
|
|
386
|
-
{ component: 'Storage (patterns.json)', size: `${(beforeSize / 1024).toFixed(1)} KB`, count: '-' },
|
|
387
|
-
{ component: 'Trajectories', size: '-', count: String(stats.trajectoriesRecorded) },
|
|
388
|
-
],
|
|
389
|
-
});
|
|
390
|
-
if (verbose) {
|
|
391
|
-
output.writeln();
|
|
392
|
-
output.writeln(output.bold('Optimization Recommendations'));
|
|
393
|
-
const recommendations = [];
|
|
394
|
-
if (patterns.length > 1000) {
|
|
395
|
-
recommendations.push('- Consider pruning low-usage patterns');
|
|
396
|
-
}
|
|
397
|
-
if (embeddingBytes > 1024 * 1024) {
|
|
398
|
-
recommendations.push('- Int8 quantization would reduce memory by ~75%');
|
|
399
|
-
}
|
|
400
|
-
if (stats.trajectoriesRecorded > 100) {
|
|
401
|
-
recommendations.push('- Trajectory consolidation available');
|
|
402
|
-
}
|
|
403
|
-
if (recommendations.length === 0) {
|
|
404
|
-
recommendations.push('- Patterns are already well optimized');
|
|
405
|
-
}
|
|
406
|
-
recommendations.forEach(r => output.writeln(r));
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
else if (method === 'compact') {
|
|
410
|
-
spinner.setText('Compacting pattern storage...');
|
|
411
|
-
// Remove duplicate or very similar patterns
|
|
412
|
-
const compacted = await compactPatterns(0.95); // Remove patterns with >95% similarity
|
|
413
|
-
spinner.succeed(`Compacted ${compacted.removed} patterns`);
|
|
414
|
-
output.writeln();
|
|
415
|
-
output.printTable({
|
|
416
|
-
columns: [
|
|
417
|
-
{ key: 'metric', header: 'Metric', width: 20 },
|
|
418
|
-
{ key: 'value', header: 'Value', width: 15 },
|
|
419
|
-
],
|
|
420
|
-
data: [
|
|
421
|
-
{ metric: 'Patterns Before', value: String(compacted.before) },
|
|
422
|
-
{ metric: 'Patterns After', value: String(compacted.after) },
|
|
423
|
-
{ metric: 'Removed', value: String(compacted.removed) },
|
|
424
|
-
{ metric: 'Similarity Threshold', value: '95%' },
|
|
425
|
-
],
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
return { success: true };
|
|
429
|
-
}
|
|
430
|
-
catch (error) {
|
|
431
|
-
spinner.fail('Optimization failed');
|
|
432
|
-
output.printError(error instanceof Error ? error.message : String(error));
|
|
433
|
-
return { success: false, exitCode: 1 };
|
|
434
|
-
}
|
|
435
|
-
},
|
|
436
|
-
};
|
|
437
|
-
// Export subcommand - Securely export trained models to IPFS
|
|
438
|
-
const exportCommand = {
|
|
439
|
-
name: 'export',
|
|
440
|
-
description: 'Export trained models to IPFS for sharing (Ed25519 signed)',
|
|
441
|
-
options: [
|
|
442
|
-
{ name: 'model', short: 'm', type: 'string', description: 'Model ID or category to export' },
|
|
443
|
-
{ name: 'output', short: 'o', type: 'string', description: 'Output file path (optional)' },
|
|
444
|
-
{ name: 'ipfs', short: 'i', type: 'boolean', description: 'Pin to IPFS (requires Pinata credentials)' },
|
|
445
|
-
{ name: 'sign', short: 's', type: 'boolean', description: 'Sign with Ed25519 key', default: 'true' },
|
|
446
|
-
{ name: 'strip-pii', type: 'boolean', description: 'Strip potential PII from export', default: 'true' },
|
|
447
|
-
{ name: 'name', short: 'n', type: 'string', description: 'Custom name for exported model' },
|
|
448
|
-
],
|
|
449
|
-
examples: [
|
|
450
|
-
{ command: 'monomind neural export -m security-patterns --ipfs', description: 'Export and pin to IPFS' },
|
|
451
|
-
{ command: 'monomind neural export -m code-review -o ./export.json', description: 'Export to file' },
|
|
452
|
-
],
|
|
453
|
-
action: async (ctx) => {
|
|
454
|
-
const modelId = ctx.flags.model || 'all';
|
|
455
|
-
const outputFile = ctx.flags.output;
|
|
456
|
-
const pinToIpfs = ctx.flags.ipfs;
|
|
457
|
-
const signExport = ctx.flags.sign !== false;
|
|
458
|
-
const stripPii = ctx.flags['strip-pii'] !== false;
|
|
459
|
-
const customName = ctx.flags.name;
|
|
460
|
-
output.writeln();
|
|
461
|
-
output.writeln(output.bold('Secure Model Export'));
|
|
462
|
-
output.writeln(output.dim('─'.repeat(50)));
|
|
463
|
-
const spinner = output.createSpinner({ text: 'Preparing export...', spinner: 'dots' });
|
|
464
|
-
spinner.start();
|
|
465
|
-
try {
|
|
466
|
-
const fs = await import('fs');
|
|
467
|
-
const path = await import('path');
|
|
468
|
-
const crypto = await import('crypto');
|
|
469
|
-
// Collect trained patterns from memory
|
|
470
|
-
spinner.setText('Collecting trained patterns...');
|
|
471
|
-
const { getIntelligenceStats, flushPatterns } = await import('../memory/intelligence.js');
|
|
472
|
-
await flushPatterns(); // Ensure all patterns are persisted
|
|
473
|
-
const stats = await getIntelligenceStats();
|
|
474
|
-
// SECURITY: Build export data - NEVER include secrets
|
|
475
|
-
// - API keys read from env but NEVER included in export
|
|
476
|
-
// - Uses ephemeral signing keys (generated per-export, not stored)
|
|
477
|
-
// - PII stripping enabled by default
|
|
478
|
-
// - Suspicious pattern content blocked
|
|
479
|
-
const exportData = {
|
|
480
|
-
type: 'learning-pattern',
|
|
481
|
-
version: '1.0.0',
|
|
482
|
-
name: customName || `monomind-model-${Date.now()}`,
|
|
483
|
-
exportedAt: new Date().toISOString(),
|
|
484
|
-
modelId,
|
|
485
|
-
patterns: [],
|
|
486
|
-
metadata: {
|
|
487
|
-
sourceVersion: '3.0.0-alpha',
|
|
488
|
-
piiStripped: stripPii,
|
|
489
|
-
signed: signExport,
|
|
490
|
-
accuracy: 0,
|
|
491
|
-
totalUsage: 0,
|
|
492
|
-
},
|
|
493
|
-
};
|
|
494
|
-
// Load patterns from local storage
|
|
495
|
-
const memoryDir = path.join(process.cwd(), '.monomind', 'neural');
|
|
496
|
-
const patternsFile = path.join(memoryDir, 'patterns.json');
|
|
497
|
-
if (fs.existsSync(patternsFile)) {
|
|
498
|
-
const MAX_PATTERNS_BYTES = 100 * 1024 * 1024;
|
|
499
|
-
const patStat = fs.statSync(patternsFile);
|
|
500
|
-
if (patStat.size > MAX_PATTERNS_BYTES) {
|
|
501
|
-
spinner.fail(`patterns.json too large to export safely (${patStat.size} bytes)`);
|
|
502
|
-
return { success: false, exitCode: 1 };
|
|
503
|
-
}
|
|
504
|
-
const patternsRaw = fs.readFileSync(patternsFile, 'utf8');
|
|
505
|
-
const patternsJson = JSON.parse(patternsRaw);
|
|
506
|
-
if (patternsJson && typeof patternsJson === 'object' && ('__proto__' in patternsJson || 'constructor' in patternsJson)) {
|
|
507
|
-
spinner.fail('Prototype pollution attempt detected in patterns.json');
|
|
508
|
-
return { success: false, exitCode: 1 };
|
|
509
|
-
}
|
|
510
|
-
const patterns = patternsJson;
|
|
511
|
-
for (const pattern of patterns) {
|
|
512
|
-
// Security: Strip potential PII
|
|
513
|
-
if (stripPii) {
|
|
514
|
-
// Remove any paths, usernames, or sensitive data
|
|
515
|
-
if (pattern.content) {
|
|
516
|
-
pattern.content = pattern.content
|
|
517
|
-
.replace(/\/Users\/[^\/]+/g, '/Users/[REDACTED]')
|
|
518
|
-
.replace(/\/home\/[^\/]+/g, '/home/[REDACTED]')
|
|
519
|
-
.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, '[EMAIL_REDACTED]')
|
|
520
|
-
.replace(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g, '[IP_REDACTED]');
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
exportData.patterns.push({
|
|
524
|
-
id: pattern.id || crypto.randomBytes(8).toString('hex'),
|
|
525
|
-
trigger: pattern.trigger || pattern.type || 'general',
|
|
526
|
-
action: pattern.action || pattern.recommendation || 'apply-pattern',
|
|
527
|
-
confidence: pattern.confidence || 0.85,
|
|
528
|
-
usageCount: pattern.usageCount || 1,
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
// Add stats metadata
|
|
533
|
-
exportData.metadata.accuracy = stats.retrievalPrecision || 0.85;
|
|
534
|
-
exportData.metadata.totalUsage = exportData.patterns.reduce((sum, p) => sum + p.usageCount, 0);
|
|
535
|
-
spinner.setText('Generating secure signature...');
|
|
536
|
-
// Sign with Ed25519 if requested
|
|
537
|
-
let signature = null;
|
|
538
|
-
let publicKey = null;
|
|
539
|
-
if (signExport) {
|
|
540
|
-
// Generate ephemeral key pair for signing
|
|
541
|
-
// Use Node.js webcrypto for Ed25519 signing
|
|
542
|
-
const { webcrypto } = crypto;
|
|
543
|
-
const keyPair = await webcrypto.subtle.generateKey({ name: 'Ed25519' }, true, ['sign', 'verify']
|
|
544
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
545
|
-
);
|
|
546
|
-
const exportBytes = new TextEncoder().encode(JSON.stringify(exportData));
|
|
547
|
-
const signatureBytes = await webcrypto.subtle.sign('Ed25519', keyPair.privateKey, exportBytes);
|
|
548
|
-
signature = Buffer.from(signatureBytes).toString('hex');
|
|
549
|
-
const publicKeyBytes = await webcrypto.subtle.exportKey('raw', keyPair.publicKey);
|
|
550
|
-
publicKey = Buffer.from(publicKeyBytes).toString('hex');
|
|
551
|
-
}
|
|
552
|
-
// SECURITY: Final export package - verify no secrets leaked
|
|
553
|
-
const exportPackage = {
|
|
554
|
-
pinataContent: exportData,
|
|
555
|
-
pinataMetadata: {
|
|
556
|
-
name: exportData.name,
|
|
557
|
-
keyvalues: {
|
|
558
|
-
type: 'learning-pattern',
|
|
559
|
-
version: '1.0.0',
|
|
560
|
-
signed: signExport ? 'true' : 'false',
|
|
561
|
-
},
|
|
562
|
-
},
|
|
563
|
-
signature,
|
|
564
|
-
publicKey: publicKey ? `ed25519:${publicKey}` : null,
|
|
565
|
-
// Note: Private key is ephemeral and NEVER stored or exported
|
|
566
|
-
};
|
|
567
|
-
// SECURITY AUDIT: Ensure no secrets in export
|
|
568
|
-
const exportStr = JSON.stringify(exportPackage);
|
|
569
|
-
const secretPatterns = [
|
|
570
|
-
/sk-ant-[a-zA-Z0-9-]+/, // Anthropic keys
|
|
571
|
-
/sk-[a-zA-Z0-9]{48}/, // OpenAI keys
|
|
572
|
-
/AIza[a-zA-Z0-9-_]{35}/, // Google keys
|
|
573
|
-
/pinata_[a-zA-Z0-9]{20,}/, // Pinata JWT (min 20 chars to avoid false positives on short names)
|
|
574
|
-
/-----BEGIN.*KEY-----/, // PEM keys
|
|
575
|
-
];
|
|
576
|
-
for (const pattern of secretPatterns) {
|
|
577
|
-
if (pattern.test(exportStr)) {
|
|
578
|
-
spinner.fail('SECURITY: Export contains potential API keys - aborting');
|
|
579
|
-
return { success: false, exitCode: 1 };
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
// Output handling
|
|
583
|
-
if (outputFile) {
|
|
584
|
-
const resolvedOut = path.resolve(outputFile);
|
|
585
|
-
const cwd = process.cwd();
|
|
586
|
-
if (!resolvedOut.startsWith(cwd + path.sep) && resolvedOut !== cwd) {
|
|
587
|
-
spinner.fail(`--output path escapes project directory: ${outputFile}`);
|
|
588
|
-
return { success: false, exitCode: 1 };
|
|
589
|
-
}
|
|
590
|
-
const tmpOutput = outputFile + '.tmp';
|
|
591
|
-
fs.writeFileSync(tmpOutput, JSON.stringify(exportPackage, null, 2));
|
|
592
|
-
fs.renameSync(tmpOutput, outputFile);
|
|
593
|
-
spinner.succeed(`Exported to: ${outputFile}`);
|
|
594
|
-
}
|
|
595
|
-
if (pinToIpfs) {
|
|
596
|
-
spinner.setText('Pinning to IPFS...');
|
|
597
|
-
// Check for Pinata credentials
|
|
598
|
-
const pinataKey = process.env.PINATA_API_KEY;
|
|
599
|
-
const pinataSecret = process.env.PINATA_API_SECRET;
|
|
600
|
-
if (!pinataKey || !pinataSecret) {
|
|
601
|
-
spinner.fail('PINATA_API_KEY and PINATA_API_SECRET required for IPFS export');
|
|
602
|
-
output.writeln(output.dim('Set these in your environment or .env file'));
|
|
603
|
-
return { success: false, exitCode: 1 };
|
|
604
|
-
}
|
|
605
|
-
const response = await fetch('https://api.pinata.cloud/pinning/pinJSONToIPFS', {
|
|
606
|
-
method: 'POST',
|
|
607
|
-
headers: {
|
|
608
|
-
'Content-Type': 'application/json',
|
|
609
|
-
'pinata_api_key': pinataKey,
|
|
610
|
-
'pinata_secret_api_key': pinataSecret,
|
|
611
|
-
},
|
|
612
|
-
body: JSON.stringify(exportPackage),
|
|
613
|
-
});
|
|
614
|
-
if (!response.ok) {
|
|
615
|
-
const error = await response.text();
|
|
616
|
-
spinner.fail(`IPFS pin failed: ${error}`);
|
|
617
|
-
return { success: false, exitCode: 1 };
|
|
618
|
-
}
|
|
619
|
-
const result = await response.json();
|
|
620
|
-
spinner.succeed('Successfully exported to IPFS');
|
|
621
|
-
output.writeln();
|
|
622
|
-
output.printTable({
|
|
623
|
-
columns: [
|
|
624
|
-
{ key: 'property', header: 'Property', width: 20 },
|
|
625
|
-
{ key: 'value', header: 'Value', width: 50 },
|
|
626
|
-
],
|
|
627
|
-
data: [
|
|
628
|
-
{ property: 'CID', value: result.IpfsHash },
|
|
629
|
-
{ property: 'Size', value: `${result.PinSize} bytes` },
|
|
630
|
-
{ property: 'Gateway URL', value: `https://gateway.pinata.cloud/ipfs/${result.IpfsHash}` },
|
|
631
|
-
{ property: 'Patterns', value: String(exportData.patterns.length) },
|
|
632
|
-
{ property: 'Signed', value: signExport ? 'Yes (Ed25519)' : 'No' },
|
|
633
|
-
{ property: 'PII Stripped', value: stripPii ? 'Yes' : 'No' },
|
|
634
|
-
],
|
|
635
|
-
});
|
|
636
|
-
output.writeln();
|
|
637
|
-
output.writeln(output.success('Share this CID for others to import your trained patterns'));
|
|
638
|
-
output.writeln(output.dim(`Import command: monomind neural import --cid ${result.IpfsHash}`));
|
|
639
|
-
}
|
|
640
|
-
if (!outputFile && !pinToIpfs) {
|
|
641
|
-
// Just display the export
|
|
642
|
-
spinner.succeed('Export prepared');
|
|
643
|
-
output.writeln();
|
|
644
|
-
output.writeln(JSON.stringify(exportPackage, null, 2));
|
|
645
|
-
}
|
|
646
|
-
return { success: true };
|
|
647
|
-
}
|
|
648
|
-
catch (error) {
|
|
649
|
-
spinner.fail(`Export failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
650
|
-
return { success: false, exitCode: 1 };
|
|
651
|
-
}
|
|
652
|
-
},
|
|
653
|
-
};
|
|
654
|
-
// List subcommand - List available pre-trained models
|
|
655
|
-
const listCommand = {
|
|
656
|
-
name: 'list',
|
|
657
|
-
description: 'List available pre-trained models from the official registry',
|
|
658
|
-
options: [
|
|
659
|
-
{ name: 'category', type: 'string', description: 'Filter by category (security, quality, performance, etc.)' },
|
|
660
|
-
{ name: 'format', short: 'f', type: 'string', description: 'Output format: table, json, simple', default: 'table' },
|
|
661
|
-
{ name: 'cid', type: 'string', description: 'Custom registry CID (default: official registry)' },
|
|
662
|
-
],
|
|
663
|
-
examples: [
|
|
664
|
-
{ command: 'monomind neural list', description: 'List all available models' },
|
|
665
|
-
{ command: 'monomind neural list --category security', description: 'List only security models' },
|
|
666
|
-
{ command: 'monomind neural list -f json', description: 'Output as JSON' },
|
|
667
|
-
],
|
|
668
|
-
action: async (ctx) => {
|
|
669
|
-
const category = ctx.flags.category;
|
|
670
|
-
const format = ctx.flags.format || 'table';
|
|
671
|
-
const customCid = ctx.flags.cid;
|
|
672
|
-
// Official model registry CID
|
|
673
|
-
const registryCid = customCid || 'QmNr1yYMKi7YBaL8JSztQyuB5ZUaTdRMLxJC1pBpGbjsTc';
|
|
674
|
-
output.writeln();
|
|
675
|
-
output.writeln(output.bold('Pre-trained Model Registry'));
|
|
676
|
-
output.writeln(output.dim('─'.repeat(60)));
|
|
677
|
-
const spinner = output.createSpinner({ text: 'Fetching model registry...', spinner: 'dots' });
|
|
678
|
-
spinner.start();
|
|
679
|
-
try {
|
|
680
|
-
const gateways = [
|
|
681
|
-
'https://gateway.pinata.cloud',
|
|
682
|
-
'https://ipfs.io',
|
|
683
|
-
'https://dweb.link',
|
|
684
|
-
];
|
|
685
|
-
let registry = null;
|
|
686
|
-
for (const gateway of gateways) {
|
|
687
|
-
try {
|
|
688
|
-
const response = await fetch(`${gateway}/ipfs/${registryCid}`, {
|
|
689
|
-
signal: AbortSignal.timeout(15000),
|
|
690
|
-
headers: { 'Accept': 'application/json' },
|
|
691
|
-
});
|
|
692
|
-
if (response.ok) {
|
|
693
|
-
const MAX_REGISTRY_BYTES = 50 * 1024 * 1024;
|
|
694
|
-
const buf = await response.arrayBuffer();
|
|
695
|
-
if (buf.byteLength > MAX_REGISTRY_BYTES)
|
|
696
|
-
throw new Error(`Registry response too large: ${buf.byteLength} bytes`);
|
|
697
|
-
registry = JSON.parse(new TextDecoder().decode(buf));
|
|
698
|
-
break;
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
catch {
|
|
702
|
-
continue;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
if (!registry || !registry.models) {
|
|
706
|
-
spinner.fail('Could not fetch model registry');
|
|
707
|
-
return { success: false, exitCode: 1 };
|
|
708
|
-
}
|
|
709
|
-
const registryData = registry;
|
|
710
|
-
// Filter by category if specified
|
|
711
|
-
let models = registryData.models;
|
|
712
|
-
if (category) {
|
|
713
|
-
models = models.filter(m => m.category === category ||
|
|
714
|
-
m.id.includes(category) ||
|
|
715
|
-
m.name.toLowerCase().includes(category.toLowerCase()));
|
|
716
|
-
spinner.succeed(`Found ${models.length} models matching "${category}"`);
|
|
717
|
-
}
|
|
718
|
-
else {
|
|
719
|
-
spinner.succeed(`Found ${registryData.models.length} models`);
|
|
720
|
-
}
|
|
721
|
-
if (models.length === 0) {
|
|
722
|
-
output.writeln(output.warning(`No models found for category: ${category}`));
|
|
723
|
-
output.writeln(output.dim('Available categories: security, quality, performance, testing, api, debugging, refactoring, documentation'));
|
|
724
|
-
return { success: false, exitCode: 1 };
|
|
725
|
-
}
|
|
726
|
-
output.writeln();
|
|
727
|
-
if (format === 'json') {
|
|
728
|
-
output.writeln(JSON.stringify(models, null, 2));
|
|
729
|
-
}
|
|
730
|
-
else if (format === 'simple') {
|
|
731
|
-
for (const model of models) {
|
|
732
|
-
output.writeln(`${model.id} (${model.category}) - ${model.patterns.length} patterns, ${(model.metadata.accuracy * 100).toFixed(0)}% accuracy`);
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
else {
|
|
736
|
-
// Table format
|
|
737
|
-
output.printTable({
|
|
738
|
-
columns: [
|
|
739
|
-
{ key: 'id', header: 'Model ID', width: 35 },
|
|
740
|
-
{ key: 'category', header: 'Category', width: 14 },
|
|
741
|
-
{ key: 'patterns', header: 'Patterns', width: 10 },
|
|
742
|
-
{ key: 'accuracy', header: 'Accuracy', width: 10 },
|
|
743
|
-
{ key: 'usage', header: 'Usage', width: 10 },
|
|
744
|
-
],
|
|
745
|
-
data: models.map(m => ({
|
|
746
|
-
id: m.id,
|
|
747
|
-
category: m.category,
|
|
748
|
-
patterns: String(m.patterns.length),
|
|
749
|
-
accuracy: `${(m.metadata.accuracy * 100).toFixed(0)}%`,
|
|
750
|
-
usage: m.metadata.totalUsage.toLocaleString(),
|
|
751
|
-
})),
|
|
752
|
-
});
|
|
753
|
-
output.writeln();
|
|
754
|
-
output.writeln(output.dim('Registry CID: ' + registryCid));
|
|
755
|
-
output.writeln();
|
|
756
|
-
output.writeln(output.bold('Import Commands:'));
|
|
757
|
-
output.writeln(output.dim(' All models: ') + `monomind neural import --cid ${registryCid}`);
|
|
758
|
-
if (category) {
|
|
759
|
-
output.writeln(output.dim(` ${category} only: `) + `monomind neural import --cid ${registryCid} --category ${category}`);
|
|
760
|
-
}
|
|
761
|
-
else {
|
|
762
|
-
output.writeln(output.dim(' By category: ') + `monomind neural import --cid ${registryCid} --category <category>`);
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
return { success: true };
|
|
766
|
-
}
|
|
767
|
-
catch (error) {
|
|
768
|
-
spinner.fail(`Failed to list models: ${error instanceof Error ? error.message : String(error)}`);
|
|
769
|
-
return { success: false, exitCode: 1 };
|
|
770
|
-
}
|
|
771
|
-
},
|
|
772
|
-
};
|
|
773
|
-
// Import subcommand - Securely import models from IPFS
|
|
774
|
-
const importCommand = {
|
|
775
|
-
name: 'import',
|
|
776
|
-
description: 'Import trained models from IPFS with signature verification',
|
|
777
|
-
options: [
|
|
778
|
-
{ name: 'cid', short: 'c', type: 'string', description: 'IPFS CID to import from' },
|
|
779
|
-
{ name: 'file', short: 'f', type: 'string', description: 'Local file to import' },
|
|
780
|
-
{ name: 'verify', short: 'v', type: 'boolean', description: 'Verify Ed25519 signature', default: 'true' },
|
|
781
|
-
{ name: 'merge', type: 'boolean', description: 'Merge with existing patterns (vs replace)', default: 'true' },
|
|
782
|
-
{ name: 'category', type: 'string', description: 'Only import patterns from specific category' },
|
|
783
|
-
],
|
|
784
|
-
examples: [
|
|
785
|
-
{ command: 'monomind neural import --cid QmXxx...', description: 'Import from IPFS' },
|
|
786
|
-
{ command: 'monomind neural import -f ./patterns.json --verify', description: 'Import from file' },
|
|
787
|
-
{ command: 'monomind neural import --cid QmNr1yYMK... --category security', description: 'Import only security patterns' },
|
|
788
|
-
],
|
|
789
|
-
action: async (ctx) => {
|
|
790
|
-
const cid = ctx.flags.cid;
|
|
791
|
-
const file = ctx.flags.file;
|
|
792
|
-
const verifySignature = ctx.flags.verify !== false;
|
|
793
|
-
const merge = ctx.flags.merge !== false;
|
|
794
|
-
const categoryFilter = ctx.flags.category;
|
|
795
|
-
if (!cid && !file) {
|
|
796
|
-
output.writeln(output.error('Either --cid or --file is required'));
|
|
797
|
-
return { success: false, exitCode: 1 };
|
|
798
|
-
}
|
|
799
|
-
output.writeln();
|
|
800
|
-
output.writeln(output.bold('Secure Model Import'));
|
|
801
|
-
output.writeln(output.dim('─'.repeat(50)));
|
|
802
|
-
const spinner = output.createSpinner({ text: 'Fetching model...', spinner: 'dots' });
|
|
803
|
-
spinner.start();
|
|
804
|
-
try {
|
|
805
|
-
const fs = await import('fs');
|
|
806
|
-
const path = await import('path');
|
|
807
|
-
const crypto = await import('crypto');
|
|
808
|
-
let importData = null;
|
|
809
|
-
// Fetch from IPFS or file
|
|
810
|
-
if (cid) {
|
|
811
|
-
const gateways = [
|
|
812
|
-
'https://gateway.pinata.cloud',
|
|
813
|
-
'https://ipfs.io',
|
|
814
|
-
'https://dweb.link',
|
|
815
|
-
];
|
|
816
|
-
for (const gateway of gateways) {
|
|
817
|
-
try {
|
|
818
|
-
spinner.setText(`Fetching from ${gateway}...`);
|
|
819
|
-
const response = await fetch(`${gateway}/ipfs/${cid}`, {
|
|
820
|
-
signal: AbortSignal.timeout(30000),
|
|
821
|
-
headers: { 'Accept': 'application/json' },
|
|
822
|
-
});
|
|
823
|
-
if (response.ok) {
|
|
824
|
-
const MAX_IMPORT_BYTES = 50 * 1024 * 1024;
|
|
825
|
-
const importBuf = await response.arrayBuffer();
|
|
826
|
-
if (importBuf.byteLength > MAX_IMPORT_BYTES)
|
|
827
|
-
throw new Error(`Import response too large: ${importBuf.byteLength} bytes`);
|
|
828
|
-
importData = JSON.parse(new TextDecoder().decode(importBuf));
|
|
829
|
-
break;
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
catch {
|
|
833
|
-
continue;
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
if (!importData) {
|
|
837
|
-
spinner.fail('Could not fetch from any IPFS gateway');
|
|
838
|
-
return { success: false, exitCode: 1 };
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
else {
|
|
842
|
-
if (!fs.existsSync(file)) {
|
|
843
|
-
spinner.fail(`File not found: ${file}`);
|
|
844
|
-
return { success: false, exitCode: 1 };
|
|
845
|
-
}
|
|
846
|
-
// Cap import file size to prevent OOM on attacker-controlled content.
|
|
847
|
-
const stat = fs.statSync(file);
|
|
848
|
-
const MAX_IMPORT_BYTES = 50 * 1024 * 1024; // 50 MB
|
|
849
|
-
if (stat.size > MAX_IMPORT_BYTES) {
|
|
850
|
-
spinner.fail(`Import file too large: ${stat.size} bytes (max ${MAX_IMPORT_BYTES})`);
|
|
851
|
-
return { success: false, exitCode: 1 };
|
|
852
|
-
}
|
|
853
|
-
importData = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
854
|
-
}
|
|
855
|
-
if (!importData) {
|
|
856
|
-
spinner.fail('No import data available');
|
|
857
|
-
return { success: false, exitCode: 1 };
|
|
858
|
-
}
|
|
859
|
-
// SECURITY: Verify signature when --verify is set (default true).
|
|
860
|
-
// Previously two bypasses existed:
|
|
861
|
-
// (a) catch-fall-through made any malformed signature/key skip verification
|
|
862
|
-
// and proceed to import — fail-OPEN.
|
|
863
|
-
// (b) the entire block was guarded on `signature && publicKey`, so an
|
|
864
|
-
// attacker who simply omitted those fields skipped verification
|
|
865
|
-
// regardless of --verify. Both now fail-CLOSED.
|
|
866
|
-
if (verifySignature) {
|
|
867
|
-
if (!importData.signature || !importData.publicKey) {
|
|
868
|
-
spinner.fail('SECURITY: --verify requested but payload is unsigned. Aborting (use --no-verify to override).');
|
|
869
|
-
return { success: false, exitCode: 1 };
|
|
870
|
-
}
|
|
871
|
-
spinner.setText('Verifying Ed25519 signature...');
|
|
872
|
-
try {
|
|
873
|
-
const { webcrypto } = crypto;
|
|
874
|
-
const publicKeyHex = importData.publicKey.replace('ed25519:', '');
|
|
875
|
-
const publicKeyBytes = Buffer.from(publicKeyHex, 'hex');
|
|
876
|
-
const signatureBytes = Buffer.from(importData.signature, 'hex');
|
|
877
|
-
const publicKey = await webcrypto.subtle.importKey('raw', publicKeyBytes, { name: 'Ed25519' }, false, ['verify']);
|
|
878
|
-
const dataBytes = new TextEncoder().encode(JSON.stringify(importData.pinataContent));
|
|
879
|
-
const valid = await webcrypto.subtle.verify('Ed25519', publicKey, signatureBytes, dataBytes);
|
|
880
|
-
if (!valid) {
|
|
881
|
-
spinner.fail('Signature verification FAILED - data may be tampered');
|
|
882
|
-
return { success: false, exitCode: 1 };
|
|
883
|
-
}
|
|
884
|
-
output.writeln(output.success('Signature verified'));
|
|
885
|
-
}
|
|
886
|
-
catch (err) {
|
|
887
|
-
// FAIL-CLOSED: any error during verification (malformed key, wrong
|
|
888
|
-
// algorithm, runtime not supporting Ed25519, etc.) must reject the
|
|
889
|
-
// import, NOT fall through with a warning.
|
|
890
|
-
spinner.fail(`SECURITY: Signature verification error: ${err instanceof Error ? err.message : String(err)}. Aborting.`);
|
|
891
|
-
return { success: false, exitCode: 1 };
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
// Extract patterns - handle both single model and model registry formats
|
|
895
|
-
spinner.setText('Importing patterns...');
|
|
896
|
-
const content = importData.pinataContent || importData;
|
|
897
|
-
let patterns = [];
|
|
898
|
-
// Check if this is a model registry (has models array)
|
|
899
|
-
const registry = content;
|
|
900
|
-
if (registry.models && Array.isArray(registry.models)) {
|
|
901
|
-
// Model registry format - extract patterns from each model
|
|
902
|
-
for (const model of registry.models) {
|
|
903
|
-
if (!categoryFilter || model.category === categoryFilter || model.id.includes(categoryFilter)) {
|
|
904
|
-
for (const pattern of model.patterns || []) {
|
|
905
|
-
patterns.push({
|
|
906
|
-
...pattern,
|
|
907
|
-
category: model.category, // Tag with model category
|
|
908
|
-
});
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
else {
|
|
914
|
-
// Single model format - patterns at top level
|
|
915
|
-
patterns = content.patterns || [];
|
|
916
|
-
}
|
|
917
|
-
// Filter by category if specified (additional filtering)
|
|
918
|
-
if (categoryFilter && patterns.length > 0) {
|
|
919
|
-
patterns = patterns.filter(p => p.category === categoryFilter ||
|
|
920
|
-
p.trigger.includes(categoryFilter));
|
|
921
|
-
}
|
|
922
|
-
// Validate patterns (security check)
|
|
923
|
-
const validPatterns = patterns.filter(p => {
|
|
924
|
-
// Security: Reject patterns with suspicious content
|
|
925
|
-
const suspicious = [
|
|
926
|
-
'eval(', 'Function(', 'exec(', 'spawn(',
|
|
927
|
-
'child_process', 'rm -rf', 'sudo',
|
|
928
|
-
'<script>', 'javascript:', 'data:',
|
|
929
|
-
];
|
|
930
|
-
const content = JSON.stringify(p);
|
|
931
|
-
return !suspicious.some(s => content.includes(s));
|
|
932
|
-
});
|
|
933
|
-
if (validPatterns.length < patterns.length) {
|
|
934
|
-
output.writeln(output.warning(`Filtered ${patterns.length - validPatterns.length} suspicious patterns`));
|
|
935
|
-
}
|
|
936
|
-
// Save to neural store (same location intelligence.ts writes to)
|
|
937
|
-
const memoryDir = path.join(process.cwd(), '.monomind', 'neural');
|
|
938
|
-
if (!fs.existsSync(memoryDir)) {
|
|
939
|
-
fs.mkdirSync(memoryDir, { recursive: true });
|
|
940
|
-
}
|
|
941
|
-
const patternsFile = path.join(memoryDir, 'patterns.json');
|
|
942
|
-
let existingPatterns = [];
|
|
943
|
-
if (merge && fs.existsSync(patternsFile) && fs.statSync(patternsFile).size <= 50 * 1024 * 1024) {
|
|
944
|
-
existingPatterns = JSON.parse(fs.readFileSync(patternsFile, 'utf8'));
|
|
945
|
-
}
|
|
946
|
-
// Merge or replace
|
|
947
|
-
const existingIds = new Set(existingPatterns.map(p => p.id));
|
|
948
|
-
const newPatterns = validPatterns.filter(p => !existingIds.has(p.id));
|
|
949
|
-
const finalPatterns = merge ? [...existingPatterns, ...newPatterns] : validPatterns;
|
|
950
|
-
// Unique tmp filename so concurrent invocations don't clobber each other's
|
|
951
|
-
// .tmp files mid-write (which would produce a corrupt patterns.json on rename).
|
|
952
|
-
const tmpPatterns = `${patternsFile}.${process.pid}.${Date.now()}.tmp`;
|
|
953
|
-
fs.writeFileSync(tmpPatterns, JSON.stringify(finalPatterns, null, 2), { flag: 'wx' });
|
|
954
|
-
fs.renameSync(tmpPatterns, patternsFile);
|
|
955
|
-
spinner.succeed('Import complete');
|
|
956
|
-
output.writeln();
|
|
957
|
-
output.printTable({
|
|
958
|
-
columns: [
|
|
959
|
-
{ key: 'metric', header: 'Metric', width: 25 },
|
|
960
|
-
{ key: 'value', header: 'Value', width: 20 },
|
|
961
|
-
],
|
|
962
|
-
data: [
|
|
963
|
-
{ metric: 'Patterns Imported', value: String(validPatterns.length) },
|
|
964
|
-
{ metric: 'New Patterns', value: String(newPatterns.length) },
|
|
965
|
-
{ metric: 'Total Patterns', value: String(finalPatterns.length) },
|
|
966
|
-
{ metric: 'Signature Verified', value: importData.signature ? 'Yes' : 'N/A' },
|
|
967
|
-
{ metric: 'Merge Mode', value: merge ? 'Yes' : 'Replace' },
|
|
968
|
-
],
|
|
969
|
-
});
|
|
970
|
-
output.writeln();
|
|
971
|
-
output.writeln(output.success('Patterns imported and ready to use'));
|
|
972
|
-
output.writeln(output.dim('Run "monomind neural patterns --action list" to see imported patterns'));
|
|
973
|
-
return { success: true };
|
|
974
|
-
}
|
|
975
|
-
catch (error) {
|
|
976
|
-
spinner.fail(`Import failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
977
|
-
return { success: false, exitCode: 1 };
|
|
978
|
-
}
|
|
979
|
-
},
|
|
980
|
-
};
|
|
981
|
-
// Main neural command
|
|
8
|
+
import { statusCommand, patternsCommand, predictCommand } from './neural-core.js';
|
|
9
|
+
import { optimizeCommand, exportCommand } from './neural-optimize.js';
|
|
10
|
+
import { listCommand, importCommand } from './neural-registry.js';
|
|
982
11
|
export const neuralCommand = {
|
|
983
12
|
name: 'neural',
|
|
984
13
|
description: 'Pattern learning, search, and prediction (pure-JS intelligence layer)',
|