coaia-visualizer 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/.dockerignore +9 -0
  2. package/COMPLETE_IMPLEMENTATION_REPORT.md +215 -0
  3. package/QUICK_START_MCP_TESTING.md +236 -0
  4. package/README.md +1 -1
  5. package/README_TELESCOPED_NAVIGATION.md +247 -0
  6. package/STC.md +24 -0
  7. package/STCGOAL.md +0 -0
  8. package/STCISSUE.md +48 -0
  9. package/STCKIN.md +0 -0
  10. package/STCMASTERY.md +0 -0
  11. package/STUDY_REPORT.md +510 -0
  12. package/app/page.tsx +4 -23
  13. package/cli.ts +8 -0
  14. package/components/chart-detail-editable.tsx +3 -2
  15. package/components/chart-detail.tsx +1 -6
  16. package/components/edit-action-step.tsx +0 -2
  17. package/components/metadata-projections.tsx +208 -0
  18. package/components/relation-graph.tsx +27 -91
  19. package/components.json +21 -0
  20. package/direct-test.sh +180 -0
  21. package/dist/cli.js +7 -0
  22. package/dist/skill.js +342 -0
  23. package/docker-compose.test.yml +69 -0
  24. package/index.tsx +0 -26
  25. package/jgwill.coaia-visualizer-8--496dca71-d476-4ac9-ba9f-376add118dd8--260208.txt +2612 -0
  26. package/lib/chart-editor.ts +13 -13
  27. package/lib/jsonl-parser.ts +55 -8
  28. package/lib/jsonl-preservation.ts +405 -0
  29. package/lib/types.ts +32 -15
  30. package/mcp/dist/api-client.d.ts +138 -0
  31. package/mcp/dist/api-client.d.ts.map +1 -0
  32. package/mcp/dist/api-client.js +115 -0
  33. package/mcp/dist/api-client.js.map +1 -0
  34. package/mcp/dist/index.d.ts +2 -0
  35. package/mcp/dist/index.d.ts.map +1 -0
  36. package/mcp/dist/index.js +286 -0
  37. package/mcp/dist/index.js.map +1 -0
  38. package/mcp/dist/tools/index.d.ts +18 -0
  39. package/mcp/dist/tools/index.d.ts.map +1 -0
  40. package/mcp/dist/tools/index.js +322 -0
  41. package/mcp/dist/tools/index.js.map +1 -0
  42. package/mcp/package-lock.json +210 -0
  43. package/package.json +2 -27
  44. package/rispecs/github-project-runtime-memory-integration.spec.md +381 -0
  45. package/skill.ts +385 -0
  46. package/test-data/test-master.jsonl +11 -0
  47. package/test-results/.last-run.json +4 -0
  48. package/test-scripts/README.md +325 -0
  49. package/test-scripts/rich-jsonl-preservation.spec.ts +118 -0
  50. package/test-scripts/run-all-tests.sh +38 -0
  51. package/test-scripts/test-01-basic-operations.sh +87 -0
  52. package/test-scripts/test-02-telescope-creation.sh +91 -0
  53. package/test-scripts/test-03-navigation.sh +87 -0
  54. package/test-scripts/test-global-cli.spec.ts +220 -0
  55. package/test-scripts/verify-global-cli.sh +87 -0
  56. package/tsconfig.json +41 -0
  57. package/components/github-provenance.tsx +0 -226
  58. package/lib/github-provenance.ts +0 -316
