coaia-visualizer 1.6.3 → 1.6.4
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/Dockerfile +61 -0
- package/Dockerfile.app +50 -0
- package/cli.ts +11 -5
- package/components/chart-detail-editable.tsx +2 -0
- package/components/chart-detail.tsx +6 -1
- package/components/edit-action-step.tsx +2 -0
- package/components/github-provenance.tsx +226 -0
- package/components/relation-graph.tsx +91 -27
- package/dist/cli.js +9 -5
- package/docker-build-push.sh +20 -0
- package/docker-entrypoint.sh +27 -0
- package/index.tsx +26 -0
- package/lib/chart-editor.ts +3 -3
- package/lib/github-provenance.ts +316 -0
- package/lib/jsonl-parser.ts +2 -2
- package/lib/types.ts +15 -2
- package/mcp/test_mcp/.gemini/settings.json +18 -0
- package/package.json +14 -13
- package/rispecs/README.md +170 -0
- package/rispecs/accountability-responsibility.rispec.md +110 -0
- package/rispecs/api-mcp-interface.spec.md +287 -0
- package/rispecs/app.spec.md +364 -0
- package/rispecs/chart-editing-workflow.spec.md +297 -0
- package/rispecs/chart-visualization-hierarchy.spec.md +235 -0
- package/rispecs/cli-mode.spec.md +224 -0
- package/rispecs/github-project-runtime-memory-integration.spec.md +381 -0
- package/rispecs/jsonl-parsing-data-types.spec.md +243 -0
- package/rispecs/narrative-beats-display.spec.md +189 -0
- package/rispecs/pde-integration.spec.md +280 -0
- package/rispecs/planning-integration.spec.md +329 -0
- package/rispecs/relation-graph-visualization.spec.md +171 -0
- package/rispecs/relation-to-mcp-structural-thinking.kin.md +65 -0
- package/rispecs/ui-component-library.spec.md +258 -0
- package/mcp/dist/api-client.d.ts +0 -138
- package/mcp/dist/api-client.d.ts.map +0 -1
- package/mcp/dist/api-client.js +0 -115
- package/mcp/dist/api-client.js.map +0 -1
- package/mcp/dist/index.d.ts +0 -2
- package/mcp/dist/index.d.ts.map +0 -1
- package/mcp/dist/index.js +0 -286
- package/mcp/dist/index.js.map +0 -1
- package/mcp/dist/tools/index.d.ts +0 -18
- package/mcp/dist/tools/index.d.ts.map +0 -1
- package/mcp/dist/tools/index.js +0 -322
- package/mcp/dist/tools/index.js.map +0 -1
- package/mcp/package-lock.json +0 -210
package/index.tsx
CHANGED
|
@@ -13,6 +13,31 @@ export type {
|
|
|
13
13
|
// JSONL Parser
|
|
14
14
|
export { parseJSONL, organizeData, getChartSummary, getChartProgress } from "./lib/jsonl-parser"
|
|
15
15
|
|
|
16
|
+
// GitHub provenance helpers
|
|
17
|
+
export {
|
|
18
|
+
GITHUB_BRIDGE_RELATION_TYPES,
|
|
19
|
+
GITHUB_PROJECT_FIELD_KEYS,
|
|
20
|
+
GITHUB_SYNC_STATES,
|
|
21
|
+
formatGithubIssueRef,
|
|
22
|
+
formatProjectItemRef,
|
|
23
|
+
getGithubBridgeRelationType,
|
|
24
|
+
getGithubEntityProvenance,
|
|
25
|
+
getGithubIssueRef,
|
|
26
|
+
getGithubProjectItems,
|
|
27
|
+
getGithubSource,
|
|
28
|
+
getGithubSyncState,
|
|
29
|
+
getProjectFieldEntries,
|
|
30
|
+
} from "./lib/github-provenance"
|
|
31
|
+
export type {
|
|
32
|
+
GithubBridgeRelationType,
|
|
33
|
+
GithubEntityProvenance,
|
|
34
|
+
GithubIssueRef,
|
|
35
|
+
GithubProjectFieldEntry,
|
|
36
|
+
GithubProjectItemRef,
|
|
37
|
+
GithubSourceProvenance,
|
|
38
|
+
GithubSyncState,
|
|
39
|
+
} from "./lib/github-provenance"
|
|
40
|
+
|
|
16
41
|
// Utilities
|
|
17
42
|
export { cn } from "./lib/utils"
|
|
18
43
|
|
|
@@ -28,6 +53,7 @@ export { ChartDetail } from "./components/chart-detail"
|
|
|
28
53
|
export { DataStats } from "./components/data-stats"
|
|
29
54
|
export { CreateChartForm } from "./components/create-chart-form"
|
|
30
55
|
export { NarrativeBeats } from "./components/narrative-beats"
|
|
56
|
+
export { ActionStepIssueBadge, ProjectSourceBadge, SyncStatePill } from "./components/github-provenance"
|
|
31
57
|
export { LiveIndicator } from "./components/live-indicator"
|
|
32
58
|
export { ThemeToggle } from "./components/theme-toggle"
|
|
33
59
|
export { EditDesiredOutcome } from "./components/edit-desired-outcome"
|
package/lib/chart-editor.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// lib/chart-editor.ts - Chart editing logic
|
|
2
2
|
|
|
3
|
-
import type { EntityRecord, RelationRecord, ParsedData } from "./types"
|
|
4
|
-
import { buildMetadataWarnings, buildRecordProjections, serializeParsedData } from "./jsonl-preservation"
|
|
5
|
-
import { buildRiseSpecs } from "./jsonl-parser"
|
|
3
|
+
import type { EntityRecord, RelationRecord, ParsedData } from "./types.ts"
|
|
4
|
+
import { buildMetadataWarnings, buildRecordProjections, serializeParsedData } from "./jsonl-preservation.ts"
|
|
5
|
+
import { buildRiseSpecs } from "./jsonl-parser.ts"
|
|
6
6
|
|
|
7
7
|
export interface ChartUpdate {
|
|
8
8
|
type:
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import type { EntityRecord, RelationRecord } from "./types.ts"
|
|
2
|
+
|
|
3
|
+
export const GITHUB_SYNC_STATES = ["synced", "diverged", "conflict", "project-only", "chart-only"] as const
|
|
4
|
+
|
|
5
|
+
export type GithubSyncState = (typeof GITHUB_SYNC_STATES)[number]
|
|
6
|
+
|
|
7
|
+
export interface GithubIssueRef {
|
|
8
|
+
owner: string
|
|
9
|
+
repo: string
|
|
10
|
+
number: number
|
|
11
|
+
nodeId?: string
|
|
12
|
+
url?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface GithubProjectItemRef {
|
|
16
|
+
projectOwner?: string
|
|
17
|
+
projectNumber?: number
|
|
18
|
+
projectTitle?: string
|
|
19
|
+
itemId?: string
|
|
20
|
+
projectId?: string
|
|
21
|
+
url?: string
|
|
22
|
+
fields?: Record<string, unknown>
|
|
23
|
+
fieldValues?: Record<string, unknown>
|
|
24
|
+
projectFields?: Record<string, unknown>
|
|
25
|
+
[key: string]: unknown
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface GithubSourceProvenance {
|
|
29
|
+
system?: string
|
|
30
|
+
toolName?: string
|
|
31
|
+
sessionId?: string
|
|
32
|
+
createdAt?: string
|
|
33
|
+
version?: string
|
|
34
|
+
[key: string]: unknown
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface GithubEntityProvenance {
|
|
38
|
+
issue?: GithubIssueRef
|
|
39
|
+
projectItems: GithubProjectItemRef[]
|
|
40
|
+
syncState?: GithubSyncState
|
|
41
|
+
lastSyncedAt?: string
|
|
42
|
+
source?: GithubSourceProvenance
|
|
43
|
+
hasGithubMetadata: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface GithubProjectFieldEntry {
|
|
47
|
+
key: string
|
|
48
|
+
label: string
|
|
49
|
+
value: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const GITHUB_PROJECT_FIELD_KEYS = [
|
|
53
|
+
"goal",
|
|
54
|
+
"current_reality",
|
|
55
|
+
"observations",
|
|
56
|
+
"question",
|
|
57
|
+
"Status",
|
|
58
|
+
"phase",
|
|
59
|
+
"session_id",
|
|
60
|
+
"four_dir_east",
|
|
61
|
+
"four_dir_south",
|
|
62
|
+
"four_dir_west",
|
|
63
|
+
"four_dir_north",
|
|
64
|
+
"relational_assessed",
|
|
65
|
+
"relational_principles",
|
|
66
|
+
] as const
|
|
67
|
+
|
|
68
|
+
export const GITHUB_BRIDGE_RELATION_TYPES = ["synced_to_github", "linked_to_issue", "project_lens_of"] as const
|
|
69
|
+
|
|
70
|
+
export type GithubBridgeRelationType = (typeof GITHUB_BRIDGE_RELATION_TYPES)[number]
|
|
71
|
+
|
|
72
|
+
const PROJECT_FIELD_LABELS: Record<string, string> = {
|
|
73
|
+
goal: "goal",
|
|
74
|
+
current_reality: "current reality",
|
|
75
|
+
observations: "observations",
|
|
76
|
+
question: "question",
|
|
77
|
+
Status: "Status",
|
|
78
|
+
phase: "phase",
|
|
79
|
+
session_id: "session",
|
|
80
|
+
four_dir_east: "East",
|
|
81
|
+
four_dir_south: "South",
|
|
82
|
+
four_dir_west: "West",
|
|
83
|
+
four_dir_north: "North",
|
|
84
|
+
relational_assessed: "relational assessed",
|
|
85
|
+
relational_principles: "relational principles",
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
89
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function firstString(...values: unknown[]): string | undefined {
|
|
93
|
+
for (const value of values) {
|
|
94
|
+
if (typeof value === "string" && value.trim()) {
|
|
95
|
+
return value.trim()
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function firstNumber(...values: unknown[]): number | undefined {
|
|
101
|
+
for (const value of values) {
|
|
102
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
103
|
+
return value
|
|
104
|
+
}
|
|
105
|
+
if (typeof value === "string" && value.trim()) {
|
|
106
|
+
const parsed = Number(value)
|
|
107
|
+
if (Number.isFinite(parsed)) {
|
|
108
|
+
return parsed
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function normalizeIssueCandidate(candidate: unknown): GithubIssueRef | undefined {
|
|
115
|
+
if (!isRecord(candidate)) return undefined
|
|
116
|
+
|
|
117
|
+
const owner = firstString(candidate.owner, candidate.ownerLogin, candidate.repositoryOwner)
|
|
118
|
+
const repo = firstString(candidate.repo, candidate.repository, candidate.repositoryName)
|
|
119
|
+
const number = firstNumber(candidate.number, candidate.issue_number, candidate.issueNumber, candidate.issue)
|
|
120
|
+
|
|
121
|
+
if (!owner || !repo || !number) return undefined
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
owner,
|
|
125
|
+
repo,
|
|
126
|
+
number,
|
|
127
|
+
nodeId: firstString(candidate.nodeId, candidate.node_id),
|
|
128
|
+
url: firstString(candidate.url, candidate.html_url) ?? `https://github.com/${owner}/${repo}/issues/${number}`,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function normalizeProjectItemCandidate(candidate: unknown, fallbackOwner?: string): GithubProjectItemRef | undefined {
|
|
133
|
+
if (!isRecord(candidate)) return undefined
|
|
134
|
+
|
|
135
|
+
const projectOwner = firstString(candidate.projectOwner, candidate.owner, candidate.ownerLogin, fallbackOwner)
|
|
136
|
+
const projectNumber = firstNumber(candidate.projectNumber, candidate.project_number, candidate.number)
|
|
137
|
+
const itemId = firstString(candidate.itemId, candidate.item_id, candidate.projectItemId, candidate.project_id)
|
|
138
|
+
const projectId = firstString(candidate.projectId, candidate.project_id)
|
|
139
|
+
const projectTitle = firstString(candidate.projectTitle, candidate.title, candidate.project)
|
|
140
|
+
const url = firstString(candidate.url)
|
|
141
|
+
|
|
142
|
+
if (!projectOwner && !projectNumber && !itemId && !projectId && !projectTitle && !url) {
|
|
143
|
+
return undefined
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
...candidate,
|
|
148
|
+
projectOwner,
|
|
149
|
+
projectNumber,
|
|
150
|
+
projectTitle,
|
|
151
|
+
itemId,
|
|
152
|
+
projectId,
|
|
153
|
+
url,
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function getGithubIssueRef(entity?: EntityRecord): GithubIssueRef | undefined {
|
|
158
|
+
if (!entity) return undefined
|
|
159
|
+
|
|
160
|
+
const metadata = entity.metadata ?? {}
|
|
161
|
+
const github = isRecord(metadata.github) ? metadata.github : undefined
|
|
162
|
+
const syncTarget = isRecord(metadata.sync_target) ? metadata.sync_target : undefined
|
|
163
|
+
const githubRef = isRecord(metadata.github_ref) ? metadata.github_ref : undefined
|
|
164
|
+
|
|
165
|
+
return normalizeIssueCandidate(github?.issue) ?? normalizeIssueCandidate(syncTarget) ?? normalizeIssueCandidate(githubRef)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function getGithubProjectItems(entity?: EntityRecord): GithubProjectItemRef[] {
|
|
169
|
+
if (!entity) return []
|
|
170
|
+
|
|
171
|
+
const metadata = entity.metadata ?? {}
|
|
172
|
+
const github = isRecord(metadata.github) ? metadata.github : undefined
|
|
173
|
+
const syncTarget = isRecord(metadata.sync_target) ? metadata.sync_target : undefined
|
|
174
|
+
const fallbackOwner = getGithubIssueRef(entity)?.owner
|
|
175
|
+
|
|
176
|
+
if (Array.isArray(github?.projectItems)) {
|
|
177
|
+
return github.projectItems
|
|
178
|
+
.map((item) => normalizeProjectItemCandidate(item, fallbackOwner))
|
|
179
|
+
.filter((item): item is GithubProjectItemRef => Boolean(item))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const canonicalProjectItem = normalizeProjectItemCandidate(github?.projectItem, fallbackOwner)
|
|
183
|
+
if (canonicalProjectItem) {
|
|
184
|
+
return [canonicalProjectItem]
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const legacyProjectItem = normalizeProjectItemCandidate(syncTarget, fallbackOwner)
|
|
188
|
+
return legacyProjectItem ? [legacyProjectItem] : []
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function getGithubSyncState(entityOrMetadata?: EntityRecord | Record<string, unknown>): GithubSyncState | undefined {
|
|
192
|
+
const metadata =
|
|
193
|
+
entityOrMetadata && "metadata" in entityOrMetadata && isRecord(entityOrMetadata.metadata)
|
|
194
|
+
? entityOrMetadata.metadata
|
|
195
|
+
: entityOrMetadata
|
|
196
|
+
|
|
197
|
+
if (!isRecord(metadata)) return undefined
|
|
198
|
+
|
|
199
|
+
const github = isRecord(metadata.github) ? metadata.github : undefined
|
|
200
|
+
const syncState = github?.syncState ?? metadata.syncState
|
|
201
|
+
|
|
202
|
+
return typeof syncState === "string" && GITHUB_SYNC_STATES.includes(syncState as GithubSyncState)
|
|
203
|
+
? (syncState as GithubSyncState)
|
|
204
|
+
: undefined
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function getGithubSource(entity?: EntityRecord): GithubSourceProvenance | undefined {
|
|
208
|
+
if (!entity) return undefined
|
|
209
|
+
|
|
210
|
+
const source = entity.metadata?.source
|
|
211
|
+
return isRecord(source) ? (source as GithubSourceProvenance) : undefined
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function getGithubEntityProvenance(entity?: EntityRecord): GithubEntityProvenance {
|
|
215
|
+
const metadata = entity?.metadata ?? {}
|
|
216
|
+
const github = isRecord(metadata.github) ? metadata.github : undefined
|
|
217
|
+
const issue = getGithubIssueRef(entity)
|
|
218
|
+
const projectItems = getGithubProjectItems(entity)
|
|
219
|
+
const syncTarget = isRecord(metadata.sync_target) ? metadata.sync_target : undefined
|
|
220
|
+
const githubRef = isRecord(metadata.github_ref) ? metadata.github_ref : undefined
|
|
221
|
+
const source = getGithubSource(entity)
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
issue,
|
|
225
|
+
projectItems,
|
|
226
|
+
syncState: getGithubSyncState(entity),
|
|
227
|
+
lastSyncedAt: firstString(github?.lastSyncedAt, metadata.lastSyncedAt),
|
|
228
|
+
source,
|
|
229
|
+
hasGithubMetadata:
|
|
230
|
+
Boolean(github) ||
|
|
231
|
+
Boolean(syncTarget) ||
|
|
232
|
+
Boolean(githubRef) ||
|
|
233
|
+
source?.system === "coaia-github" ||
|
|
234
|
+
Boolean(issue) ||
|
|
235
|
+
projectItems.length > 0,
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function formatGithubIssueRef(issue?: GithubIssueRef): string | undefined {
|
|
240
|
+
if (!issue) return undefined
|
|
241
|
+
return `${issue.owner}/${issue.repo}#${issue.number}`
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function formatProjectItemRef(projectItem?: GithubProjectItemRef): string | undefined {
|
|
245
|
+
if (!projectItem) return undefined
|
|
246
|
+
|
|
247
|
+
if (projectItem.projectOwner && projectItem.projectNumber) {
|
|
248
|
+
return `${projectItem.projectOwner}/${projectItem.projectNumber}`
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return projectItem.projectTitle || projectItem.itemId || projectItem.projectId || projectItem.url
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function getProjectFieldEntries(
|
|
255
|
+
entity: EntityRecord | undefined,
|
|
256
|
+
projectItem?: GithubProjectItemRef,
|
|
257
|
+
): GithubProjectFieldEntry[] {
|
|
258
|
+
const metadata = entity?.metadata ?? {}
|
|
259
|
+
const github = isRecord(metadata.github) ? metadata.github : undefined
|
|
260
|
+
const sources = [
|
|
261
|
+
projectItem?.fields,
|
|
262
|
+
projectItem?.fieldValues,
|
|
263
|
+
projectItem?.projectFields,
|
|
264
|
+
github?.fields,
|
|
265
|
+
github?.fieldValues,
|
|
266
|
+
github?.projectFields,
|
|
267
|
+
metadata,
|
|
268
|
+
github,
|
|
269
|
+
].filter(isRecord)
|
|
270
|
+
|
|
271
|
+
return GITHUB_PROJECT_FIELD_KEYS.flatMap((key) => {
|
|
272
|
+
for (const source of sources) {
|
|
273
|
+
if (!(key in source)) continue
|
|
274
|
+
const value = stringifyFieldValue(source[key])
|
|
275
|
+
if (value) {
|
|
276
|
+
return [{ key, label: PROJECT_FIELD_LABELS[key], value }]
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return []
|
|
280
|
+
})
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function getGithubBridgeRelationType(relation: RelationRecord): GithubBridgeRelationType | undefined {
|
|
284
|
+
const relationType = relation.relationType
|
|
285
|
+
if (GITHUB_BRIDGE_RELATION_TYPES.includes(relationType as GithubBridgeRelationType)) {
|
|
286
|
+
return relationType as GithubBridgeRelationType
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const metadata = relation.metadata ?? {}
|
|
290
|
+
const github = isRecord(metadata.github) ? metadata.github : undefined
|
|
291
|
+
const context = firstString(
|
|
292
|
+
metadata.context,
|
|
293
|
+
metadata.relation,
|
|
294
|
+
metadata.relationName,
|
|
295
|
+
metadata.semanticType,
|
|
296
|
+
github?.context,
|
|
297
|
+
github?.relation,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
return GITHUB_BRIDGE_RELATION_TYPES.includes(context as GithubBridgeRelationType)
|
|
301
|
+
? (context as GithubBridgeRelationType)
|
|
302
|
+
: undefined
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function stringifyFieldValue(value: unknown): string | undefined {
|
|
306
|
+
if (value === null || value === undefined) return undefined
|
|
307
|
+
if (typeof value === "string") return value.trim() || undefined
|
|
308
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value)
|
|
309
|
+
if (Array.isArray(value)) {
|
|
310
|
+
const rendered = value.map(stringifyFieldValue).filter(Boolean).join(", ")
|
|
311
|
+
return rendered || undefined
|
|
312
|
+
}
|
|
313
|
+
if (isRecord(value)) {
|
|
314
|
+
return firstString(value.name, value.text, value.value, value.title, value.label) ?? JSON.stringify(value)
|
|
315
|
+
}
|
|
316
|
+
}
|
package/lib/jsonl-parser.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Chart, EntityRecord, JSONLRecord, ParsedData, RawJSONLRecord, RelationRecord, RiseSpecProjection } from "./types"
|
|
2
|
-
import { buildMetadataWarnings, buildRecordProjections, normalizeJsonlRecord } from "./jsonl-preservation"
|
|
1
|
+
import type { Chart, EntityRecord, JSONLRecord, ParsedData, RawJSONLRecord, RelationRecord, RiseSpecProjection } from "./types.ts"
|
|
2
|
+
import { buildMetadataWarnings, buildRecordProjections, normalizeJsonlRecord } from "./jsonl-preservation.ts"
|
|
3
3
|
|
|
4
4
|
export function parseJSONL(content: string): RawJSONLRecord[] {
|
|
5
5
|
const lines = content.trim().split("\n")
|
package/lib/types.ts
CHANGED
|
@@ -3,7 +3,14 @@
|
|
|
3
3
|
export interface EntityRecord {
|
|
4
4
|
type: "entity"
|
|
5
5
|
name: string
|
|
6
|
-
entityType:
|
|
6
|
+
entityType:
|
|
7
|
+
| "structural_tension_chart"
|
|
8
|
+
| "desired_outcome"
|
|
9
|
+
| "current_reality"
|
|
10
|
+
| "action_step"
|
|
11
|
+
| "narrative_beat"
|
|
12
|
+
| "custom"
|
|
13
|
+
| (string & {})
|
|
7
14
|
observations: string[]
|
|
8
15
|
metadata: Record<string, any>
|
|
9
16
|
[key: string]: any
|
|
@@ -13,7 +20,13 @@ export interface RelationRecord {
|
|
|
13
20
|
type: "relation"
|
|
14
21
|
from: string
|
|
15
22
|
to: string
|
|
16
|
-
relationType:
|
|
23
|
+
relationType:
|
|
24
|
+
| "contains"
|
|
25
|
+
| "creates_tension_with"
|
|
26
|
+
| "advances_toward"
|
|
27
|
+
| "documents"
|
|
28
|
+
| "custom"
|
|
29
|
+
| (string & {})
|
|
17
30
|
metadata: Record<string, any>
|
|
18
31
|
[key: string]: any
|
|
19
32
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"chart-coaia-narrative-legacy-mcp": {
|
|
4
|
+
"command": "npx",
|
|
5
|
+
"args": ["-y", "${CNCV}", "--memory-path", "${memories}/trading-chart.coaia-narrative.jsonl" ]
|
|
6
|
+
},
|
|
7
|
+
"charts-mcp": {
|
|
8
|
+
"command": "node",
|
|
9
|
+
"args": [
|
|
10
|
+
"/workspace/repos/jgwill/coaia-visualizer-feat-4/mcp/dist/index.js"
|
|
11
|
+
],
|
|
12
|
+
"env": {
|
|
13
|
+
"COAIAN_API_TOKEN":"123",
|
|
14
|
+
"COAIAV_API_URL":"http://localhost:4321"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coaia-visualizer",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.tsx",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
".": "./index.tsx",
|
|
10
10
|
"./lib/types": "./lib/types.ts",
|
|
11
11
|
"./lib/jsonl-parser": "./lib/jsonl-parser.ts",
|
|
12
|
+
"./lib/github-provenance": "./lib/github-provenance.ts",
|
|
12
13
|
"./lib/utils": "./lib/utils.ts",
|
|
13
14
|
"./hooks/*": "./hooks/*.ts",
|
|
14
15
|
"./components/*": "./components/*.tsx",
|
|
@@ -23,25 +24,24 @@
|
|
|
23
24
|
"components/**/*",
|
|
24
25
|
"hooks/**/*",
|
|
25
26
|
"lib/**/*",
|
|
26
|
-
"styles/**/*",
|
|
27
|
-
"public/**/*",
|
|
28
27
|
"dist/**/*",
|
|
29
|
-
"mcp
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"mcp/README.md",
|
|
34
|
-
"mcp/Dockerfile",
|
|
35
|
-
"mcp/tsconfig.json",
|
|
36
|
-
"index.tsx",
|
|
28
|
+
"mcp/**/*",
|
|
29
|
+
"public/**/*",
|
|
30
|
+
"rispecs/**/*",
|
|
31
|
+
"styles/**/*",
|
|
37
32
|
"cli.ts",
|
|
33
|
+
"index.tsx",
|
|
38
34
|
"skill.ts",
|
|
39
|
-
"components.json",
|
|
40
|
-
"mcp-config.json",
|
|
41
35
|
"next-env.d.ts",
|
|
42
36
|
"next.config.mjs",
|
|
43
37
|
"postcss.config.mjs",
|
|
44
38
|
"tsconfig.json",
|
|
39
|
+
"Dockerfile",
|
|
40
|
+
"Dockerfile.app",
|
|
41
|
+
"docker-build-push.sh",
|
|
42
|
+
"docker-entrypoint.sh",
|
|
43
|
+
"components.json",
|
|
44
|
+
"mcp-config.json",
|
|
45
45
|
"README.md",
|
|
46
46
|
"README_TELESCOPED_NAVIGATION.md",
|
|
47
47
|
"KINSHIP.md",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"test": "playwright test",
|
|
58
58
|
"test:rich-jsonl": "playwright test test-scripts/rich-jsonl-preservation.spec.ts",
|
|
59
59
|
"test:global-cli": "playwright test test-scripts/test-global-cli.spec.ts",
|
|
60
|
+
"verify:github-provenance": "node --experimental-strip-types --experimental-specifier-resolution=node test-scripts/verify-github-provenance.mjs",
|
|
60
61
|
"docker:build": "docker build -t jgwill/coaia:visualizer .",
|
|
61
62
|
"docker:push": "docker push jgwill/coaia:visualizer",
|
|
62
63
|
"docker:build-push": "npm run docker:build && npm run docker:push",
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# COAIA Visualizer — RISE Specification Directory
|
|
2
|
+
|
|
3
|
+
> Complete RISE specifications for `coaia-visualizer` v1.5.5 — the interactive web-based visualizer and editor for Structural Tension Charts from coaia-narrative JSONL files.
|
|
4
|
+
|
|
5
|
+
**Framework**: RISE (Reverse-engineer · Intent · Specifications · Export)
|
|
6
|
+
**Target Audience**: LLM agents, developers, and contributors rebuilding or extending coaia-visualizer
|
|
7
|
+
**Canonical Types**: [`coaia-visualizer/lib/types.ts`](../lib/types.ts)
|
|
8
|
+
**Parent Kinship**: [`coaia-narrative`](../../coaia-narrative/) — data source and memory layer
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 📚 Main Overview
|
|
13
|
+
|
|
14
|
+
- **[app.spec.md](./app.spec.md)** — Complete application specification
|
|
15
|
+
- What coaia-visualizer enables users to create
|
|
16
|
+
- How all components interconnect
|
|
17
|
+
- Creative intent and structural orientation
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 🔧 Component Specifications
|
|
22
|
+
|
|
23
|
+
1. **[jsonl-parsing-data-types.spec.md](./jsonl-parsing-data-types.spec.md)**
|
|
24
|
+
- JSONL parsing engine and 4-pass organization algorithm
|
|
25
|
+
- EntityRecord, RelationRecord, Chart, ParsedData type system
|
|
26
|
+
- KnowledgeGraph assembly from flat JSONL lines
|
|
27
|
+
- File upload, export, and live reload
|
|
28
|
+
|
|
29
|
+
2. **[chart-visualization-hierarchy.spec.md](./chart-visualization-hierarchy.spec.md)**
|
|
30
|
+
- ChartList with hierarchy and flat list modes
|
|
31
|
+
- ChartDetail and ChartDetailEditable views
|
|
32
|
+
- Telescoped navigation and sub-chart drilling
|
|
33
|
+
- Progress bars, summary truncation, narrative beat indicators
|
|
34
|
+
|
|
35
|
+
3. **[chart-editing-workflow.spec.md](./chart-editing-workflow.spec.md)**
|
|
36
|
+
- Edit desired outcomes, current reality observations, action steps
|
|
37
|
+
- Due date management with calendar popover
|
|
38
|
+
- Add/remove/toggle actions and charts
|
|
39
|
+
- CreateChartForm and AddMasterChart creation flows
|
|
40
|
+
- ChartEditor class mutation patterns
|
|
41
|
+
|
|
42
|
+
4. **[api-mcp-interface.spec.md](./api-mcp-interface.spec.md)**
|
|
43
|
+
- REST API routes (charts CRUD, search, JSONL I/O, watch, audio)
|
|
44
|
+
- MCP server with 15 tools for AI agent access
|
|
45
|
+
- Bearer token authentication (COAIAN_API_TOKEN)
|
|
46
|
+
- CoaiaVisualizerClient API wrapper
|
|
47
|
+
|
|
48
|
+
5. **[cli-mode.spec.md](./cli-mode.spec.md)**
|
|
49
|
+
- CLI entry point with `--memory-path` flag
|
|
50
|
+
- Docker and local Next.js execution modes
|
|
51
|
+
- Environment variable configuration
|
|
52
|
+
- Auto-open browser and graceful shutdown
|
|
53
|
+
|
|
54
|
+
6. **[narrative-beats-display.spec.md](./narrative-beats-display.spec.md)**
|
|
55
|
+
- Narrative beat card rendering
|
|
56
|
+
- Multi-universe perspective badges
|
|
57
|
+
- Act sequencing, dramatic type, prose display
|
|
58
|
+
- Lessons learned and timestamp metadata
|
|
59
|
+
|
|
60
|
+
7. **[relation-graph-visualization.spec.md](./relation-graph-visualization.spec.md)**
|
|
61
|
+
- Inter-chart relation graph rendering
|
|
62
|
+
- Entity connection display grouped by relation type
|
|
63
|
+
- contains, creates_tension_with, advances_toward, documents relations
|
|
64
|
+
- Entity observation preview
|
|
65
|
+
|
|
66
|
+
8. **[ui-component-library.spec.md](./ui-component-library.spec.md)**
|
|
67
|
+
- Radix UI / shadcn-based component primitives
|
|
68
|
+
- Dark/light theme support via next-themes
|
|
69
|
+
- DataStats dashboard cards
|
|
70
|
+
- LiveIndicator polling status display
|
|
71
|
+
- Tailwind CSS utility patterns
|
|
72
|
+
|
|
73
|
+
9. **[accountability-responsibility.rispec.md](./accountability-responsibility.rispec.md)** *(existing)*
|
|
74
|
+
- Rendering accountability/responsibility distinctions in chart UI
|
|
75
|
+
- Schema consumption from entity metadata
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 🔮 Future Integration Specs
|
|
80
|
+
|
|
81
|
+
10. **[pde-integration.spec.md](./pde-integration.spec.md)** *(future/aspirational)*
|
|
82
|
+
- Four Directions Medicine Wheel quadrant visualization
|
|
83
|
+
- PDE source linking with decomposition provenance trail
|
|
84
|
+
- Implicit intent badges and confidence indicators
|
|
85
|
+
- Direction-based action grouping (EAST/SOUTH/WEST/NORTH)
|
|
86
|
+
- Extends: chart-visualization-hierarchy, jsonl-parsing-data-types
|
|
87
|
+
|
|
88
|
+
11. **[planning-integration.spec.md](./planning-integration.spec.md)** *(future/aspirational)*
|
|
89
|
+
- Plan source indicator with parse confidence scoring
|
|
90
|
+
- Bidirectional sync status (synced/diverged/conflict)
|
|
91
|
+
- Plan section mapping for action step provenance
|
|
92
|
+
- Confidence-based color coding for human review focus
|
|
93
|
+
- Re-sync trigger to push edits back to plan markdown
|
|
94
|
+
- Extends: chart-visualization-hierarchy, chart-editing-workflow, api-mcp-interface
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 🎯 Core Creative Intent
|
|
99
|
+
|
|
100
|
+
coaia-visualizer enables users to **create living, navigable maps of structural tension** — transforming flat JSONL memory files into interactive hierarchies where:
|
|
101
|
+
|
|
102
|
+
- **Desired outcomes** are visible as aspirational anchors
|
|
103
|
+
- **Current reality** is honestly assessed and continuously updated
|
|
104
|
+
- **Action steps** telescope into full sub-charts, creating fractal depth
|
|
105
|
+
- **Narrative beats** weave story across the structural work
|
|
106
|
+
- **Relations** reveal the kinship between charts, actions, and outcomes
|
|
107
|
+
|
|
108
|
+
This is **not** a dashboard for monitoring problems. It is a **ceremonial instrument** for holding creative tension — the generative gap between what is and what must become.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 🏗️ Three Core Pillars
|
|
113
|
+
|
|
114
|
+
### 1. Creative Orientation (NOT Problem-Solving)
|
|
115
|
+
Every chart begins with a **Desired Outcome** — what the user wants to create. The system refuses "fix" and "solve" framing. Current Reality is an honest assessment, never a complaint.
|
|
116
|
+
|
|
117
|
+
### 2. Structural Tension (Current Reality ↔ Desired Outcome)
|
|
118
|
+
The irreducible gap between reality and vision generates the *mana* that drives action. The visualizer makes this tension **visible and navigable** — not as anxiety but as creative fuel.
|
|
119
|
+
|
|
120
|
+
### 3. Advancing Patterns (NOT Oscillating)
|
|
121
|
+
Action steps advance toward outcomes. Completed actions update parent reality. Telescoped charts create depth without losing context. The system structurally prevents oscillation between problem and relief.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 📐 Data Flow Architecture
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
JSONL File / API
|
|
129
|
+
│
|
|
130
|
+
▼
|
|
131
|
+
parseJSONL() ─── Flat records (entities + relations)
|
|
132
|
+
│
|
|
133
|
+
▼
|
|
134
|
+
organizeData() ─── 4-pass: separate → chart assembly → populate → hierarchy
|
|
135
|
+
│
|
|
136
|
+
▼
|
|
137
|
+
ParsedData ─── Charts[], Entities Map, Relations[], RootCharts[]
|
|
138
|
+
│
|
|
139
|
+
├──▶ page.tsx ─── Main orchestrator (state management, routing)
|
|
140
|
+
├──▶ ChartList ─── Hierarchy/List navigation
|
|
141
|
+
├──▶ ChartDetail ─── Read-only display (unused by page.tsx)
|
|
142
|
+
├──▶ ChartDetailEdit ─── Full editing via ChartEditor
|
|
143
|
+
├──▶ NarrativeBeats ─── Story layer
|
|
144
|
+
├──▶ RelationGraph ─── Inter-entity connections
|
|
145
|
+
└──▶ DataStats ─── Summary metrics
|
|
146
|
+
│
|
|
147
|
+
▼
|
|
148
|
+
ChartEditor.exportToJSONL() ─── Serialize back to JSONL
|
|
149
|
+
│
|
|
150
|
+
▼
|
|
151
|
+
POST /api/jsonl ─── Persist with backup
|
|
152
|
+
│
|
|
153
|
+
▼
|
|
154
|
+
COAIAV_MEMORY_PATH ─── Filesystem path resolved by CLI / env
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## ✅ Quality Test: Can Another LLM Rebuild This?
|
|
160
|
+
|
|
161
|
+
Given these specifications alone, an LLM agent should be able to:
|
|
162
|
+
|
|
163
|
+
- ✅ Parse any coaia-narrative JSONL file into navigable chart hierarchies
|
|
164
|
+
- ✅ Render structural tension (desired outcome vs current reality) with correct visual language
|
|
165
|
+
- ✅ Implement telescoped navigation between parent and sub-charts
|
|
166
|
+
- ✅ Build all 15 MCP tools matching the existing API surface
|
|
167
|
+
- ✅ Create the CLI with Docker and local execution modes
|
|
168
|
+
- ✅ Reproduce the edit workflow: desired outcome → current reality → action steps → completion
|
|
169
|
+
- ✅ Display narrative beats with multi-universe perspectives
|
|
170
|
+
- ✅ Maintain creative orientation framing throughout all UI text
|