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
package/dist/skill.js ADDED
@@ -0,0 +1,342 @@
1
+ import { lstatSync, mkdirSync, readlinkSync, realpathSync, rmSync, symlinkSync, unlinkSync, writeFileSync, } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { dirname, relative as relativePath, resolve } from 'path';
4
+ import { createInterface } from 'readline/promises';
5
+ const SKILL_NAME = 'coaia-visualizer';
6
+ const COAIA_VISUALIZER_SKILL_MD = [
7
+ '---',
8
+ 'name: coaia-visualizer',
9
+ 'description: Use coaia-visualizer to launch, inspect, edit, and expose COAIA structural tension chart JSONL files through a web UI, REST API, and optional MCP bridge.',
10
+ 'license: MIT',
11
+ 'compatibility: Requires the coaia-visualizer package. Optional MCP bridge uses coaia-visualizer-mcp.',
12
+ 'metadata:',
13
+ ' author: Guillaume D. Isabelle',
14
+ ' version: "1.0.0"',
15
+ 'allowed-tools: Bash(coaia-visualizer:*), Bash(coaia-visualizer-mcp:*)',
16
+ '---',
17
+ '',
18
+ '# coaia-visualizer',
19
+ '',
20
+ '`coaia-visualizer` is the visual and API surface for COAIA Structural Tension Chart JSONL files. Use it when an LLM or human needs to inspect chart hierarchy, current reality, action-step telescoping, narrative beats, and chart progress through the web UI or the visualizer MCP bridge.',
21
+ '',
22
+ '## Status',
23
+ '',
24
+ '!`coaia-visualizer --help 2>/dev/null || echo "Not installed: npm install -g coaia-visualizer"`',
25
+ '',
26
+ '## Relationship To coaia-narrative',
27
+ '',
28
+ '- `coaia-narrative` is the primary chart-memory authoring MCP and CLI.',
29
+ '- `coaia-visualizer` reads the same JSONL format and presents it as an interactive UI.',
30
+ '- `coaia-visualizer-mcp` connects an LLM to a running visualizer REST API, not directly to the file.',
31
+ '- Prefer `coaia-narrative` MCP for pure memory creation. Prefer `coaia-visualizer` when the UI/API context matters.',
32
+ '',
33
+ '## Launch The UI',
34
+ '',
35
+ '```bash',
36
+ 'coaia-visualizer -M ./memory.jsonl --no-open',
37
+ 'coaia-visualizer --docker -M ./memory.jsonl --port 4321',
38
+ 'coaia-visualizer -M ./memory.jsonl --live --poll-interval 2000',
39
+ '```',
40
+ '',
41
+ 'The memory file must exist. The CLI sets `COAIAV_MEMORY_PATH` and starts the Next.js visualizer against that file.',
42
+ '',
43
+ '## MCP Bridge Setup',
44
+ '',
45
+ 'The visualizer MCP requires a running visualizer API and a token shared through `COAIAN_API_TOKEN`.',
46
+ '',
47
+ '```bash',
48
+ 'export COAIAN_API_TOKEN="$(openssl rand -hex 32)"',
49
+ 'coaia-visualizer -M ./memory.jsonl --port 4321 --no-open',
50
+ '```',
51
+ '',
52
+ 'Then configure the MCP client:',
53
+ '',
54
+ '```json',
55
+ '{',
56
+ ' "mcpServers": {',
57
+ ' "coaia-visualizer": {',
58
+ ' "command": "npx",',
59
+ ' "args": ["-y", "coaia-visualizer-mcp"],',
60
+ ' "env": {',
61
+ ' "COAIAN_API_TOKEN": "the-same-token",',
62
+ ' "COAIAV_API_URL": "http://localhost:4321"',
63
+ ' }',
64
+ ' }',
65
+ ' }',
66
+ '}',
67
+ '```',
68
+ '',
69
+ 'If no token is provided to the visualizer, it generates a temporary one and logs it on first API access. For repeatable agent work, set `COAIAN_API_TOKEN` explicitly.',
70
+ '',
71
+ '## Tool Map',
72
+ '',
73
+ '| MCP Tool | Use |',
74
+ '|---|---|',
75
+ '| `list_charts` | Start here; read chart hierarchy from the visualizer API |',
76
+ '| `get_chart` | Inspect one chart with outcome, reality, actions, metadata |',
77
+ '| `create_chart` | Create a chart through the visualizer API |',
78
+ '| `update_chart` | Update desired outcome, current reality, or due date |',
79
+ '| `add_action_step` | Add a new telescoped action chart with explicit current reality |',
80
+ '| `update_action_step` | Change an action description |',
81
+ '| `mark_action_complete` | Mark an action complete |',
82
+ '| `update_action_progress` | Add progress without completion |',
83
+ '| `update_current_reality` | Add current-reality observations |',
84
+ '| `toggle_action_completion` | Toggle action completion state |',
85
+ '| `delete_action_step` | Remove an action step |',
86
+ '| `create_telescoped_chart` | Convert an existing action into a sub-chart |',
87
+ '| `delete_chart` | Delete a chart and its descendants |',
88
+ '| `search_charts` | Search by text and filters |',
89
+ '| `get_chart_progress` | Calculate chart progress |',
90
+ '',
91
+ '## Structural-Tension Discipline',
92
+ '',
93
+ '- Do not treat charts as generic task boards.',
94
+ '- Do not default current reality to "ready to begin."',
95
+ '- Action steps are strategic secondary choices, and visualizer action creation should include an honest current reality.',
96
+ '- Use `create_telescoped_chart` only for an existing action. Use `add_action_step` for a new action.',
97
+ '',
98
+ '## References',
99
+ '',
100
+ '- `references/visualizer-workflow.md` for UI and CLI launch patterns.',
101
+ '- `references/mcp-bridge.md` for API/MCP wiring.',
102
+ '- `references/structural-tension-foundation.md` for shared methodology.',
103
+ '- `references/install-and-environment.md` for install and environment notes.',
104
+ ].join('\n') + '\n';
105
+ const VISUALIZER_WORKFLOW_MD = [
106
+ '# Visualizer Workflow',
107
+ '',
108
+ 'Use `coaia-visualizer` when the work benefits from seeing the chart hierarchy or when an agent needs REST/API access to the visualizer runtime.',
109
+ '',
110
+ 'Common launch forms:',
111
+ '',
112
+ '```bash',
113
+ 'coaia-visualizer -M ./memory.jsonl',
114
+ 'coaia-visualizer -M ./memory.jsonl --port 4321 --no-open',
115
+ 'coaia-visualizer --docker -M ./memory.jsonl --port 4321',
116
+ 'coaia-visualizer -M ./memory.jsonl --live --poll-interval 2000',
117
+ '```',
118
+ '',
119
+ 'The UI reads structural tension charts, current realities, action steps, narrative beats, and relations from JSONL. It can also edit charts through API routes when running as the app server.',
120
+ ].join('\n') + '\n';
121
+ const MCP_BRIDGE_MD = [
122
+ '# MCP Bridge',
123
+ '',
124
+ '`coaia-visualizer-mcp` is a stdio MCP server that talks to the visualizer REST API. It does not parse the JSONL file by itself.',
125
+ '',
126
+ 'Required runtime pieces:',
127
+ '',
128
+ '1. A running visualizer app.',
129
+ '2. `COAIAN_API_TOKEN` set in the visualizer process.',
130
+ '3. The same `COAIAN_API_TOKEN` set for the MCP process.',
131
+ '4. `COAIAV_API_URL` pointing at the running visualizer, usually `http://localhost:4321`.',
132
+ '',
133
+ 'MCP client config:',
134
+ '',
135
+ '```json',
136
+ '{',
137
+ ' "mcpServers": {',
138
+ ' "coaia-visualizer": {',
139
+ ' "command": "npx",',
140
+ ' "args": ["-y", "coaia-visualizer-mcp"],',
141
+ ' "env": {',
142
+ ' "COAIAN_API_TOKEN": "your-token",',
143
+ ' "COAIAV_API_URL": "http://localhost:4321"',
144
+ ' }',
145
+ ' }',
146
+ ' }',
147
+ '}',
148
+ '```',
149
+ '',
150
+ 'Use `list_charts` before mutation tools so the agent has current chart IDs and hierarchy.',
151
+ ].join('\n') + '\n';
152
+ const STRUCTURAL_TENSION_FOUNDATION_MD = [
153
+ '# Structural Tension Foundation',
154
+ '',
155
+ 'The visualizer presents the same methodology used by `coaia-narrative`.',
156
+ '',
157
+ 'Core rules:',
158
+ '',
159
+ '- Desired Outcome is what is being created.',
160
+ '- Current Reality is the factual present state in relation to that outcome.',
161
+ '- Structural tension is active disequilibrium, not a gap to fill.',
162
+ '- Action steps are strategic secondary choices, not independent to-do items.',
163
+ '- Each action step can become a telescoped chart with its own current reality.',
164
+ '',
165
+ 'Current reality must not be invented. Avoid "ready to begin", "prepared to start", or motivational filler. Use concrete present facts.',
166
+ ].join('\n') + '\n';
167
+ const INSTALL_AND_ENVIRONMENT_MD = [
168
+ '# Install And Environment',
169
+ '',
170
+ 'Install:',
171
+ '',
172
+ '```bash',
173
+ 'npm install -g coaia-visualizer',
174
+ 'coaia-visualizer skill show',
175
+ 'coaia-visualizer skill install --yes',
176
+ '```',
177
+ '',
178
+ 'Important environment variables:',
179
+ '',
180
+ '| Variable | Purpose |',
181
+ '|---|---|',
182
+ '| `COAIAN_MF` | Default memory file path |',
183
+ '| `COAIAV_PORT` | Default server port |',
184
+ '| `COAIAV_LIVE` | Enable live mode when `true` |',
185
+ '| `COAIAV_POLL_INTERVAL` | Live polling interval in milliseconds |',
186
+ '| `COAIAV_AUTO_PLAY` | Enable audio autoplay when `true` |',
187
+ '| `COAIAV_AUDIO_DIR` | Audio directory path |',
188
+ '| `COAIAN_API_TOKEN` | Bearer token for REST API and MCP bridge |',
189
+ '| `COAIAV_API_URL` | MCP bridge target API URL |',
190
+ '',
191
+ '`coaia-visualizer skill install` writes `.agents/skills/coaia-visualizer`. Add `--global` for `~/.agents/skills/coaia-visualizer`. Add `--yes` to create the matching `.claude/skills/coaia-visualizer` symlink.',
192
+ ].join('\n') + '\n';
193
+ const EMBEDDED_SKILL_FILES = [
194
+ { relativePath: 'SKILL.md', content: COAIA_VISUALIZER_SKILL_MD },
195
+ { relativePath: 'references/visualizer-workflow.md', content: VISUALIZER_WORKFLOW_MD },
196
+ { relativePath: 'references/mcp-bridge.md', content: MCP_BRIDGE_MD },
197
+ { relativePath: 'references/structural-tension-foundation.md', content: STRUCTURAL_TENSION_FOUNDATION_MD },
198
+ { relativePath: 'references/install-and-environment.md', content: INSTALL_AND_ENVIRONMENT_MD },
199
+ ];
200
+ function getSkillInstallDir(globalInstall) {
201
+ return globalInstall
202
+ ? resolve(homedir(), '.agents', 'skills', SKILL_NAME)
203
+ : resolve(process.cwd(), '.agents', 'skills', SKILL_NAME);
204
+ }
205
+ function getClaudeSkillLinkPath(globalInstall) {
206
+ return globalInstall
207
+ ? resolve(homedir(), '.claude', 'skills', SKILL_NAME)
208
+ : resolve(process.cwd(), '.claude', 'skills', SKILL_NAME);
209
+ }
210
+ function pathExists(pathName) {
211
+ try {
212
+ lstatSync(pathName);
213
+ return true;
214
+ }
215
+ catch {
216
+ return false;
217
+ }
218
+ }
219
+ function removePath(pathName) {
220
+ const stat = lstatSync(pathName);
221
+ if (stat.isDirectory() && !stat.isSymbolicLink()) {
222
+ rmSync(pathName, { recursive: true, force: true });
223
+ }
224
+ else {
225
+ unlinkSync(pathName);
226
+ }
227
+ }
228
+ function writeEmbeddedSkill(targetDir, force) {
229
+ if (pathExists(targetDir)) {
230
+ if (!force) {
231
+ throw new Error(`Skill already exists: ${targetDir} (use --force to replace it)`);
232
+ }
233
+ removePath(targetDir);
234
+ }
235
+ mkdirSync(targetDir, { recursive: true });
236
+ for (const file of EMBEDDED_SKILL_FILES) {
237
+ const destination = resolve(targetDir, file.relativePath);
238
+ mkdirSync(dirname(destination), { recursive: true });
239
+ writeFileSync(destination, file.content, 'utf-8');
240
+ }
241
+ }
242
+ function ensureClaudeSymlink(linkPath, targetDir, force) {
243
+ const parentDir = dirname(linkPath);
244
+ if (pathExists(parentDir)) {
245
+ const resolvedTargetParent = realpathSync(dirname(targetDir));
246
+ const resolvedLinkParent = realpathSync(parentDir);
247
+ if (resolvedTargetParent === resolvedLinkParent) {
248
+ return false;
249
+ }
250
+ }
251
+ const linkTarget = relativePath(parentDir, targetDir) || '.';
252
+ mkdirSync(parentDir, { recursive: true });
253
+ if (pathExists(linkPath)) {
254
+ const stat = lstatSync(linkPath);
255
+ if (stat.isSymbolicLink() && readlinkSync(linkPath) === linkTarget) {
256
+ return true;
257
+ }
258
+ if (!force) {
259
+ throw new Error(`Claude skill path already exists: ${linkPath} (use --force to replace it)`);
260
+ }
261
+ removePath(linkPath);
262
+ }
263
+ symlinkSync(linkTarget, linkPath, 'dir');
264
+ return true;
265
+ }
266
+ async function shouldCreateClaudeSymlink(linkPath, autoYes) {
267
+ if (autoYes) {
268
+ return true;
269
+ }
270
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
271
+ console.log(`Tip: create a Claude symlink manually at ${linkPath}`);
272
+ return false;
273
+ }
274
+ const rl = createInterface({
275
+ input: process.stdin,
276
+ output: process.stdout,
277
+ });
278
+ try {
279
+ const answer = await rl.question(`Create a symlink in ${linkPath}? [y/N] `);
280
+ const normalized = answer.trim().toLowerCase();
281
+ return normalized === 'y' || normalized === 'yes';
282
+ }
283
+ finally {
284
+ rl.close();
285
+ }
286
+ }
287
+ async function installSkill(globalInstall, force, autoYes) {
288
+ const installDir = getSkillInstallDir(globalInstall);
289
+ writeEmbeddedSkill(installDir, force);
290
+ console.log(`Installed coaia-visualizer skill to ${installDir}`);
291
+ const claudeLinkPath = getClaudeSkillLinkPath(globalInstall);
292
+ if (!(await shouldCreateClaudeSymlink(claudeLinkPath, autoYes))) {
293
+ return;
294
+ }
295
+ const linked = ensureClaudeSymlink(claudeLinkPath, installDir, force);
296
+ if (linked) {
297
+ console.log(`Linked Claude skill at ${claudeLinkPath}`);
298
+ }
299
+ else {
300
+ console.log(`Claude already sees the skill via ${dirname(claudeLinkPath)}`);
301
+ }
302
+ }
303
+ export function showCoaiaVisualizerSkill() {
304
+ console.log('coaia-visualizer Skill (embedded)');
305
+ console.log('');
306
+ process.stdout.write(COAIA_VISUALIZER_SKILL_MD);
307
+ }
308
+ export function showSkillCommandHelp() {
309
+ console.log([
310
+ 'Usage: coaia-visualizer skill <show|install|help> [options]',
311
+ '',
312
+ 'Commands:',
313
+ ' show Print the packaged coaia-visualizer skill',
314
+ ' install Install into ./.agents/skills/coaia-visualizer',
315
+ ' help Show this help',
316
+ '',
317
+ 'Options for install:',
318
+ ' --global Install into ~/.agents/skills/coaia-visualizer',
319
+ ' --yes, -y Also create the .claude/skills/coaia-visualizer symlink',
320
+ ' --force, -f Replace existing install or symlink',
321
+ ].join('\n'));
322
+ }
323
+ export async function handleSkillCommand(args) {
324
+ const subcommand = String(args._[1] || '');
325
+ if (args.help || args.h || subcommand === 'help') {
326
+ showSkillCommandHelp();
327
+ return;
328
+ }
329
+ if (!subcommand) {
330
+ showSkillCommandHelp();
331
+ return;
332
+ }
333
+ if (subcommand === 'show') {
334
+ showCoaiaVisualizerSkill();
335
+ return;
336
+ }
337
+ if (subcommand === 'install') {
338
+ await installSkill(Boolean(args.global), Boolean(args.force || args.f), Boolean(args.yes || args.y));
339
+ return;
340
+ }
341
+ throw new Error(`Unknown skill command: ${subcommand}`);
342
+ }
@@ -0,0 +1,69 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ # Next.js application server
5
+ visualizer-app:
6
+ build:
7
+ context: .
8
+ dockerfile: Dockerfile.app
9
+ ports:
10
+ - "4321:4321"
11
+ volumes:
12
+ - ./samples:/app/samples:ro
13
+ - ./test-data:/app/test-data
14
+ environment:
15
+ - NODE_ENV=production
16
+ - PORT=4321
17
+ networks:
18
+ - test-network
19
+ healthcheck:
20
+ test: ["CMD", "curl", "-f", "http://localhost:4321/api/charts"]
21
+ interval: 10s
22
+ timeout: 5s
23
+ retries: 5
24
+
25
+ # MCP Server
26
+ mcp-server:
27
+ build:
28
+ context: ./mcp
29
+ dockerfile: Dockerfile
30
+ environment:
31
+ - VISUALIZER_API_URL=http://visualizer-app:4321
32
+ - VISUALIZER_API_KEY=${VISUALIZER_API_KEY:-test-api-key}
33
+ - NODE_ENV=production
34
+ volumes:
35
+ - ./test-data:/data
36
+ networks:
37
+ - test-network
38
+ depends_on:
39
+ visualizer-app:
40
+ condition: service_healthy
41
+ stdin_open: true
42
+ tty: true
43
+
44
+ # Test runner using claude-code CLI or gemini-cli
45
+ test-runner:
46
+ build:
47
+ context: .
48
+ dockerfile: Dockerfile.test
49
+ environment:
50
+ - MCP_SERVER_HOST=mcp-server
51
+ - VISUALIZER_API_URL=http://visualizer-app:4321
52
+ - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
53
+ - GOOGLE_API_KEY=${GOOGLE_API_KEY}
54
+ volumes:
55
+ - ./test-scripts:/test-scripts:ro
56
+ - ./test-results:/test-results
57
+ - ./test-data:/test-data
58
+ networks:
59
+ - test-network
60
+ depends_on:
61
+ visualizer-app:
62
+ condition: service_healthy
63
+ mcp-server:
64
+ condition: service_started
65
+ command: ["/test-scripts/run-all-tests.sh"]
66
+
67
+ networks:
68
+ test-network:
69
+ driver: bridge
package/index.tsx CHANGED
@@ -13,31 +13,6 @@ 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
-
41
16
  // Utilities
42
17
  export { cn } from "./lib/utils"
43
18
 
@@ -53,7 +28,6 @@ export { ChartDetail } from "./components/chart-detail"
53
28
  export { DataStats } from "./components/data-stats"
54
29
  export { CreateChartForm } from "./components/create-chart-form"
55
30
  export { NarrativeBeats } from "./components/narrative-beats"
56
- export { ActionStepIssueBadge, ProjectSourceBadge, SyncStatePill } from "./components/github-provenance"
57
31
  export { LiveIndicator } from "./components/live-indicator"
58
32
  export { ThemeToggle } from "./components/theme-toggle"
59
33
  export { EditDesiredOutcome } from "./components/edit-desired-outcome"