@@ -0,0 +1,208 @@
1
+ "use client"
2
+
3
+ import { AlertTriangle, Blocks, FileJson, Github, ListChecks, Sparkles, Target } from "lucide-react"
4
+ import { Badge } from "@/components/ui/badge"
5
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
6
+ import type { Chart, EntityRecord, ParsedData } from "@/lib/types"
7
+
8
+ interface MetadataProjectionsProps {
9
+ chart: Chart
10
+ data: ParsedData
11
+ }
12
+
13
+ const KNOWN_METADATA_KEYS = new Set([
14
+ "dueDate",
15
+ "chartId",
16
+ "phase",
17
+ "completionStatus",
18
+ "completedAt",
19
+ "parentChart",
20
+ "parentActionStep",
21
+ "level",
22
+ "createdAt",
23
+ "updatedAt",
24
+ "act",
25
+ "type_dramatic",
26
+ "universes",
27
+ "timestamp",
28
+ "elementsOfPerformance",
29
+ "mmotEvaluations",
30
+ "relationalAlignment",
31
+ "fourDirections",
32
+ "narrative",
33
+ "isTelescopedChart",
34
+ "telescopedChartId",
35
+ "github",
36
+ ])
37
+
38
+ function JsonBlock({ value }: { value: unknown }) {
39
+ return (
40
+ <pre className="max-h-64 overflow-auto rounded-md border bg-muted/40 p-3 text-xs leading-relaxed">
41
+ {JSON.stringify(value, null, 2)}
42
+ </pre>
43
+ )
44
+ }
45
+
46
+ function ProjectionRecord({ entity, label }: { entity?: EntityRecord; label: string }) {
47
+ if (!entity) return null
48
+
49
+ const metadata = entity.metadata || {}
50
+ const unknownKeys = Object.keys(metadata).filter((key) => !KNOWN_METADATA_KEYS.has(key))
51
+
52
+ return (
53
+ <div className="rounded-md border p-3">
54
+ <div className="flex flex-wrap items-center gap-2">
55
+ <Badge variant="outline">{label}</Badge>
56
+ <span className="font-mono text-xs text-muted-foreground">{entity.name}</span>
57
+ {metadata.github && (
58
+ <Badge variant="secondary" className="gap-1">
59
+ <Github className="h-3 w-3" />
60
+ GitHub
61
+ </Badge>
62
+ )}
63
+ {unknownKeys.length > 0 && <Badge variant="secondary">{unknownKeys.length} extensions</Badge>}
64
+ </div>
65
+
66
+ {metadata.github && (
67
+ <div className="mt-3">
68
+ <div className="mb-1 flex items-center gap-2 text-sm font-medium">
69
+ <Github className="h-4 w-4" />
70
+ GitHub Provenance
71
+ </div>
72
+ <JsonBlock value={metadata.github} />
73
+ </div>
74
+ )}
75
+
76
+ <details className="mt-3">
77
+ <summary className="cursor-pointer text-sm font-medium">Full Metadata</summary>
78
+ <div className="mt-2">
79
+ <JsonBlock value={metadata} />
80
+ </div>
81
+ </details>
82
+ </div>
83
+ )
84
+ }
85
+
86
+ export function MetadataProjections({ chart, data }: MetadataProjectionsProps) {
87
+ const riseSpec = (data.riseSpecs || []).find((spec) => spec.chartId === chart.id)
88
+ const chartRecordNames = new Set(
89
+ [
90
+ chart.chartEntity.name,
91
+ chart.desiredOutcome?.name,
92
+ chart.currentReality?.name,
93
+ ...chart.actions.map((action) => action.name),
94
+ ...chart.narrativeBeats.map((beat) => beat.name),
95
+ ].filter(Boolean),
96
+ )
97
+ const warnings = (data.preservationWarnings || []).filter(
98
+ (warning) => !warning.recordName || chartRecordNames.has(warning.recordName),
99
+ )
100
+
101
+ return (
102
+ <div className="space-y-6">
103
+ {warnings.length > 0 && (
104
+ <Card className="border-amber-500/40">
105
+ <CardHeader>
106
+ <CardTitle className="flex items-center gap-2 text-base">
107
+ <AlertTriangle className="h-5 w-5 text-amber-600" />
108
+ Preservation Warnings
109
+ </CardTitle>
110
+ </CardHeader>
111
+ <CardContent className="space-y-2">
112
+ {warnings.map((warning, index) => (
113
+ <div key={`${warning.recordName}-${index}`} className="rounded-md border border-amber-500/30 bg-amber-500/5 p-3 text-sm">
114
+ <div className="font-medium">{warning.recordName || warning.recordType || "JSONL record"}</div>
115
+ <div className="text-muted-foreground">{warning.message}</div>
116
+ {warning.upstreamIssue && (
117
+ <a className="text-xs underline" href={warning.upstreamIssue} target="_blank" rel="noreferrer">
118
+ avadisabelle/coaia-narrative#35
119
+ </a>
120
+ )}
121
+ </div>
122
+ ))}
123
+ </CardContent>
124
+ </Card>
125
+ )}
126
+
127
+ <Card>
128
+ <CardHeader>
129
+ <CardTitle className="flex items-center gap-2 text-base">
130
+ <Blocks className="h-5 w-5" />
131
+ RISE Projection
132
+ </CardTitle>
133
+ </CardHeader>
134
+ <CardContent className="grid gap-4 md:grid-cols-2">
135
+ <div>
136
+ <div className="mb-1 flex items-center gap-2 text-sm font-medium">
137
+ <Target className="h-4 w-4" />
138
+ Desired Outcome
139
+ </div>
140
+ <p className="text-sm text-muted-foreground">{riseSpec?.desiredOutcome || "No desired outcome"}</p>
141
+ </div>
142
+ <div>
143
+ <div className="mb-1 text-sm font-medium">Current Reality</div>
144
+ <ul className="space-y-1 text-sm text-muted-foreground">
145
+ {(riseSpec?.currentReality.length ? riseSpec.currentReality : ["No current reality"]).map((item, index) => (
146
+ <li key={index}>{item}</li>
147
+ ))}
148
+ </ul>
149
+ </div>
150
+ <div>
151
+ <div className="mb-1 text-sm font-medium">Natural Progression</div>
152
+ <ul className="space-y-1 text-sm text-muted-foreground">
153
+ {(riseSpec?.naturalProgression.length ? riseSpec.naturalProgression : ["No projected progression"]).map((item, index) => (
154
+ <li key={index}>{item}</li>
155
+ ))}
156
+ </ul>
157
+ </div>
158
+ <div>
159
+ <div className="mb-1 text-sm font-medium">Remaining Blockers</div>
160
+ <ul className="space-y-1 text-sm text-muted-foreground">
161
+ {(riseSpec?.remainingBlockers.length ? riseSpec.remainingBlockers : ["No blockers recorded"]).map((item, index) => (
162
+ <li key={index}>{item}</li>
163
+ ))}
164
+ </ul>
165
+ </div>
166
+ <div className="md:col-span-2">
167
+ <div className="mb-1 text-sm font-medium">Next Chart Operations</div>
168
+ <ul className="space-y-1 text-sm text-muted-foreground">
169
+ {(riseSpec?.nextOperations.length ? riseSpec.nextOperations : ["No next operations recorded"]).map((item, index) => (
170
+ <li key={index}>{item}</li>
171
+ ))}
172
+ </ul>
173
+ </div>
174
+ </CardContent>
175
+ </Card>
176
+
177
+ <Card>
178
+ <CardHeader>
179
+ <CardTitle className="flex items-center gap-2 text-base">
180
+ <FileJson className="h-5 w-5" />
181
+ Rich Metadata Projection
182
+ </CardTitle>
183
+ </CardHeader>
184
+ <CardContent className="space-y-3">
185
+ <ProjectionRecord label="Chart" entity={chart.chartEntity} />
186
+ <ProjectionRecord label="Desired Outcome" entity={chart.desiredOutcome} />
187
+ <ProjectionRecord label="Current Reality" entity={chart.currentReality} />
188
+ {chart.actions.map((action) => (
189
+ <ProjectionRecord key={action.name} label="Action Step" entity={action} />
190
+ ))}
191
+ {chart.narrativeBeats.map((beat) => (
192
+ <ProjectionRecord key={beat.name} label="Narrative Beat" entity={beat} />
193
+ ))}
194
+ <div className="flex flex-wrap gap-2 pt-1 text-sm text-muted-foreground">
195
+ <Badge variant="outline" className="gap-1">
196
+ <ListChecks className="h-3 w-3" />
197
+ {data.projections?.actionStepRecords.length || 0} actions
198
+ </Badge>
199
+ <Badge variant="outline" className="gap-1">
200
+ <Sparkles className="h-3 w-3" />
201
+ {data.projections?.narrativeBeatRecords.length || 0} beats
202
+ </Badge>
203
+ </div>
204
+ </CardContent>
205
+ </Card>
206
+ </div>
207
+ )
208
+ }
@@ -2,10 +2,8 @@
2
2
 
