@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.
Files changed (70) hide show
  1. package/.deepquark/skills/bundled/knowledge-graph/SKILL.md +385 -0
  2. package/.deepquark/skills/bundled/knowledge-graph/STANDARDS.md +461 -0
  3. package/.deepquark/skills/bundled/knowledge-graph/lib/cli.ts +588 -0
  4. package/.deepquark/skills/bundled/knowledge-graph/lib/config.ts +630 -0
  5. package/.deepquark/skills/bundled/knowledge-graph/lib/connection-profile.ts +629 -0
  6. package/.deepquark/skills/bundled/knowledge-graph/lib/container.ts +756 -0
  7. package/.deepquark/skills/bundled/knowledge-graph/lib/mcp-client.ts +1310 -0
  8. package/.deepquark/skills/bundled/knowledge-graph/lib/output-formatter.ts +997 -0
  9. package/.deepquark/skills/bundled/knowledge-graph/lib/token-metrics.ts +335 -0
  10. package/.deepquark/skills/bundled/knowledge-graph/lib/transformation-log.ts +137 -0
  11. package/.deepquark/skills/bundled/knowledge-graph/lib/wrapper-config.ts +113 -0
  12. package/.deepquark/skills/bundled/knowledge-graph/server/.env.example +129 -0
  13. package/.deepquark/skills/bundled/knowledge-graph/server/compare-embeddings.ts +175 -0
  14. package/.deepquark/skills/bundled/knowledge-graph/server/config-falkordb.yaml +108 -0
  15. package/.deepquark/skills/bundled/knowledge-graph/server/config-neo4j.yaml +111 -0
  16. package/.deepquark/skills/bundled/knowledge-graph/server/diagnose.ts +483 -0
  17. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-falkordb-dev.yml +146 -0
  18. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-falkordb.yml +151 -0
  19. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j-dev-local.yml +161 -0
  20. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j-dev.yml +161 -0
  21. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-neo4j.yml +169 -0
  22. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-production.yml +128 -0
  23. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose-test.yml +10 -0
  24. package/.deepquark/skills/bundled/knowledge-graph/server/docker-compose.yml +84 -0
  25. package/.deepquark/skills/bundled/knowledge-graph/server/entrypoint.sh +40 -0
  26. package/.deepquark/skills/bundled/knowledge-graph/server/install.ts +2054 -0
  27. package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose-falkordb.yml +78 -0
  28. package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose-neo4j.yml +88 -0
  29. package/.deepquark/skills/bundled/knowledge-graph/server/podman-compose.yml +83 -0
  30. package/.deepquark/skills/bundled/knowledge-graph/server/test-all-llms-mcp.ts +387 -0
  31. package/.deepquark/skills/bundled/knowledge-graph/server/test-embedding-models.ts +201 -0
  32. package/.deepquark/skills/bundled/knowledge-graph/server/test-embedding-providers.ts +641 -0
  33. package/.deepquark/skills/bundled/knowledge-graph/server/test-graphiti-model.ts +217 -0
  34. package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-correct.ts +141 -0
  35. package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-llms-mcp.ts +386 -0
  36. package/.deepquark/skills/bundled/knowledge-graph/server/test-grok-models.ts +173 -0
  37. package/.deepquark/skills/bundled/knowledge-graph/server/test-llama-extraction.ts +188 -0
  38. package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-final.ts +240 -0
  39. package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-live.ts +187 -0
  40. package/.deepquark/skills/bundled/knowledge-graph/server/test-mcp-session.ts +127 -0
  41. package/.deepquark/skills/bundled/knowledge-graph/server/test-model-combinations.ts +316 -0
  42. package/.deepquark/skills/bundled/knowledge-graph/server/test-ollama-models.ts +228 -0
  43. package/.deepquark/skills/bundled/knowledge-graph/server/test-openrouter-models.ts +460 -0
  44. package/.deepquark/skills/bundled/knowledge-graph/server/test-real-life-mcp.ts +311 -0
  45. package/.deepquark/skills/bundled/knowledge-graph/server/test-search-debug.ts +199 -0
  46. package/.deepquark/skills/bundled/knowledge-graph/tools/Install.md +104 -0
  47. package/.deepquark/skills/bundled/knowledge-graph/tools/README.md +120 -0
  48. package/.deepquark/skills/bundled/knowledge-graph/tools/knowledge-cli.ts +996 -0
  49. package/.deepquark/skills/bundled/knowledge-graph/tools/server-cli.ts +531 -0
  50. package/.deepquark/skills/bundled/knowledge-graph/workflows/BulkImport.md +514 -0
  51. package/.deepquark/skills/bundled/knowledge-graph/workflows/CaptureEpisode.md +242 -0
  52. package/.deepquark/skills/bundled/knowledge-graph/workflows/ClearGraph.md +392 -0
  53. package/.deepquark/skills/bundled/knowledge-graph/workflows/GetRecent.md +352 -0
  54. package/.deepquark/skills/bundled/knowledge-graph/workflows/GetStatus.md +373 -0
  55. package/.deepquark/skills/bundled/knowledge-graph/workflows/HealthReport.md +212 -0
  56. package/.deepquark/skills/bundled/knowledge-graph/workflows/InvestigateEntity.md +142 -0
  57. package/.deepquark/skills/bundled/knowledge-graph/workflows/OntologyManagement.md +201 -0
  58. package/.deepquark/skills/bundled/knowledge-graph/workflows/RunMaintenance.md +302 -0
  59. package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchByDate.md +255 -0
  60. package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchFacts.md +382 -0
  61. package/.deepquark/skills/bundled/knowledge-graph/workflows/SearchKnowledge.md +374 -0
  62. package/.deepquark/skills/bundled/knowledge-graph/workflows/StixImport.md +212 -0
  63. package/bin/deepquark +0 -0
  64. package/package.json +1 -1
  65. package/.deepquark/skills/bundled/ge-payroll/SKILL.md +0 -153
  66. package/.deepquark/skills/bundled/ge-payroll/evals/evals.json +0 -23
  67. package/.deepquark/skills/bundled/ge-payroll/references/pain-points-improvements.md +0 -106
  68. package/.deepquark/skills/bundled/ge-payroll/references/process-detail.md +0 -217
  69. package/.deepquark/skills/bundled/ge-payroll/references/raci-stakeholders.md +0 -85
  70. 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
+ });