coaian 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1879 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import { promises as fs } from 'fs';
6
+ import path from 'path';
7
+ import { LLM_GUIDANCE } from "./generated-llm-guidance.js";
8
+ import { fileURLToPath } from 'url';
9
+ import minimist from 'minimist';
10
+ import { isAbsolute } from 'path';
11
+ import { validate, ValidationSchemas } from './validation.js';
12
+ // Parse args and handle paths safely
13
+ const argv = minimist(process.argv.slice(2));
14
+ // Handle help command
15
+ if (argv.help || argv.h) {
16
+ console.log(`
17
+ 🧠 COAIA Memory - Creative-Oriented AI Assistant Memory System v2.1.0
18
+ Based on Robert Fritz's Structural Tension methodology
19
+
20
+ DESCRIPTION:
21
+ MCP server that extends knowledge graphs with structural tension charts for
22
+ creative-oriented memory management. Supports advancing patterns, telescoping
23
+ charts, and natural language interaction for AI assistants.
24
+
25
+ USAGE:
26
+ coaia-memory [OPTIONS]
27
+ npx coaia-memory [OPTIONS]
28
+
29
+ OPTIONS:
30
+ --memory-path PATH Custom path for memory storage (default: ./memory.jsonl)
31
+ --help, -h Show this help message
32
+
33
+ ENVIRONMENT VARIABLES:
34
+ COAIA_TOOLS Comma or space separated list of tool groups and/or individual tools to enable
35
+ (default: "STC_TOOLS,init_llm_guidance")
36
+
37
+ COAIA_DISABLED_TOOLS Comma or space separated list of tools to disable
38
+ (useful for selectively removing tools from a group)
39
+
40
+ TOOL GROUPS:
41
+ STC_TOOLS All structural tension chart tools (11 tools) - recommended for creative work
42
+ KG_TOOLS All knowledge graph tools (9 tools) - for traditional entity/relation work
43
+ CORE_TOOLS Essential tools only (4 tools) - minimal viable set
44
+
45
+ EXAMPLES:
46
+ # Use only STC tools (default)
47
+ coaia-memory --memory-path ./memory.jsonl
48
+
49
+ # Enable both STC and KG tools
50
+ COAIA_TOOLS="STC_TOOLS KG_TOOLS" coaia-memory --memory-path ./memory.jsonl
51
+
52
+ # Use only core tools
53
+ COAIA_TOOLS="CORE_TOOLS" coaia-memory --memory-path ./memory.jsonl
54
+
55
+ # Enable STC tools but disable specific tools
56
+ COAIA_TOOLS="STC_TOOLS" COAIA_DISABLED_TOOLS="delete_entities,delete_relations" coaia-memory
57
+
58
+ # Enable specific individual tools
59
+ COAIA_TOOLS="create_structural_tension_chart add_action_step list_active_charts" coaia-memory
60
+
61
+ CORE FEATURES:
62
+
63
+ šŸ“Š Structural Tension Charts
64
+ • Create charts with desired outcomes, current reality, and action steps
65
+ • Automatic due date distribution for strategic timing
66
+ • Progress tracking and completion monitoring
67
+
68
+ šŸ”­ Telescoping Support
69
+ • Break down action steps into detailed sub-charts
70
+ • Proper due date inheritance from parent steps
71
+ • Navigate between overview and details seamlessly
72
+
73
+ šŸ“ˆ Advancing Patterns
74
+ • Completed actions flow into current reality automatically
75
+ • Success builds momentum for continued advancement
76
+ • Prevents oscillating patterns through structural awareness
77
+
78
+ MCP TOOLS AVAILABLE:
79
+
80
+ Chart Management (Common Workflow):
81
+ • list_active_charts - START HERE: See all charts and their progress
82
+ • add_action_step - Add strategic actions to existing charts
83
+ • telescope_action_step - Break down action steps into detailed sub-charts
84
+ • update_action_progress - Track progress without completing actions
85
+ • mark_action_complete - Complete actions & update reality
86
+ • update_current_reality - Add observations directly to current reality
87
+ • create_structural_tension_chart - Create new chart with outcome & reality
88
+
89
+ Chart Analysis (Advanced):
90
+ • get_chart_progress - Detailed progress (redundant after list_active_charts)
91
+ • open_nodes - Inspect specific chart components by exact name
92
+ • read_graph - Dump all data (rarely needed)
93
+
94
+ Knowledge Graph (Traditional):
95
+ • create_entities - Add entities (people, concepts, events)
96
+ • create_relations - Connect entities with relationships
97
+ • add_observations - Record information about entities
98
+ • search_nodes - Search across all stored information
99
+ • read_graph - Export complete graph structure
100
+
101
+ EXAMPLE USAGE:
102
+
103
+ # Start with custom memory path
104
+ coaia-memory --memory-path /path/to/my-charts.jsonl
105
+
106
+ # Use in Claude Desktop (add to claude_desktop_config.json):
107
+ {
108
+ "mcpServers": {
109
+ "coaia-memory": {
110
+ "command": "npx",
111
+ "args": ["-y", "coaia-memory", "--memory-path", "./charts.jsonl"]
112
+ }
113
+ }
114
+ }
115
+
116
+ NATURAL LANGUAGE PATTERNS:
117
+
118
+ Creating Charts:
119
+ "I want to create a mobile app in 3 months"
120
+ "My desired outcome is to establish a morning routine"
121
+
122
+ Progress Tracking:
123
+ "I completed the research phase yesterday"
124
+ "Show me progress on my Python learning goal"
125
+
126
+ Telescoping:
127
+ "Break down the Django tutorial step further"
128
+ "I need more detail on the deployment action"
129
+
130
+ CREATIVE ORIENTATION PRINCIPLES:
131
+
132
+ āœ… Focus on Creation (not problem-solving):
133
+ • "I want to create..." vs "I need to fix..."
134
+ • "My desired outcome..." vs "The problem is..."
135
+
136
+ āœ… Structural Tension Awareness:
137
+ • Always pair desired outcomes with current reality
138
+ • Honest assessment creates productive tension
139
+ • Action steps are strategic secondary action we choose todo to achive the primary goal
140
+
141
+ āœ… Advancing Patterns:
142
+ • Success builds on success
143
+ • Completed actions become part of current reality
144
+ • Momentum creates natural progression toward goals
145
+
146
+ PHILOSOPHY:
147
+ COAIA Memory recognizes that structure determines behavior. By organizing
148
+ memory around structural tension rather than problem-solving patterns, it
149
+ naturally forms a structure that advances and helps build, not just the life you want, but the technologies to supports it's manifestation (hopefully!).
150
+
151
+ CREDITS:
152
+ • Author: J.Guillaume D.-Isabelle <jgi@jgwill.com>
153
+ • Methodology: Robert Fritz - https://robertfritz.com
154
+ • Foundation: Shane Holloman (original mcp-knowledge-graph)
155
+ • License: MIT
156
+
157
+ For more information, see: CLAUDE.md in the package directory
158
+ `);
159
+ process.exit(0);
160
+ }
161
+ let memoryPath = argv['memory-path'];
162
+ // If a custom path is provided, ensure it's absolute
163
+ if (memoryPath && !isAbsolute(memoryPath)) {
164
+ memoryPath = path.resolve(process.cwd(), memoryPath);
165
+ }
166
+ // Tool filtering configuration
167
+ const TOOL_GROUPS = {
168
+ STC_TOOLS: [
169
+ 'create_structural_tension_chart',
170
+ 'telescope_action_step',
171
+ 'add_action_step',
172
+ 'remove_action_step',
173
+ 'mark_action_complete',
174
+ 'get_chart_progress',
175
+ 'list_active_charts',
176
+ 'update_action_progress',
177
+ 'update_current_reality',
178
+ 'update_desired_outcome',
179
+ 'creator_moment_of_truth'
180
+ ],
181
+ NARRATIVE_TOOLS: [
182
+ 'create_narrative_beat',
183
+ 'telescope_narrative_beat',
184
+ 'list_narrative_beats'
185
+ ],
186
+ KG_TOOLS: [
187
+ 'create_entities',
188
+ 'create_relations',
189
+ 'add_observations',
190
+ 'delete_entities',
191
+ 'delete_observations',
192
+ 'delete_relations',
193
+ 'search_nodes',
194
+ 'open_nodes',
195
+ 'read_graph'
196
+ ],
197
+ CORE_TOOLS: [
198
+ 'list_active_charts',
199
+ 'create_structural_tension_chart',
200
+ 'add_action_step',
201
+ 'mark_action_complete'
202
+ ]
203
+ };
204
+ function getEnabledTools() {
205
+ const enabledTools = new Set();
206
+ // Check for COAIA_DISABLED_TOOLS env var (comma or space separated)
207
+ const disabledStr = process.env.COAIA_DISABLED_TOOLS || '';
208
+ const disabledTools = new Set(disabledStr.split(/[,\s]+/).filter(t => t.trim()));
209
+ // Determine which tools to enable
210
+ const enabledGroupsStr = process.env.COAIA_TOOLS || 'STC_TOOLS,NARRATIVE_TOOLS,init_llm_guidance';
211
+ const enabledGroups = enabledGroupsStr.split(/[,\s]+/).filter(t => t.trim());
212
+ enabledGroups.forEach(group => {
213
+ const groupTools = TOOL_GROUPS[group];
214
+ if (groupTools) {
215
+ groupTools.forEach(tool => enabledTools.add(tool));
216
+ }
217
+ else {
218
+ // Assume it's an individual tool name
219
+ enabledTools.add(group);
220
+ }
221
+ });
222
+ // Remove disabled tools
223
+ disabledTools.forEach(tool => enabledTools.delete(tool));
224
+ return enabledTools;
225
+ }
226
+ // Define the path to the JSONL file
227
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
228
+ // Use the custom path or default to the installation directory
229
+ const MEMORY_FILE_PATH = memoryPath || path.join(__dirname, 'memory.jsonl');
230
+ // The KnowledgeGraphManager class contains all operations to interact with the knowledge graph
231
+ class KnowledgeGraphManager {
232
+ async loadGraph() {
233
+ try {
234
+ const data = await fs.readFile(MEMORY_FILE_PATH, "utf-8");
235
+ const lines = data.split("\n").filter(line => line.trim() !== "");
236
+ return lines.reduce((graph, line) => {
237
+ const item = JSON.parse(line);
238
+ if (item.type === "entity")
239
+ graph.entities.push(item);
240
+ if (item.type === "relation")
241
+ graph.relations.push(item);
242
+ // Support narrative_beat entities (convert to entity format)
243
+ if (item.type === "narrative_beat") {
244
+ const narrativeBeat = {
245
+ name: item.name,
246
+ entityType: 'narrative_beat',
247
+ observations: item.observations || [],
248
+ metadata: {
249
+ ...item.metadata,
250
+ narrative: item.narrative,
251
+ relationalAlignment: item.relational_alignment,
252
+ fourDirections: item.four_directions
253
+ }
254
+ };
255
+ graph.entities.push(narrativeBeat);
256
+ }
257
+ return graph;
258
+ }, { entities: [], relations: [] });
259
+ }
260
+ catch (error) {
261
+ if (error instanceof Error && 'code' in error && error.code === "ENOENT") {
262
+ return { entities: [], relations: [] };
263
+ }
264
+ throw error;
265
+ }
266
+ }
267
+ // Helper function to extract current reality from user context
268
+ // Maintains structural tension by requiring explicit assessment
269
+ extractCurrentRealityFromContext(userInput, actionStepTitle) {
270
+ // Common patterns that indicate current reality assessment
271
+ const realityPatterns = [
272
+ /(?:currently|right now|at present|today)\s+(.{10,})/i,
273
+ /(?:i am|we are|the situation is)\s+(.{10,})/i,
274
+ /(?:i have|we have|there is|there are)\s+(.{10,})/i,
275
+ /(?:my current|our current|the current)\s+(.{10,})/i
276
+ ];
277
+ for (const pattern of realityPatterns) {
278
+ const match = userInput.match(pattern);
279
+ if (match && match[1]) {
280
+ return match[1].trim();
281
+ }
282
+ }
283
+ // If no explicit current reality found, require assessment
284
+ return null;
285
+ }
286
+ async saveGraph(graph) {
287
+ const lines = [
288
+ ...graph.entities.map(e => JSON.stringify({ type: "entity", ...e })),
289
+ ...graph.relations.map(r => JSON.stringify({ type: "relation", ...r })),
290
+ ];
291
+ await fs.writeFile(MEMORY_FILE_PATH, lines.join("\n"));
292
+ }
293
+ async createEntities(entities) {
294
+ const graph = await this.loadGraph();
295
+ const newEntities = entities.filter(e => !graph.entities.some(existingEntity => existingEntity.name === e.name));
296
+ graph.entities.push(...newEntities);
297
+ await this.saveGraph(graph);
298
+ return newEntities;
299
+ }
300
+ async createRelations(relations) {
301
+ const graph = await this.loadGraph();
302
+ const newRelations = relations.filter(r => !graph.relations.some(existingRelation => existingRelation.from === r.from &&
303
+ existingRelation.to === r.to &&
304
+ existingRelation.relationType === r.relationType));
305
+ graph.relations.push(...newRelations);
306
+ await this.saveGraph(graph);
307
+ return newRelations;
308
+ }
309
+ async addObservations(observations) {
310
+ const graph = await this.loadGraph();
311
+ const results = observations.map(o => {
312
+ const entity = graph.entities.find(e => e.name === o.entityName);
313
+ if (!entity) {
314
+ throw new Error(`Entity with name ${o.entityName} not found`);
315
+ }
316
+ const newObservations = o.contents.filter(content => !entity.observations.includes(content));
317
+ entity.observations.push(...newObservations);
318
+ return { entityName: o.entityName, addedObservations: newObservations };
319
+ });
320
+ await this.saveGraph(graph);
321
+ return results;
322
+ }
323
+ async deleteEntities(entityNames) {
324
+ const graph = await this.loadGraph();
325
+ graph.entities = graph.entities.filter(e => !entityNames.includes(e.name));
326
+ graph.relations = graph.relations.filter(r => !entityNames.includes(r.from) && !entityNames.includes(r.to));
327
+ await this.saveGraph(graph);
328
+ }
329
+ async deleteObservations(deletions) {
330
+ const graph = await this.loadGraph();
331
+ deletions.forEach(d => {
332
+ const entity = graph.entities.find(e => e.name === d.entityName);
333
+ if (entity) {
334
+ entity.observations = entity.observations.filter(o => !d.observations.includes(o));
335
+ }
336
+ });
337
+ await this.saveGraph(graph);
338
+ }
339
+ async deleteRelations(relations) {
340
+ const graph = await this.loadGraph();
341
+ graph.relations = graph.relations.filter(r => !relations.some(delRelation => r.from === delRelation.from &&
342
+ r.to === delRelation.to &&
343
+ r.relationType === delRelation.relationType));
344
+ await this.saveGraph(graph);
345
+ }
346
+ async readGraph() {
347
+ return this.loadGraph();
348
+ }
349
+ // Very basic search function
350
+ async searchNodes(query) {
351
+ const graph = await this.loadGraph();
352
+ // Filter entities
353
+ const filteredEntities = graph.entities.filter(e => e.name.toLowerCase().includes(query.toLowerCase()) ||
354
+ e.entityType.toLowerCase().includes(query.toLowerCase()) ||
355
+ e.observations.some(o => o.toLowerCase().includes(query.toLowerCase())));
356
+ // Create a Set of filtered entity names for quick lookup
357
+ const filteredEntityNames = new Set(filteredEntities.map(e => e.name));
358
+ // Filter relations to only include those between filtered entities
359
+ const filteredRelations = graph.relations.filter(r => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to));
360
+ const filteredGraph = {
361
+ entities: filteredEntities,
362
+ relations: filteredRelations,
363
+ };
364
+ return filteredGraph;
365
+ }
366
+ async openNodes(names) {
367
+ const graph = await this.loadGraph();
368
+ // Filter entities
369
+ const filteredEntities = graph.entities.filter(e => names.includes(e.name));
370
+ // Create a Set of filtered entity names for quick lookup
371
+ const filteredEntityNames = new Set(filteredEntities.map(e => e.name));
372
+ // Filter relations to only include those between filtered entities
373
+ const filteredRelations = graph.relations.filter(r => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to));
374
+ const filteredGraph = {
375
+ entities: filteredEntities,
376
+ relations: filteredRelations,
377
+ };
378
+ return filteredGraph;
379
+ }
380
+ // COAIA-specific methods for structural tension charts and creative processes
381
+ async createStructuralTensionChart(desiredOutcome, currentReality, dueDate, actionSteps) {
382
+ // Educational validation for creative orientation
383
+ const problemSolvingWords = ['fix', 'solve', 'eliminate', 'prevent', 'stop', 'avoid', 'reduce', 'remove'];
384
+ const detectedProblemWords = problemSolvingWords.filter(word => desiredOutcome.toLowerCase().includes(word));
385
+ if (detectedProblemWords.length > 0) {
386
+ throw new Error(`🌊 CREATIVE ORIENTATION REQUIRED
387
+
388
+ Desired Outcome: "${desiredOutcome}"
389
+
390
+ āŒ **Problem**: Contains problem-solving language: "${detectedProblemWords.join(', ')}"
391
+ šŸ“š **Principle**: Structural Tension Charts use creative orientation - focus on what you want to CREATE, not what you want to eliminate.
392
+
393
+ šŸŽÆ **Reframe Your Outcome**:
394
+ Instead of elimination → Creation focus
395
+
396
+ āœ… **Examples**:
397
+ - Instead of: "Fix communication problems"
398
+ - Use: "Establish clear, effective communication practices"
399
+
400
+ - Instead of: "Reduce website loading time"
401
+ - Use: "Achieve fast, responsive website performance"
402
+
403
+ **Why This Matters**: Problem-solving creates oscillating patterns. Creative orientation creates advancing patterns toward desired outcomes.
404
+
405
+ šŸ’” **Tip**: Run 'init_llm_guidance' for complete methodology overview.`);
406
+ }
407
+ // Educational validation for current reality
408
+ const readinessWords = ['ready to', 'prepared to', 'all set', 'ready for', 'set to'];
409
+ const detectedReadinessWords = readinessWords.filter(phrase => currentReality.toLowerCase().includes(phrase));
410
+ if (detectedReadinessWords.length > 0) {
411
+ throw new Error(`🌊 DELAYED RESOLUTION PRINCIPLE VIOLATION
412
+
413
+ Current Reality: "${currentReality}"
414
+
415
+ āŒ **Problem**: Contains readiness assumptions: "${detectedReadinessWords.join(', ')}"
416
+ šŸ“š **Principle**: "Tolerate discrepancy, tension, and delayed resolution" - Robert Fritz
417
+
418
+ šŸŽÆ **What's Needed**: Factual assessment of your actual current state (not readiness or preparation).
419
+
420
+ āœ… **Examples**:
421
+ - Instead of: "Ready to learn Python"
422
+ - Use: "Never programmed before, interested in web development"
423
+
424
+ - Instead of: "Prepared to start the project"
425
+ - Use: "Have project requirements, no code written yet"
426
+
427
+ **Why This Matters**: Readiness assumptions prematurely resolve the structural tension needed for creative advancement.
428
+
429
+ šŸ’” **Tip**: Run 'init_llm_guidance' for complete methodology overview.`);
430
+ }
431
+ const chartId = `chart_${Date.now()}`;
432
+ const timestamp = new Date().toISOString();
433
+ // Create chart, desired outcome, and current reality entities
434
+ const entities = [
435
+ {
436
+ name: `${chartId}_chart`,
437
+ entityType: 'structural_tension_chart',
438
+ observations: [`Chart created on ${timestamp}`],
439
+ metadata: {
440
+ chartId,
441
+ dueDate,
442
+ level: 0,
443
+ createdAt: timestamp,
444
+ updatedAt: timestamp
445
+ }
446
+ },
447
+ {
448
+ name: `${chartId}_desired_outcome`,
449
+ entityType: 'desired_outcome',
450
+ observations: [desiredOutcome],
451
+ metadata: {
452
+ chartId,
453
+ dueDate,
454
+ createdAt: timestamp,
455
+ updatedAt: timestamp
456
+ }
457
+ },
458
+ {
459
+ name: `${chartId}_current_reality`,
460
+ entityType: 'current_reality',
461
+ observations: [currentReality],
462
+ metadata: {
463
+ chartId,
464
+ createdAt: timestamp,
465
+ updatedAt: timestamp
466
+ }
467
+ }
468
+ ];
469
+ // Add action steps if provided
470
+ if (actionSteps && actionSteps.length > 0) {
471
+ const stepDueDates = this.distributeActionStepDates(new Date(), new Date(dueDate), actionSteps.length);
472
+ actionSteps.forEach((step, index) => {
473
+ entities.push({
474
+ name: `${chartId}_action_${index + 1}`,
475
+ entityType: 'action_step',
476
+ observations: [step],
477
+ metadata: {
478
+ chartId,
479
+ dueDate: stepDueDates[index].toISOString(),
480
+ completionStatus: false,
481
+ createdAt: timestamp,
482
+ updatedAt: timestamp
483
+ }
484
+ });
485
+ });
486
+ }
487
+ // Create relations
488
+ const relations = [
489
+ {
490
+ from: `${chartId}_chart`,
491
+ to: `${chartId}_desired_outcome`,
492
+ relationType: 'contains',
493
+ metadata: { createdAt: timestamp }
494
+ },
495
+ {
496
+ from: `${chartId}_chart`,
497
+ to: `${chartId}_current_reality`,
498
+ relationType: 'contains',
499
+ metadata: { createdAt: timestamp }
500
+ },
501
+ {
502
+ from: `${chartId}_current_reality`,
503
+ to: `${chartId}_desired_outcome`,
504
+ relationType: 'creates_tension_with',
505
+ metadata: { createdAt: timestamp }
506
+ }
507
+ ];
508
+ // Add action step relations
509
+ if (actionSteps && actionSteps.length > 0) {
510
+ actionSteps.forEach((_, index) => {
511
+ const actionName = `${chartId}_action_${index + 1}`;
512
+ relations.push({
513
+ from: `${chartId}_chart`,
514
+ to: actionName,
515
+ relationType: 'contains',
516
+ metadata: { createdAt: timestamp }
517
+ }, {
518
+ from: actionName,
519
+ to: `${chartId}_desired_outcome`,
520
+ relationType: 'advances_toward',
521
+ metadata: { createdAt: timestamp }
522
+ });
523
+ });
524
+ }
525
+ // Save to graph
526
+ await this.createEntities(entities);
527
+ await this.createRelations(relations);
528
+ return { chartId, entities, relations };
529
+ }
530
+ async telescopeActionStep(actionStepName, newCurrentReality, initialActionSteps) {
531
+ const graph = await this.loadGraph();
532
+ const actionStep = graph.entities.find(e => e.name === actionStepName && e.entityType === 'action_step');
533
+ if (!actionStep || !actionStep.metadata?.chartId) {
534
+ throw new Error(`Action step ${actionStepName} not found or not properly configured`);
535
+ }
536
+ const parentChartId = actionStep.metadata.chartId;
537
+ const inheritedDueDate = actionStep.metadata.dueDate || new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
538
+ const desiredOutcome = actionStep.observations[0]; // Use the action step description as the new desired outcome
539
+ const result = await this.createStructuralTensionChart(desiredOutcome, newCurrentReality, inheritedDueDate, initialActionSteps);
540
+ // Update the new chart's metadata to reflect telescoping relationship
541
+ const newChart = await this.loadGraph();
542
+ const chartEntity = newChart.entities.find(e => e.name === `${result.chartId}_chart`);
543
+ if (chartEntity && chartEntity.metadata) {
544
+ chartEntity.metadata.parentChart = parentChartId;
545
+ chartEntity.metadata.parentActionStep = actionStepName;
546
+ chartEntity.metadata.level = (actionStep.metadata.level || 0) + 1;
547
+ chartEntity.metadata.updatedAt = new Date().toISOString();
548
+ }
549
+ await this.saveGraph(newChart);
550
+ return { chartId: result.chartId, parentChart: parentChartId };
551
+ }
552
+ async markActionStepComplete(actionStepName) {
553
+ const graph = await this.loadGraph();
554
+ // An "action step" can be a 'desired_outcome' of a sub-chart, or a simple 'action_step' entity.
555
+ const actionStep = graph.entities.find(e => e.name === actionStepName && (e.entityType === 'action_step' || e.entityType === 'desired_outcome'));
556
+ if (!actionStep) {
557
+ throw new Error(`Action step ${actionStepName} not found`);
558
+ }
559
+ const chartId = actionStep.metadata?.chartId;
560
+ if (!chartId) {
561
+ throw new Error(`Chart ID not found for action step ${actionStepName}`);
562
+ }
563
+ // Mark the action step itself as complete
564
+ if (actionStep.metadata) {
565
+ actionStep.metadata.completionStatus = true;
566
+ actionStep.metadata.updatedAt = new Date().toISOString();
567
+ }
568
+ // Also mark the parent chart entity as complete
569
+ const chartEntity = graph.entities.find(e => e.name === `${chartId}_chart`);
570
+ if (chartEntity && chartEntity.metadata) {
571
+ chartEntity.metadata.completionStatus = true;
572
+ chartEntity.metadata.updatedAt = new Date().toISOString();
573
+ }
574
+ // Structural tension principle: completed action steps flow into the CURRENT REALITY
575
+ // of the PARENT chart, advancing the overall structure.
576
+ const parentChartId = chartEntity?.metadata?.parentChart;
577
+ if (parentChartId) {
578
+ const parentCurrentReality = graph.entities.find(e => e.name === `${parentChartId}_current_reality` &&
579
+ e.entityType === 'current_reality');
580
+ if (parentCurrentReality) {
581
+ const completionMessage = `Completed: ${actionStep.observations[0]}`;
582
+ if (!parentCurrentReality.observations.includes(completionMessage)) {
583
+ parentCurrentReality.observations.push(completionMessage);
584
+ if (parentCurrentReality.metadata) {
585
+ parentCurrentReality.metadata.updatedAt = new Date().toISOString();
586
+ }
587
+ }
588
+ }
589
+ }
590
+ await this.saveGraph(graph);
591
+ }
592
+ async getChartProgress(chartId) {
593
+ const graph = await this.loadGraph();
594
+ const actionSteps = graph.entities.filter(e => e.entityType === 'action_step' &&
595
+ e.metadata?.chartId === chartId);
596
+ const completedActions = actionSteps.filter(e => e.metadata?.completionStatus === true).length;
597
+ const totalActions = actionSteps.length;
598
+ const progress = totalActions > 0 ? completedActions / totalActions : 0;
599
+ // Find next incomplete action step with earliest due date
600
+ const incompleteActions = actionSteps
601
+ .filter(e => e.metadata?.completionStatus !== true)
602
+ .sort((a, b) => {
603
+ const dateA = new Date(a.metadata?.dueDate || '').getTime();
604
+ const dateB = new Date(b.metadata?.dueDate || '').getTime();
605
+ return dateA - dateB;
606
+ });
607
+ const chart = graph.entities.find(e => e.name === `${chartId}_chart`);
608
+ return {
609
+ chartId,
610
+ progress,
611
+ completedActions,
612
+ totalActions,
613
+ nextAction: incompleteActions[0]?.name,
614
+ dueDate: chart?.metadata?.dueDate
615
+ };
616
+ }
617
+ distributeActionStepDates(startDate, endDate, stepCount) {
618
+ const totalTime = endDate.getTime() - startDate.getTime();
619
+ const stepInterval = totalTime / (stepCount + 1); // +1 to leave space before final due date
620
+ const dates = [];
621
+ for (let i = 1; i <= stepCount; i++) {
622
+ dates.push(new Date(startDate.getTime() + (stepInterval * i)));
623
+ }
624
+ return dates;
625
+ }
626
+ async listActiveCharts() {
627
+ const graph = await this.loadGraph();
628
+ const charts = graph.entities.filter(e => e.entityType === 'structural_tension_chart');
629
+ const chartSummaries = await Promise.all(charts.map(async (chart) => {
630
+ const chartId = chart.metadata?.chartId || chart.name.replace('_chart', '');
631
+ const progress = await this.getChartProgress(chartId);
632
+ // Get desired outcome
633
+ const desiredOutcome = graph.entities.find(e => e.name === `${chartId}_desired_outcome` && e.entityType === 'desired_outcome');
634
+ return {
635
+ chartId,
636
+ desiredOutcome: desiredOutcome?.observations[0] || 'Unknown outcome',
637
+ dueDate: chart.metadata?.dueDate,
638
+ progress: progress.progress,
639
+ completedActions: progress.completedActions,
640
+ totalActions: progress.totalActions,
641
+ level: chart.metadata?.level || 0,
642
+ parentChart: chart.metadata?.parentChart
643
+ };
644
+ }));
645
+ return chartSummaries.sort((a, b) => {
646
+ // Sort by level first (master charts first), then by due date
647
+ if (a.level !== b.level)
648
+ return a.level - b.level;
649
+ const dateA = new Date(a.dueDate || '').getTime();
650
+ const dateB = new Date(b.dueDate || '').getTime();
651
+ return dateA - dateB;
652
+ });
653
+ }
654
+ async updateActionProgress(actionStepName, progressObservation, updateCurrentReality) {
655
+ const graph = await this.loadGraph();
656
+ const actionStep = graph.entities.find(e => e.name === actionStepName && (e.entityType === 'action_step' || e.entityType === 'desired_outcome'));
657
+ if (!actionStep) {
658
+ throw new Error(`Action step ${actionStepName} not found`);
659
+ }
660
+ // Add progress observation to action step
661
+ actionStep.observations.push(progressObservation);
662
+ if (actionStep.metadata) {
663
+ actionStep.metadata.updatedAt = new Date().toISOString();
664
+ }
665
+ // Optionally update current reality with progress
666
+ if (updateCurrentReality && actionStep.metadata?.chartId) {
667
+ const chartEntity = graph.entities.find(e => e.name === `${actionStep.metadata.chartId}_chart`);
668
+ const parentChartId = chartEntity?.metadata?.parentChart;
669
+ const targetChartId = parentChartId || actionStep.metadata.chartId;
670
+ const currentReality = graph.entities.find(e => e.name === `${targetChartId}_current_reality` &&
671
+ e.entityType === 'current_reality');
672
+ if (currentReality) {
673
+ // Progress observations flow into current reality, changing the structural dynamic
674
+ const progressMessage = `Progress on ${actionStep.observations[0]}: ${progressObservation}`;
675
+ if (!currentReality.observations.includes(progressMessage)) {
676
+ currentReality.observations.push(progressMessage);
677
+ if (currentReality.metadata) {
678
+ currentReality.metadata.updatedAt = new Date().toISOString();
679
+ }
680
+ }
681
+ }
682
+ }
683
+ await this.saveGraph(graph);
684
+ }
685
+ async updateCurrentReality(chartId, newObservations) {
686
+ const graph = await this.loadGraph();
687
+ const currentReality = graph.entities.find(e => e.name === `${chartId}_current_reality` &&
688
+ e.entityType === 'current_reality');
689
+ if (!currentReality) {
690
+ throw new Error(`Chart ${chartId} not found or missing current reality`);
691
+ }
692
+ // Add new observations to current reality
693
+ const uniqueObservations = newObservations.filter(obs => !currentReality.observations.includes(obs));
694
+ currentReality.observations.push(...uniqueObservations);
695
+ if (currentReality.metadata) {
696
+ currentReality.metadata.updatedAt = new Date().toISOString();
697
+ }
698
+ await this.saveGraph(graph);
699
+ }
700
+ async updateDesiredOutcome(chartId, newDesiredOutcome) {
701
+ const graph = await this.loadGraph();
702
+ const desiredOutcomeEntity = graph.entities.find(e => e.name === `${chartId}_desired_outcome` && e.entityType === 'desired_outcome');
703
+ if (!desiredOutcomeEntity) {
704
+ throw new Error(`Chart ${chartId} desired outcome not found`);
705
+ }
706
+ // Replace the first observation (which is the desired outcome text)
707
+ desiredOutcomeEntity.observations[0] = newDesiredOutcome;
708
+ if (desiredOutcomeEntity.metadata) {
709
+ desiredOutcomeEntity.metadata.updatedAt = new Date().toISOString();
710
+ }
711
+ await this.saveGraph(graph);
712
+ }
713
+ // Narrative beat creation functionality
714
+ async createNarrativeBeat(parentChartId, title, act, type_dramatic, universes, description, prose, lessons, assessRelationalAlignment = false, initiateFourDirectionsInquiry = false, filePath) {
715
+ const timestamp = Date.now();
716
+ const beatName = `${parentChartId}_beat_${timestamp}`;
717
+ // Create narrative beat entity
718
+ const entity = {
719
+ name: beatName,
720
+ entityType: 'narrative_beat',
721
+ observations: [
722
+ `Act ${act} ${type_dramatic}`,
723
+ `Timestamp: ${new Date().toISOString()}`,
724
+ `Universe: ${universes.join(', ')}`
725
+ ],
726
+ metadata: {
727
+ chartId: parentChartId,
728
+ act,
729
+ type_dramatic,
730
+ universes,
731
+ timestamp: new Date().toISOString(),
732
+ createdAt: new Date().toISOString(),
733
+ narrative: {
734
+ description,
735
+ prose,
736
+ lessons
737
+ },
738
+ relationalAlignment: {
739
+ assessed: false,
740
+ score: null,
741
+ principles: []
742
+ },
743
+ fourDirections: {
744
+ north_vision: null,
745
+ east_intention: null,
746
+ south_emotion: null,
747
+ west_introspection: null
748
+ }
749
+ }
750
+ };
751
+ // Add to graph
752
+ await this.createEntities([entity]);
753
+ // Create relation to parent chart if it exists
754
+ const graph = await this.loadGraph();
755
+ const parentChart = graph.entities.find(e => e.entityType === 'structural_tension_chart' && e.metadata?.chartId === parentChartId);
756
+ if (parentChart) {
757
+ await this.createRelations([{
758
+ from: beatName,
759
+ to: `${parentChartId}_chart`,
760
+ relationType: 'documents',
761
+ metadata: {
762
+ createdAt: new Date().toISOString(),
763
+ description: 'Narrative beat documents chart progress'
764
+ }
765
+ }]);
766
+ }
767
+ // TODO: IAIP integration would go here
768
+ if (assessRelationalAlignment) {
769
+ console.log('šŸ”® Relational alignment assessment requested (iaip-mcp integration pending)');
770
+ }
771
+ if (initiateFourDirectionsInquiry) {
772
+ console.log('🧭 Four Directions inquiry requested (iaip-mcp integration pending)');
773
+ }
774
+ return { entity, beatName };
775
+ }
776
+ async telescopeNarrativeBeat(parentBeatName, newCurrentReality, initialSubBeats) {
777
+ const graph = await this.loadGraph();
778
+ const parentBeat = graph.entities.find(e => e.name === parentBeatName && e.entityType === 'narrative_beat');
779
+ if (!parentBeat) {
780
+ throw new Error(`Parent narrative beat not found: ${parentBeatName}`);
781
+ }
782
+ // Update parent beat's current reality (add to observations)
783
+ parentBeat.observations.push(`Telescoped: ${newCurrentReality}`);
784
+ if (parentBeat.metadata) {
785
+ parentBeat.metadata.updatedAt = new Date().toISOString();
786
+ }
787
+ const subBeats = [];
788
+ // Create sub-beats if provided
789
+ if (initialSubBeats && initialSubBeats.length > 0) {
790
+ for (let i = 0; i < initialSubBeats.length; i++) {
791
+ const subBeat = initialSubBeats[i];
792
+ const result = await this.createNarrativeBeat(parentBeatName, // Use parent beat as chart ID
793
+ subBeat.title, i + 1, // Sequential act numbers
794
+ subBeat.type_dramatic, parentBeat.metadata?.universes || ['engineer-world'], subBeat.description, subBeat.prose, subBeat.lessons);
795
+ subBeats.push(result.entity);
796
+ }
797
+ }
798
+ await this.saveGraph(graph);
799
+ return { parentBeat, subBeats };
800
+ }
801
+ async listNarrativeBeats(parentChartId) {
802
+ const graph = await this.loadGraph();
803
+ const beats = graph.entities.filter(e => e.entityType === 'narrative_beat');
804
+ if (parentChartId) {
805
+ return beats.filter(beat => beat.metadata?.chartId === parentChartId);
806
+ }
807
+ return beats;
808
+ }
809
+ async addActionStep(parentChartId, actionStepTitle, dueDate, currentReality) {
810
+ const graph = await this.loadGraph();
811
+ const parentChart = graph.entities.find(e => e.entityType === 'structural_tension_chart' && e.metadata?.chartId === parentChartId);
812
+ if (!parentChart) {
813
+ throw new Error(`Parent chart ${parentChartId} not found`);
814
+ }
815
+ // Get parent chart's due date for auto-distribution
816
+ const parentDueDate = parentChart.metadata?.dueDate;
817
+ if (!parentDueDate) {
818
+ throw new Error(`Parent chart ${parentChartId} has no due date`);
819
+ }
820
+ // Calculate due date for action step if not provided
821
+ let actionStepDueDate = dueDate;
822
+ if (!actionStepDueDate) {
823
+ // Distribute between now and parent due date (simple midpoint for now)
824
+ const now = new Date();
825
+ const parentEnd = new Date(parentDueDate);
826
+ const midpoint = new Date(now.getTime() + (parentEnd.getTime() - now.getTime()) / 2);
827
+ actionStepDueDate = midpoint.toISOString();
828
+ }
829
+ // Require current reality assessment - no defaults that prematurely resolve tension
830
+ if (!currentReality) {
831
+ throw new Error(`🌊 DELAYED RESOLUTION PRINCIPLE VIOLATION
832
+
833
+ Action step: "${actionStepTitle}"
834
+
835
+ āŒ **Problem**: Current reality assessment missing
836
+ šŸ“š **Principle**: "Tolerate discrepancy, tension, and delayed resolution" - Robert Fritz
837
+
838
+ šŸŽÆ **What's Needed**: Honest assessment of your actual current state relative to this action step.
839
+
840
+ āœ… **Examples**:
841
+ - "Never used Django, completed Python basics"
842
+ - "Built one API, struggling with authentication"
843
+ - "Read 3 chapters, concepts still unclear"
844
+
845
+ āŒ **Avoid**: "Ready to begin", "Prepared to start", "All set to..."
846
+
847
+ **Why This Matters**: Premature resolution destroys the structural tension that generates creative advancement. The system NEEDS honest current reality to create productive tension.
848
+
849
+ šŸ’” **Tip**: Run 'init_llm_guidance' for complete methodology overview.`);
850
+ }
851
+ const actionCurrentReality = currentReality;
852
+ // Create telescoped structural tension chart
853
+ const telescopedChart = await this.createStructuralTensionChart(actionStepTitle, actionCurrentReality, actionStepDueDate);
854
+ // Update the telescoped chart's metadata to show parent relationship
855
+ const updatedGraph = await this.loadGraph();
856
+ const telescopedChartEntity = updatedGraph.entities.find(e => e.name === `${telescopedChart.chartId}_chart`);
857
+ if (telescopedChartEntity && telescopedChartEntity.metadata) {
858
+ telescopedChartEntity.metadata.parentChart = parentChartId;
859
+ telescopedChartEntity.metadata.level = (parentChart.metadata?.level || 0) + 1;
860
+ telescopedChartEntity.metadata.updatedAt = new Date().toISOString();
861
+ }
862
+ // Create relationship: telescoped chart advances toward parent's desired outcome
863
+ const parentDesiredOutcome = updatedGraph.entities.find(e => e.name === `${parentChartId}_desired_outcome` && e.entityType === 'desired_outcome');
864
+ if (parentDesiredOutcome) {
865
+ const timestamp = new Date().toISOString();
866
+ await this.createRelations([{
867
+ from: `${telescopedChart.chartId}_desired_outcome`,
868
+ to: parentDesiredOutcome.name,
869
+ relationType: 'advances_toward',
870
+ metadata: { createdAt: timestamp }
871
+ }]);
872
+ }
873
+ await this.saveGraph(updatedGraph);
874
+ return {
875
+ chartId: telescopedChart.chartId,
876
+ actionStepName: `${telescopedChart.chartId}_desired_outcome`
877
+ };
878
+ }
879
+ // Enhanced method for LLMs to telescope with intelligent current reality extraction
880
+ async telescopeActionStepWithContext(parentChartId, actionStepTitle, userContext, currentReality, dueDate) {
881
+ // If current reality not provided, try to extract from context
882
+ let finalCurrentReality = currentReality;
883
+ if (!finalCurrentReality) {
884
+ finalCurrentReality = this.extractCurrentRealityFromContext(userContext, actionStepTitle) ?? undefined;
885
+ }
886
+ // If still no current reality, provide guidance while maintaining tension
887
+ if (!finalCurrentReality) {
888
+ throw new Error(`Current reality assessment needed for "${actionStepTitle}". ` +
889
+ `Please assess your actual current state relative to this action step. ` +
890
+ `Example: "I have never used Django before" or "I completed the basics but haven't built a real project" ` +
891
+ `rather than assuming readiness. Structural tension requires honest current reality assessment.`);
892
+ }
893
+ // Proceed with telescoping using the assessed current reality
894
+ return this.addActionStep(parentChartId, actionStepTitle, dueDate, finalCurrentReality);
895
+ }
896
+ // Unified interface for managing action steps - handles both creation and expansion
897
+ async manageActionStep(parentReference, actionDescription, currentReality, initialActionSteps, dueDate) {
898
+ const graph = await this.loadGraph();
899
+ // Pattern detection: Determine if parentReference is entity name or chart ID
900
+ const actionStepPattern = /^chart_\d+_action_\d+$/;
901
+ const desiredOutcomePattern = /^chart_\d+_desired_outcome$/;
902
+ const chartIdPattern = /^chart_\d+$/;
903
+ const isActionStepEntity = actionStepPattern.test(parentReference);
904
+ const isDesiredOutcomeEntity = desiredOutcomePattern.test(parentReference);
905
+ const isChartId = chartIdPattern.test(parentReference);
906
+ // Route 1: Expanding existing action_step entity (legacy pattern)
907
+ if (isActionStepEntity) {
908
+ const actionStep = graph.entities.find(e => e.name === parentReference && e.entityType === 'action_step');
909
+ if (!actionStep) {
910
+ // Provide helpful error with available actions
911
+ const allActionSteps = graph.entities
912
+ .filter(e => e.entityType === 'action_step')
913
+ .map(e => `- ${e.name}: "${e.observations[0]}"`);
914
+ throw new Error(`šŸ” ACTION STEP ENTITY NOT FOUND
915
+
916
+ Received: "${parentReference}"
917
+ Expected: Valid action_step entity name (e.g., "chart_123_action_1")
918
+
919
+ Available action steps in memory:
920
+ ${allActionSteps.length > 0 ? allActionSteps.join('\n') : '(none found)'}
921
+
922
+ Tip: If creating a new action step, use the parent chart ID instead.`);
923
+ }
924
+ // Use telescoping logic for legacy action_step entities
925
+ const currentRealityToUse = currentReality || "Expanding action step into detailed sub-chart";
926
+ const telescopedResult = await this.telescopeActionStep(parentReference, currentRealityToUse, initialActionSteps);
927
+ // Transform result to include actionStepName
928
+ return {
929
+ chartId: telescopedResult.chartId,
930
+ actionStepName: `${telescopedResult.chartId}_desired_outcome`
931
+ };
932
+ }
933
+ // Route 2: Expanding existing desired_outcome entity (modern pattern)
934
+ if (isDesiredOutcomeEntity) {
935
+ const desiredOutcome = graph.entities.find(e => e.name === parentReference && e.entityType === 'desired_outcome');
936
+ if (!desiredOutcome || !desiredOutcome.metadata?.chartId) {
937
+ throw new Error(`šŸ” DESIRED OUTCOME ENTITY NOT FOUND
938
+
939
+ Received: "${parentReference}"
940
+ Expected: Valid desired_outcome entity name (e.g., "chart_123_desired_outcome")
941
+
942
+ Tip: If creating a new action step, use the parent chart ID instead.`);
943
+ }
944
+ // Use telescoping logic for desired_outcome entities
945
+ const currentRealityToUse = currentReality || "Expanding desired outcome into detailed sub-chart";
946
+ const telescopedResult = await this.telescopeActionStep(parentReference, currentRealityToUse, initialActionSteps);
947
+ // Transform result to include actionStepName
948
+ return {
949
+ chartId: telescopedResult.chartId,
950
+ actionStepName: `${telescopedResult.chartId}_desired_outcome`
951
+ };
952
+ }
953
+ // Route 3: Creating new action step under parent chart (modern pattern)
954
+ if (isChartId) {
955
+ // Validate parent chart exists
956
+ const parentChart = graph.entities.find(e => e.entityType === 'structural_tension_chart' &&
957
+ e.metadata?.chartId === parentReference);
958
+ if (!parentChart) {
959
+ // Provide helpful error with available charts
960
+ const allCharts = graph.entities
961
+ .filter(e => e.entityType === 'structural_tension_chart')
962
+ .map(e => {
963
+ const outcome = graph.entities.find(o => o.name === `${e.metadata?.chartId}_desired_outcome`);
964
+ return `- ${e.metadata?.chartId}: "${outcome?.observations[0] || 'Unknown'}"`;
965
+ });
966
+ throw new Error(`šŸ” PARENT CHART NOT FOUND
967
+
968
+ Received: "${parentReference}"
969
+ Expected: Valid chart ID (e.g., "chart_123")
970
+
971
+ Available charts in memory:
972
+ ${allCharts.length > 0 ? allCharts.join('\n') : '(none found)'}
973
+
974
+ Tip: Use 'list_active_charts' to see all available charts.`);
975
+ }
976
+ // Enforce delayed resolution principle for new action creation
977
+ if (!currentReality) {
978
+ throw new Error(`🌊 DELAYED RESOLUTION PRINCIPLE VIOLATION
979
+
980
+ Action step: "${actionDescription}"
981
+ Parent chart: "${parentReference}"
982
+
983
+ āŒ **Problem**: Current reality assessment missing
984
+ šŸ“š **Principle**: "Tolerate discrepancy, tension, and delayed resolution" - Robert Fritz
985
+
986
+ šŸŽÆ **What's Needed**: Honest assessment of actual current state relative to this action step.
987
+
988
+ āœ… **Examples**:
989
+ - "Never used Django, completed Python basics"
990
+ - "Built one API, struggling with authentication"
991
+ - "Read 3 chapters, concepts still unclear"
992
+
993
+ āŒ **Avoid**: "Ready to begin", "Prepared to start", "All set to..."
994
+
995
+ **Why This Matters**: Premature resolution destroys structural tension essential for creative advancement.
996
+
997
+ šŸ’” **Tip**: Run 'init_llm_guidance' for complete methodology overview.`);
998
+ }
999
+ // Create new action step as telescoped chart
1000
+ return await this.addActionStep(parentReference, actionDescription, dueDate, currentReality);
1001
+ }
1002
+ // Route 4: Invalid format - provide comprehensive guidance
1003
+ throw new Error(`🚨 INVALID PARENT REFERENCE FORMAT
1004
+
1005
+ Received: "${parentReference}"
1006
+
1007
+ Valid formats:
1008
+ 1. Chart ID: "chart_123" → Creates new action step
1009
+ 2. Action entity: "chart_123_action_1" → Expands existing legacy action step
1010
+ 3. Desired outcome: "chart_123_desired_outcome" → Expands existing modern action step
1011
+
1012
+ Examples:
1013
+ - Create new action: manageActionStep("chart_123", "Complete tutorial", "Never used Django")
1014
+ - Expand existing: manageActionStep("chart_123_action_1", "Complete tutorial", undefined, ["Step 1", "Step 2"])
1015
+
1016
+ šŸ’” **Tip**: Use 'list_active_charts' to see available charts and their IDs.`);
1017
+ }
1018
+ async removeActionStep(parentChartId, actionStepName) {
1019
+ const graph = await this.loadGraph();
1020
+ // Find the action step (which is actually a telescoped chart's desired outcome)
1021
+ const actionStepEntity = graph.entities.find(e => e.name === actionStepName);
1022
+ if (!actionStepEntity || !actionStepEntity.metadata?.chartId) {
1023
+ throw new Error(`Action step ${actionStepName} not found`);
1024
+ }
1025
+ const telescopedChartId = actionStepEntity.metadata.chartId;
1026
+ // Verify it belongs to the parent chart
1027
+ const telescopedChart = graph.entities.find(e => e.entityType === 'structural_tension_chart' &&
1028
+ e.metadata?.chartId === telescopedChartId &&
1029
+ e.metadata?.parentChart === parentChartId);
1030
+ if (!telescopedChart) {
1031
+ throw new Error(`Action step ${actionStepName} does not belong to chart ${parentChartId}`);
1032
+ }
1033
+ // Remove all entities belonging to the telescoped chart
1034
+ const entitiesToRemove = graph.entities
1035
+ .filter(e => e.metadata?.chartId === telescopedChartId)
1036
+ .map(e => e.name);
1037
+ await this.deleteEntities(entitiesToRemove);
1038
+ }
1039
+ }
1040
+ const knowledgeGraphManager = new KnowledgeGraphManager();
1041
+ // The server instance and tools exposed to AI models
1042
+ const server = new Server({
1043
+ name: "coaia-narrative",
1044
+ version: "0.1.0",
1045
+ description: "COAIA Narrative - Structural Tension Charts with Narrative Beat Extension for multi-universe story capture. Extends coaia-memory with relational and ceremonial integration. 🚨 NEW LLM? Run 'init_llm_guidance' first."
1046
+ }, {
1047
+ capabilities: {
1048
+ tools: {},
1049
+ },
1050
+ });
1051
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
1052
+ const enabledTools = getEnabledTools();
1053
+ const allTools = [
1054
+ {
1055
+ name: "create_entities",
1056
+ description: "ADVANCED: Create traditional knowledge graph entities. For structural tension charts, use create_structural_tension_chart or add_action_step instead.",
1057
+ inputSchema: {
1058
+ type: "object",
1059
+ properties: {
1060
+ entities: {
1061
+ type: "array",
1062
+ items: {
1063
+ type: "object",
1064
+ properties: {
1065
+ name: { type: "string", description: "The name of the entity" },
1066
+ entityType: { type: "string", description: "The type of the entity" },
1067
+ observations: {
1068
+ type: "array",
1069
+ items: { type: "string" },
1070
+ description: "An array of observation contents associated with the entity"
1071
+ },
1072
+ },
1073
+ required: ["name", "entityType", "observations"],
1074
+ },
1075
+ },
1076
+ },
1077
+ required: ["entities"],
1078
+ },
1079
+ },
1080
+ {
1081
+ name: "create_relations",
1082
+ description: "Create multiple new relations between entities in the knowledge graph. Relations should be in active voice",
1083
+ inputSchema: {
1084
+ type: "object",
1085
+ properties: {
1086
+ relations: {
1087
+ type: "array",
1088
+ items: {
1089
+ type: "object",
1090
+ properties: {
1091
+ from: { type: "string", description: "The name of the entity where the relation starts" },
1092
+ to: { type: "string", description: "The name of the entity where the relation ends" },
1093
+ relationType: { type: "string", description: "The type of the relation" },
1094
+ },
1095
+ required: ["from", "to", "relationType"],
1096
+ },
1097
+ },
1098
+ },
1099
+ required: ["relations"],
1100
+ },
1101
+ },
1102
+ {
1103
+ name: "add_observations",
1104
+ description: "ADVANCED: Add observations to traditional knowledge graph entities. For structural tension charts, use update_current_reality instead.",
1105
+ inputSchema: {
1106
+ type: "object",
1107
+ properties: {
1108
+ observations: {
1109
+ type: "array",
1110
+ items: {
1111
+ type: "object",
1112
+ properties: {
1113
+ entityName: { type: "string", description: "The name of the entity to add the observations to" },
1114
+ contents: {
1115
+ type: "array",
1116
+ items: { type: "string" },
1117
+ description: "An array of observation contents to add"
1118
+ },
1119
+ },
1120
+ required: ["entityName", "contents"],
1121
+ },
1122
+ },
1123
+ },
1124
+ required: ["observations"],
1125
+ },
1126
+ },
1127
+ {
1128
+ name: "delete_entities",
1129
+ description: "Delete multiple entities and their associated relations from the knowledge graph",
1130
+ inputSchema: {
1131
+ type: "object",
1132
+ properties: {
1133
+ entityNames: {
1134
+ type: "array",
1135
+ items: { type: "string" },
1136
+ description: "An array of entity names to delete"
1137
+ },
1138
+ },
1139
+ required: ["entityNames"],
1140
+ },
1141
+ },
1142
+ {
1143
+ name: "delete_observations",
1144
+ description: "Delete specific observations from entities in the knowledge graph",
1145
+ inputSchema: {
1146
+ type: "object",
1147
+ properties: {
1148
+ deletions: {
1149
+ type: "array",
1150
+ items: {
1151
+ type: "object",
1152
+ properties: {
1153
+ entityName: { type: "string", description: "The name of the entity containing the observations" },
1154
+ observations: {
1155
+ type: "array",
1156
+ items: { type: "string" },
1157
+ description: "An array of observations to delete"
1158
+ },
1159
+ },
1160
+ required: ["entityName", "observations"],
1161
+ },
1162
+ },
1163
+ },
1164
+ required: ["deletions"],
1165
+ },
1166
+ },
1167
+ {
1168
+ name: "delete_relations",
1169
+ description: "Delete multiple relations from the knowledge graph",
1170
+ inputSchema: {
1171
+ type: "object",
1172
+ properties: {
1173
+ relations: {
1174
+ type: "array",
1175
+ items: {
1176
+ type: "object",
1177
+ properties: {
1178
+ from: { type: "string", description: "The name of the entity where the relation starts" },
1179
+ to: { type: "string", description: "The name of the entity where the relation ends" },
1180
+ relationType: { type: "string", description: "The type of the relation" },
1181
+ },
1182
+ required: ["from", "to", "relationType"],
1183
+ },
1184
+ description: "An array of relations to delete"
1185
+ },
1186
+ },
1187
+ required: ["relations"],
1188
+ },
1189
+ },
1190
+ {
1191
+ name: "read_graph",
1192
+ description: "RARELY USED: Dumps entire knowledge graph (all entities and relations). Only use for debugging or when you need to see ALL data. For chart work, use list_active_charts instead.",
1193
+ inputSchema: {
1194
+ type: "object",
1195
+ properties: {},
1196
+ },
1197
+ },
1198
+ {
1199
+ name: "search_nodes",
1200
+ description: "Search for nodes in the knowledge graph based on a query",
1201
+ inputSchema: {
1202
+ type: "object",
1203
+ properties: {
1204
+ query: { type: "string", description: "The search query to match against entity names, types, and observation content" },
1205
+ },
1206
+ required: ["query"],
1207
+ },
1208
+ },
1209
+ {
1210
+ name: "open_nodes",
1211
+ description: "ADVANCED: Open specific entity nodes by exact name (e.g. 'chart_123_current_reality'). Only use if you need to inspect specific chart components. NOT for general chart viewing - use list_active_charts instead.",
1212
+ inputSchema: {
1213
+ type: "object",
1214
+ properties: {
1215
+ names: {
1216
+ type: "array",
1217
+ items: { type: "string" },
1218
+ description: "An array of exact entity names to retrieve (e.g. 'chart_123_desired_outcome')",
1219
+ },
1220
+ },
1221
+ required: ["names"],
1222
+ },
1223
+ },
1224
+ {
1225
+ name: "create_structural_tension_chart",
1226
+ description: "Create a new structural tension chart with desired outcome, current reality, and optional action steps. CRITICAL: Use creative orientation (what you want to CREATE) not problem-solving (what you want to fix/solve). Current reality must be factual assessment, never 'ready to begin'.",
1227
+ inputSchema: {
1228
+ type: "object",
1229
+ properties: {
1230
+ desiredOutcome: { type: "string", description: "What you want to CREATE (not solve/fix). Focus on positive outcomes, not problems to eliminate." },
1231
+ currentReality: { type: "string", description: "Your current situation - factual assessment only. NEVER use 'ready to begin' or similar readiness statements." },
1232
+ dueDate: { type: "string", description: "When you want to achieve this outcome (ISO date string)" },
1233
+ actionSteps: {
1234
+ type: "array",
1235
+ items: { type: "string" },
1236
+ description: "Optional list of action steps needed to achieve the outcome"
1237
+ }
1238
+ },
1239
+ required: ["desiredOutcome", "currentReality", "dueDate"]
1240
+ }
1241
+ },
1242
+ {
1243
+ name: "telescope_action_step",
1244
+ description: "āš ļø DEPRECATED: Use 'manage_action_step' instead for unified interface. Break down an action step into a detailed structural tension chart. CRITICAL: Current reality must be an honest assessment of actual current state relative to this specific action step, NOT readiness or preparation statements. This maintains structural tension essential for creative advancement.",
1245
+ inputSchema: {
1246
+ type: "object",
1247
+ properties: {
1248
+ actionStepName: { type: "string", description: "Name of the action step to telescope" },
1249
+ newCurrentReality: {
1250
+ type: "string",
1251
+ description: "REQUIRED: Honest assessment of actual current state relative to this action step. Examples: 'Never used Django before', 'Completed models section, struggling with views'. AVOID: 'Ready to begin', 'Prepared to start'."
1252
+ },
1253
+ initialActionSteps: {
1254
+ type: "array",
1255
+ items: { type: "string" },
1256
+ description: "Optional list of initial action steps for the telescoped chart"
1257
+ }
1258
+ },
1259
+ required: ["actionStepName", "newCurrentReality"]
1260
+ }
1261
+ },
1262
+ {
1263
+ name: "mark_action_complete",
1264
+ description: "Mark an action step as completed and update current reality",
1265
+ inputSchema: {
1266
+ type: "object",
1267
+ properties: {
1268
+ actionStepName: { type: "string", description: "Name of the completed action step" }
1269
+ },
1270
+ required: ["actionStepName"]
1271
+ }
1272
+ },
1273
+ {
1274
+ name: "get_chart_progress",
1275
+ description: "Get detailed progress for a specific chart (redundant if you just used list_active_charts which shows progress). Only use if you need the nextAction details.",
1276
+ inputSchema: {
1277
+ type: "object",
1278
+ properties: {
1279
+ chartId: { type: "string", description: "ID of the chart to check progress for" }
1280
+ },
1281
+ required: ["chartId"]
1282
+ }
1283
+ },
1284
+ {
1285
+ name: "list_active_charts",
1286
+ description: "List all active structural tension charts with their progress. Use this FIRST to see all charts and their IDs. This shows chart overview with progress - you don't need other tools after this for basic chart information.",
1287
+ inputSchema: {
1288
+ type: "object",
1289
+ properties: {}
1290
+ }
1291
+ },
1292
+ {
1293
+ name: "update_action_progress",
1294
+ description: "Update progress on an action step without marking it complete, optionally updating current reality",
1295
+ inputSchema: {
1296
+ type: "object",
1297
+ properties: {
1298
+ actionStepName: { type: "string", description: "Name of the action step to update progress for" },
1299
+ progressObservation: { type: "string", description: "Description of progress made on this action step" },
1300
+ updateCurrentReality: {
1301
+ type: "boolean",
1302
+ description: "Whether to also add this progress to current reality (optional, defaults to false)"
1303
+ }
1304
+ },
1305
+ required: ["actionStepName", "progressObservation"]
1306
+ }
1307
+ },
1308
+ {
1309
+ name: "update_current_reality",
1310
+ description: "FOR STRUCTURAL TENSION CHARTS: Add observations to current reality. DO NOT use add_observations or create_entities for chart work - use this instead.",
1311
+ inputSchema: {
1312
+ type: "object",
1313
+ properties: {
1314
+ chartId: { type: "string", description: "ID of the chart to update current reality for" },
1315
+ newObservations: {
1316
+ type: "array",
1317
+ items: { type: "string" },
1318
+ description: "Array of new observations to add to current reality"
1319
+ }
1320
+ },
1321
+ required: ["chartId", "newObservations"]
1322
+ }
1323
+ },
1324
+ {
1325
+ name: "manage_action_step",
1326
+ description: "✨ RECOMMENDED: Unified interface for managing action steps - handles both creation and expansion. Automatically detects whether you're creating a new action step (chart ID) or expanding an existing one (entity name). Provides clear error messages when parameters are invalid.",
1327
+ inputSchema: {
1328
+ type: "object",
1329
+ properties: {
1330
+ parentReference: {
1331
+ type: "string",
1332
+ description: "Chart ID (e.g., 'chart_123') to create new action step, OR action step entity name (e.g., 'chart_123_action_1' or 'chart_123_desired_outcome') to expand existing action step"
1333
+ },
1334
+ actionDescription: {
1335
+ type: "string",
1336
+ description: "Title/description of the action step"
1337
+ },
1338
+ currentReality: {
1339
+ type: "string",
1340
+ description: "REQUIRED for new action creation. Honest assessment of actual current state relative to this action step. Examples: 'Never used Django', 'Completed models, struggling with views'. AVOID: 'Ready to begin'. Optional when expanding existing actions."
1341
+ },
1342
+ initialActionSteps: {
1343
+ type: "array",
1344
+ items: { type: "string" },
1345
+ description: "Optional list of sub-actions for the action step"
1346
+ },
1347
+ dueDate: {
1348
+ type: "string",
1349
+ description: "Optional due date (ISO string). Auto-distributed if not provided."
1350
+ }
1351
+ },
1352
+ required: ["parentReference", "actionDescription"]
1353
+ }
1354
+ },
1355
+ {
1356
+ name: "add_action_step",
1357
+ description: "āš ļø DEPRECATED: Use 'manage_action_step' instead for unified interface. Add a strategic action step to an existing structural tension chart (creates telescoped chart). WARNING: Requires honest current reality assessment - avoid 'ready to begin' language. Action steps become full structural tension charts.",
1358
+ inputSchema: {
1359
+ type: "object",
1360
+ properties: {
1361
+ parentChartId: { type: "string", description: "ID of the parent chart to add the action step to" },
1362
+ actionStepTitle: { type: "string", description: "Title of the action step (becomes desired outcome of telescoped chart)" },
1363
+ dueDate: {
1364
+ type: "string",
1365
+ description: "Optional due date for the action step (ISO string). If not provided, auto-distributed between now and parent due date"
1366
+ },
1367
+ currentReality: {
1368
+ type: "string",
1369
+ description: "Current reality specific to this action step. Required to maintain structural tension - assess the actual current state relative to this action step, not readiness to begin."
1370
+ }
1371
+ },
1372
+ required: ["parentChartId", "actionStepTitle", "currentReality"]
1373
+ }
1374
+ },
1375
+ {
1376
+ name: "remove_action_step",
1377
+ description: "Remove an action step from a structural tension chart (deletes telescoped chart)",
1378
+ inputSchema: {
1379
+ type: "object",
1380
+ properties: {
1381
+ parentChartId: { type: "string", description: "ID of the parent chart containing the action step" },
1382
+ actionStepName: { type: "string", description: "Name of the action step to remove (telescoped chart's desired outcome name)" }
1383
+ },
1384
+ required: ["parentChartId", "actionStepName"]
1385
+ }
1386
+ },
1387
+ {
1388
+ name: "update_desired_outcome",
1389
+ description: "Update a chart's desired outcome (goal). Works for BOTH master charts AND action steps (which are telescoped charts). Provide the chart ID of the chart you want to update - whether it's a master chart or an action step chart.",
1390
+ inputSchema: {
1391
+ type: "object",
1392
+ properties: {
1393
+ chartId: { type: "string", description: "ID of the chart to update (works for master charts like 'chart_123' or action step charts like 'chart_456')" },
1394
+ newDesiredOutcome: { type: "string", description: "New desired outcome text" }
1395
+ },
1396
+ required: ["chartId", "newDesiredOutcome"]
1397
+ }
1398
+ },
1399
+ {
1400
+ name: "creator_moment_of_truth",
1401
+ description: "Guide through the Creator Moment of Truth - a four-step review process for assessing chart progress. Transforms discrepancies between expected and delivered into learning opportunities. Use when reviewing progress or when action steps aren't going as planned.",
1402
+ inputSchema: {
1403
+ type: "object",
1404
+ properties: {
1405
+ chartId: { type: "string", description: "ID of the chart to review" },
1406
+ step: {
1407
+ type: "string",
1408
+ enum: ["full_review", "acknowledge", "analyze", "plan", "feedback"],
1409
+ default: "full_review",
1410
+ description: "Which step to guide through: 'full_review' for complete process, or individual steps"
1411
+ },
1412
+ userInput: {
1413
+ type: "string",
1414
+ description: "Optional: User's observations or responses for the current step"
1415
+ }
1416
+ },
1417
+ required: ["chartId"]
1418
+ }
1419
+ },
1420
+ {
1421
+ name: "init_llm_guidance",
1422
+ description: "🚨 NEW LLM? Essential guidance for understanding COAIA Memory's structural tension methodology, delayed resolution principle, and proper tool usage. Run this FIRST to avoid common mistakes.",
1423
+ inputSchema: {
1424
+ type: "object",
1425
+ properties: {
1426
+ format: {
1427
+ type: "string",
1428
+ enum: ["full", "quick", "save_directive"],
1429
+ default: "full",
1430
+ description: "Level of detail: 'full' for complete guidance, 'quick' for essentials only, 'save_directive' for session memory instructions"
1431
+ }
1432
+ }
1433
+ }
1434
+ },
1435
+ {
1436
+ name: "create_narrative_beat",
1437
+ description: "Create a new narrative beat with multi-universe perspective and optional IAIP integration. Documents story progression across three archetypal universes (engineer-world, ceremony-world, story-engine-world).",
1438
+ inputSchema: {
1439
+ type: "object",
1440
+ properties: {
1441
+ parentChartId: { type: "string", description: "ID of the parent structural tension chart" },
1442
+ title: { type: "string", description: "Title of the narrative beat" },
1443
+ act: { type: "number", description: "Act number in the narrative sequence" },
1444
+ type_dramatic: { type: "string", description: "Dramatic type (e.g. 'Crisis/Antagonist Force', 'Setup', 'Turning Point')" },
1445
+ universes: {
1446
+ type: "array",
1447
+ items: { type: "string" },
1448
+ description: "Universe perspectives (engineer-world, ceremony-world, story-engine-world)"
1449
+ },
1450
+ description: { type: "string", description: "Detailed description of the narrative beat" },
1451
+ prose: { type: "string", description: "Prose narrative of the beat" },
1452
+ lessons: {
1453
+ type: "array",
1454
+ items: { type: "string" },
1455
+ description: "Key lessons or insights from this beat"
1456
+ },
1457
+ assessRelationalAlignment: { type: "boolean", description: "Whether to call iaip-mcp assess_relational_alignment" },
1458
+ initiateFourDirectionsInquiry: { type: "boolean", description: "Whether to call iaip-mcp get_direction_guidance" },
1459
+ filePath: { type: "string", description: "Path to narrative JSONL file (optional)" }
1460
+ },
1461
+ required: ["parentChartId", "title", "act", "type_dramatic", "universes", "description", "prose", "lessons"]
1462
+ }
1463
+ },
1464
+ {
1465
+ name: "telescope_narrative_beat",
1466
+ description: "Telescope a narrative beat into sub-beats for detailed exploration. Creates detailed sub-narrative structure from a parent beat.",
1467
+ inputSchema: {
1468
+ type: "object",
1469
+ properties: {
1470
+ parentBeatName: { type: "string", description: "Name of the parent narrative beat to telescope" },
1471
+ newCurrentReality: { type: "string", description: "Updated current reality for the telescoped beat" },
1472
+ initialSubBeats: {
1473
+ type: "array",
1474
+ items: {
1475
+ type: "object",
1476
+ properties: {
1477
+ title: { type: "string" },
1478
+ type_dramatic: { type: "string" },
1479
+ description: { type: "string" },
1480
+ prose: { type: "string" },
1481
+ lessons: { type: "array", items: { type: "string" } }
1482
+ },
1483
+ required: ["title", "type_dramatic", "description", "prose", "lessons"]
1484
+ },
1485
+ description: "Optional initial sub-beats to create"
1486
+ }
1487
+ },
1488
+ required: ["parentBeatName", "newCurrentReality"]
1489
+ }
1490
+ },
1491
+ {
1492
+ name: "list_narrative_beats",
1493
+ description: "List all narrative beats, optionally filtered by parent chart ID. Shows multi-universe story progression.",
1494
+ inputSchema: {
1495
+ type: "object",
1496
+ properties: {
1497
+ parentChartId: { type: "string", description: "Optional: Filter by parent chart ID" }
1498
+ }
1499
+ }
1500
+ }
1501
+ ];
1502
+ // Filter tools based on enabled tools set
1503
+ const filteredTools = allTools.filter(tool => enabledTools.has(tool.name));
1504
+ return {
1505
+ tools: filteredTools,
1506
+ };
1507
+ });
1508
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1509
+ try {
1510
+ const { name, arguments: args } = request.params;
1511
+ // Strict validation: name must exist
1512
+ if (!name || typeof name !== 'string') {
1513
+ return {
1514
+ content: [{ type: "text", text: `Error: Invalid tool name: ${name}` }],
1515
+ isError: true
1516
+ };
1517
+ }
1518
+ // Strict validation: args must be object or undefined
1519
+ if (args !== undefined && (typeof args !== 'object' || args === null || Array.isArray(args))) {
1520
+ return {
1521
+ content: [{ type: "text", text: `Error: Tool arguments must be an object, received: ${typeof args}` }],
1522
+ isError: true
1523
+ };
1524
+ }
1525
+ const toolArgs = args || {};
1526
+ switch (name) {
1527
+ case "create_entities": {
1528
+ const valResult = validate(toolArgs, { entities: ValidationSchemas.entityArray() });
1529
+ if (!valResult.valid)
1530
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1531
+ const result = await knowledgeGraphManager.createEntities(toolArgs.entities);
1532
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1533
+ }
1534
+ case "create_relations": {
1535
+ const valResult = validate(toolArgs, { relations: ValidationSchemas.relationArray() });
1536
+ if (!valResult.valid)
1537
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1538
+ const result = await knowledgeGraphManager.createRelations(toolArgs.relations);
1539
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1540
+ }
1541
+ case "add_observations": {
1542
+ const valResult = validate(toolArgs, {
1543
+ observations: {
1544
+ type: 'array',
1545
+ required: true,
1546
+ items: {
1547
+ type: 'object',
1548
+ properties: {
1549
+ entityName: { type: 'string', required: true },
1550
+ contents: { type: 'array', required: true, items: { type: 'string' } }
1551
+ }
1552
+ }
1553
+ }
1554
+ });
1555
+ if (!valResult.valid)
1556
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1557
+ const result = await knowledgeGraphManager.addObservations(toolArgs.observations);
1558
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1559
+ }
1560
+ case "delete_entities": {
1561
+ const valResult = validate(toolArgs, { entityNames: ValidationSchemas.stringArray() });
1562
+ if (!valResult.valid)
1563
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1564
+ await knowledgeGraphManager.deleteEntities(toolArgs.entityNames);
1565
+ return { content: [{ type: "text", text: "Entities deleted successfully" }] };
1566
+ }
1567
+ case "delete_observations": {
1568
+ const valResult = validate(toolArgs, {
1569
+ deletions: {
1570
+ type: 'array',
1571
+ required: true,
1572
+ items: {
1573
+ type: 'object',
1574
+ properties: {
1575
+ entityName: { type: 'string', required: true },
1576
+ observations: { type: 'array', required: true, items: { type: 'string' } }
1577
+ }
1578
+ }
1579
+ }
1580
+ });
1581
+ if (!valResult.valid)
1582
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1583
+ await knowledgeGraphManager.deleteObservations(toolArgs.deletions);
1584
+ return { content: [{ type: "text", text: "Observations deleted successfully" }] };
1585
+ }
1586
+ case "delete_relations": {
1587
+ const valResult = validate(toolArgs, { relations: ValidationSchemas.relationArray() });
1588
+ if (!valResult.valid)
1589
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1590
+ await knowledgeGraphManager.deleteRelations(toolArgs.relations);
1591
+ return { content: [{ type: "text", text: "Relations deleted successfully" }] };
1592
+ }
1593
+ case "read_graph": {
1594
+ const result = await knowledgeGraphManager.readGraph();
1595
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1596
+ }
1597
+ case "search_nodes": {
1598
+ const valResult = validate(toolArgs, { query: ValidationSchemas.nonEmptyString() });
1599
+ if (!valResult.valid)
1600
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1601
+ const result = await knowledgeGraphManager.searchNodes(toolArgs.query);
1602
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1603
+ }
1604
+ case "open_nodes": {
1605
+ const valResult = validate(toolArgs, { names: { type: 'array', required: true, minLength: 1, items: { type: 'string' } } });
1606
+ if (!valResult.valid)
1607
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1608
+ const result = await knowledgeGraphManager.openNodes(toolArgs.names);
1609
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1610
+ }
1611
+ case "create_structural_tension_chart": {
1612
+ const valResult = validate(toolArgs, {
1613
+ desiredOutcome: ValidationSchemas.nonEmptyString(),
1614
+ currentReality: ValidationSchemas.nonEmptyString(),
1615
+ dueDate: ValidationSchemas.isoDate(),
1616
+ actionSteps: { type: 'array', items: { type: 'string' } }
1617
+ });
1618
+ if (!valResult.valid)
1619
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1620
+ const chartResult = await knowledgeGraphManager.createStructuralTensionChart(toolArgs.desiredOutcome, toolArgs.currentReality, toolArgs.dueDate, (Array.isArray(toolArgs.actionSteps) ? toolArgs.actionSteps : []));
1621
+ return { content: [{ type: "text", text: JSON.stringify(chartResult, null, 2) }] };
1622
+ }
1623
+ case "telescope_action_step": {
1624
+ const valResult = validate(toolArgs, {
1625
+ actionStepName: ValidationSchemas.nonEmptyString(),
1626
+ newCurrentReality: ValidationSchemas.nonEmptyString(),
1627
+ initialActionSteps: { type: 'array', items: { type: 'string' } }
1628
+ });
1629
+ if (!valResult.valid)
1630
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1631
+ const telescopeResult = await knowledgeGraphManager.telescopeActionStep(toolArgs.actionStepName, toolArgs.newCurrentReality, (Array.isArray(toolArgs.initialActionSteps) ? toolArgs.initialActionSteps : []));
1632
+ return { content: [{ type: "text", text: JSON.stringify(telescopeResult, null, 2) }] };
1633
+ }
1634
+ case "mark_action_complete": {
1635
+ const valResult = validate(toolArgs, { actionStepName: ValidationSchemas.nonEmptyString() });
1636
+ if (!valResult.valid)
1637
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1638
+ await knowledgeGraphManager.markActionStepComplete(toolArgs.actionStepName);
1639
+ return { content: [{ type: "text", text: `Action step '${toolArgs.actionStepName}' marked as complete and current reality updated` }] };
1640
+ }
1641
+ case "get_chart_progress": {
1642
+ const valResult = validate(toolArgs, { chartId: ValidationSchemas.nonEmptyString() });
1643
+ if (!valResult.valid)
1644
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1645
+ const progressResult = await knowledgeGraphManager.getChartProgress(toolArgs.chartId);
1646
+ return { content: [{ type: "text", text: JSON.stringify(progressResult, null, 2) }] };
1647
+ }
1648
+ case "list_active_charts": {
1649
+ const chartsResult = await knowledgeGraphManager.listActiveCharts();
1650
+ let hierarchyText = "## Structural Tension Charts Hierarchy\n\n";
1651
+ const masterCharts = chartsResult.filter(c => c.level === 0);
1652
+ const actionCharts = chartsResult.filter(c => c.level > 0);
1653
+ masterCharts.forEach(master => {
1654
+ const progress = master.progress > 0 ? ` (${Math.round(master.progress * 100)}% complete)` : "";
1655
+ const dueDate = master.dueDate ? ` [Due: ${new Date(master.dueDate).toLocaleDateString()}]` : "";
1656
+ hierarchyText += `šŸ“‹ **${master.desiredOutcome}** (Master Chart)${progress}${dueDate}\n`;
1657
+ hierarchyText += ` ID: ${master.chartId}\n`;
1658
+ const actions = actionCharts.filter(a => a.parentChart === master.chartId);
1659
+ if (actions.length > 0) {
1660
+ actions.forEach((action, index) => {
1661
+ const isLast = index === actions.length - 1;
1662
+ const connector = isLast ? "└── " : "ā”œā”€ā”€ ";
1663
+ const actionProgress = action.progress > 0 ? ` (${Math.round(action.progress * 100)}%)` : "";
1664
+ const actionDue = action.dueDate ? ` [${new Date(action.dueDate).toLocaleDateString()}]` : "";
1665
+ hierarchyText += ` ${connector}šŸŽÆ ${action.desiredOutcome} (Action Step)${actionProgress}${actionDue}\n`;
1666
+ hierarchyText += ` ID: ${action.chartId}\n`;
1667
+ });
1668
+ }
1669
+ else {
1670
+ hierarchyText += ` └── (No action steps yet)\n`;
1671
+ }
1672
+ hierarchyText += "\n";
1673
+ });
1674
+ if (masterCharts.length === 0) {
1675
+ hierarchyText += "No active structural tension charts found.\n\n";
1676
+ hierarchyText += "šŸ’” Create your first chart with: create_structural_tension_chart\n";
1677
+ }
1678
+ return { content: [{ type: "text", text: hierarchyText }] };
1679
+ }
1680
+ case "update_action_progress": {
1681
+ const valResult = validate(toolArgs, {
1682
+ actionStepName: ValidationSchemas.nonEmptyString(),
1683
+ progressObservation: ValidationSchemas.nonEmptyString(),
1684
+ updateCurrentReality: { type: 'boolean' }
1685
+ });
1686
+ if (!valResult.valid)
1687
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1688
+ await knowledgeGraphManager.updateActionProgress(toolArgs.actionStepName, toolArgs.progressObservation, toolArgs.updateCurrentReality === true);
1689
+ return { content: [{ type: "text", text: `Action step '${toolArgs.actionStepName}' progress updated` }] };
1690
+ }
1691
+ case "update_current_reality": {
1692
+ const valResult = validate(toolArgs, {
1693
+ chartId: ValidationSchemas.nonEmptyString(),
1694
+ newObservations: { type: 'array', required: true, minLength: 1, items: { type: 'string' } }
1695
+ });
1696
+ if (!valResult.valid)
1697
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1698
+ await knowledgeGraphManager.updateCurrentReality(toolArgs.chartId, toolArgs.newObservations);
1699
+ return { content: [{ type: "text", text: `Current reality updated for chart '${toolArgs.chartId}'` }] };
1700
+ }
1701
+ case "manage_action_step": {
1702
+ const valResult = validate(toolArgs, {
1703
+ parentReference: ValidationSchemas.nonEmptyString(),
1704
+ actionDescription: ValidationSchemas.nonEmptyString(),
1705
+ currentReality: { type: 'string' },
1706
+ initialActionSteps: { type: 'array', items: { type: 'string' } },
1707
+ dueDate: { type: 'date' }
1708
+ });
1709
+ if (!valResult.valid)
1710
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1711
+ const manageActionResult = await knowledgeGraphManager.manageActionStep(toolArgs.parentReference, toolArgs.actionDescription, toolArgs.currentReality, toolArgs.initialActionSteps, toolArgs.dueDate);
1712
+ return { content: [{ type: "text", text: `Action step '${toolArgs.actionDescription}' managed for parent '${toolArgs.parentReference}'. Result: ${JSON.stringify(manageActionResult, null, 2)}` }] };
1713
+ }
1714
+ case "add_action_step": {
1715
+ const valResult = validate(toolArgs, {
1716
+ parentChartId: ValidationSchemas.nonEmptyString(),
1717
+ actionStepTitle: ValidationSchemas.nonEmptyString(),
1718
+ currentReality: ValidationSchemas.nonEmptyString(),
1719
+ dueDate: { type: 'date' }
1720
+ });
1721
+ if (!valResult.valid)
1722
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1723
+ const addActionResult = await knowledgeGraphManager.addActionStep(toolArgs.parentChartId, toolArgs.actionStepTitle, toolArgs.dueDate, toolArgs.currentReality);
1724
+ return { content: [{ type: "text", text: `Action step '${toolArgs.actionStepTitle}' added to chart '${toolArgs.parentChartId}' as telescoped chart '${addActionResult.chartId}'` }] };
1725
+ }
1726
+ case "remove_action_step": {
1727
+ const valResult = validate(toolArgs, {
1728
+ parentChartId: ValidationSchemas.nonEmptyString(),
1729
+ actionStepName: ValidationSchemas.nonEmptyString()
1730
+ });
1731
+ if (!valResult.valid)
1732
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1733
+ await knowledgeGraphManager.removeActionStep(toolArgs.parentChartId, toolArgs.actionStepName);
1734
+ return { content: [{ type: "text", text: `Action step '${toolArgs.actionStepName}' removed from chart '${toolArgs.parentChartId}'` }] };
1735
+ }
1736
+ case "update_desired_outcome": {
1737
+ const valResult = validate(toolArgs, {
1738
+ chartId: ValidationSchemas.nonEmptyString(),
1739
+ newDesiredOutcome: ValidationSchemas.nonEmptyString()
1740
+ });
1741
+ if (!valResult.valid)
1742
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1743
+ await knowledgeGraphManager.updateDesiredOutcome(toolArgs.chartId, toolArgs.newDesiredOutcome);
1744
+ return { content: [{ type: "text", text: `Desired outcome updated for chart '${toolArgs.chartId}'` }] };
1745
+ }
1746
+ case "creator_moment_of_truth": {
1747
+ const valResult = validate(toolArgs, {
1748
+ chartId: ValidationSchemas.nonEmptyString(),
1749
+ step: { type: 'enum', enumValues: ['full_review', 'acknowledge', 'analyze', 'plan', 'feedback'] },
1750
+ userInput: { type: 'string' }
1751
+ });
1752
+ if (!valResult.valid)
1753
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1754
+ const cmotStep = toolArgs.step || "full_review";
1755
+ const cmotUserInput = toolArgs.userInput;
1756
+ const cmotProgress = await knowledgeGraphManager.getChartProgress(toolArgs.chartId);
1757
+ const cmotGraph = await knowledgeGraphManager.readGraph();
1758
+ const cmotDesiredOutcome = cmotGraph.entities.find(e => e.name === `${toolArgs.chartId}_desired_outcome`);
1759
+ const cmotCurrentReality = cmotGraph.entities.find(e => e.name === `${toolArgs.chartId}_current_reality`);
1760
+ const cmotGuidance = {
1761
+ full_review: `## Creator Moment of Truth - Chart Review\n\n**Chart**: ${toolArgs.chartId}\n**Desired Outcome**: ${cmotDesiredOutcome?.observations[0] || 'Unknown'}\n**Current Reality**: ${cmotCurrentReality?.observations.join('; ') || 'Unknown'}\n**Progress**: ${Math.round(cmotProgress.progress * 100)}% (${cmotProgress.completedActions}/${cmotProgress.totalActions} action steps)\n\n---\n\n### The Four-Step Review Process\n\nGuide the user through each step to transform discrepancies into learning:\n\n**Step 1: ACKNOWLEDGE THE TRUTH**\nWhat difference exists between what was expected and what was delivered?\n- Report facts only, no excuses\n- "We expected X, we delivered Y"\n- Ask: "Looking at this chart, what expected progress didn't happen? What did happen instead?"\n\n**Step 2: ANALYZE HOW IT HAPPENED**\nHow did this come to pass?\n- Step-by-step tracking (not blame)\n- What assumptions were made?\n- How was it approached?\n- Ask: "Walk me through what happened. What did you tell yourself? What assumptions turned out to be wrong?"\n\n**Step 3: CREATE A PLAN FOR NEXT TIME**\nGiven what you discovered, how will you change your approach?\n- What patterns need to change?\n- What specific actions will be different?\n- Ask: "Based on what you learned, what will you do differently? What new action steps should we add?"\n\n**Step 4: SET UP A FEEDBACK SYSTEM**\nHow will you track whether you're actually making the changes?\n- Simple self-management system\n- How to notice old patterns returning\n- Ask: "How will you know if you're falling back to old patterns? What will remind you of the new approach?"\n\n---\n\n**After completing the review**: Update current reality with new observations, adjust action steps as needed.\n\n**Remember**: The goal is not perfection but effectiveness. Discrepancies are learning opportunities, not failures.`,
1762
+ acknowledge: `## Step 1: ACKNOWLEDGE THE TRUTH\n\n**Chart Progress**: ${Math.round(cmotProgress.progress * 100)}%\n**Desired Outcome**: ${cmotDesiredOutcome?.observations[0] || 'Unknown'}\n\n**Question**: What difference exists between what was expected and what was delivered?\n\nGuidelines:\n- Simply report the facts\n- No excuses, no blame\n- "We expected X, we delivered Y"\n- Focus on seeing reality clearly\n\n${cmotUserInput ? `\n**User's Observation**: ${cmotUserInput}\n\nNext: Proceed to Step 2 (analyze) to explore how this came to pass.` : 'Please share what you expected vs. what actually happened.'}`,
1763
+ analyze: `## Step 2: ANALYZE HOW IT HAPPENED\n\n**Question**: How did this come to pass?\n\nGuidelines:\n- Step-by-step tracking (this is co-exploration, not blame)\n- What assumptions were made?\n- What did you tell yourself?\n- How did you approach it?\n\n${cmotUserInput ? `\n**User's Analysis**: ${cmotUserInput}\n\nNext: Proceed to Step 3 (plan) to create adjustments based on these insights.` : 'Walk through the sequence of events. What assumptions turned out not to be true?'}`,
1764
+ plan: `## Step 3: CREATE A PLAN FOR NEXT TIME\n\n**Question**: Given what you discovered, how will you change your approach?\n\nGuidelines:\n- What assumptions turned out not to be true?\n- What patterns need to change?\n- What specific actions will you take differently?\n\n${cmotUserInput ? `\n**User's Plan**: ${cmotUserInput}\n\nNext: Use add_action_step or update_current_reality to record these changes, then proceed to Step 4 (feedback).` : 'What concrete adjustments will you make? Should we add new action steps or update the chart?'}`,
1765
+ feedback: `## Step 4: SET UP A FEEDBACK SYSTEM\n\n**Question**: How will you track whether you're actually making the changes?\n\nGuidelines:\n- Simple system for self-management\n- How will you notice if you're falling back to old patterns?\n- What will remind you of the new approach?\n\n${cmotUserInput ? `\n**User's Feedback System**: ${cmotUserInput}\n\nāœ… **Review Complete**. Use update_current_reality to record key learnings from this review.` : 'What simple tracking will help you stay on the new course?'}`
1766
+ };
1767
+ return { content: [{ type: "text", text: cmotGuidance[cmotStep] || cmotGuidance.full_review }] };
1768
+ }
1769
+ case "create_narrative_beat": {
1770
+ const valResult = validate(toolArgs, {
1771
+ parentChartId: ValidationSchemas.nonEmptyString(),
1772
+ title: ValidationSchemas.nonEmptyString(),
1773
+ act: { type: 'number', required: true, minValue: 1 },
1774
+ type_dramatic: ValidationSchemas.nonEmptyString(),
1775
+ universes: { type: 'array', required: true, minLength: 1, items: { type: 'string' } },
1776
+ description: ValidationSchemas.nonEmptyString(),
1777
+ prose: ValidationSchemas.nonEmptyString(),
1778
+ lessons: { type: 'array', required: true, items: { type: 'string' } },
1779
+ assessRelationalAlignment: { type: 'boolean' },
1780
+ initiateFourDirectionsInquiry: { type: 'boolean' },
1781
+ filePath: { type: 'string' }
1782
+ });
1783
+ if (!valResult.valid)
1784
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1785
+ const beatResult = await knowledgeGraphManager.createNarrativeBeat(toolArgs.parentChartId, toolArgs.title, toolArgs.act, toolArgs.type_dramatic, toolArgs.universes, toolArgs.description, toolArgs.prose, toolArgs.lessons, toolArgs.assessRelationalAlignment || false, toolArgs.initiateFourDirectionsInquiry || false, toolArgs.filePath);
1786
+ return { content: [{ type: "text", text: JSON.stringify(beatResult, null, 2) }] };
1787
+ }
1788
+ case "telescope_narrative_beat": {
1789
+ const valResult = validate(toolArgs, {
1790
+ parentBeatName: ValidationSchemas.nonEmptyString(),
1791
+ newCurrentReality: ValidationSchemas.nonEmptyString(),
1792
+ initialSubBeats: {
1793
+ type: 'array',
1794
+ items: {
1795
+ type: 'object',
1796
+ properties: {
1797
+ title: { type: 'string', required: true },
1798
+ type_dramatic: { type: 'string', required: true },
1799
+ description: { type: 'string', required: true },
1800
+ prose: { type: 'string', required: true },
1801
+ lessons: { type: 'array', required: true, items: { type: 'string' } }
1802
+ }
1803
+ }
1804
+ }
1805
+ });
1806
+ if (!valResult.valid)
1807
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1808
+ const telescopeResult = await knowledgeGraphManager.telescopeNarrativeBeat(toolArgs.parentBeatName, toolArgs.newCurrentReality, (Array.isArray(toolArgs.initialSubBeats) ? toolArgs.initialSubBeats : []));
1809
+ return { content: [{ type: "text", text: JSON.stringify(telescopeResult, null, 2) }] };
1810
+ }
1811
+ case "list_narrative_beats": {
1812
+ const valResult = validate(toolArgs, {
1813
+ parentChartId: { type: 'string' }
1814
+ });
1815
+ if (!valResult.valid)
1816
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1817
+ const parentChartId = toolArgs.parentChartId;
1818
+ const beatsResult = await knowledgeGraphManager.listNarrativeBeats(parentChartId);
1819
+ if (beatsResult.length === 0) {
1820
+ return { content: [{ type: "text", text: "No narrative beats found." }] };
1821
+ }
1822
+ let beatsText = "## šŸ“– Narrative Beats\n\n";
1823
+ beatsResult.forEach((beat) => {
1824
+ const act = beat.metadata?.act || '?';
1825
+ const type = beat.metadata?.type_dramatic || 'Unknown';
1826
+ const universes = beat.metadata?.universes?.join(', ') || 'Unknown';
1827
+ const lessons = beat.metadata?.narrative?.lessons || [];
1828
+ beatsText += `### Act ${act}: ${type}\n`;
1829
+ beatsText += `**Name**: ${beat.name}\n`;
1830
+ beatsText += `**Universes**: ${universes}\n`;
1831
+ beatsText += `**Description**: ${beat.metadata?.narrative?.description || 'N/A'}\n`;
1832
+ if (lessons.length > 0) {
1833
+ beatsText += `**Lessons**: ${lessons.join(', ')}\n`;
1834
+ }
1835
+ beatsText += "\n";
1836
+ });
1837
+ return { content: [{ type: "text", text: beatsText }] };
1838
+ }
1839
+ case "init_llm_guidance": {
1840
+ const valResult = validate(toolArgs, {
1841
+ format: { type: 'enum', enumValues: ['full', 'quick', 'save_directive'] }
1842
+ });
1843
+ if (!valResult.valid)
1844
+ return { content: [{ type: "text", text: `Error: ${valResult.error}` }], isError: true };
1845
+ const format = toolArgs.format || "full";
1846
+ if (format === "save_directive") {
1847
+ return { content: [{ type: "text", text: `## šŸ’” RECOMMENDED: Save This Guidance\n\n**Claude Code**: Add this guidance to CLAUDE.md in your project directory\n**Gemini**: Save as GEMINI.md in your workspace \n**Other Agents**: Create AGENTS.md or similar session memory file\n\nThis ensures you remember COAIA Memory's structural tension principles across our entire conversation.\n\n**Quick Command**: Use format="full" to get the complete guidance content to save.` }] };
1848
+ }
1849
+ if (format === "quick") {
1850
+ return { content: [{ type: "text", text: `## 🚨 COAIA Memory Quick Reference\n\n**CRITICAL**: "Ready to begin" = WRONG. Current reality must be factual assessment.\n\n**Core Tools**:\n1. \`list_active_charts\` → Start here, see all charts\n2. \`create_structural_tension_chart\` → New chart (outcome + reality + actions)\n3. \`add_action_step\` → Add strategic actions (creates telescoped chart)\n4. \`telescope_action_step\` → Break down actions into detailed sub-charts\n\n**Common Mistakes**:\nāŒ "Ready to begin Django tutorial" \nāœ… "Never used Django, completed Python basics"\n\nUse format="full" for complete guidance.` }] };
1851
+ }
1852
+ // Default: full guidance
1853
+ return { content: [{ type: "text", text: LLM_GUIDANCE }] };
1854
+ }
1855
+ default: {
1856
+ return {
1857
+ content: [{ type: "text", text: `Error: Unknown tool: ${name}` }],
1858
+ isError: true
1859
+ };
1860
+ }
1861
+ }
1862
+ }
1863
+ catch (error) {
1864
+ const errorMessage = error instanceof Error ? error.message : String(error);
1865
+ return {
1866
+ content: [{ type: "text", text: `Error executing tool: ${errorMessage}` }],
1867
+ isError: true
1868
+ };
1869
+ }
1870
+ });
1871
+ async function main() {
1872
+ const transport = new StdioServerTransport();
1873
+ await server.connect(transport);
1874
+ console.error("COAIA Narrative - Creative Oriented AI Assistant Memory Server - Narrative Beat Extension running on stdio");
1875
+ }
1876
+ main().catch((error) => {
1877
+ console.error("Fatal error in main():", error);
1878
+ process.exit(1);
1879
+ });