3
3
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
4
4
  import { Badge } from "@/components/ui/badge"
5
- import type { ParsedData, Chart, EntityRecord, RelationRecord } from "@/lib/types"
6
- import { ArrowRight, Github } from "lucide-react"
7
- import { getGithubBridgeRelationType, getGithubSyncState } from "@/lib/github-provenance"
8
- import { SyncStatePill } from "./github-provenance"
5
+ import type { ParsedData, Chart, RelationRecord } from "@/lib/types"
6
+ import { ArrowRight } from "lucide-react"
9
7
 
10
8
  interface RelationGraphProps {
11
9
  chart: Chart
@@ -25,10 +23,7 @@ export function RelationGraph({ chart, data }: RelationGraphProps) {
25
23
  )
26
24
  }
27
25
 
28
- const githubBridgeRelations = relations.filter((relation) => getGithubBridgeRelationType(relation))
29
- const standardRelations = relations.filter((relation) => !getGithubBridgeRelationType(relation))
30
-
31
- const relationsByType = standardRelations.reduce(
26
+ const relationsByType = relations.reduce(
32
27
  (acc, rel) => {
33
28
  if (!acc[rel.relationType]) {
34
29
  acc[rel.relationType] = []
@@ -46,28 +41,6 @@ export function RelationGraph({ chart, data }: RelationGraphProps) {
46
41
  <CardDescription>Connections between chart entities</CardDescription>
47
42
  </CardHeader>
48
43
  <CardContent className="space-y-6">
49
- {githubBridgeRelations.length > 0 && (
50
- <div>
51
- <h3 className="text-sm font-semibold mb-3 flex items-center gap-2">
52
- <Badge className="gap-1 bg-indigo-600 text-white">
53
- <Github className="w-3 h-3" />
54
- GitHub bridge
55
- </Badge>
56
- <span className="text-muted-foreground">({githubBridgeRelations.length})</span>
57
- </h3>
58
- <div className="space-y-2">
59
- {githubBridgeRelations.map((rel, idx) => (
60
- <RelationRow
61
- key={`github-${idx}`}
62
- relation={rel}
63
- data={data}
64
- bridgeLabel={getGithubBridgeRelationType(rel)}
65
- />
66
- ))}
67
- </div>
68
- </div>
69
- )}
70
-
71
44
  {Object.entries(relationsByType).map(([type, rels]) => (
72
45
  <div key={type}>
73
46
  <h3 className="text-sm font-semibold mb-3 flex items-center gap-2">
@@ -75,9 +48,30 @@ export function RelationGraph({ chart, data }: RelationGraphProps) {
75
48
  <span className="text-muted-foreground">({rels.length})</span>
76
49
  </h3>
77
50
  <div className="space-y-2">
78
- {rels.map((rel, idx) => (
79
- <RelationRow key={idx} relation={rel} data={data} />
80
- ))}
51
+ {rels.map((rel, idx) => {
52
+ const fromEntity = data.entities.get(rel.from)
53
+ const toEntity = data.entities.get(rel.to)
54
+
55
+ return (
56
+ <div key={idx} className="flex items-center gap-3 p-3 bg-muted/30 rounded-lg text-sm">
57
+ <div className="flex-1 min-w-0">
58
+ <div className="font-mono text-xs text-muted-foreground mb-1">{rel.from}</div>
59
+ {fromEntity && (
60
+ <div className="text-xs line-clamp-1">
61
+ {fromEntity.observations[0] || fromEntity.entityType}
62
+ </div>
63
+ )}
64
+ </div>
65
+ <ArrowRight className="w-4 h-4 text-muted-foreground flex-shrink-0" />
66
+ <div className="flex-1 min-w-0">
67
+ <div className="font-mono text-xs text-muted-foreground mb-1">{rel.to}</div>
68
+ {toEntity && (
69
+ <div className="text-xs line-clamp-1">{toEntity.observations[0] || toEntity.entityType}</div>
70
+ )}
71
+ </div>
72
+ </div>
73
+ )
74
+ })}
81
75
  </div>
82
76
  </div>
83
77
  ))}
@@ -85,61 +79,3 @@ export function RelationGraph({ chart, data }: RelationGraphProps) {
85
79
  </Card>
86
80
  )
87
81
  }
88
-
89
- interface RelationRowProps {
90
- relation: RelationRecord
91
- data: ParsedData
92
- bridgeLabel?: string
93
- }
94
-
95
- function RelationRow({ relation, data, bridgeLabel }: RelationRowProps) {
96
- const fromEntity = data.entities.get(relation.from)
97
- const toEntity = data.entities.get(relation.to)
98
- const syncState = getGithubSyncState(relation.metadata)
99
-
100
- return (
101
- <div className="flex flex-col gap-2 p-3 bg-muted/30 rounded-lg text-sm sm:flex-row sm:items-center sm:gap-3">
102
- <RelationEndpoint name={relation.from} entity={fromEntity} />
103
-
104
- <div className="flex items-center gap-2 text-muted-foreground sm:flex-col">
105
- <ArrowRight className="w-4 h-4 flex-shrink-0 rotate-90 sm:rotate-0" />
106
- {(bridgeLabel || syncState) && (
107
- <div className="flex flex-wrap justify-center gap-1">
108
- {bridgeLabel && (
109
- <Badge variant="outline" className="text-xs">
110
- {bridgeLabel.replace(/_/g, " ")}
111
- </Badge>
112
- )}
113
- <SyncStatePill syncState={syncState} />
114
- </div>
115
- )}
116
- </div>
117
-
118
- <RelationEndpoint name={relation.to} entity={toEntity} />
119
- </div>
120
- )
121
- }
122
-
123
- function RelationEndpoint({
124
- name,
125
- entity,
126
- }: {
127
- name: string
128
- entity?: EntityRecord
129
- }) {
130
- const isGithubVirtualNode = name.startsWith("gh:")
131
-
132
- return (
133
- <div className="flex-1 min-w-0">
134
- <div className="font-mono text-xs text-muted-foreground mb-1 break-all">{name}</div>
135
- {entity ? (
136
- <div className="text-xs line-clamp-1">{entity.observations[0] || entity.entityType}</div>
137
- ) : isGithubVirtualNode ? (
138
- <Badge variant="outline" className="gap-1 text-xs">
139
- <Github className="w-3 h-3" />
140
- GitHub issue
141
- </Badge>
142
- ) : null}
143
- </div>
144
- )
145
- }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "app/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils",
16
+ "ui": "@/components/ui",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ },
20
+ "iconLibrary": "lucide"
21
+ }
package/direct-test.sh ADDED
@@ -0,0 +1,180 @@
1
+ #!/bin/bash
2
+
3
+ # Direct MCP Integration Test
4
+ # Tests the MCP server and Next.js API without Docker complexity
5
+
6
+ set -e
7
+
8
+ echo "========================================="
9
+ echo "Direct MCP Integration Test"
10
+ echo "========================================="
11
+ echo ""
12
+
13
+ PROJECT_DIR="/workspace/repos/jgwill/coaia-visualizer-feat-4"
14
+ cd "$PROJECT_DIR"
15
+
16
+ # Start the Next.js app in the background
17
+ echo "1. Starting Next.js application..."
18
+ timeout 30 npm run dev > /tmp/nextjs.log 2>&1 &
19
+ NEXTJS_PID=$!
20
+ echo " Process ID: $NEXTJS_PID"
21
+
22
+ # Give the app time to start
23
+ sleep 10
24
+
25
+ # Check if the app is responding
26
+ echo "2. Checking Next.js app health..."
27
+ if curl -sf http://localhost:3000/api/charts > /dev/null; then
28
+ echo " ✓ Next.js app is responding"
29
+ else
30
+ echo " ⚠ Warning: App not responding on port 3000, trying port 4321..."
31
+ if curl -sf http://localhost:4321/api/charts > /dev/null; then
32
+ echo " ✓ Next.js app is responding on port 4321"
33
+ else
34
+ echo " ✗ Failed to reach Next.js app"
35
+ kill $NEXTJS_PID 2>/dev/null || true
36
+ exit 1
37
+ fi
38
+ fi
39
+
40
+ API_PORT=3000
41
+ if ! curl -sf http://localhost:3000/api/charts > /dev/null 2>&1; then
42
+ API_PORT=4321
43
+ fi
44
+
45
+ echo ""
46
+ echo "3. Testing MCP API endpoints..."
47
+ echo ""
48
+
49
+ # Test 1: Create a chart
50
+ echo " Test 1: Creating a chart..."
51
+ CHART_RESPONSE=$(curl -s -X POST "http://localhost:$API_PORT/api/charts" \
52
+ -H "Content-Type: application/json" \
53
+ -d '{
54
+ "desiredOutcome": "Test MCP Integration",
55
+ "currentReality": "Starting tests",
56
+ "dueDate": "2026-02-15"
57
+ }')
58
+
59
+ CHART_ID=$(echo "$CHART_RESPONSE" | jq -r '.chart.id' 2>/dev/null)
60
+
61
+ if [ -n "$CHART_ID" ] && [ "$CHART_ID" != "null" ]; then
62
+ echo " ✓ Chart created: $CHART_ID"
63
+ else
64
+ echo " ✗ Failed to create chart"
65
+ echo " Response: $CHART_RESPONSE"
66
+ kill $NEXTJS_PID 2>/dev/null || true
67
+ exit 1
68
+ fi
69
+
70
+ # Test 2: Add action step
71
+ echo ""
72
+ echo " Test 2: Adding action step..."
73
+ ACTION_RESPONSE=$(curl -s -X POST "http://localhost:$API_PORT/api/charts/$CHART_ID" \
74
+ -H "Content-Type: application/json" \
75
+ -d '{
76
+ "addActionStep": {
77
+ "actionName": "Test Action 1",
78
+ "createAsTelescopedChart": false
79
+ }
80
+ }')
81
+
82
+ ACTION_UPDATES=$(echo "$ACTION_RESPONSE" | jq -r '.updates | length' 2>/dev/null || echo "0")
83
+
84
+ if [ "$ACTION_UPDATES" -gt 0 ]; then
85
+ echo " ✓ Action step added"
86
+ else
87
+ echo " ⚠ Could not verify action addition"
88
+ fi
89
+
90
+ # Test 3: Create telescoped chart
91
+ echo ""
92
+ echo " Test 3: Creating telescoped chart..."
93
+ TELESCOPE_RESPONSE=$(curl -s -X POST "http://localhost:$API_PORT/api/charts/$CHART_ID" \
94
+ -H "Content-Type: application/json" \
95
+ -d '{
96
+ "createTelescopedChart": {
97
+ "actionName": "action_Test Action 1"
98
+ }
99
+ }')
100
+
101
+ TELESCOPE_ID=$(echo "$TELESCOPE_RESPONSE" | jq -r '.chart.id' 2>/dev/null)
102
+
103
+ if [ -n "$TELESCOPE_ID" ] && [ "$TELESCOPE_ID" != "null" ]; then
104
+ echo " ✓ Telescoped chart created: $TELESCOPE_ID"
105
+ else
106
+ echo " ⚠ Telescope response: $(echo "$TELESCOPE_RESPONSE" | jq -c . 2>/dev/null || echo "$TELESCOPE_RESPONSE")"
107
+ fi
108
+
109
+ # Test 4: Retrieve parent chart
110
+ echo ""
111
+ echo " Test 4: Retrieving parent chart with subCharts..."
112
+ PARENT_RESPONSE=$(curl -s -X GET "http://localhost:$API_PORT/api/charts/$CHART_ID")
113
+
114
+ SUBCHARTS_COUNT=$(echo "$PARENT_RESPONSE" | jq -r '.chart.subCharts | length' 2>/dev/null || echo "0")
115
+
116
+ if [ "$SUBCHARTS_COUNT" -gt 0 ]; then
117
+ echo " ✓ Parent chart has $SUBCHARTS_COUNT subCharts"
118
+ else
119
+ echo " ⚠ Parent chart subCharts count: $SUBCHARTS_COUNT"
120
+ fi
121
+
122
+ # Test 5: Test MCP server directly (if available)
123
+ echo ""
124
+ echo "4. Testing MCP Server..."
125
+
126
+ if [ -f "$PROJECT_DIR/mcp/dist/index.js" ]; then
127
+ echo " ✓ MCP server built"
128
+ echo ""
129
+ echo " To test MCP server:"
130
+ echo " 1. Build: cd mcp && npm run build"
131
+ echo " 2. Run: node dist/index.js"
132
+ echo " 3. Configure in Claude Desktop"
133
+ else
134
+ echo " ⚠ MCP server not built yet"
135
+ echo " Build with: cd mcp && npm run build"
136
+ fi
137
+
138
+ # Cleanup
139
+ echo ""
140
+ echo "5. Cleaning up..."
141
+ kill $NEXTJS_PID 2>/dev/null || true
142
+ sleep 1
143
+
144
+ echo ""
145
+ echo "========================================="
146
+ echo "Test Results Summary"
147
+ echo "========================================="
148
+ echo "✓ Chart created"
149
+ echo "✓ Action step added"
150
+ if [ -n "$TELESCOPE_ID" ] && [ "$TELESCOPE_ID" != "null" ]; then
151
+ echo "✓ Telescoped chart created"
152
+ fi
153
+ if [ "$SUBCHARTS_COUNT" -gt 0 ]; then
154
+ echo "✓ Parent-child relationship established"
155
+ fi
156
+ echo ""
157
+ echo "All basic tests passed!"
158
+ echo "========================================="
159
+
160
+ # Cleanup
161
+ echo ""
162
+ echo "5. Cleaning up..."
163
+ kill $NEXTJS_PID 2>/dev/null || true
164
+ sleep 1
165
+
166
+ echo ""
167
+ echo "========================================="
168
+ echo "Test Results Summary"
169
+ echo "========================================="
170
+ echo "✓ Chart created"
171
+ echo "✓ Action step added"
172
+ if [ -n "$TELESCOPE_ID" ] && [ "$TELESCOPE_ID" != "null" ]; then
173
+ echo "✓ Telescoped chart created"
174
+ fi
175
+ if [ "$SUBCHARTS_COUNT" -gt 0 ]; then
176
+ echo "✓ Parent-child relationship established"
177
+ fi
178
+ echo ""
179
+ echo "All basic tests passed!"
180
+ echo "========================================="
package/dist/cli.js CHANGED
@@ -11,6 +11,7 @@ import path from 'path';
11
11
  import minimist from 'minimist';
12
12
  import * as dotenv from 'dotenv';
13
13
  import { fileURLToPath } from 'url';
14
+ import { handleSkillCommand } from './skill.js';
14
15
  const __filename = fileURLToPath(import.meta.url);
15
16
  const __dirname = path.dirname(__filename);
16
17
  const DOCKER_IMAGE = 'jgwill/coaia:visualizer';
@@ -95,6 +96,11 @@ function loadConfig(args) {
95
96
  }
96
97
  async function main() {
97
98
  const args = minimist(process.argv.slice(2));
99
+ const command = args._[0];
100
+ if (command === 'skill') {
101
+ await handleSkillCommand(args);
102
+ return;
103
+ }
98
104
  if (args.help || args.h) {
99
105
  console.log(`
100
106
  🎨 COAIA Visualizer - Interactive Chart Viewer
@@ -106,6 +112,7 @@ DESCRIPTION:
106
112
  USAGE:
107
113
  coaia-visualizer [OPTIONS]
108
114
  npx coaia-visualizer [OPTIONS]
115
+ coaia-visualizer skill <show|install|help>
109
116
 
110
117
  OPTIONS:
111
118
  --memory-path PATH, -M PATH Path to memory.jsonl file (default: ./memory.jsonl)