coaiajs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +56 -0
- package/KINSHIP.md +63 -0
- package/README.md +148 -0
- package/articles/academic/creative-orientation-vs-problem-solving.md +177 -0
- package/articles/academic/jsonl-knowledge-graphs-agent-memory.md +142 -0
- package/articles/academic/langfuse-observability-llm-pipelines.md +144 -0
- package/articles/academic/medicine-wheel-software-architecture.md +163 -0
- package/articles/academic/mmot-autonomous-agents.md +156 -0
- package/articles/academic/model-context-protocol-interagent.md +161 -0
- package/articles/academic/pde-prompt-decomposition.md +186 -0
- package/articles/academic/structural-tension-in-ai-agents.md +134 -0
- package/articles/reviews/mcp-protocol-design-review.md +170 -0
- package/articles/reviews/observability-ai-systems-review.md +176 -0
- package/articles/reviews/prompt-engineering-decomposition-review.md +184 -0
- package/articles/surveys/agent-orchestration-survey.md +186 -0
- package/articles/surveys/knowledge-graph-storage-survey.md +204 -0
- package/articles/surveys/structural-tension-methodology-survey.md +154 -0
- package/articles/technical/aws-sdk-v3-polly.md +270 -0
- package/articles/technical/commander-cli-framework.md +262 -0
- package/articles/technical/dotenv-config-patterns.md +360 -0
- package/articles/technical/ioredis-vs-redis.md +142 -0
- package/articles/technical/langfuse-js-sdk-vs-rest.md +191 -0
- package/articles/technical/mcp-sdk-typescript.md +291 -0
- package/articles/technical/octokit-github-api.md +293 -0
- package/articles/technical/openai-sdk-modern.md +231 -0
- package/articles/technical/yaml-parsing-node.md +266 -0
- package/articles/technical/zod-runtime-validation.md +212 -0
- package/dist/mcp/config.d.ts +14 -0
- package/dist/mcp/config.js +185 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.js +324 -0
- package/dist/mcp/tools/coaiapy-tools.d.ts +8 -0
- package/dist/mcp/tools/coaiapy-tools.js +326 -0
- package/dist/mcp/tools/index.d.ts +3 -0
- package/dist/mcp/tools/index.js +3 -0
- package/dist/src/audio.d.ts +12 -0
- package/dist/src/audio.js +57 -0
- package/dist/src/cli-helpers.d.ts +9 -0
- package/dist/src/cli-helpers.js +78 -0
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.js +1086 -0
- package/dist/src/config.d.ts +15 -0
- package/dist/src/config.js +145 -0
- package/dist/src/environment.d.ts +44 -0
- package/dist/src/environment.js +146 -0
- package/dist/src/github.d.ts +47 -0
- package/dist/src/github.js +79 -0
- package/dist/src/langfuse/client.d.ts +30 -0
- package/dist/src/langfuse/client.js +75 -0
- package/dist/src/langfuse/comments.d.ts +16 -0
- package/dist/src/langfuse/comments.js +36 -0
- package/dist/src/langfuse/datasets.d.ts +19 -0
- package/dist/src/langfuse/datasets.js +140 -0
- package/dist/src/langfuse/index.d.ts +13 -0
- package/dist/src/langfuse/index.js +10 -0
- package/dist/src/langfuse/media.d.ts +11 -0
- package/dist/src/langfuse/media.js +167 -0
- package/dist/src/langfuse/observations.d.ts +18 -0
- package/dist/src/langfuse/observations.js +113 -0
- package/dist/src/langfuse/prompts.d.ts +14 -0
- package/dist/src/langfuse/prompts.js +127 -0
- package/dist/src/langfuse/scores.d.ts +54 -0
- package/dist/src/langfuse/scores.js +366 -0
- package/dist/src/langfuse/traces.d.ts +29 -0
- package/dist/src/langfuse/traces.js +225 -0
- package/dist/src/llm.d.ts +23 -0
- package/dist/src/llm.js +76 -0
- package/dist/src/narrative/graph-manager.d.ts +106 -0
- package/dist/src/narrative/graph-manager.js +915 -0
- package/dist/src/narrative/index.d.ts +9 -0
- package/dist/src/narrative/index.js +7 -0
- package/dist/src/narrative/markdown-export.d.ts +21 -0
- package/dist/src/narrative/markdown-export.js +383 -0
- package/dist/src/narrative/tool-definitions.d.ts +21 -0
- package/dist/src/narrative/tool-definitions.js +588 -0
- package/dist/src/narrative/tool-handlers.d.ts +11 -0
- package/dist/src/narrative/tool-handlers.js +462 -0
- package/dist/src/narrative/types.d.ts +2 -0
- package/dist/src/narrative/types.js +2 -0
- package/dist/src/narrative/validation.d.ts +29 -0
- package/dist/src/narrative/validation.js +144 -0
- package/dist/src/pde/index.d.ts +8 -0
- package/dist/src/pde/index.js +8 -0
- package/dist/src/pde/mcp-handlers.d.ts +7 -0
- package/dist/src/pde/mcp-handlers.js +300 -0
- package/dist/src/pde/mcp-tools.d.ts +15 -0
- package/dist/src/pde/mcp-tools.js +191 -0
- package/dist/src/pde/session-manager.d.ts +32 -0
- package/dist/src/pde/session-manager.js +205 -0
- package/dist/src/pde/stc-mapper.d.ts +26 -0
- package/dist/src/pde/stc-mapper.js +234 -0
- package/dist/src/pipeline/index.d.ts +2 -0
- package/dist/src/pipeline/index.js +3 -0
- package/dist/src/pipeline/template-engine.d.ts +20 -0
- package/dist/src/pipeline/template-engine.js +361 -0
- package/dist/src/planning/index.d.ts +7 -0
- package/dist/src/planning/index.js +7 -0
- package/dist/src/planning/mcp-handlers.d.ts +7 -0
- package/dist/src/planning/mcp-handlers.js +310 -0
- package/dist/src/planning/mcp-tools.d.ts +15 -0
- package/dist/src/planning/mcp-tools.js +145 -0
- package/dist/src/planning/plan-parser.d.ts +35 -0
- package/dist/src/planning/plan-parser.js +506 -0
- package/dist/src/redis.d.ts +21 -0
- package/dist/src/redis.js +78 -0
- package/dist/src/types.d.ts +244 -0
- package/dist/src/types.js +8 -0
- package/mcp/config.ts +196 -0
- package/mcp/server.ts +402 -0
- package/mcp/tools/coaiapy-tools.ts +364 -0
- package/mcp/tools/index.ts +4 -0
- package/package.json +52 -0
- package/rispecs/00-coaiajs-platform.spec.md +102 -0
- package/rispecs/01-core-config.spec.md +89 -0
- package/rispecs/02-redis-module.spec.md +62 -0
- package/rispecs/03-langfuse-module.spec.md +101 -0
- package/rispecs/04-narrative-engine.spec.md +185 -0
- package/rispecs/05-pde-engine.spec.md +112 -0
- package/rispecs/06-planning-engine.spec.md +128 -0
- package/rispecs/07-pipeline-templates.spec.md +97 -0
- package/rispecs/08-cli-interface.spec.md +134 -0
- package/rispecs/09-mcp-server.spec.md +140 -0
- package/rispecs/10-audio-module.spec.md +63 -0
- package/rispecs/KINSHIP.md +56 -0
- package/rispecs/README.md +100 -0
- package/src/audio.ts +76 -0
- package/src/cli-helpers.ts +86 -0
- package/src/cli.ts +1223 -0
- package/src/config.ts +172 -0
- package/src/environment.ts +171 -0
- package/src/github.ts +143 -0
- package/src/langfuse/client.ts +105 -0
- package/src/langfuse/comments.ts +52 -0
- package/src/langfuse/datasets.ts +178 -0
- package/src/langfuse/index.ts +33 -0
- package/src/langfuse/media.ts +193 -0
- package/src/langfuse/observations.ts +131 -0
- package/src/langfuse/prompts.ts +157 -0
- package/src/langfuse/scores.ts +456 -0
- package/src/langfuse/traces.ts +276 -0
- package/src/llm.ts +106 -0
- package/src/narrative/graph-manager.ts +1358 -0
- package/src/narrative/index.ts +32 -0
- package/src/narrative/markdown-export.ts +535 -0
- package/src/narrative/tool-definitions.ts +635 -0
- package/src/narrative/tool-handlers.ts +528 -0
- package/src/narrative/types.ts +9 -0
- package/src/narrative/validation.ts +179 -0
- package/src/pde/index.ts +8 -0
- package/src/pde/mcp-handlers.ts +359 -0
- package/src/pde/mcp-tools.ts +201 -0
- package/src/pde/session-manager.ts +248 -0
- package/src/pde/stc-mapper.ts +298 -0
- package/src/pipeline/index.ts +7 -0
- package/src/pipeline/template-engine.ts +398 -0
- package/src/planning/index.ts +13 -0
- package/src/planning/mcp-handlers.ts +369 -0
- package/src/planning/mcp-tools.ts +155 -0
- package/src/planning/plan-parser.ts +587 -0
- package/src/redis.ts +97 -0
- package/src/types.ts +280 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// narrative/index.ts — Module barrel export
|
|
2
|
+
|
|
3
|
+
export { KnowledgeGraphManager } from './graph-manager.js';
|
|
4
|
+
export { handleToolCall } from './tool-handlers.js';
|
|
5
|
+
export { validate, ValidationSchemas } from './validation.js';
|
|
6
|
+
export {
|
|
7
|
+
ALL_TOOL_DEFINITIONS,
|
|
8
|
+
STC_TOOLS,
|
|
9
|
+
NARRATIVE_TOOLS,
|
|
10
|
+
KG_TOOLS,
|
|
11
|
+
CORE_TOOLS,
|
|
12
|
+
} from './tool-definitions.js';
|
|
13
|
+
export type { ToolDefinition } from './tool-definitions.js';
|
|
14
|
+
export {
|
|
15
|
+
exportChartToMarkdown,
|
|
16
|
+
exportAllCharts,
|
|
17
|
+
exportChartProgress,
|
|
18
|
+
exportChartStats,
|
|
19
|
+
writeMarkdownToFile,
|
|
20
|
+
getDefaultFilename,
|
|
21
|
+
} from './markdown-export.js';
|
|
22
|
+
export type { MarkdownOptions } from './markdown-export.js';
|
|
23
|
+
|
|
24
|
+
// Re-export narrative-relevant types
|
|
25
|
+
export type {
|
|
26
|
+
Entity,
|
|
27
|
+
EntityMetadata,
|
|
28
|
+
Relation,
|
|
29
|
+
RelationMetadata,
|
|
30
|
+
KnowledgeGraph,
|
|
31
|
+
McpToolResult,
|
|
32
|
+
} from './types.js';
|
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown Export Module for COAIA Narrative
|
|
3
|
+
*
|
|
4
|
+
* Provides markdown generation for structural tension charts,
|
|
5
|
+
* narrative beats, and associated data structures.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { promises as fs } from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import type { Entity, KnowledgeGraph } from '../types.js';
|
|
11
|
+
import type { KnowledgeGraphManager } from './graph-manager.js';
|
|
12
|
+
|
|
13
|
+
export interface MarkdownOptions {
|
|
14
|
+
includeMetadata?: boolean;
|
|
15
|
+
includeObservations?: boolean;
|
|
16
|
+
observationLimit?: number;
|
|
17
|
+
includeToc?: boolean;
|
|
18
|
+
outputPath?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ─── Utility Functions ────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
function escapeMarkdown(text: string): string {
|
|
24
|
+
return text
|
|
25
|
+
.replace(/\\/g, '\\\\')
|
|
26
|
+
.replace(/\*/g, '\\*')
|
|
27
|
+
.replace(/\[/g, '\\[')
|
|
28
|
+
.replace(/\]/g, '\\]')
|
|
29
|
+
.replace(/\(/g, '\\(')
|
|
30
|
+
.replace(/\)/g, '\\)');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function formatDateForMarkdown(dateStr?: string): string {
|
|
34
|
+
if (!dateStr) return 'No due date';
|
|
35
|
+
const date = new Date(dateStr);
|
|
36
|
+
const now = new Date();
|
|
37
|
+
const days = Math.ceil((date.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
|
38
|
+
|
|
39
|
+
if (days < 0) return `Overdue by ${Math.abs(days)} days`;
|
|
40
|
+
if (days === 0) return 'Due today';
|
|
41
|
+
if (days === 1) return 'Due tomorrow';
|
|
42
|
+
if (days <= 7) return `Due in ${days} days`;
|
|
43
|
+
return `${date.toLocaleDateString()}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function formatProgressBar(progress: number): string {
|
|
47
|
+
const width = 20;
|
|
48
|
+
const filled = Math.round(progress * width);
|
|
49
|
+
const empty = width - filled;
|
|
50
|
+
const bar = '█'.repeat(filled) + '░'.repeat(empty);
|
|
51
|
+
const percent = Math.round(progress * 100);
|
|
52
|
+
return `[${bar}] ${percent}%`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getStatusIcon(completed: boolean): string {
|
|
56
|
+
return completed ? '✅' : '⏳';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function slugify(text: string): string {
|
|
60
|
+
return text
|
|
61
|
+
.toLowerCase()
|
|
62
|
+
.replace(/[^\w\s-]/g, '')
|
|
63
|
+
.replace(/\s+/g, '-')
|
|
64
|
+
.replace(/-+/g, '-');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ─── Single Chart Export ──────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
export async function exportChartToMarkdown(
|
|
70
|
+
chartId: string,
|
|
71
|
+
manager: KnowledgeGraphManager,
|
|
72
|
+
options: MarkdownOptions = {},
|
|
73
|
+
): Promise<string> {
|
|
74
|
+
const graph = await manager.readGraph();
|
|
75
|
+
const {
|
|
76
|
+
includeMetadata = true,
|
|
77
|
+
includeObservations = true,
|
|
78
|
+
observationLimit = 0,
|
|
79
|
+
} = options;
|
|
80
|
+
|
|
81
|
+
const chart = graph.entities.find(
|
|
82
|
+
e =>
|
|
83
|
+
e.entityType === 'structural_tension_chart' &&
|
|
84
|
+
e.metadata?.chartId === chartId,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (!chart) {
|
|
88
|
+
return `# Error\n\nChart '${chartId}' not found.`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let md = '';
|
|
92
|
+
|
|
93
|
+
md += `# Chart: ${chartId}\n\n`;
|
|
94
|
+
|
|
95
|
+
if (includeMetadata) {
|
|
96
|
+
if (chart.metadata?.createdAt) {
|
|
97
|
+
const created = new Date(chart.metadata.createdAt).toLocaleString();
|
|
98
|
+
md += `> **Created**: ${created}\n`;
|
|
99
|
+
}
|
|
100
|
+
if (chart.metadata?.parentChart) {
|
|
101
|
+
md += `> **Parent Chart**: ${chart.metadata.parentChart} (Level ${chart.metadata.level})\n`;
|
|
102
|
+
} else {
|
|
103
|
+
md += `> **Master Chart** (Level ${chart.metadata?.level || 0})\n`;
|
|
104
|
+
}
|
|
105
|
+
if (chart.metadata?.dueDate) {
|
|
106
|
+
md += `> **Due**: ${formatDateForMarkdown(chart.metadata.dueDate)}\n`;
|
|
107
|
+
}
|
|
108
|
+
md += '\n';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Desired Outcome
|
|
112
|
+
const outcome = graph.entities.find(
|
|
113
|
+
e =>
|
|
114
|
+
e.name === `${chartId}_desired_outcome` &&
|
|
115
|
+
e.entityType === 'desired_outcome',
|
|
116
|
+
);
|
|
117
|
+
md += '## Desired Outcome\n\n';
|
|
118
|
+
if (outcome) {
|
|
119
|
+
md += `${escapeMarkdown(outcome.observations[0])}\n\n`;
|
|
120
|
+
} else {
|
|
121
|
+
md += '*(Unknown)*\n\n';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Current Reality
|
|
125
|
+
const currentReality = graph.entities.find(
|
|
126
|
+
e =>
|
|
127
|
+
e.name === `${chartId}_current_reality` &&
|
|
128
|
+
e.entityType === 'current_reality',
|
|
129
|
+
);
|
|
130
|
+
md += '## Current Reality\n\n';
|
|
131
|
+
if (currentReality && currentReality.observations.length > 0) {
|
|
132
|
+
if (includeObservations && observationLimit > 0) {
|
|
133
|
+
const obs = currentReality.observations.slice(-observationLimit);
|
|
134
|
+
obs.forEach(o => (md += `- ${escapeMarkdown(o)}\n`));
|
|
135
|
+
} else if (includeObservations) {
|
|
136
|
+
currentReality.observations.forEach(o => (md += `- ${escapeMarkdown(o)}\n`));
|
|
137
|
+
} else {
|
|
138
|
+
md += `- ${escapeMarkdown(currentReality.observations[currentReality.observations.length - 1])}\n`;
|
|
139
|
+
}
|
|
140
|
+
md += '\n';
|
|
141
|
+
} else {
|
|
142
|
+
md += '*(Not assessed)*\n\n';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Progress & Structural Tension
|
|
146
|
+
const actionSteps = graph.entities.filter(
|
|
147
|
+
e => e.entityType === 'action_step' && e.metadata?.chartId === chartId,
|
|
148
|
+
);
|
|
149
|
+
const completed = actionSteps.filter(
|
|
150
|
+
a => a.metadata?.completionStatus === true,
|
|
151
|
+
).length;
|
|
152
|
+
const total = actionSteps.length;
|
|
153
|
+
const progress = total > 0 ? completed / total : 0;
|
|
154
|
+
|
|
155
|
+
md += '## Structural Tension\n\n';
|
|
156
|
+
md += `${formatProgressBar(progress)}\n\n`;
|
|
157
|
+
md += `- **Completed**: ${completed}/${total} action steps\n\n`;
|
|
158
|
+
|
|
159
|
+
// Action Steps
|
|
160
|
+
if (actionSteps.length > 0) {
|
|
161
|
+
md += '## Action Steps\n\n';
|
|
162
|
+
actionSteps.forEach((step, idx) => {
|
|
163
|
+
const isComplete = step.metadata?.completionStatus === true;
|
|
164
|
+
const status = getStatusIcon(isComplete);
|
|
165
|
+
const stepId = (step.metadata?.chartId as string) || `step_${idx + 1}`;
|
|
166
|
+
|
|
167
|
+
md += `### ${status} ${escapeMarkdown(step.observations[0])}\n\n`;
|
|
168
|
+
md += `- **ID**: ${stepId}\n`;
|
|
169
|
+
md += `- **Status**: ${isComplete ? 'Completed' : 'In Progress'}\n`;
|
|
170
|
+
if (step.metadata?.dueDate) {
|
|
171
|
+
md += `- **Due**: ${formatDateForMarkdown(step.metadata.dueDate)}\n`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (step.observations.length > 1 && includeObservations) {
|
|
175
|
+
md += `- **Notes**:\n`;
|
|
176
|
+
step.observations.slice(1).forEach(note => {
|
|
177
|
+
md += ` - ${escapeMarkdown(note)}\n`;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
md += '\n';
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Narrative Beats
|
|
185
|
+
const narrativeBeats = graph.entities
|
|
186
|
+
.filter(
|
|
187
|
+
e =>
|
|
188
|
+
e.entityType === 'narrative_beat' && e.metadata?.chartId === chartId,
|
|
189
|
+
)
|
|
190
|
+
.sort((a, b) => ((a.metadata?.act as number) || 0) - ((b.metadata?.act as number) || 0));
|
|
191
|
+
|
|
192
|
+
if (narrativeBeats.length > 0) {
|
|
193
|
+
md += '## Narrative Beats\n\n';
|
|
194
|
+
narrativeBeats.forEach(beat => {
|
|
195
|
+
const act = beat.metadata?.act || '?';
|
|
196
|
+
const type = beat.metadata?.type_dramatic || 'Unknown';
|
|
197
|
+
const universes = (beat.metadata?.universes as string[]) || [];
|
|
198
|
+
|
|
199
|
+
md += `### Act ${act}: ${escapeMarkdown(type as string)}\n\n`;
|
|
200
|
+
|
|
201
|
+
if (universes.length > 0) {
|
|
202
|
+
md += `- **Universes**: ${universes.join(', ')}\n`;
|
|
203
|
+
}
|
|
204
|
+
if (beat.metadata?.timestamp) {
|
|
205
|
+
const ts = new Date(beat.metadata.timestamp).toLocaleString();
|
|
206
|
+
md += `- **Timestamp**: ${ts}\n`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (beat.metadata?.narrative?.description) {
|
|
210
|
+
md += `\n**Description**:\n\n${escapeMarkdown(beat.metadata.narrative.description)}\n`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (beat.metadata?.narrative?.prose) {
|
|
214
|
+
md += `\n**Prose**:\n\n${escapeMarkdown(beat.metadata.narrative.prose)}\n`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (
|
|
218
|
+
beat.metadata?.narrative?.lessons &&
|
|
219
|
+
beat.metadata.narrative.lessons.length > 0
|
|
220
|
+
) {
|
|
221
|
+
md += `\n**Lessons**:\n\n`;
|
|
222
|
+
beat.metadata.narrative.lessons.forEach(lesson => {
|
|
223
|
+
md += `- ${escapeMarkdown(lesson)}\n`;
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const dirs = beat.metadata?.fourDirections;
|
|
228
|
+
if (
|
|
229
|
+
dirs &&
|
|
230
|
+
(dirs.north_vision ||
|
|
231
|
+
dirs.east_intention ||
|
|
232
|
+
dirs.south_emotion ||
|
|
233
|
+
dirs.west_introspection)
|
|
234
|
+
) {
|
|
235
|
+
md += `\n**Four Directions**:\n\n`;
|
|
236
|
+
if (dirs.north_vision) md += `- **North (Vision)**: ${escapeMarkdown(dirs.north_vision)}\n`;
|
|
237
|
+
if (dirs.east_intention) md += `- **East (Intention)**: ${escapeMarkdown(dirs.east_intention)}\n`;
|
|
238
|
+
if (dirs.south_emotion) md += `- **South (Emotion)**: ${escapeMarkdown(dirs.south_emotion)}\n`;
|
|
239
|
+
if (dirs.west_introspection) md += `- **West (Introspection)**: ${escapeMarkdown(dirs.west_introspection)}\n`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
md += '\n';
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return md;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ─── All Charts Export ────────────────────────────────────────────────
|
|
250
|
+
|
|
251
|
+
export async function exportAllCharts(
|
|
252
|
+
manager: KnowledgeGraphManager,
|
|
253
|
+
options: MarkdownOptions = {},
|
|
254
|
+
): Promise<string> {
|
|
255
|
+
const graph = await manager.readGraph();
|
|
256
|
+
const { includeToc = true } = options;
|
|
257
|
+
|
|
258
|
+
const charts = graph.entities.filter(
|
|
259
|
+
e => e.entityType === 'structural_tension_chart',
|
|
260
|
+
);
|
|
261
|
+
const masterCharts = charts.filter(c => c.metadata?.level === 0);
|
|
262
|
+
|
|
263
|
+
let md = '# Structural Tension Charts\n\n';
|
|
264
|
+
|
|
265
|
+
if (includeToc && masterCharts.length > 0) {
|
|
266
|
+
md += '## Table of Contents\n\n';
|
|
267
|
+
masterCharts.forEach(master => {
|
|
268
|
+
const cId = (master.metadata?.chartId as string) || master.name;
|
|
269
|
+
const slug = slugify(cId);
|
|
270
|
+
md += `- [${cId}](#${slug})\n`;
|
|
271
|
+
});
|
|
272
|
+
md += '\n---\n\n';
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
masterCharts.forEach((master, masterIdx) => {
|
|
276
|
+
const cId = (master.metadata?.chartId as string) || master.name;
|
|
277
|
+
|
|
278
|
+
md += `## ${cId}\n\n`;
|
|
279
|
+
|
|
280
|
+
const outcome = graph.entities.find(
|
|
281
|
+
e =>
|
|
282
|
+
e.name === `${cId}_desired_outcome` &&
|
|
283
|
+
e.entityType === 'desired_outcome',
|
|
284
|
+
);
|
|
285
|
+
md += `**Desired Outcome**: ${outcome?.observations[0] || 'Unknown'}\n\n`;
|
|
286
|
+
|
|
287
|
+
const cr = graph.entities.find(
|
|
288
|
+
e =>
|
|
289
|
+
e.name === `${cId}_current_reality` &&
|
|
290
|
+
e.entityType === 'current_reality',
|
|
291
|
+
);
|
|
292
|
+
if (cr && cr.observations.length > 0) {
|
|
293
|
+
md += `**Current Reality**: ${cr.observations[cr.observations.length - 1]}\n\n`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const actionSteps = graph.entities.filter(
|
|
297
|
+
e => e.entityType === 'action_step' && e.metadata?.chartId === cId,
|
|
298
|
+
);
|
|
299
|
+
const completed = actionSteps.filter(
|
|
300
|
+
a => a.metadata?.completionStatus === true,
|
|
301
|
+
).length;
|
|
302
|
+
const total = actionSteps.length;
|
|
303
|
+
const progress = total > 0 ? completed / total : 0;
|
|
304
|
+
|
|
305
|
+
md += `**Progress**: ${formatProgressBar(progress)} (${completed}/${total})\n\n`;
|
|
306
|
+
|
|
307
|
+
if (master.metadata?.dueDate) {
|
|
308
|
+
md += `**Due**: ${formatDateForMarkdown(master.metadata.dueDate)}\n\n`;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const actionCharts = charts.filter(
|
|
312
|
+
c => c.metadata?.parentChart === cId && c.metadata?.level === 1,
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
if (actionCharts.length > 0) {
|
|
316
|
+
md += '### Action Steps\n\n';
|
|
317
|
+
actionCharts.forEach(actionChart => {
|
|
318
|
+
const actionChartId = actionChart.metadata?.chartId as string;
|
|
319
|
+
const actionOutcome = graph.entities.find(
|
|
320
|
+
e =>
|
|
321
|
+
e.name === `${actionChartId}_desired_outcome` &&
|
|
322
|
+
e.entityType === 'desired_outcome',
|
|
323
|
+
);
|
|
324
|
+
const actionActions = graph.entities.filter(
|
|
325
|
+
e =>
|
|
326
|
+
e.entityType === 'action_step' &&
|
|
327
|
+
e.metadata?.chartId === actionChartId,
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
const actionCompleted = actionActions.filter(
|
|
331
|
+
a => a.metadata?.completionStatus === true,
|
|
332
|
+
).length;
|
|
333
|
+
const actionTotal = actionActions.length;
|
|
334
|
+
const actionProgress =
|
|
335
|
+
actionTotal > 0 ? actionCompleted / actionTotal : 0;
|
|
336
|
+
const isComplete = actionChart.metadata?.completionStatus === true;
|
|
337
|
+
const status = isComplete ? '✅' : actionProgress > 0 ? '🔄' : '⏳';
|
|
338
|
+
|
|
339
|
+
md += `- ${status} **${escapeMarkdown(actionOutcome?.observations[0] || 'Unknown')}**\n`;
|
|
340
|
+
md += ` - ID: ${actionChartId}\n`;
|
|
341
|
+
if (actionChart.metadata?.dueDate) {
|
|
342
|
+
md += ` - Due: ${formatDateForMarkdown(actionChart.metadata.dueDate)}\n`;
|
|
343
|
+
}
|
|
344
|
+
if (actionTotal > 0) {
|
|
345
|
+
md += ` - Progress: ${formatProgressBar(actionProgress)}\n`;
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
md += '\n';
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (masterIdx < masterCharts.length - 1) {
|
|
352
|
+
md += '---\n\n';
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
return md;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// ─── Progress Report ──────────────────────────────────────────────────
|
|
360
|
+
|
|
361
|
+
export async function exportChartProgress(
|
|
362
|
+
chartId: string,
|
|
363
|
+
manager: KnowledgeGraphManager,
|
|
364
|
+
): Promise<string> {
|
|
365
|
+
const graph = await manager.readGraph();
|
|
366
|
+
|
|
367
|
+
const chart = graph.entities.find(
|
|
368
|
+
e =>
|
|
369
|
+
e.entityType === 'structural_tension_chart' &&
|
|
370
|
+
e.metadata?.chartId === chartId,
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
if (!chart) {
|
|
374
|
+
return `# Error\n\nChart '${chartId}' not found.`;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const outcome = graph.entities.find(
|
|
378
|
+
e => e.name === `${chartId}_desired_outcome`,
|
|
379
|
+
);
|
|
380
|
+
const actions = graph.entities.filter(
|
|
381
|
+
e => e.entityType === 'action_step' && e.metadata?.chartId === chartId,
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
const completed = actions.filter(
|
|
385
|
+
a => a.metadata?.completionStatus === true,
|
|
386
|
+
);
|
|
387
|
+
const incomplete = actions.filter(
|
|
388
|
+
a => a.metadata?.completionStatus !== true,
|
|
389
|
+
);
|
|
390
|
+
const progress = actions.length > 0 ? completed.length / actions.length : 0;
|
|
391
|
+
|
|
392
|
+
let md = `# Progress Report: ${chartId}\n\n`;
|
|
393
|
+
|
|
394
|
+
md += `**Goal**: ${outcome?.observations[0] || 'Unknown'}\n\n`;
|
|
395
|
+
|
|
396
|
+
md += `## Overall Progress\n\n`;
|
|
397
|
+
md += `${formatProgressBar(progress)}\n\n`;
|
|
398
|
+
|
|
399
|
+
md += '## Completed Actions\n\n';
|
|
400
|
+
if (completed.length > 0) {
|
|
401
|
+
completed.forEach((action, idx) => {
|
|
402
|
+
md += `${idx + 1}. ${escapeMarkdown(action.observations[0])}\n`;
|
|
403
|
+
if (action.metadata?.dueDate) {
|
|
404
|
+
md += ` - Completed: ${formatDateForMarkdown(action.metadata.dueDate)}\n`;
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
} else {
|
|
408
|
+
md += '*No completed actions yet.*\n';
|
|
409
|
+
}
|
|
410
|
+
md += '\n';
|
|
411
|
+
|
|
412
|
+
md += '## Remaining Actions\n\n';
|
|
413
|
+
if (incomplete.length > 0) {
|
|
414
|
+
incomplete.forEach((action, idx) => {
|
|
415
|
+
md += `${idx + 1}. ${escapeMarkdown(action.observations[0])}\n`;
|
|
416
|
+
if (action.metadata?.dueDate) {
|
|
417
|
+
md += ` - Due: ${formatDateForMarkdown(action.metadata.dueDate)}\n`;
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
} else {
|
|
421
|
+
md += '*All actions completed!*\n';
|
|
422
|
+
}
|
|
423
|
+
md += '\n';
|
|
424
|
+
|
|
425
|
+
const currentReality = graph.entities.find(
|
|
426
|
+
e => e.name === `${chartId}_current_reality`,
|
|
427
|
+
);
|
|
428
|
+
if (currentReality && currentReality.observations.length > 0) {
|
|
429
|
+
md += '## Current Reality Notes\n\n';
|
|
430
|
+
currentReality.observations.slice(-5).forEach(obs => {
|
|
431
|
+
md += `- ${escapeMarkdown(obs)}\n`;
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return md;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// ─── Statistics ───────────────────────────────────────────────────────
|
|
439
|
+
|
|
440
|
+
export async function exportChartStats(
|
|
441
|
+
manager: KnowledgeGraphManager,
|
|
442
|
+
): Promise<string> {
|
|
443
|
+
const graph = await manager.readGraph();
|
|
444
|
+
|
|
445
|
+
const charts = graph.entities.filter(
|
|
446
|
+
e => e.entityType === 'structural_tension_chart',
|
|
447
|
+
);
|
|
448
|
+
const masterCharts = charts.filter(c => c.metadata?.level === 0);
|
|
449
|
+
const actionCharts = charts.filter(c => c.metadata?.level === 1);
|
|
450
|
+
|
|
451
|
+
const allActions = graph.entities.filter(
|
|
452
|
+
e => e.entityType === 'action_step',
|
|
453
|
+
);
|
|
454
|
+
const completedActions = allActions.filter(
|
|
455
|
+
a => a.metadata?.completionStatus === true,
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
const narrativeBeats = graph.entities.filter(
|
|
459
|
+
e => e.entityType === 'narrative_beat',
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
const overdueCharts = charts.filter(chart => {
|
|
463
|
+
if (!chart.metadata?.dueDate) return false;
|
|
464
|
+
const dueDate = new Date(chart.metadata.dueDate);
|
|
465
|
+
return dueDate < new Date();
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
let md = '# Structural Tension Charts Statistics\n\n';
|
|
469
|
+
|
|
470
|
+
md += '## Overview\n\n';
|
|
471
|
+
md += `| Metric | Count |\n`;
|
|
472
|
+
md += `|--------|-------|\n`;
|
|
473
|
+
md += `| Master Charts | ${masterCharts.length} |\n`;
|
|
474
|
+
md += `| Action Step Charts | ${actionCharts.length} |\n`;
|
|
475
|
+
md += `| Total Charts | ${charts.length} |\n`;
|
|
476
|
+
md += `| Narrative Beats | ${narrativeBeats.length} |\n`;
|
|
477
|
+
md += '\n';
|
|
478
|
+
|
|
479
|
+
md += '## Completion Status\n\n';
|
|
480
|
+
md += `| Status | Count |\n`;
|
|
481
|
+
md += `|--------|-------|\n`;
|
|
482
|
+
md += `| Completed Actions | ${completedActions.length}/${allActions.length} |\n`;
|
|
483
|
+
const overallProgress =
|
|
484
|
+
allActions.length > 0
|
|
485
|
+
? Math.round((completedActions.length / allActions.length) * 100)
|
|
486
|
+
: 0;
|
|
487
|
+
md += `| Overall Progress | ${overallProgress}% |\n`;
|
|
488
|
+
md += '\n';
|
|
489
|
+
|
|
490
|
+
if (overdueCharts.length > 0) {
|
|
491
|
+
md += '## ⚠️ Overdue Charts\n\n';
|
|
492
|
+
overdueCharts.forEach((chart, idx) => {
|
|
493
|
+
const chartId = (chart.metadata?.chartId as string) || chart.name;
|
|
494
|
+
md += `${idx + 1}. **${chartId}** - Due: ${formatDateForMarkdown(chart.metadata?.dueDate)}\n`;
|
|
495
|
+
});
|
|
496
|
+
} else {
|
|
497
|
+
md += '## ✅ Status\n\n';
|
|
498
|
+
md += 'All charts are on track!\n';
|
|
499
|
+
}
|
|
500
|
+
md += '\n';
|
|
501
|
+
|
|
502
|
+
return md;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// ─── File Output ──────────────────────────────────────────────────────
|
|
506
|
+
|
|
507
|
+
export async function writeMarkdownToFile(
|
|
508
|
+
content: string,
|
|
509
|
+
outputPath: string,
|
|
510
|
+
): Promise<void> {
|
|
511
|
+
const dir = path.dirname(outputPath);
|
|
512
|
+
try {
|
|
513
|
+
await fs.mkdir(dir, { recursive: true });
|
|
514
|
+
} catch (_e) {
|
|
515
|
+
// Directory may exist
|
|
516
|
+
}
|
|
517
|
+
await fs.writeFile(outputPath, content, 'utf-8');
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export function getDefaultFilename(exportType: string, chartId?: string): string {
|
|
521
|
+
const timestamp = new Date().toISOString().split('T')[0];
|
|
522
|
+
|
|
523
|
+
switch (exportType) {
|
|
524
|
+
case 'chart':
|
|
525
|
+
return `${chartId}-export-${timestamp}.md`;
|
|
526
|
+
case 'list':
|
|
527
|
+
return `structural-tension-charts-${timestamp}.md`;
|
|
528
|
+
case 'progress':
|
|
529
|
+
return `${chartId}-progress-${timestamp}.md`;
|
|
530
|
+
case 'stats':
|
|
531
|
+
return `statistics-${timestamp}.md`;
|
|
532
|
+
default:
|
|
533
|
+
return `export-${timestamp}.md`;
|
|
534
|
+
}
|
|
535
|
+
}
|