@duckmind/deepquark-darwin-arm64 0.9.83 → 0.9.90
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/.deepquark/skills/bundled/knowledge-graph/SKILL.md +385 -0
- package/.deepquark/skills/bundled/knowledge-graph/STANDARDS.md +461 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/cli.ts +588 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/config.ts +630 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/connection-profile.ts +629 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/container.ts +756 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/mcp-client.ts +1310 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/output-formatter.ts +997 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/token-metrics.ts +335 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/transformation-log.ts +137 -0
- package/.deepquark/skills/bundled/knowledge-graph/lib/wrapper-config.ts +113 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/.env.example +129 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/compare-embeddings.ts +175 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/config-falkordb.yaml +108 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/config-neo4j.yaml +111 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/diagnose.ts +483 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-falkordb-dev.yml +146 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-falkordb.yml +151 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j-dev-local.yml +161 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j-dev.yml +161 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j.yml +169 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-production.yml +128 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-test.yml +10 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose.yml +84 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/entrypoint.sh +40 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/install.ts +2054 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose-falkordb.yml +78 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose-neo4j.yml +88 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose.yml +83 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-all-llms-mcp.ts +387 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-embedding-models.ts +201 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-embedding-providers.ts +641 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-graphiti-model.ts +217 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-correct.ts +141 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-llms-mcp.ts +386 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-models.ts +173 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-llama-extraction.ts +188 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-final.ts +240 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-live.ts +187 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-session.ts +127 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-model-combinations.ts +316 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-ollama-models.ts +228 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-openrouter-models.ts +460 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-real-life-mcp.ts +311 -0
- package/.deepquark/skills/bundled/knowledge-graph/server/test-search-debug.ts +199 -0
- package/.deepquark/skills/bundled/knowledge-graph/tools/Install.md +104 -0
- package/.deepquark/skills/bundled/knowledge-graph/tools/README.md +120 -0
- package/.deepquark/skills/bundled/knowledge-graph/tools/knowledge-cli.ts +996 -0
- package/.deepquark/skills/bundled/knowledge-graph/tools/server-cli.ts +531 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/BulkImport.md +514 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/CaptureEpisode.md +242 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/ClearGraph.md +392 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/GetRecent.md +352 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/GetStatus.md +373 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/HealthReport.md +212 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/InvestigateEntity.md +142 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/OntologyManagement.md +201 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/RunMaintenance.md +302 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchByDate.md +255 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchFacts.md +382 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchKnowledge.md +374 -0
- package/.deepquark/skills/bundled/knowledge-graph/workflows/StixImport.md +212 -0
- package/bin/deepquark +0 -0
- package/package.json +1 -1
- package/.deepquark/skills/bundled/ge-payroll/SKILL.md +0 -153
- package/.deepquark/skills/bundled/ge-payroll/evals/evals.json +0 -23
- package/.deepquark/skills/bundled/ge-payroll/references/pain-points-improvements.md +0 -106
- package/.deepquark/skills/bundled/ge-payroll/references/process-detail.md +0 -217
- package/.deepquark/skills/bundled/ge-payroll/references/raci-stakeholders.md +0 -85
- package/.deepquark/skills/bundled/ge-payroll/references/timeline-mandays.md +0 -64
|
@@ -0,0 +1,996 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Knowledge CLI for Madeinoz Knowledge System
|
|
4
|
+
*
|
|
5
|
+
* This script provides a simple command-line interface to the Graphiti MCP server.
|
|
6
|
+
* It handles JSON-RPC communication and provides compact, token-efficient output.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* bun run src/skills/tools/knowledge-cli.ts add_episode "Episode title" "Episode body"
|
|
10
|
+
* bun run src/skills/tools/knowledge-cli.ts search_nodes "search query"
|
|
11
|
+
* bun run src/skills/tools/knowledge-cli.ts get_status
|
|
12
|
+
*
|
|
13
|
+
* Flags:
|
|
14
|
+
* --raw Output raw JSON instead of compact format
|
|
15
|
+
* --metrics Display token metrics after each operation
|
|
16
|
+
* --metrics-file Write metrics to JSONL file
|
|
17
|
+
* --weighted Apply weighted scoring (60% semantic + 25% recency + 15% importance)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { createMCPClient, type MCPClientConfigExtended, type MCPClient } from '../lib/mcp-client';
|
|
21
|
+
import { cli } from '../lib/cli';
|
|
22
|
+
import { formatOutput, type FormatOptions } from '../lib/output-formatter';
|
|
23
|
+
import {
|
|
24
|
+
profileManager,
|
|
25
|
+
loadProfileWithOverrides,
|
|
26
|
+
getProfileName,
|
|
27
|
+
type ConnectionState,
|
|
28
|
+
} from '../lib/connection-profile';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Command definitions
|
|
32
|
+
*/
|
|
33
|
+
interface Command {
|
|
34
|
+
name: string;
|
|
35
|
+
description: string;
|
|
36
|
+
handler: (
|
|
37
|
+
args: string[]
|
|
38
|
+
) => Promise<{ success: boolean; data?: unknown; error?: string; query?: string }>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* CLI flags parsed from arguments
|
|
43
|
+
*/
|
|
44
|
+
interface CLIFlags {
|
|
45
|
+
raw: boolean;
|
|
46
|
+
metrics: boolean;
|
|
47
|
+
metricsFile?: string;
|
|
48
|
+
help: boolean;
|
|
49
|
+
since?: string;
|
|
50
|
+
until?: string;
|
|
51
|
+
weighted?: boolean;
|
|
52
|
+
profile?: string;
|
|
53
|
+
host?: string;
|
|
54
|
+
port?: string;
|
|
55
|
+
protocol?: string;
|
|
56
|
+
tlsNoVerify?: boolean;
|
|
57
|
+
depth?: number;
|
|
58
|
+
relationshipType?: string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parse CLI flags from arguments
|
|
63
|
+
*/
|
|
64
|
+
function parseFlags(args: string[]): { flags: CLIFlags; positionalArgs: string[] } {
|
|
65
|
+
const flags: CLIFlags = {
|
|
66
|
+
raw: false,
|
|
67
|
+
metrics: false,
|
|
68
|
+
metricsFile: undefined,
|
|
69
|
+
help: false,
|
|
70
|
+
since: undefined,
|
|
71
|
+
until: undefined,
|
|
72
|
+
weighted: undefined,
|
|
73
|
+
profile: undefined,
|
|
74
|
+
host: undefined,
|
|
75
|
+
port: undefined,
|
|
76
|
+
protocol: undefined,
|
|
77
|
+
tlsNoVerify: undefined,
|
|
78
|
+
depth: undefined,
|
|
79
|
+
relationshipType: [],
|
|
80
|
+
};
|
|
81
|
+
const positionalArgs: string[] = [];
|
|
82
|
+
|
|
83
|
+
for (let i = 0; i < args.length; i++) {
|
|
84
|
+
const arg = args[i];
|
|
85
|
+
|
|
86
|
+
if (arg === '--raw') {
|
|
87
|
+
flags.raw = true;
|
|
88
|
+
} else if (arg === '--metrics') {
|
|
89
|
+
flags.metrics = true;
|
|
90
|
+
} else if (arg === '--metrics-file') {
|
|
91
|
+
flags.metricsFile = args[++i];
|
|
92
|
+
} else if (arg === '-h' || arg === '--help') {
|
|
93
|
+
flags.help = true;
|
|
94
|
+
} else if (arg === '--since') {
|
|
95
|
+
flags.since = args[++i];
|
|
96
|
+
} else if (arg === '--until') {
|
|
97
|
+
flags.until = args[++i];
|
|
98
|
+
} else if (arg === '--weighted') {
|
|
99
|
+
flags.weighted = true;
|
|
100
|
+
} else if (arg === '--profile') {
|
|
101
|
+
flags.profile = args[++i];
|
|
102
|
+
} else if (arg === '--host') {
|
|
103
|
+
flags.host = args[++i];
|
|
104
|
+
} else if (arg === '--port') {
|
|
105
|
+
flags.port = args[++i];
|
|
106
|
+
} else if (arg === '--protocol') {
|
|
107
|
+
flags.protocol = args[++i];
|
|
108
|
+
} else if (arg === '--tls-no-verify') {
|
|
109
|
+
flags.tlsNoVerify = true;
|
|
110
|
+
} else if (arg === '--depth') {
|
|
111
|
+
flags.depth = Number.parseInt(args[++i], 10);
|
|
112
|
+
} else if (arg === '--relationship-type') {
|
|
113
|
+
if (!flags.relationshipType) {
|
|
114
|
+
flags.relationshipType = [];
|
|
115
|
+
}
|
|
116
|
+
flags.relationshipType.push(args[++i]);
|
|
117
|
+
} else if (!arg.startsWith('--')) {
|
|
118
|
+
positionalArgs.push(arg);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return { flags, positionalArgs };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* MCP Wrapper class
|
|
127
|
+
*/
|
|
128
|
+
class MCPWrapper {
|
|
129
|
+
private commands: Map<string, Command>;
|
|
130
|
+
private flags: CLIFlags;
|
|
131
|
+
|
|
132
|
+
constructor(flags: CLIFlags) {
|
|
133
|
+
this.commands = new Map();
|
|
134
|
+
this.flags = flags;
|
|
135
|
+
this.registerCommands();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Create MCP client with CLI flags applied
|
|
140
|
+
*
|
|
141
|
+
* T017 [US1]: Add --host, --port, --protocol CLI flags
|
|
142
|
+
* T016 [US1]: Add environment variable parsing
|
|
143
|
+
*
|
|
144
|
+
* Priority order (highest to lowest):
|
|
145
|
+
* 1. CLI flags (--host, --port, --protocol, --profile)
|
|
146
|
+
* 2. Individual environment variables (MADEINOZ_KNOWLEDGE_HOST, etc.)
|
|
147
|
+
* 3. Profile from MADEINOZ_KNOWLEDGE_PROFILE environment variable
|
|
148
|
+
* 4. Default profile from YAML file
|
|
149
|
+
* 5. Code defaults (localhost:8001, http)
|
|
150
|
+
*/
|
|
151
|
+
private createClient(): MCPClient {
|
|
152
|
+
// Build config from CLI flags
|
|
153
|
+
const cliConfig: MCPClientConfigExtended = {};
|
|
154
|
+
|
|
155
|
+
// Apply CLI flags if provided
|
|
156
|
+
if (this.flags.host) {
|
|
157
|
+
cliConfig.host = this.flags.host;
|
|
158
|
+
}
|
|
159
|
+
if (this.flags.port) {
|
|
160
|
+
cliConfig.port = Number.parseInt(this.flags.port, 10);
|
|
161
|
+
}
|
|
162
|
+
if (this.flags.protocol) {
|
|
163
|
+
cliConfig.protocol = this.flags.protocol as 'http' | 'https';
|
|
164
|
+
}
|
|
165
|
+
if (this.flags.profile) {
|
|
166
|
+
cliConfig.profile = this.flags.profile;
|
|
167
|
+
}
|
|
168
|
+
if (this.flags.tlsNoVerify) {
|
|
169
|
+
cliConfig.tls = { ...cliConfig.tls, verify: false };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// If any CLI flags provided, create client with that config
|
|
173
|
+
if (Object.keys(cliConfig).length > 0) {
|
|
174
|
+
return createMCPClient(cliConfig);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Otherwise use default (environment variables, profiles, or code defaults)
|
|
178
|
+
return createMCPClient();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Register all available commands
|
|
183
|
+
*/
|
|
184
|
+
private registerCommands(): void {
|
|
185
|
+
this.addCommand({
|
|
186
|
+
name: 'add_episode',
|
|
187
|
+
description: 'Add knowledge to graph',
|
|
188
|
+
handler: this.cmdAddEpisode.bind(this),
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
this.addCommand({
|
|
192
|
+
name: 'search_nodes',
|
|
193
|
+
description: 'Search entities',
|
|
194
|
+
handler: this.cmdSearchNodes.bind(this),
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
this.addCommand({
|
|
198
|
+
name: 'search_facts',
|
|
199
|
+
description: 'Search relationships',
|
|
200
|
+
handler: this.cmdSearchFacts.bind(this),
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
this.addCommand({
|
|
204
|
+
name: 'get_episodes',
|
|
205
|
+
description: 'Get recent episodes',
|
|
206
|
+
handler: this.cmdGetEpisodes.bind(this),
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
this.addCommand({
|
|
210
|
+
name: 'get_status',
|
|
211
|
+
description: 'Get graph status',
|
|
212
|
+
handler: this.cmdGetStatus.bind(this),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
this.addCommand({
|
|
216
|
+
name: 'clear_graph',
|
|
217
|
+
description: 'Delete all knowledge',
|
|
218
|
+
handler: this.cmdClearGraph.bind(this),
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
this.addCommand({
|
|
222
|
+
name: 'health',
|
|
223
|
+
description: 'Check server health',
|
|
224
|
+
handler: this.cmdHealth.bind(this),
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Feature 009: Memory decay commands
|
|
228
|
+
this.addCommand({
|
|
229
|
+
name: 'health_metrics',
|
|
230
|
+
description: 'Get memory lifecycle health metrics',
|
|
231
|
+
handler: this.cmdHealthMetrics.bind(this),
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
this.addCommand({
|
|
235
|
+
name: 'run_maintenance',
|
|
236
|
+
description: 'Run decay maintenance cycle',
|
|
237
|
+
handler: this.cmdRunMaintenance.bind(this),
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
this.addCommand({
|
|
241
|
+
name: 'classify_memory',
|
|
242
|
+
description: 'Classify memory importance and stability',
|
|
243
|
+
handler: this.cmdClassifyMemory.bind(this),
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
this.addCommand({
|
|
247
|
+
name: 'recover_memory',
|
|
248
|
+
description: 'Recover a soft-deleted memory',
|
|
249
|
+
handler: this.cmdRecoverMemory.bind(this),
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Feature 018: OSINT/CTI Ontology commands
|
|
253
|
+
this.addCommand({
|
|
254
|
+
name: 'ontology:list',
|
|
255
|
+
description: 'List custom entity and relationship types',
|
|
256
|
+
handler: this.cmdOntologyList.bind(this),
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
this.addCommand({
|
|
260
|
+
name: 'ontology:validate',
|
|
261
|
+
description: 'Validate ontology configuration',
|
|
262
|
+
handler: this.cmdOntologyValidate.bind(this),
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
this.addCommand({
|
|
266
|
+
name: 'ontology:reload',
|
|
267
|
+
description: 'Reload ontology configuration',
|
|
268
|
+
handler: this.cmdOntologyReload.bind(this),
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Feature 018: STIX 2.1 import commands
|
|
272
|
+
this.addCommand({
|
|
273
|
+
name: 'stix:import',
|
|
274
|
+
description: 'Import STIX 2.1 bundle from file or URL',
|
|
275
|
+
handler: this.cmdStixImport.bind(this),
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
this.addCommand({
|
|
279
|
+
name: 'stix:status',
|
|
280
|
+
description: 'Get STIX import status',
|
|
281
|
+
handler: this.cmdStixStatus.bind(this),
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// Feature 010: Connection profile commands
|
|
285
|
+
this.addCommand({
|
|
286
|
+
name: 'list_profiles',
|
|
287
|
+
description: 'List available connection profiles',
|
|
288
|
+
handler: this.cmdListProfiles.bind(this),
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
this.addCommand({
|
|
292
|
+
name: 'status',
|
|
293
|
+
description: 'Show connection status and current profile',
|
|
294
|
+
handler: this.cmdStatus.bind(this),
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Feature 020: Investigative search
|
|
298
|
+
this.addCommand({
|
|
299
|
+
name: 'investigate',
|
|
300
|
+
description: 'Investigate entity connections (graph traversal)',
|
|
301
|
+
handler: this.cmdInvestigate.bind(this),
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Add a command
|
|
307
|
+
*/
|
|
308
|
+
private addCommand(command: Command): void {
|
|
309
|
+
this.commands.set(command.name, command);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get a command by name
|
|
314
|
+
*/
|
|
315
|
+
private getCommand(name: string): Command | undefined {
|
|
316
|
+
return this.commands.get(name);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* List all available commands
|
|
321
|
+
*/
|
|
322
|
+
listCommands(): Command[] {
|
|
323
|
+
return Array.from(this.commands.values());
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Command: add_episode
|
|
328
|
+
*/
|
|
329
|
+
private async cmdAddEpisode(
|
|
330
|
+
args: string[]
|
|
331
|
+
): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
332
|
+
if (args.length < 2) {
|
|
333
|
+
return {
|
|
334
|
+
success: false,
|
|
335
|
+
error: 'Usage: add_episode <title> <body> [source_description]',
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const title = args[0];
|
|
340
|
+
const body = args[1];
|
|
341
|
+
const sourceDescription = args[2];
|
|
342
|
+
|
|
343
|
+
const client = this.createClient();
|
|
344
|
+
const result = await client.addEpisode({
|
|
345
|
+
name: title,
|
|
346
|
+
episode_body: body,
|
|
347
|
+
source_description: sourceDescription,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
return result;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Command: search_nodes
|
|
355
|
+
*/
|
|
356
|
+
private async cmdSearchNodes(
|
|
357
|
+
args: string[]
|
|
358
|
+
): Promise<{ success: boolean; data?: unknown; error?: string; query?: string }> {
|
|
359
|
+
if (args.length < 1) {
|
|
360
|
+
return {
|
|
361
|
+
success: false,
|
|
362
|
+
error: 'Usage: search_nodes <query> [limit] [--since <date>] [--until <date>] [--weighted]',
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const query = args[0];
|
|
367
|
+
const limit = args.length > 1 ? Number.parseInt(args[1], 10) : 5;
|
|
368
|
+
const weighted = this.flags.weighted;
|
|
369
|
+
|
|
370
|
+
const client = this.createClient();
|
|
371
|
+
const result = await client.searchNodes({
|
|
372
|
+
query,
|
|
373
|
+
limit,
|
|
374
|
+
since: this.flags.since,
|
|
375
|
+
until: this.flags.until,
|
|
376
|
+
include_weighted_scores: weighted,
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
return { ...result, query };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Command: search_facts
|
|
384
|
+
*/
|
|
385
|
+
private async cmdSearchFacts(
|
|
386
|
+
args: string[]
|
|
387
|
+
): Promise<{ success: boolean; data?: unknown; error?: string; query?: string }> {
|
|
388
|
+
if (args.length < 1) {
|
|
389
|
+
return {
|
|
390
|
+
success: false,
|
|
391
|
+
error: 'Usage: search_facts <query> [limit] [--since <date>] [--until <date>]',
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const query = args[0];
|
|
396
|
+
const limit = args.length > 1 ? Number.parseInt(args[1], 10) : 5;
|
|
397
|
+
|
|
398
|
+
const client = this.createClient();
|
|
399
|
+
const result = await client.searchFacts({
|
|
400
|
+
query,
|
|
401
|
+
max_facts: limit,
|
|
402
|
+
since: this.flags.since,
|
|
403
|
+
until: this.flags.until,
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
return { ...result, query };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Command: get_episodes
|
|
411
|
+
*/
|
|
412
|
+
private async cmdGetEpisodes(
|
|
413
|
+
args: string[]
|
|
414
|
+
): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
415
|
+
const limit = args.length > 0 ? Number.parseInt(args[0], 10) : 5;
|
|
416
|
+
|
|
417
|
+
const client = this.createClient();
|
|
418
|
+
const result = await client.getEpisodes({ limit });
|
|
419
|
+
|
|
420
|
+
return result;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Command: get_status
|
|
425
|
+
*/
|
|
426
|
+
private async cmdGetStatus(): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
427
|
+
const client = this.createClient();
|
|
428
|
+
const result = await client.getStatus();
|
|
429
|
+
|
|
430
|
+
return result;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Command: clear_graph
|
|
435
|
+
*/
|
|
436
|
+
private async cmdClearGraph(
|
|
437
|
+
args: string[]
|
|
438
|
+
): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
439
|
+
// Safety check
|
|
440
|
+
if (!args.includes('--force')) {
|
|
441
|
+
return {
|
|
442
|
+
success: false,
|
|
443
|
+
error: 'This will delete ALL knowledge. Use --force to confirm.',
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const client = this.createClient();
|
|
448
|
+
const result = await client.clearGraph();
|
|
449
|
+
|
|
450
|
+
return result;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Command: health
|
|
455
|
+
*/
|
|
456
|
+
private async cmdHealth(): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
457
|
+
const client = this.createClient();
|
|
458
|
+
const result = await client.testConnection();
|
|
459
|
+
|
|
460
|
+
return result;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Feature 009: Command: health_metrics
|
|
465
|
+
*/
|
|
466
|
+
private async cmdHealthMetrics(
|
|
467
|
+
args: string[]
|
|
468
|
+
): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
469
|
+
// Parse optional --group-id flag
|
|
470
|
+
const groupId = args.includes('--group-id')
|
|
471
|
+
? args[args.indexOf('--group-id') + 1]
|
|
472
|
+
: undefined;
|
|
473
|
+
|
|
474
|
+
const client = this.createClient();
|
|
475
|
+
const result = await client.getKnowledgeHealth({ group_id: groupId });
|
|
476
|
+
|
|
477
|
+
return result;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Feature 009: Command: run_maintenance
|
|
482
|
+
*/
|
|
483
|
+
private async cmdRunMaintenance(
|
|
484
|
+
args: string[]
|
|
485
|
+
): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
486
|
+
const dryRun = args.includes('--dry-run');
|
|
487
|
+
|
|
488
|
+
const client = this.createClient();
|
|
489
|
+
const result = await client.runDecayMaintenance({ dry_run: dryRun });
|
|
490
|
+
|
|
491
|
+
return result;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Feature 009: Command: classify_memory
|
|
496
|
+
*/
|
|
497
|
+
private async cmdClassifyMemory(
|
|
498
|
+
args: string[]
|
|
499
|
+
): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
500
|
+
if (args.length < 1) {
|
|
501
|
+
return {
|
|
502
|
+
success: false,
|
|
503
|
+
error: 'Usage: classify_memory <content> [--source <description>]',
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const content = args[0];
|
|
508
|
+
const sourceIdx = args.indexOf('--source');
|
|
509
|
+
const sourceDescription = sourceIdx >= 0 ? args[sourceIdx + 1] : undefined;
|
|
510
|
+
|
|
511
|
+
const client = this.createClient();
|
|
512
|
+
const result = await client.classifyMemory({
|
|
513
|
+
content,
|
|
514
|
+
source_description: sourceDescription,
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
return result;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Feature 009: Command: recover_memory
|
|
522
|
+
*/
|
|
523
|
+
private async cmdRecoverMemory(
|
|
524
|
+
args: string[]
|
|
525
|
+
): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
526
|
+
if (args.length < 1) {
|
|
527
|
+
return {
|
|
528
|
+
success: false,
|
|
529
|
+
error: 'Usage: recover_memory <uuid>',
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const uuid = args[0];
|
|
534
|
+
|
|
535
|
+
const client = this.createClient();
|
|
536
|
+
const result = await client.recoverSoftDeleted({ uuid });
|
|
537
|
+
|
|
538
|
+
return result;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Feature 018: Command: ontology:list
|
|
543
|
+
*/
|
|
544
|
+
private async cmdOntologyList(): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
545
|
+
const client = this.createClient();
|
|
546
|
+
const result = await client.listOntologyTypes();
|
|
547
|
+
|
|
548
|
+
return result;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Feature 018: Command: ontology:validate
|
|
553
|
+
*/
|
|
554
|
+
private async cmdOntologyValidate(): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
555
|
+
const client = this.createClient();
|
|
556
|
+
const result = await client.validateOntology();
|
|
557
|
+
|
|
558
|
+
return result;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Feature 018: Command: ontology:reload
|
|
563
|
+
*/
|
|
564
|
+
private async cmdOntologyReload(): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
565
|
+
const client = this.createClient();
|
|
566
|
+
const result = await client.reloadOntology();
|
|
567
|
+
|
|
568
|
+
return result;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Feature 018: Command: stix:import
|
|
573
|
+
*/
|
|
574
|
+
private async cmdStixImport(args: string[]): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
575
|
+
if (args.length < 1) {
|
|
576
|
+
return {
|
|
577
|
+
success: false,
|
|
578
|
+
error: 'Usage: stix:import <file.json|url> [--batch-size N] [--continue-on-error]',
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const source = args[0];
|
|
583
|
+
const batchSize = args.includes('--batch-size')
|
|
584
|
+
? Number.parseInt(args[args.indexOf('--batch-size') + 1], 10)
|
|
585
|
+
: undefined;
|
|
586
|
+
const continueOnError = args.includes('--continue-on-error');
|
|
587
|
+
|
|
588
|
+
const client = this.createClient();
|
|
589
|
+
|
|
590
|
+
// Check if source is a file path or URL
|
|
591
|
+
let bundleData: Record<string, unknown>;
|
|
592
|
+
|
|
593
|
+
try {
|
|
594
|
+
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
595
|
+
// It's a URL - we'd need to fetch it, but for now let's return an error
|
|
596
|
+
return {
|
|
597
|
+
success: false,
|
|
598
|
+
error: 'URL import not yet supported. Please save the STIX bundle to a file and import from the file path.',
|
|
599
|
+
};
|
|
600
|
+
} else {
|
|
601
|
+
// It's a file path - read the file
|
|
602
|
+
const fs = await import('node:fs');
|
|
603
|
+
const fileContent = await fs.promises.readFile(source, 'utf-8');
|
|
604
|
+
bundleData = JSON.parse(fileContent);
|
|
605
|
+
}
|
|
606
|
+
} catch (error) {
|
|
607
|
+
return {
|
|
608
|
+
success: false,
|
|
609
|
+
error: `Failed to read STIX bundle: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const result = await client.importStixBundle({
|
|
614
|
+
bundle_data: bundleData,
|
|
615
|
+
batch_size: batchSize,
|
|
616
|
+
continue_on_error: continueOnError,
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
return result;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Feature 018: Command: stix:status
|
|
624
|
+
*/
|
|
625
|
+
private async cmdStixStatus(args: string[]): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
626
|
+
if (args.length < 1) {
|
|
627
|
+
return {
|
|
628
|
+
success: false,
|
|
629
|
+
error: 'Usage: stix:status <import_id>',
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const importId = args[0];
|
|
634
|
+
const client = this.createClient();
|
|
635
|
+
|
|
636
|
+
// Call with import_id parameter
|
|
637
|
+
const result = await (client as unknown as { getImportStatus: (params: { import_id: string }) => Promise<{ success: boolean; data?: unknown; error?: string }> }).getImportStatus({ import_id: importId });
|
|
638
|
+
|
|
639
|
+
return result;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Feature 010: Command: list_profiles
|
|
644
|
+
*/
|
|
645
|
+
private async cmdListProfiles(): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
646
|
+
try {
|
|
647
|
+
const profiles = profileManager.listProfiles();
|
|
648
|
+
const defaultProfile = profileManager.getDefaultProfile();
|
|
649
|
+
const currentProfile = this.flags.profile || getProfileName();
|
|
650
|
+
const configPath = profileManager.getConfigPath();
|
|
651
|
+
|
|
652
|
+
const data = {
|
|
653
|
+
default: defaultProfile,
|
|
654
|
+
current: currentProfile,
|
|
655
|
+
profiles: profiles,
|
|
656
|
+
config_path: configPath,
|
|
657
|
+
count: profiles.length,
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
return { success: true, data };
|
|
661
|
+
} catch (error) {
|
|
662
|
+
if (error instanceof Error) {
|
|
663
|
+
return { success: false, error: error.message };
|
|
664
|
+
}
|
|
665
|
+
return { success: false, error: 'Unknown error' };
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Feature 010: Command: status
|
|
671
|
+
*/
|
|
672
|
+
private async cmdStatus(): Promise<{ success: boolean; data?: unknown; error?: string }> {
|
|
673
|
+
try {
|
|
674
|
+
// Get current profile
|
|
675
|
+
const profileName = this.flags.profile || getProfileName();
|
|
676
|
+
const profile = loadProfileWithOverrides(profileName);
|
|
677
|
+
|
|
678
|
+
// Apply CLI flag overrides (highest priority)
|
|
679
|
+
const effectiveHost = this.flags.host || profile.host;
|
|
680
|
+
const effectivePort = this.flags.port ? Number.parseInt(this.flags.port, 10) : profile.port;
|
|
681
|
+
const effectiveProtocol = (this.flags.protocol as 'http' | 'https' | undefined) || profile.protocol;
|
|
682
|
+
|
|
683
|
+
// Test connection with overridden values
|
|
684
|
+
const client = createMCPClient({
|
|
685
|
+
protocol: effectiveProtocol,
|
|
686
|
+
host: effectiveHost,
|
|
687
|
+
port: effectivePort,
|
|
688
|
+
basePath: profile.basePath,
|
|
689
|
+
timeout: profile.timeout,
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
const healthResult = await client.testConnection();
|
|
693
|
+
|
|
694
|
+
const connectionState: ConnectionState = {
|
|
695
|
+
profile: profileName,
|
|
696
|
+
host: effectiveHost,
|
|
697
|
+
port: effectivePort,
|
|
698
|
+
protocol: effectiveProtocol,
|
|
699
|
+
status: healthResult.success ? 'connected' : 'error',
|
|
700
|
+
lastError: healthResult.success ? undefined : healthResult.error,
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
if (healthResult.success && healthResult.data) {
|
|
704
|
+
const healthData = healthResult.data as { status: string; version?: string };
|
|
705
|
+
connectionState.serverVersion = healthData.version;
|
|
706
|
+
connectionState.lastConnected = new Date();
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
return { success: true, data: connectionState };
|
|
710
|
+
} catch (error) {
|
|
711
|
+
if (error instanceof Error) {
|
|
712
|
+
return { success: false, error: error.message };
|
|
713
|
+
}
|
|
714
|
+
return { success: false, error: 'Unknown error' };
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Feature 020: Command: investigate
|
|
720
|
+
*/
|
|
721
|
+
private async cmdInvestigate(
|
|
722
|
+
args: string[]
|
|
723
|
+
): Promise<{ success: boolean; data?: unknown; error?: string; query?: string }> {
|
|
724
|
+
if (args.length < 1) {
|
|
725
|
+
return {
|
|
726
|
+
success: false,
|
|
727
|
+
error: 'Usage: investigate <entity_name> [--depth N] [--relationship-type TYPE]',
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
const entityName = args[0];
|
|
732
|
+
const depth = this.flags.depth ?? 1;
|
|
733
|
+
const relationshipTypes = this.flags.relationshipType;
|
|
734
|
+
|
|
735
|
+
// Validate depth
|
|
736
|
+
if (depth < 1 || depth > 3) {
|
|
737
|
+
return {
|
|
738
|
+
success: false,
|
|
739
|
+
error: 'Invalid depth: must be between 1 and 3',
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
const client = this.createClient();
|
|
744
|
+
const result = await client.investigateEntity({
|
|
745
|
+
entity_name: entityName,
|
|
746
|
+
max_depth: depth,
|
|
747
|
+
relationship_types: relationshipTypes && relationshipTypes.length > 0 ? relationshipTypes : undefined,
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
return { ...result, query: entityName };
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Print help message
|
|
755
|
+
*/
|
|
756
|
+
printHelp(): void {
|
|
757
|
+
cli.blank();
|
|
758
|
+
cli.header('Madeinoz Knowledge System - Knowledge CLI', 60);
|
|
759
|
+
cli.blank();
|
|
760
|
+
cli.info('Token-efficient command-line interface to the Graphiti MCP server.');
|
|
761
|
+
cli.info('Achieves 25-35% token savings through compact output formatting.');
|
|
762
|
+
cli.blank();
|
|
763
|
+
cli.info('Usage:');
|
|
764
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts <command> [args...] [options]');
|
|
765
|
+
cli.blank();
|
|
766
|
+
cli.info('Commands:');
|
|
767
|
+
cli.blank();
|
|
768
|
+
|
|
769
|
+
const commands = this.listCommands();
|
|
770
|
+
const maxLength = Math.max(...commands.map((c) => c.name.length));
|
|
771
|
+
|
|
772
|
+
commands.forEach((cmd) => {
|
|
773
|
+
const paddedName = cmd.name.padEnd(maxLength);
|
|
774
|
+
cli.dim(` ${paddedName} ${cmd.description}`);
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
cli.blank();
|
|
778
|
+
cli.info('Options:');
|
|
779
|
+
cli.blank();
|
|
780
|
+
cli.dim(' --raw Output raw JSON instead of compact format');
|
|
781
|
+
cli.dim(' --metrics Display token metrics after each operation');
|
|
782
|
+
cli.dim(' --metrics-file <p> Write metrics to JSONL file');
|
|
783
|
+
cli.dim(' --since <date> Filter results created after this date');
|
|
784
|
+
cli.dim(' --until <date> Filter results created before this date');
|
|
785
|
+
cli.dim(' --weighted Apply weighted scoring (semantic+recency+importance)');
|
|
786
|
+
cli.dim(' --profile <name> Use specific connection profile');
|
|
787
|
+
cli.dim(' --host <hostname> Override profile host');
|
|
788
|
+
cli.dim(' --port <port> Override profile port');
|
|
789
|
+
cli.dim(' --protocol <proto> Override profile protocol (http/https)');
|
|
790
|
+
cli.dim(' --tls-no-verify Disable TLS certificate verification');
|
|
791
|
+
cli.dim(' --depth <N> Investigation depth (1-3, default: 1)');
|
|
792
|
+
cli.dim(' --relationship-type Filter by relationship type (can specify multiple)');
|
|
793
|
+
cli.dim(' -h, --help Show this help message');
|
|
794
|
+
cli.blank();
|
|
795
|
+
cli.info('Date Formats (for --since/--until):');
|
|
796
|
+
cli.blank();
|
|
797
|
+
cli.dim(' ISO 8601: 2026-01-26, 2026-01-26T00:00:00Z');
|
|
798
|
+
cli.dim(' Relative: today, yesterday, now');
|
|
799
|
+
cli.dim(' Duration: 7d, 7 days, 1w, 1 week, 1m, 1 month');
|
|
800
|
+
cli.dim(' (all relative dates look backward from now)');
|
|
801
|
+
cli.blank();
|
|
802
|
+
cli.info('Environment Variables:');
|
|
803
|
+
cli.blank();
|
|
804
|
+
cli.dim(" MADEINOZ_WRAPPER_COMPACT Set to 'false' to disable compact output");
|
|
805
|
+
cli.dim(" MADEINOZ_WRAPPER_METRICS Set to 'true' to enable metrics collection");
|
|
806
|
+
cli.dim(' MADEINOZ_WRAPPER_METRICS_FILE Path to write metrics JSONL file');
|
|
807
|
+
cli.dim(' MADEINOZ_WRAPPER_LOG_FILE Path to write transformation error logs');
|
|
808
|
+
cli.dim(' MADEINOZ_WRAPPER_SLOW_THRESHOLD Slow processing threshold in ms (default: 50)');
|
|
809
|
+
cli.dim(' MADEINOZ_WRAPPER_TIMEOUT Processing timeout in ms (default: 100)');
|
|
810
|
+
cli.blank();
|
|
811
|
+
cli.info('TLS/SSL Environment Variables (Feature 010):');
|
|
812
|
+
cli.blank();
|
|
813
|
+
cli.dim(' MADEINOZ_KNOWLEDGE_PROTOCOL http or https (default: http)');
|
|
814
|
+
cli.dim(' MADEINOZ_KNOWLEDGE_HOST Hostname or IP address (default: localhost)');
|
|
815
|
+
cli.dim(' MADEINOZ_KNOWLEDGE_PORT TCP port (default: 8001)');
|
|
816
|
+
cli.dim(' MADEINOZ_KNOWLEDGE_TLS_VERIFY Enable certificate verification (default: true)');
|
|
817
|
+
cli.dim(' MADEINOZ_KNOWLEDGE_TLS_CA Path to CA certificate file');
|
|
818
|
+
cli.dim(' MADEINOZ_KNOWLEDGE_TLS_CERT Path to client certificate file');
|
|
819
|
+
cli.dim(' MADEINOZ_KNOWLEDGE_TLS_KEY Path to client private key file');
|
|
820
|
+
cli.blank();
|
|
821
|
+
cli.info('Examples:');
|
|
822
|
+
cli.blank();
|
|
823
|
+
cli.dim(' # Add knowledge to graph');
|
|
824
|
+
cli.dim(
|
|
825
|
+
' bun run src/skills/tools/knowledge-cli.ts add_episode "Test Episode" "This is a test episode"'
|
|
826
|
+
);
|
|
827
|
+
cli.blank();
|
|
828
|
+
cli.dim(' # Search for entities (compact output, 30%+ token savings)');
|
|
829
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts search_nodes "PAI" 10');
|
|
830
|
+
cli.blank();
|
|
831
|
+
cli.dim(' # Search with raw JSON output');
|
|
832
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts search_nodes "PAI" --raw');
|
|
833
|
+
cli.blank();
|
|
834
|
+
cli.dim(' # Search with metrics display');
|
|
835
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts search_nodes "PAI" --metrics');
|
|
836
|
+
cli.blank();
|
|
837
|
+
cli.dim(' # Save metrics to file for analysis');
|
|
838
|
+
cli.dim(
|
|
839
|
+
' bun run src/skills/tools/knowledge-cli.ts search_nodes "PAI" --metrics-file ~/.madeinoz-knowledge/metrics.jsonl'
|
|
840
|
+
);
|
|
841
|
+
cli.blank();
|
|
842
|
+
cli.dim(' # Get graph status');
|
|
843
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts get_status');
|
|
844
|
+
cli.blank();
|
|
845
|
+
cli.dim(' # Check server health');
|
|
846
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts health');
|
|
847
|
+
cli.blank();
|
|
848
|
+
cli.dim(' # Clear graph (destructive - requires --force)');
|
|
849
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts clear_graph --force');
|
|
850
|
+
cli.blank();
|
|
851
|
+
cli.dim(' # Feature 009: Memory decay and lifecycle');
|
|
852
|
+
cli.blank();
|
|
853
|
+
cli.dim(' # Get memory lifecycle health metrics');
|
|
854
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts health_metrics');
|
|
855
|
+
cli.blank();
|
|
856
|
+
cli.dim(' # Run decay maintenance cycle');
|
|
857
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts run_maintenance');
|
|
858
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts run_maintenance --dry-run');
|
|
859
|
+
cli.blank();
|
|
860
|
+
cli.dim(' # Classify memory importance and stability');
|
|
861
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts classify_memory "I prefer dark mode"');
|
|
862
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts classify_memory "Allergic to peanuts" --source "medical"');
|
|
863
|
+
cli.blank();
|
|
864
|
+
cli.dim(' # Recover a soft-deleted memory');
|
|
865
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts recover_memory abc123-def456-...');
|
|
866
|
+
cli.blank();
|
|
867
|
+
cli.info('Temporal Search Examples:');
|
|
868
|
+
cli.blank();
|
|
869
|
+
cli.dim(" # Search nodes from today");
|
|
870
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts search_nodes "PAI" --since today');
|
|
871
|
+
cli.blank();
|
|
872
|
+
cli.dim(" # Search facts from last 7 days");
|
|
873
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts search_facts "decisions" --since 7d');
|
|
874
|
+
cli.blank();
|
|
875
|
+
cli.dim(" # Search within a date range");
|
|
876
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts search_nodes "project" --since 2026-01-01 --until 2026-01-15');
|
|
877
|
+
cli.blank();
|
|
878
|
+
cli.dim(" # Yesterday's knowledge only");
|
|
879
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts search_nodes "learning" --since yesterday --until today');
|
|
880
|
+
cli.blank();
|
|
881
|
+
cli.dim(" # Last month's architecture decisions");
|
|
882
|
+
cli.dim(' bun run src/skills/tools/knowledge-cli.ts search_facts "architecture" --since 1m');
|
|
883
|
+
cli.blank();
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Execute a command
|
|
888
|
+
*/
|
|
889
|
+
async execute(commandName: string, args: string[]): Promise<number> {
|
|
890
|
+
const command = this.getCommand(commandName);
|
|
891
|
+
|
|
892
|
+
if (!command) {
|
|
893
|
+
cli.error(`Unknown command: ${commandName}`);
|
|
894
|
+
cli.blank();
|
|
895
|
+
cli.info(`Available commands: ${Array.from(this.commands.keys()).join(', ')}`);
|
|
896
|
+
return 1;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// Execute command
|
|
900
|
+
const result = await command.handler(args);
|
|
901
|
+
|
|
902
|
+
// Output result
|
|
903
|
+
if (result.success) {
|
|
904
|
+
if (result.data !== undefined) {
|
|
905
|
+
// Use raw JSON output if --raw flag is set
|
|
906
|
+
if (this.flags.raw) {
|
|
907
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
908
|
+
} else {
|
|
909
|
+
// Use compact formatter
|
|
910
|
+
const formatOptions: FormatOptions = {
|
|
911
|
+
collectMetrics: this.flags.metrics,
|
|
912
|
+
query: result.query,
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
const formatted = formatOutput(commandName, result.data, formatOptions);
|
|
916
|
+
console.log(formatted.output);
|
|
917
|
+
|
|
918
|
+
// Show metrics if requested
|
|
919
|
+
if (this.flags.metrics && formatted.metrics) {
|
|
920
|
+
console.log();
|
|
921
|
+
console.log('--- Token Metrics ---');
|
|
922
|
+
console.log(`Operation: ${commandName}`);
|
|
923
|
+
console.log(
|
|
924
|
+
`Raw size: ${formatted.metrics.rawBytes.toLocaleString()} bytes (${Math.ceil(formatted.metrics.rawBytes / 4)} est. tokens)`
|
|
925
|
+
);
|
|
926
|
+
console.log(
|
|
927
|
+
`Compact size: ${formatted.metrics.compactBytes.toLocaleString()} bytes (${Math.ceil(formatted.metrics.compactBytes / 4)} est. tokens)`
|
|
928
|
+
);
|
|
929
|
+
const tokensSaved =
|
|
930
|
+
Math.ceil(formatted.metrics.rawBytes / 4) -
|
|
931
|
+
Math.ceil(formatted.metrics.compactBytes / 4);
|
|
932
|
+
console.log(
|
|
933
|
+
`Savings: ${formatted.metrics.savingsPercent.toFixed(1)}% (${tokensSaved} tokens saved)`
|
|
934
|
+
);
|
|
935
|
+
console.log(`Processing time: ${formatted.metrics.processingTimeMs.toFixed(0)}ms`);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// Write to metrics file if specified
|
|
939
|
+
if (this.flags.metricsFile && formatted.metrics) {
|
|
940
|
+
const { appendFile, mkdir } = await import('node:fs/promises');
|
|
941
|
+
const { dirname } = await import('node:path');
|
|
942
|
+
|
|
943
|
+
try {
|
|
944
|
+
await mkdir(dirname(this.flags.metricsFile), { recursive: true });
|
|
945
|
+
const metricsLine = `${JSON.stringify({
|
|
946
|
+
operation: commandName,
|
|
947
|
+
timestamp: new Date().toISOString(),
|
|
948
|
+
rawBytes: formatted.metrics.rawBytes,
|
|
949
|
+
compactBytes: formatted.metrics.compactBytes,
|
|
950
|
+
savingsPercent: formatted.metrics.savingsPercent,
|
|
951
|
+
estimatedTokensBefore: Math.ceil(formatted.metrics.rawBytes / 4),
|
|
952
|
+
estimatedTokensAfter: Math.ceil(formatted.metrics.compactBytes / 4),
|
|
953
|
+
processingTimeMs: formatted.metrics.processingTimeMs,
|
|
954
|
+
})}\n`;
|
|
955
|
+
await appendFile(this.flags.metricsFile, metricsLine, 'utf-8');
|
|
956
|
+
} catch (error) {
|
|
957
|
+
cli.warning(`Failed to write metrics: ${error}`);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
return 0;
|
|
963
|
+
}
|
|
964
|
+
cli.error(`Error: ${result.error}`);
|
|
965
|
+
return 1;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
/**
|
|
970
|
+
* Main function
|
|
971
|
+
*/
|
|
972
|
+
async function main() {
|
|
973
|
+
const { flags, positionalArgs } = parseFlags(process.argv.slice(2));
|
|
974
|
+
|
|
975
|
+
// Show help if no arguments or help flag
|
|
976
|
+
if (positionalArgs.length === 0 || flags.help) {
|
|
977
|
+
const wrapper = new MCPWrapper(flags);
|
|
978
|
+
wrapper.printHelp();
|
|
979
|
+
process.exit(0);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// Execute command
|
|
983
|
+
const commandName = positionalArgs[0];
|
|
984
|
+
const commandArgs = positionalArgs.slice(1);
|
|
985
|
+
|
|
986
|
+
const wrapper = new MCPWrapper(flags);
|
|
987
|
+
const exitCode = await wrapper.execute(commandName, commandArgs);
|
|
988
|
+
process.exit(exitCode);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// Run main function
|
|
992
|
+
main().catch((error) => {
|
|
993
|
+
cli.error('Unexpected error:');
|
|
994
|
+
console.error(error);
|
|
995
|
+
process.exit(1);
|
|
996
|
+
});
|