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.
- package/.dockerignore +9 -0
- package/COMPLETE_IMPLEMENTATION_REPORT.md +215 -0
- package/QUICK_START_MCP_TESTING.md +236 -0
- package/README.md +1 -1
- package/README_TELESCOPED_NAVIGATION.md +247 -0
- package/STC.md +24 -0
- package/STCGOAL.md +0 -0
- package/STCISSUE.md +48 -0
- package/STCKIN.md +0 -0
- package/STCMASTERY.md +0 -0
- package/STUDY_REPORT.md +510 -0
- package/app/page.tsx +4 -23
- package/cli.ts +8 -0
- package/components/chart-detail-editable.tsx +3 -2
- package/components/chart-detail.tsx +1 -6
- package/components/edit-action-step.tsx +0 -2
- package/components/metadata-projections.tsx +208 -0
- package/components/relation-graph.tsx +27 -91
- package/components.json +21 -0
- package/direct-test.sh +180 -0
- package/dist/cli.js +7 -0
- package/dist/skill.js +342 -0
- package/docker-compose.test.yml +69 -0
- package/index.tsx +0 -26
- package/jgwill.coaia-visualizer-8--496dca71-d476-4ac9-ba9f-376add118dd8--260208.txt +2612 -0
- package/lib/chart-editor.ts +13 -13
- package/lib/jsonl-parser.ts +55 -8
- package/lib/jsonl-preservation.ts +405 -0
- package/lib/types.ts +32 -15
- package/mcp/dist/api-client.d.ts +138 -0
- package/mcp/dist/api-client.d.ts.map +1 -0
- package/mcp/dist/api-client.js +115 -0
- package/mcp/dist/api-client.js.map +1 -0
- package/mcp/dist/index.d.ts +2 -0
- package/mcp/dist/index.d.ts.map +1 -0
- package/mcp/dist/index.js +286 -0
- package/mcp/dist/index.js.map +1 -0
- package/mcp/dist/tools/index.d.ts +18 -0
- package/mcp/dist/tools/index.d.ts.map +1 -0
- package/mcp/dist/tools/index.js +322 -0
- package/mcp/dist/tools/index.js.map +1 -0
- package/mcp/package-lock.json +210 -0
- package/package.json +2 -27
- package/rispecs/github-project-runtime-memory-integration.spec.md +381 -0
- package/skill.ts +385 -0
- package/test-data/test-master.jsonl +11 -0
- package/test-results/.last-run.json +4 -0
- package/test-scripts/README.md +325 -0
- package/test-scripts/rich-jsonl-preservation.spec.ts +118 -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/test-scripts/test-global-cli.spec.ts +220 -0
- package/test-scripts/verify-global-cli.sh +87 -0
- package/tsconfig.json +41 -0
- package/components/github-provenance.tsx +0 -226
- 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,
|
|
6
|
-
import { ArrowRight
|
|
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
|
|
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
|
-
|
|
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
|
-
}
|
package/components.json
ADDED
|
@@ -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)
|