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
@@ -1,316 +0,0 @@
1
- import type { EntityRecord, RelationRecord } from "./types"
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
- }