coaia-visualizer 1.4.2 → 1.5.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/.dockerignore +9 -0
- package/Dockerfile.app +50 -0
- package/Dockerfile.test +24 -0
- package/LIVE_MODE_DESIGN.md +435 -0
- package/MCP_TESTING_COMPLETE.md +302 -0
- package/MCP_TESTING_IMPLEMENTATION_SUMMARY.md +317 -0
- package/MCP_TESTING_SETUP.md +268 -0
- package/NAMING.md +218 -0
- package/QUICK_START_MCP_TESTING.md +236 -0
- package/WS__issue_8__coaia-visualizer__260207.code-workspace +45 -0
- package/app/api/audio/[filename]/route.ts +37 -0
- package/app/api/charts/[id]/route.ts +48 -35
- package/app/api/watch/route.ts +42 -0
- package/app/page.tsx +103 -53
- package/cli.ts +56 -3
- package/components/add-master-chart.tsx +230 -0
- package/components/chart-detail-editable.tsx +27 -16
- package/components/chart-list.tsx +13 -1
- package/components/create-chart-form.tsx +248 -0
- package/components/data-stats.tsx +9 -7
- package/components/live-indicator.tsx +14 -0
- package/components/ui/dialog.tsx +143 -0
- package/components/ui/label.tsx +24 -0
- package/direct-test.sh +180 -0
- package/dist/cli.js +52 -3
- package/docker-compose.test.yml +69 -0
- package/hooks/use-live-polling.ts +45 -0
- package/jgwill.coaia-visualizer-8--496dca71-d476-4ac9-ba9f-376add118dd8--260208.txt +2612 -0
- package/lib/chart-editor.ts +281 -68
- package/mcp/Dockerfile +21 -0
- package/mcp/README.md +25 -6
- package/mcp/src/api-client.ts +15 -3
- package/mcp/src/index.ts +17 -2
- package/mcp/src/tools/index.ts +21 -1
- package/mcp/test_mcp/.gemini/settings.json +18 -0
- package/mcp-config.json +14 -0
- package/package.json +2 -2
- package/run-mcp-tests.sh +99 -0
- package/samples/tradingchart.jsonl +31 -0
- package/test-data/test-master.jsonl +11 -0
- package/test-run.log +101 -0
- package/test-scripts/README.md +239 -0
- package/test-scripts/run-all-tests.sh +38 -0
- package/test-scripts/test-01-basic-operations.sh +87 -0
- package/test-scripts/test-02-telescope-creation.sh +91 -0
- package/test-scripts/test-03-navigation.sh +87 -0
- package/validate-mcp.sh +136 -0
package/lib/chart-editor.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
// lib/chart-editor.ts - Chart editing logic
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { EntityRecord, RelationRecord, ParsedData } from "./types"
|
|
4
4
|
|
|
5
5
|
export interface ChartUpdate {
|
|
6
|
-
type:
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
type:
|
|
7
|
+
| "update_desired_outcome"
|
|
8
|
+
| "update_current_reality"
|
|
9
|
+
| "add_observation"
|
|
10
|
+
| "update_action"
|
|
11
|
+
| "add_action"
|
|
12
|
+
| "delete_action"
|
|
13
|
+
| "toggle_action_completion"
|
|
14
|
+
| "update_due_date"
|
|
15
|
+
| "update_action_due_date"
|
|
9
16
|
chartId: string
|
|
10
17
|
data: any
|
|
11
18
|
}
|
|
@@ -23,7 +30,7 @@ export class ChartEditor {
|
|
|
23
30
|
updateDesiredOutcome(chartId: string, newText: string): void {
|
|
24
31
|
const desiredOutcomeName = `${chartId}_desired_outcome`
|
|
25
32
|
const entity = this.entities.get(desiredOutcomeName)
|
|
26
|
-
|
|
33
|
+
|
|
27
34
|
if (entity) {
|
|
28
35
|
entity.observations = [newText]
|
|
29
36
|
entity.metadata.updatedAt = new Date().toISOString()
|
|
@@ -34,7 +41,7 @@ export class ChartEditor {
|
|
|
34
41
|
addCurrentRealityObservation(chartId: string, observation: string): void {
|
|
35
42
|
const currentRealityName = `${chartId}_current_reality`
|
|
36
43
|
const entity = this.entities.get(currentRealityName)
|
|
37
|
-
|
|
44
|
+
|
|
38
45
|
if (entity) {
|
|
39
46
|
entity.observations.push(observation)
|
|
40
47
|
entity.metadata.updatedAt = new Date().toISOString()
|
|
@@ -45,12 +52,12 @@ export class ChartEditor {
|
|
|
45
52
|
updateCurrentReality(chartId: string, newObservations: string[]): void {
|
|
46
53
|
const currentRealityName = `${chartId}_current_reality`
|
|
47
54
|
const entity = this.entities.get(currentRealityName)
|
|
48
|
-
|
|
55
|
+
|
|
49
56
|
if (!entity) {
|
|
50
57
|
throw new Error(`Chart ${chartId} current reality not found`)
|
|
51
58
|
}
|
|
52
59
|
|
|
53
|
-
const uniqueObservations = newObservations.filter(obs => !entity.observations.includes(obs))
|
|
60
|
+
const uniqueObservations = newObservations.filter((obs) => !entity.observations.includes(obs))
|
|
54
61
|
entity.observations.push(...uniqueObservations)
|
|
55
62
|
entity.metadata.updatedAt = new Date().toISOString()
|
|
56
63
|
this.entities.set(currentRealityName, entity)
|
|
@@ -59,7 +66,7 @@ export class ChartEditor {
|
|
|
59
66
|
updateCurrentRealityObservation(chartId: string, index: number, newText: string): void {
|
|
60
67
|
const currentRealityName = `${chartId}_current_reality`
|
|
61
68
|
const entity = this.entities.get(currentRealityName)
|
|
62
|
-
|
|
69
|
+
|
|
63
70
|
if (entity && entity.observations[index] !== undefined) {
|
|
64
71
|
entity.observations[index] = newText
|
|
65
72
|
entity.metadata.updatedAt = new Date().toISOString()
|
|
@@ -70,7 +77,7 @@ export class ChartEditor {
|
|
|
70
77
|
deleteCurrentRealityObservation(chartId: string, index: number): void {
|
|
71
78
|
const currentRealityName = `${chartId}_current_reality`
|
|
72
79
|
const entity = this.entities.get(currentRealityName)
|
|
73
|
-
|
|
80
|
+
|
|
74
81
|
if (entity) {
|
|
75
82
|
entity.observations.splice(index, 1)
|
|
76
83
|
entity.metadata.updatedAt = new Date().toISOString()
|
|
@@ -82,10 +89,10 @@ export class ChartEditor {
|
|
|
82
89
|
parentChartId: string,
|
|
83
90
|
actionStepTitle: string,
|
|
84
91
|
currentReality: string,
|
|
85
|
-
dueDate?: string
|
|
92
|
+
dueDate?: string,
|
|
86
93
|
): { chartId: string; actionStepName: string } {
|
|
87
94
|
const parentChart = this.entities.get(parentChartId)
|
|
88
|
-
if (!parentChart || parentChart.entityType !==
|
|
95
|
+
if (!parentChart || parentChart.entityType !== "structural_tension_chart") {
|
|
89
96
|
throw new Error(`Parent chart ${parentChartId} not found`)
|
|
90
97
|
}
|
|
91
98
|
|
|
@@ -97,7 +104,9 @@ export class ChartEditor {
|
|
|
97
104
|
}
|
|
98
105
|
|
|
99
106
|
if (!currentReality) {
|
|
100
|
-
throw new Error(
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Current reality required for action step "${actionStepTitle}". Provide honest assessment of actual current state relative to this action step.`,
|
|
109
|
+
)
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
let telescopedDueDate = dueDate
|
|
@@ -108,88 +117,91 @@ export class ChartEditor {
|
|
|
108
117
|
telescopedDueDate = midpoint.toISOString()
|
|
109
118
|
}
|
|
110
119
|
|
|
111
|
-
const existingCharts = Array.from(this.entities.values())
|
|
112
|
-
.filter(e => e.entityType === 'structural_tension_chart')
|
|
120
|
+
const existingCharts = Array.from(this.entities.values()).filter((e) => e.entityType === "structural_tension_chart")
|
|
113
121
|
const maxId = existingCharts.reduce((max, e) => {
|
|
114
|
-
const id = parseInt(e.metadata?.chartId?.replace(
|
|
122
|
+
const id = Number.parseInt(e.metadata?.chartId?.replace("chart_", "") || "0")
|
|
115
123
|
return Math.max(max, id)
|
|
116
124
|
}, 0)
|
|
117
125
|
const newChartId = `chart_${maxId + 1}`
|
|
118
126
|
const timestamp = new Date().toISOString()
|
|
119
127
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
128
|
+
// Extract chart ID from parentChartId (entity name) for parentChart reference
|
|
129
|
+
const parentChartId_normalized = parentChartId.replace("_chart", "")
|
|
130
|
+
|
|
131
|
+
const chartEntityName = `${newChartId}_chart`
|
|
132
|
+
this.entities.set(chartEntityName, {
|
|
133
|
+
type: "entity",
|
|
134
|
+
name: chartEntityName,
|
|
135
|
+
entityType: "structural_tension_chart",
|
|
124
136
|
observations: [`Telescoped chart for: ${actionStepTitle}`],
|
|
125
137
|
metadata: {
|
|
126
138
|
chartId: newChartId,
|
|
127
139
|
level: parentLevel + 1,
|
|
128
|
-
parentChart:
|
|
140
|
+
parentChart: parentChartId_normalized,
|
|
129
141
|
dueDate: telescopedDueDate,
|
|
130
142
|
completionStatus: false,
|
|
131
143
|
createdAt: timestamp,
|
|
132
|
-
updatedAt: timestamp
|
|
133
|
-
}
|
|
144
|
+
updatedAt: timestamp,
|
|
145
|
+
},
|
|
134
146
|
})
|
|
135
147
|
|
|
136
148
|
const desiredOutcomeName = `${newChartId}_desired_outcome`
|
|
137
149
|
this.entities.set(desiredOutcomeName, {
|
|
138
|
-
type:
|
|
150
|
+
type: "entity",
|
|
139
151
|
name: desiredOutcomeName,
|
|
140
|
-
entityType:
|
|
152
|
+
entityType: "desired_outcome",
|
|
141
153
|
observations: [actionStepTitle],
|
|
142
154
|
metadata: {
|
|
143
155
|
chartId: newChartId,
|
|
144
156
|
createdAt: timestamp,
|
|
145
|
-
updatedAt: timestamp
|
|
146
|
-
}
|
|
157
|
+
updatedAt: timestamp,
|
|
158
|
+
},
|
|
147
159
|
})
|
|
148
160
|
|
|
149
161
|
const currentRealityName = `${newChartId}_current_reality`
|
|
150
162
|
this.entities.set(currentRealityName, {
|
|
151
|
-
type:
|
|
163
|
+
type: "entity",
|
|
152
164
|
name: currentRealityName,
|
|
153
|
-
entityType:
|
|
165
|
+
entityType: "current_reality",
|
|
154
166
|
observations: [currentReality],
|
|
155
167
|
metadata: {
|
|
156
168
|
chartId: newChartId,
|
|
157
169
|
createdAt: timestamp,
|
|
158
|
-
updatedAt: timestamp
|
|
159
|
-
}
|
|
170
|
+
updatedAt: timestamp,
|
|
171
|
+
},
|
|
160
172
|
})
|
|
161
173
|
|
|
162
174
|
this.relations.push({
|
|
163
|
-
type:
|
|
175
|
+
type: "relation",
|
|
164
176
|
from: newChartId,
|
|
165
177
|
to: desiredOutcomeName,
|
|
166
|
-
relationType:
|
|
167
|
-
metadata: { createdAt: timestamp }
|
|
178
|
+
relationType: "contains",
|
|
179
|
+
metadata: { createdAt: timestamp },
|
|
168
180
|
})
|
|
169
181
|
|
|
170
182
|
this.relations.push({
|
|
171
|
-
type:
|
|
183
|
+
type: "relation",
|
|
172
184
|
from: newChartId,
|
|
173
185
|
to: currentRealityName,
|
|
174
|
-
relationType:
|
|
175
|
-
metadata: { createdAt: timestamp }
|
|
186
|
+
relationType: "contains",
|
|
187
|
+
metadata: { createdAt: timestamp },
|
|
176
188
|
})
|
|
177
189
|
|
|
178
190
|
this.relations.push({
|
|
179
|
-
type:
|
|
191
|
+
type: "relation",
|
|
180
192
|
from: currentRealityName,
|
|
181
193
|
to: desiredOutcomeName,
|
|
182
|
-
relationType:
|
|
183
|
-
metadata: { createdAt: timestamp }
|
|
194
|
+
relationType: "creates_tension_with",
|
|
195
|
+
metadata: { createdAt: timestamp },
|
|
184
196
|
})
|
|
185
197
|
|
|
186
198
|
const parentDesiredOutcome = `${parentChartId}_desired_outcome`
|
|
187
199
|
this.relations.push({
|
|
188
|
-
type:
|
|
200
|
+
type: "relation",
|
|
189
201
|
from: desiredOutcomeName,
|
|
190
202
|
to: parentDesiredOutcome,
|
|
191
|
-
relationType:
|
|
192
|
-
metadata: { createdAt: timestamp }
|
|
203
|
+
relationType: "advances_toward",
|
|
204
|
+
metadata: { createdAt: timestamp },
|
|
193
205
|
})
|
|
194
206
|
|
|
195
207
|
return { chartId: newChartId, actionStepName: desiredOutcomeName }
|
|
@@ -197,8 +209,8 @@ export class ChartEditor {
|
|
|
197
209
|
|
|
198
210
|
updateActionStep(chartId: string, actionName: string, description: string): void {
|
|
199
211
|
const entity = this.entities.get(actionName)
|
|
200
|
-
|
|
201
|
-
if (entity && (entity.entityType ===
|
|
212
|
+
|
|
213
|
+
if (entity && (entity.entityType === "action_step" || entity.entityType === "desired_outcome")) {
|
|
202
214
|
entity.observations = [description]
|
|
203
215
|
entity.metadata.updatedAt = new Date().toISOString()
|
|
204
216
|
this.entities.set(actionName, entity)
|
|
@@ -207,7 +219,7 @@ export class ChartEditor {
|
|
|
207
219
|
|
|
208
220
|
markActionStepComplete(actionStepName: string): void {
|
|
209
221
|
const actionStep = this.entities.get(actionStepName)
|
|
210
|
-
if (!actionStep || (actionStep.entityType !==
|
|
222
|
+
if (!actionStep || (actionStep.entityType !== "action_step" && actionStep.entityType !== "desired_outcome")) {
|
|
211
223
|
throw new Error(`Action step ${actionStepName} not found`)
|
|
212
224
|
}
|
|
213
225
|
|
|
@@ -233,7 +245,7 @@ export class ChartEditor {
|
|
|
233
245
|
if (parentChartId) {
|
|
234
246
|
const parentCurrentRealityName = `${parentChartId}_current_reality`
|
|
235
247
|
const parentCurrentReality = this.entities.get(parentCurrentRealityName)
|
|
236
|
-
|
|
248
|
+
|
|
237
249
|
if (parentCurrentReality) {
|
|
238
250
|
const completionMessage = `Completed: ${actionStep.observations[0]}`
|
|
239
251
|
if (!parentCurrentReality.observations.includes(completionMessage)) {
|
|
@@ -247,14 +259,10 @@ export class ChartEditor {
|
|
|
247
259
|
}
|
|
248
260
|
}
|
|
249
261
|
|
|
250
|
-
updateActionProgress(
|
|
251
|
-
actionStepName: string,
|
|
252
|
-
progressObservation: string,
|
|
253
|
-
updateCurrentReality?: boolean
|
|
254
|
-
): void {
|
|
262
|
+
updateActionProgress(actionStepName: string, progressObservation: string, updateCurrentReality?: boolean): void {
|
|
255
263
|
const actionStep = this.entities.get(actionStepName)
|
|
256
|
-
|
|
257
|
-
if (!actionStep || (actionStep.entityType !==
|
|
264
|
+
|
|
265
|
+
if (!actionStep || (actionStep.entityType !== "action_step" && actionStep.entityType !== "desired_outcome")) {
|
|
258
266
|
throw new Error(`Action step ${actionStepName} not found`)
|
|
259
267
|
}
|
|
260
268
|
|
|
@@ -267,11 +275,11 @@ export class ChartEditor {
|
|
|
267
275
|
if (updateCurrentReality && actionStep.metadata?.chartId) {
|
|
268
276
|
const chartEntity = this.entities.get(actionStep.metadata.chartId)
|
|
269
277
|
const parentChartId = chartEntity?.metadata?.parentChart
|
|
270
|
-
|
|
278
|
+
|
|
271
279
|
if (parentChartId) {
|
|
272
280
|
const parentCurrentRealityName = `${parentChartId}_current_reality`
|
|
273
281
|
const parentCurrentReality = this.entities.get(parentCurrentRealityName)
|
|
274
|
-
|
|
282
|
+
|
|
275
283
|
if (parentCurrentReality) {
|
|
276
284
|
const progressMessage = `Progress on ${actionStep.observations[0]}: ${progressObservation}`
|
|
277
285
|
if (!parentCurrentReality.observations.includes(progressMessage)) {
|
|
@@ -288,8 +296,8 @@ export class ChartEditor {
|
|
|
288
296
|
|
|
289
297
|
toggleActionCompletion(actionName: string): void {
|
|
290
298
|
const entity = this.entities.get(actionName)
|
|
291
|
-
|
|
292
|
-
if (entity && (entity.entityType ===
|
|
299
|
+
|
|
300
|
+
if (entity && (entity.entityType === "action_step" || entity.entityType === "desired_outcome")) {
|
|
293
301
|
entity.metadata.completionStatus = !entity.metadata.completionStatus
|
|
294
302
|
entity.metadata.updatedAt = new Date().toISOString()
|
|
295
303
|
if (entity.metadata.completionStatus) {
|
|
@@ -303,8 +311,8 @@ export class ChartEditor {
|
|
|
303
311
|
|
|
304
312
|
updateActionDueDate(actionName: string, dueDate: string | null): void {
|
|
305
313
|
const entity = this.entities.get(actionName)
|
|
306
|
-
|
|
307
|
-
if (entity && entity.entityType ===
|
|
314
|
+
|
|
315
|
+
if (entity && entity.entityType === "action_step") {
|
|
308
316
|
if (dueDate) {
|
|
309
317
|
entity.metadata.dueDate = dueDate
|
|
310
318
|
} else {
|
|
@@ -320,14 +328,143 @@ export class ChartEditor {
|
|
|
320
328
|
this.entities.delete(actionName)
|
|
321
329
|
|
|
322
330
|
// Remove related relations
|
|
323
|
-
this.relations = this.relations.filter(r => r.from !== actionName && r.to !== actionName)
|
|
331
|
+
this.relations = this.relations.filter((r) => r.from !== actionName && r.to !== actionName)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
createTelescopedChartFromAction(parentChartId: string, actionName: string): string {
|
|
335
|
+
const actionEntity = this.entities.get(actionName)
|
|
336
|
+
if (!actionEntity || (actionEntity.entityType !== "action_step" && actionEntity.entityType !== "desired_outcome")) {
|
|
337
|
+
throw new Error(`Action ${actionName} not found`)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const parentChart = this.entities.get(parentChartId)
|
|
341
|
+
if (!parentChart || parentChart.entityType !== "structural_tension_chart") {
|
|
342
|
+
throw new Error(`Parent chart ${parentChartId} not found`)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const actionDescription = actionEntity.observations[0] || "Untitled Action"
|
|
346
|
+
const parentLevel = parentChart.metadata?.level || 0
|
|
347
|
+
const parentDueDate = parentChart.metadata?.dueDate
|
|
348
|
+
|
|
349
|
+
if (!parentDueDate) {
|
|
350
|
+
throw new Error(`Parent chart ${parentChartId} has no due date`)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Calculate telescoped due date (midpoint between now and parent due date)
|
|
354
|
+
const now = new Date()
|
|
355
|
+
const parentEnd = new Date(parentDueDate)
|
|
356
|
+
const midpoint = new Date(now.getTime() + (parentEnd.getTime() - now.getTime()) / 2)
|
|
357
|
+
const telescopedDueDate = midpoint.toISOString()
|
|
358
|
+
|
|
359
|
+
// Generate new chart ID
|
|
360
|
+
const existingCharts = Array.from(this.entities.values()).filter((e) => e.entityType === "structural_tension_chart")
|
|
361
|
+
const maxId = existingCharts.reduce((max, e) => {
|
|
362
|
+
const id = Number.parseInt(e.metadata?.chartId?.replace("chart_", "") || "0")
|
|
363
|
+
return Math.max(max, id)
|
|
364
|
+
}, 0)
|
|
365
|
+
const newChartId = `chart_${maxId + 1}`
|
|
366
|
+
const timestamp = new Date().toISOString()
|
|
367
|
+
|
|
368
|
+
// Extract chart ID from parentChartId (entity name) for parentChart reference
|
|
369
|
+
const parentChartId_normalized = parentChartId.replace("_chart", "")
|
|
370
|
+
|
|
371
|
+
// Create new chart
|
|
372
|
+
const chartEntityName = `${newChartId}_chart`
|
|
373
|
+
this.entities.set(chartEntityName, {
|
|
374
|
+
type: "entity",
|
|
375
|
+
name: chartEntityName,
|
|
376
|
+
entityType: "structural_tension_chart",
|
|
377
|
+
observations: [`Telescoped chart for: ${actionDescription}`],
|
|
378
|
+
metadata: {
|
|
379
|
+
chartId: newChartId,
|
|
380
|
+
level: parentLevel + 1,
|
|
381
|
+
parentChart: parentChartId_normalized,
|
|
382
|
+
dueDate: telescopedDueDate,
|
|
383
|
+
completionStatus: false,
|
|
384
|
+
createdAt: timestamp,
|
|
385
|
+
updatedAt: timestamp,
|
|
386
|
+
},
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
// Create desired outcome (use action description)
|
|
390
|
+
const desiredOutcomeName = `${newChartId}_desired_outcome`
|
|
391
|
+
this.entities.set(desiredOutcomeName, {
|
|
392
|
+
type: "entity",
|
|
393
|
+
name: desiredOutcomeName,
|
|
394
|
+
entityType: "desired_outcome",
|
|
395
|
+
observations: [actionDescription],
|
|
396
|
+
metadata: {
|
|
397
|
+
chartId: newChartId,
|
|
398
|
+
createdAt: timestamp,
|
|
399
|
+
updatedAt: timestamp,
|
|
400
|
+
},
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
// Create current reality (needs initial observation)
|
|
404
|
+
const currentRealityName = `${newChartId}_current_reality`
|
|
405
|
+
this.entities.set(currentRealityName, {
|
|
406
|
+
type: "entity",
|
|
407
|
+
name: currentRealityName,
|
|
408
|
+
entityType: "current_reality",
|
|
409
|
+
observations: ["Starting work on this action step"],
|
|
410
|
+
metadata: {
|
|
411
|
+
chartId: newChartId,
|
|
412
|
+
createdAt: timestamp,
|
|
413
|
+
updatedAt: timestamp,
|
|
414
|
+
},
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
// Create relations
|
|
418
|
+
this.relations.push({
|
|
419
|
+
type: "relation",
|
|
420
|
+
from: newChartId,
|
|
421
|
+
to: desiredOutcomeName,
|
|
422
|
+
relationType: "contains",
|
|
423
|
+
metadata: { createdAt: timestamp },
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
this.relations.push({
|
|
427
|
+
type: "relation",
|
|
428
|
+
from: newChartId,
|
|
429
|
+
to: currentRealityName,
|
|
430
|
+
relationType: "contains",
|
|
431
|
+
metadata: { createdAt: timestamp },
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
this.relations.push({
|
|
435
|
+
type: "relation",
|
|
436
|
+
from: currentRealityName,
|
|
437
|
+
to: desiredOutcomeName,
|
|
438
|
+
relationType: "creates_tension_with",
|
|
439
|
+
metadata: { createdAt: timestamp },
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
// Link new chart's desired outcome to parent desired outcome
|
|
443
|
+
const parentDesiredOutcome = `${parentChartId}_desired_outcome`
|
|
444
|
+
this.relations.push({
|
|
445
|
+
type: "relation",
|
|
446
|
+
from: desiredOutcomeName,
|
|
447
|
+
to: parentDesiredOutcome,
|
|
448
|
+
relationType: "advances_toward",
|
|
449
|
+
metadata: { createdAt: timestamp },
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
// Mark the original action as a telescoped chart
|
|
453
|
+
if (actionEntity.metadata) {
|
|
454
|
+
actionEntity.metadata.isTelescopedChart = true
|
|
455
|
+
actionEntity.metadata.telescopedChartId = newChartId
|
|
456
|
+
actionEntity.metadata.updatedAt = timestamp
|
|
457
|
+
this.entities.set(actionName, actionEntity)
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return newChartId
|
|
324
461
|
}
|
|
325
462
|
|
|
326
463
|
updateChartDueDate(chartId: string, dueDate: string | null): void {
|
|
327
464
|
const chartName = chartId
|
|
328
465
|
const entity = this.entities.get(chartName)
|
|
329
|
-
|
|
330
|
-
if (entity && entity.entityType ===
|
|
466
|
+
|
|
467
|
+
if (entity && entity.entityType === "structural_tension_chart") {
|
|
331
468
|
if (dueDate) {
|
|
332
469
|
entity.metadata.dueDate = dueDate
|
|
333
470
|
} else {
|
|
@@ -351,13 +488,89 @@ export class ChartEditor {
|
|
|
351
488
|
lines.push(JSON.stringify(relation))
|
|
352
489
|
}
|
|
353
490
|
|
|
354
|
-
return lines.join(
|
|
491
|
+
return lines.join("\n") + "\n"
|
|
355
492
|
}
|
|
356
493
|
|
|
357
494
|
getUpdatedData(): ParsedData {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
495
|
+
const chartsMap = new Map<string, any>()
|
|
496
|
+
|
|
497
|
+
// Organize entities into charts
|
|
498
|
+
for (const [name, entity] of this.entities.entries()) {
|
|
499
|
+
if (entity.entityType === "structural_tension_chart") {
|
|
500
|
+
const chartId = entity.metadata?.chartId || name
|
|
501
|
+
chartsMap.set(chartId, {
|
|
502
|
+
id: chartId,
|
|
503
|
+
chartEntity: entity,
|
|
504
|
+
desiredOutcome: undefined,
|
|
505
|
+
currentReality: undefined,
|
|
506
|
+
actions: [],
|
|
507
|
+
narrativeBeats: [],
|
|
508
|
+
subCharts: [],
|
|
509
|
+
level: entity.metadata?.level || 0,
|
|
510
|
+
parentChart: entity.metadata?.parentChart,
|
|
511
|
+
relations: [],
|
|
512
|
+
})
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Populate chart components
|
|
517
|
+
for (const [name, entity] of this.entities.entries()) {
|
|
518
|
+
const chartId = entity.metadata?.chartId
|
|
519
|
+
if (!chartId) continue
|
|
520
|
+
|
|
521
|
+
const chart = chartsMap.get(chartId)
|
|
522
|
+
if (!chart) continue
|
|
523
|
+
|
|
524
|
+
switch (entity.entityType) {
|
|
525
|
+
case "desired_outcome":
|
|
526
|
+
chart.desiredOutcome = entity
|
|
527
|
+
break
|
|
528
|
+
case "current_reality":
|
|
529
|
+
chart.currentReality = entity
|
|
530
|
+
break
|
|
531
|
+
case "action_step":
|
|
532
|
+
chart.actions.push(entity)
|
|
533
|
+
break
|
|
534
|
+
case "narrative_beat":
|
|
535
|
+
chart.narrativeBeats.push(entity)
|
|
536
|
+
break
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Add relevant relations to each chart
|
|
541
|
+
for (const relation of this.relations) {
|
|
542
|
+
for (const [chartId, chart] of chartsMap.entries()) {
|
|
543
|
+
if (
|
|
544
|
+
relation.from.startsWith(chartId) ||
|
|
545
|
+
relation.to.startsWith(chartId) ||
|
|
546
|
+
relation.from === chartId ||
|
|
547
|
+
relation.to === chartId
|
|
548
|
+
) {
|
|
549
|
+
chart.relations.push(relation)
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const chartsArray: any[] = Array.from(chartsMap.values())
|
|
555
|
+
|
|
556
|
+
// Build hierarchy - add subcharts to parent charts
|
|
557
|
+
for (const chart of chartsArray) {
|
|
558
|
+
const parentChartId = chart.parentChart
|
|
559
|
+
if (parentChartId) {
|
|
560
|
+
const parentChart = chartsArray.find((c) => c.id === parentChartId)
|
|
561
|
+
if (parentChart) {
|
|
562
|
+
parentChart.subCharts.push(chart)
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const rootCharts = chartsArray.filter((c) => !c.parentChart)
|
|
568
|
+
|
|
569
|
+
return {
|
|
570
|
+
entities: this.entities,
|
|
571
|
+
relations: this.relations,
|
|
572
|
+
charts: chartsArray,
|
|
573
|
+
rootCharts: rootCharts,
|
|
574
|
+
}
|
|
362
575
|
}
|
|
363
576
|
}
|
package/mcp/Dockerfile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
FROM node:20-alpine
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Copy package files
|
|
6
|
+
COPY package.json package-lock.json* ./
|
|
7
|
+
|
|
8
|
+
# Install dependencies
|
|
9
|
+
RUN npm install
|
|
10
|
+
|
|
11
|
+
# Copy source and build
|
|
12
|
+
COPY . .
|
|
13
|
+
RUN npm run build
|
|
14
|
+
|
|
15
|
+
# Create data directory
|
|
16
|
+
RUN mkdir -p /data
|
|
17
|
+
|
|
18
|
+
ENV NODE_ENV=production
|
|
19
|
+
|
|
20
|
+
# MCP servers use stdio for communication
|
|
21
|
+
CMD ["node", "dist/index.js"]
|
package/mcp/README.md
CHANGED
|
@@ -123,22 +123,41 @@ Update a chart's desired outcome, current reality, or due date.
|
|
|
123
123
|
\`\`\`
|
|
124
124
|
|
|
125
125
|
### add_action_step
|
|
126
|
-
Add a new action step to a chart.
|
|
126
|
+
Add a new action step to a chart as a telescoped structural tension chart.
|
|
127
127
|
|
|
128
128
|
**Parameters:**
|
|
129
|
-
- `
|
|
130
|
-
- `
|
|
131
|
-
- `
|
|
129
|
+
- `parentChartId` (required): The parent chart ID
|
|
130
|
+
- `actionStepTitle` (required): Action step title (becomes desired outcome)
|
|
131
|
+
- `currentReality` (required): Honest assessment of actual current state relative to this action step
|
|
132
|
+
- `dueDate` (optional): ISO date string (auto-distributed if not provided)
|
|
132
133
|
|
|
133
134
|
**Example:**
|
|
134
135
|
\`\`\`json
|
|
135
136
|
{
|
|
136
|
-
"
|
|
137
|
-
"
|
|
137
|
+
"parentChartId": "chart_1",
|
|
138
|
+
"actionStepTitle": "Write API documentation",
|
|
139
|
+
"currentReality": "No documentation exists yet",
|
|
138
140
|
"dueDate": "2026-01-15"
|
|
139
141
|
}
|
|
140
142
|
\`\`\`
|
|
141
143
|
|
|
144
|
+
### create_telescoped_chart
|
|
145
|
+
Create a telescoped (drill-down) chart from an EXISTING action step. This converts an existing action into a detailed sub-chart for deeper work breakdown.
|
|
146
|
+
|
|
147
|
+
**Parameters:**
|
|
148
|
+
- `chartId` (required): The parent chart ID containing the action
|
|
149
|
+
- `actionName` (required): The action entity name to telescope (e.g., "chart_1_action_1")
|
|
150
|
+
|
|
151
|
+
**Example:**
|
|
152
|
+
\`\`\`json
|
|
153
|
+
{
|
|
154
|
+
"chartId": "chart_1",
|
|
155
|
+
"actionName": "chart_1_action_1"
|
|
156
|
+
}
|
|
157
|
+
\`\`\`
|
|
158
|
+
|
|
159
|
+
**Note:** This is different from `add_action_step`. Use `add_action_step` when creating a NEW action that should be a telescoped chart. Use `create_telescoped_chart` to telescope an EXISTING action into a sub-chart.
|
|
160
|
+
|
|
142
161
|
### update_action_step
|
|
143
162
|
Update an existing action step.
|
|
144
163
|
|
package/mcp/src/api-client.ts
CHANGED
|
@@ -51,7 +51,7 @@ export class CoaiaVisualizerClient {
|
|
|
51
51
|
|
|
52
52
|
private async fetchJson<T>(path: string, options: RequestInit = {}): Promise<T> {
|
|
53
53
|
const response = await this.fetch(path, options)
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
if (!response.ok) {
|
|
56
56
|
const error: any = await response.json().catch(() => ({ error: 'Unknown error' }))
|
|
57
57
|
throw new Error(`API Error (${response.status}): ${error.error || error.message || 'Unknown error'}`)
|
|
@@ -70,7 +70,7 @@ export class CoaiaVisualizerClient {
|
|
|
70
70
|
const params = new URLSearchParams()
|
|
71
71
|
if (options?.level !== undefined) params.set('level', options.level.toString())
|
|
72
72
|
if (options?.rootOnly) params.set('rootOnly', 'true')
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
const query = params.toString()
|
|
75
75
|
return this.fetchJson(`/api/charts${query ? `?${query}` : ''}`)
|
|
76
76
|
}
|
|
@@ -121,6 +121,18 @@ export class CoaiaVisualizerClient {
|
|
|
121
121
|
})
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Create telescoped chart from existing action
|
|
126
|
+
*/
|
|
127
|
+
async createTelescopedChart(chartId: string, actionName: string): Promise<{ chart: Chart; updates: string[]; message: string; backup: string }> {
|
|
128
|
+
return this.fetchJson(`/api/charts/${chartId}`, {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
body: JSON.stringify({
|
|
131
|
+
createTelescopedChart: { actionName },
|
|
132
|
+
}),
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
124
136
|
/**
|
|
125
137
|
* Delete a chart
|
|
126
138
|
*/
|
|
@@ -144,7 +156,7 @@ export class CoaiaVisualizerClient {
|
|
|
144
156
|
if (query.level !== undefined) params.set('level', query.level.toString())
|
|
145
157
|
if (query.completed !== undefined) params.set('completed', query.completed.toString())
|
|
146
158
|
if (query.hasActions !== undefined) params.set('hasActions', query.hasActions.toString())
|
|
147
|
-
|
|
159
|
+
|
|
148
160
|
return this.fetchJson(`/api/charts/search?${params.toString()}`)
|
|
149
161
|
}
|
|
150
162
|
|