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
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type React from "react"
|
|
4
|
+
|
|
5
|
+
import { useState } from "react"
|
|
6
|
+
import { Button } from "@/components/ui/button"
|
|
7
|
+
import { Input } from "@/components/ui/input"
|
|
8
|
+
import { Textarea } from "@/components/ui/textarea"
|
|
9
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
10
|
+
import { Label } from "@/components/ui/label"
|
|
11
|
+
import { Plus, Trash2, Calendar } from "lucide-react"
|
|
12
|
+
import type { ParsedData, EntityRecord, RelationRecord } from "@/lib/types"
|
|
13
|
+
|
|
14
|
+
interface CreateChartFormProps {
|
|
15
|
+
onChartCreated: (data: ParsedData) => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function CreateChartForm({ onChartCreated }: CreateChartFormProps) {
|
|
19
|
+
const [desiredOutcome, setDesiredOutcome] = useState("")
|
|
20
|
+
const [currentRealityObservations, setCurrentRealityObservations] = useState<string[]>([""])
|
|
21
|
+
const [dueDate, setDueDate] = useState("")
|
|
22
|
+
|
|
23
|
+
const addObservation = () => {
|
|
24
|
+
setCurrentRealityObservations([...currentRealityObservations, ""])
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const updateObservation = (index: number, value: string) => {
|
|
28
|
+
const updated = [...currentRealityObservations]
|
|
29
|
+
updated[index] = value
|
|
30
|
+
setCurrentRealityObservations(updated)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const removeObservation = (index: number) => {
|
|
34
|
+
const updated = currentRealityObservations.filter((_, i) => i !== index)
|
|
35
|
+
setCurrentRealityObservations(updated)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
39
|
+
e.preventDefault()
|
|
40
|
+
|
|
41
|
+
if (!desiredOutcome.trim()) {
|
|
42
|
+
alert("Please enter a desired outcome")
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const validObservations = currentRealityObservations.filter((obs) => obs.trim())
|
|
47
|
+
if (validObservations.length === 0) {
|
|
48
|
+
alert("Please add at least one current reality observation")
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Create chart ID
|
|
53
|
+
const chartId = `chart_${Date.now()}`
|
|
54
|
+
const timestamp = new Date().toISOString()
|
|
55
|
+
|
|
56
|
+
// Create entities
|
|
57
|
+
const entities = new Map<string, EntityRecord>()
|
|
58
|
+
|
|
59
|
+
// Chart entity
|
|
60
|
+
entities.set(chartId, {
|
|
61
|
+
type: "entity",
|
|
62
|
+
name: chartId,
|
|
63
|
+
entityType: "structural_tension_chart",
|
|
64
|
+
observations: ["New structural tension chart"],
|
|
65
|
+
metadata: {
|
|
66
|
+
chartId,
|
|
67
|
+
level: 0,
|
|
68
|
+
dueDate: dueDate || undefined,
|
|
69
|
+
completionStatus: false,
|
|
70
|
+
createdAt: timestamp,
|
|
71
|
+
updatedAt: timestamp,
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// Desired outcome entity
|
|
76
|
+
const desiredOutcomeName = `${chartId}_desired_outcome`
|
|
77
|
+
entities.set(desiredOutcomeName, {
|
|
78
|
+
type: "entity",
|
|
79
|
+
name: desiredOutcomeName,
|
|
80
|
+
entityType: "desired_outcome",
|
|
81
|
+
observations: [desiredOutcome],
|
|
82
|
+
metadata: {
|
|
83
|
+
chartId,
|
|
84
|
+
createdAt: timestamp,
|
|
85
|
+
updatedAt: timestamp,
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// Current reality entity
|
|
90
|
+
const currentRealityName = `${chartId}_current_reality`
|
|
91
|
+
entities.set(currentRealityName, {
|
|
92
|
+
type: "entity",
|
|
93
|
+
name: currentRealityName,
|
|
94
|
+
entityType: "current_reality",
|
|
95
|
+
observations: validObservations,
|
|
96
|
+
metadata: {
|
|
97
|
+
chartId,
|
|
98
|
+
createdAt: timestamp,
|
|
99
|
+
updatedAt: timestamp,
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
// Create relations
|
|
104
|
+
const relations: RelationRecord[] = [
|
|
105
|
+
{
|
|
106
|
+
type: "relation",
|
|
107
|
+
from: chartId,
|
|
108
|
+
to: desiredOutcomeName,
|
|
109
|
+
relationType: "contains",
|
|
110
|
+
metadata: { createdAt: timestamp },
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
type: "relation",
|
|
114
|
+
from: chartId,
|
|
115
|
+
to: currentRealityName,
|
|
116
|
+
relationType: "contains",
|
|
117
|
+
metadata: { createdAt: timestamp },
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
type: "relation",
|
|
121
|
+
from: currentRealityName,
|
|
122
|
+
to: desiredOutcomeName,
|
|
123
|
+
relationType: "creates_tension_with",
|
|
124
|
+
metadata: { createdAt: timestamp },
|
|
125
|
+
},
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
// Organize into ParsedData structure
|
|
129
|
+
const parsedData: ParsedData = {
|
|
130
|
+
entities,
|
|
131
|
+
relations,
|
|
132
|
+
charts: [
|
|
133
|
+
{
|
|
134
|
+
id: chartId,
|
|
135
|
+
name: chartId,
|
|
136
|
+
level: 0,
|
|
137
|
+
desiredOutcome,
|
|
138
|
+
currentReality: validObservations,
|
|
139
|
+
actions: [],
|
|
140
|
+
subCharts: [],
|
|
141
|
+
beats: [],
|
|
142
|
+
metadata: {
|
|
143
|
+
chartId,
|
|
144
|
+
level: 0,
|
|
145
|
+
dueDate: dueDate || undefined,
|
|
146
|
+
completionStatus: false,
|
|
147
|
+
createdAt: timestamp,
|
|
148
|
+
updatedAt: timestamp,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
onChartCreated(parsedData)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<Card className="max-w-2xl mx-auto">
|
|
159
|
+
<CardHeader>
|
|
160
|
+
<CardTitle>Create New Structural Tension Chart</CardTitle>
|
|
161
|
+
<CardDescription>Define your desired outcome and current reality to create structural tension</CardDescription>
|
|
162
|
+
</CardHeader>
|
|
163
|
+
<CardContent>
|
|
164
|
+
<form onSubmit={handleSubmit} className="space-y-6">
|
|
165
|
+
{/* Desired Outcome */}
|
|
166
|
+
<div className="space-y-2">
|
|
167
|
+
<Label htmlFor="desired-outcome" className="text-base font-semibold">
|
|
168
|
+
Desired Outcome
|
|
169
|
+
<span className="text-destructive ml-1">*</span>
|
|
170
|
+
</Label>
|
|
171
|
+
<Textarea
|
|
172
|
+
id="desired-outcome"
|
|
173
|
+
placeholder="What do you want to achieve? Be specific and concrete."
|
|
174
|
+
value={desiredOutcome}
|
|
175
|
+
onChange={(e) => setDesiredOutcome(e.target.value)}
|
|
176
|
+
className="min-h-[100px]"
|
|
177
|
+
required
|
|
178
|
+
/>
|
|
179
|
+
<p className="text-xs text-muted-foreground">
|
|
180
|
+
Describe the future state you want to create. This creates the aspirational pole of the tension.
|
|
181
|
+
</p>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
{/* Current Reality */}
|
|
185
|
+
<div className="space-y-3">
|
|
186
|
+
<Label className="text-base font-semibold">
|
|
187
|
+
Current Reality Observations
|
|
188
|
+
<span className="text-destructive ml-1">*</span>
|
|
189
|
+
</Label>
|
|
190
|
+
<p className="text-xs text-muted-foreground">
|
|
191
|
+
Honestly assess where things actually are right now. This creates the grounding pole of the tension.
|
|
192
|
+
</p>
|
|
193
|
+
{currentRealityObservations.map((obs, index) => (
|
|
194
|
+
<div key={index} className="flex gap-2">
|
|
195
|
+
<Textarea
|
|
196
|
+
placeholder={`Observation ${index + 1}: What is actually true right now?`}
|
|
197
|
+
value={obs}
|
|
198
|
+
onChange={(e) => updateObservation(index, e.target.value)}
|
|
199
|
+
className="flex-1 min-h-[80px]"
|
|
200
|
+
/>
|
|
201
|
+
{currentRealityObservations.length > 1 && (
|
|
202
|
+
<Button
|
|
203
|
+
type="button"
|
|
204
|
+
variant="ghost"
|
|
205
|
+
size="icon"
|
|
206
|
+
onClick={() => removeObservation(index)}
|
|
207
|
+
className="flex-shrink-0"
|
|
208
|
+
>
|
|
209
|
+
<Trash2 className="w-4 h-4" />
|
|
210
|
+
</Button>
|
|
211
|
+
)}
|
|
212
|
+
</div>
|
|
213
|
+
))}
|
|
214
|
+
<Button
|
|
215
|
+
type="button"
|
|
216
|
+
variant="outline"
|
|
217
|
+
size="sm"
|
|
218
|
+
onClick={addObservation}
|
|
219
|
+
className="w-full bg-transparent"
|
|
220
|
+
>
|
|
221
|
+
<Plus className="w-4 h-4 mr-2" />
|
|
222
|
+
Add Another Observation
|
|
223
|
+
</Button>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
{/* Due Date (Optional) */}
|
|
227
|
+
<div className="space-y-2">
|
|
228
|
+
<Label htmlFor="due-date" className="text-base font-semibold flex items-center gap-2">
|
|
229
|
+
<Calendar className="w-4 h-4" />
|
|
230
|
+
Due Date (Optional)
|
|
231
|
+
</Label>
|
|
232
|
+
<Input id="due-date" type="datetime-local" value={dueDate} onChange={(e) => setDueDate(e.target.value)} />
|
|
233
|
+
<p className="text-xs text-muted-foreground">
|
|
234
|
+
Set a target date to create urgency and enable telescoped planning.
|
|
235
|
+
</p>
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
{/* Submit Button */}
|
|
239
|
+
<div className="flex gap-3 pt-4">
|
|
240
|
+
<Button type="submit" className="flex-1">
|
|
241
|
+
Create Chart
|
|
242
|
+
</Button>
|
|
243
|
+
</div>
|
|
244
|
+
</form>
|
|
245
|
+
</CardContent>
|
|
246
|
+
</Card>
|
|
247
|
+
)
|
|
248
|
+
}
|
|
@@ -9,15 +9,17 @@ interface DataStatsProps {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function DataStats({ data }: DataStatsProps) {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
12
|
+
const chartsArray = Array.isArray(data.charts) ? data.charts : []
|
|
13
|
+
|
|
14
|
+
const totalEntities = data.entities?.size || 0
|
|
15
|
+
const totalRelations = data.relations?.length || 0
|
|
16
|
+
const totalCharts = chartsArray.length
|
|
17
|
+
const totalActions = chartsArray.reduce((sum, chart) => sum + (chart.actions?.length || 0), 0)
|
|
18
|
+
const completedActions = chartsArray.reduce(
|
|
19
|
+
(sum, chart) => sum + (chart.actions?.filter((a) => a.metadata?.completionStatus).length || 0),
|
|
18
20
|
0,
|
|
19
21
|
)
|
|
20
|
-
const totalBeats =
|
|
22
|
+
const totalBeats = chartsArray.reduce((sum, chart) => sum + (chart.narrativeBeats?.length || 0), 0)
|
|
21
23
|
|
|
22
24
|
const stats = [
|
|
23
25
|
{ label: "Charts", value: totalCharts, icon: Target, color: "text-chart-1" },
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function LiveIndicator({ isLive }: { isLive: boolean }) {
|
|
2
|
+
return (
|
|
3
|
+
<div className="flex items-center gap-2">
|
|
4
|
+
<div className={`w-2 h-2 rounded-full transition-all ${
|
|
5
|
+
isLive
|
|
6
|
+
? 'bg-green-500 animate-pulse shadow-lg shadow-green-500/50'
|
|
7
|
+
: 'bg-gray-400'
|
|
8
|
+
}`} />
|
|
9
|
+
<span className="text-sm font-medium">
|
|
10
|
+
{isLive ? 'LIVE' : 'Monitoring'}
|
|
11
|
+
</span>
|
|
12
|
+
</div>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog'
|
|
5
|
+
import { XIcon } from 'lucide-react'
|
|
6
|
+
|
|
7
|
+
import { cn } from '@/lib/utils'
|
|
8
|
+
|
|
9
|
+
function Dialog({
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
12
|
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function DialogTrigger({
|
|
16
|
+
...props
|
|
17
|
+
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
18
|
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function DialogPortal({
|
|
22
|
+
...props
|
|
23
|
+
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
24
|
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function DialogClose({
|
|
28
|
+
...props
|
|
29
|
+
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
|
30
|
+
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function DialogOverlay({
|
|
34
|
+
className,
|
|
35
|
+
...props
|
|
36
|
+
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
37
|
+
return (
|
|
38
|
+
<DialogPrimitive.Overlay
|
|
39
|
+
data-slot="dialog-overlay"
|
|
40
|
+
className={cn(
|
|
41
|
+
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
|
|
42
|
+
className,
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function DialogContent({
|
|
50
|
+
className,
|
|
51
|
+
children,
|
|
52
|
+
showCloseButton = true,
|
|
53
|
+
...props
|
|
54
|
+
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
|
55
|
+
showCloseButton?: boolean
|
|
56
|
+
}) {
|
|
57
|
+
return (
|
|
58
|
+
<DialogPortal data-slot="dialog-portal">
|
|
59
|
+
<DialogOverlay />
|
|
60
|
+
<DialogPrimitive.Content
|
|
61
|
+
data-slot="dialog-content"
|
|
62
|
+
className={cn(
|
|
63
|
+
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
|
|
64
|
+
className,
|
|
65
|
+
)}
|
|
66
|
+
{...props}
|
|
67
|
+
>
|
|
68
|
+
{children}
|
|
69
|
+
{showCloseButton && (
|
|
70
|
+
<DialogPrimitive.Close
|
|
71
|
+
data-slot="dialog-close"
|
|
72
|
+
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
|
73
|
+
>
|
|
74
|
+
<XIcon />
|
|
75
|
+
<span className="sr-only">Close</span>
|
|
76
|
+
</DialogPrimitive.Close>
|
|
77
|
+
)}
|
|
78
|
+
</DialogPrimitive.Content>
|
|
79
|
+
</DialogPortal>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
|
84
|
+
return (
|
|
85
|
+
<div
|
|
86
|
+
data-slot="dialog-header"
|
|
87
|
+
className={cn('flex flex-col gap-2 text-center sm:text-left', className)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
|
94
|
+
return (
|
|
95
|
+
<div
|
|
96
|
+
data-slot="dialog-footer"
|
|
97
|
+
className={cn(
|
|
98
|
+
'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end',
|
|
99
|
+
className,
|
|
100
|
+
)}
|
|
101
|
+
{...props}
|
|
102
|
+
/>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function DialogTitle({
|
|
107
|
+
className,
|
|
108
|
+
...props
|
|
109
|
+
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
|
110
|
+
return (
|
|
111
|
+
<DialogPrimitive.Title
|
|
112
|
+
data-slot="dialog-title"
|
|
113
|
+
className={cn('text-lg leading-none font-semibold', className)}
|
|
114
|
+
{...props}
|
|
115
|
+
/>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function DialogDescription({
|
|
120
|
+
className,
|
|
121
|
+
...props
|
|
122
|
+
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
|
123
|
+
return (
|
|
124
|
+
<DialogPrimitive.Description
|
|
125
|
+
data-slot="dialog-description"
|
|
126
|
+
className={cn('text-muted-foreground text-sm', className)}
|
|
127
|
+
{...props}
|
|
128
|
+
/>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export {
|
|
133
|
+
Dialog,
|
|
134
|
+
DialogClose,
|
|
135
|
+
DialogContent,
|
|
136
|
+
DialogDescription,
|
|
137
|
+
DialogFooter,
|
|
138
|
+
DialogHeader,
|
|
139
|
+
DialogOverlay,
|
|
140
|
+
DialogPortal,
|
|
141
|
+
DialogTitle,
|
|
142
|
+
DialogTrigger,
|
|
143
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import * as LabelPrimitive from '@radix-ui/react-label'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/utils'
|
|
7
|
+
|
|
8
|
+
function Label({
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
|
12
|
+
return (
|
|
13
|
+
<LabelPrimitive.Root
|
|
14
|
+
data-slot="label"
|
|
15
|
+
className={cn(
|
|
16
|
+
'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
|
|
17
|
+
className,
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { Label }
|
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 "========================================="
|