claude-autopm 3.22.0 → 3.22.1

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.
@@ -132,8 +132,7 @@ function generateEpicFlowDiagram() {
132
132
  }
133
133
  } catch {}
134
134
  if (epics.length === 0 && prds.length === 0) {
135
- lines.push(' N[No epics or PRDs found]');
136
- return lines.join('\n');
135
+ return { mermaid: '', hasData: false };
137
136
  }
138
137
  let nodeId = 0;
139
138
  for (const prd of prds) {
@@ -159,7 +158,7 @@ function generateEpicFlowDiagram() {
159
158
  lines.push(` ${eid} --> ${tid}["${t.name} ${t.icon}"]`);
160
159
  }
161
160
  }
162
- return lines.join('\n');
161
+ return { mermaid: lines.join('\n'), hasData: true };
163
162
  }
164
163
 
165
164
  function generatePluginGraph() {
@@ -263,8 +262,10 @@ function getProjectDiagrams() {
263
262
  }
264
263
 
265
264
  function getDiagramsData() {
265
+ const epic = generateEpicFlowDiagram();
266
266
  return {
267
- epicFlow: generateEpicFlowDiagram(),
267
+ epicFlow: epic.mermaid,
268
+ epicFlowHasData: epic.hasData,
268
269
  projectDiagrams: getProjectDiagrams(),
269
270
  pluginGraph: generatePluginGraph(),
270
271
  agentTree: generateAgentTree()
@@ -364,6 +365,42 @@ function renderHTML() {
364
365
  .toast.ok { background: #238636; }
365
366
  .toast.err { background: #da3633; }
366
367
  footer { margin-top: 24px; text-align: center; color: #484f58; font-size: 12px; }
368
+ /* Diagram cards */
369
+ .diagram-card { position: relative; }
370
+ .diagram-card .diagram-toolbar { display: flex; gap: 6px; position: absolute; top: 12px; right: 12px; z-index: 2; }
371
+ .diagram-card .diagram-toolbar button {
372
+ background: #21262d; color: #8b949e; border: 1px solid #30363d;
373
+ padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 12px;
374
+ }
375
+ .diagram-card .diagram-toolbar button:hover { background: #30363d; color: #c9d1d9; }
376
+ .diagram-card .mermaid-wrap { overflow: auto; max-height: 400px; cursor: pointer; }
377
+ .diagram-card .mermaid-wrap:hover { outline: 1px solid #30363d; border-radius: 4px; }
378
+ /* Fullscreen modal */
379
+ .diagram-modal {
380
+ display: none; position: fixed; inset: 0; z-index: 1000;
381
+ background: rgba(0,0,0,0.85); justify-content: center; align-items: center;
382
+ }
383
+ .diagram-modal.open { display: flex; }
384
+ .diagram-modal .modal-inner {
385
+ background: #161b22; border: 1px solid #30363d; border-radius: 12px;
386
+ width: 95vw; height: 92vh; display: flex; flex-direction: column; position: relative;
387
+ }
388
+ .diagram-modal .modal-header {
389
+ display: flex; justify-content: space-between; align-items: center;
390
+ padding: 12px 20px; border-bottom: 1px solid #30363d;
391
+ }
392
+ .diagram-modal .modal-header h3 { color: #c9d1d9; font-size: 14px; }
393
+ .diagram-modal .modal-header .modal-actions { display: flex; gap: 8px; }
394
+ .diagram-modal .modal-header button {
395
+ background: #21262d; color: #8b949e; border: 1px solid #30363d;
396
+ padding: 6px 14px; border-radius: 4px; cursor: pointer; font-size: 12px;
397
+ }
398
+ .diagram-modal .modal-header button:hover { background: #30363d; color: #c9d1d9; }
399
+ .diagram-modal .modal-body {
400
+ flex: 1; overflow: auto; padding: 20px; display: flex;
401
+ justify-content: center; align-items: flex-start;
402
+ }
403
+ .diagram-modal .modal-body svg { max-width: 100%; height: auto; }
367
404
  </style>
368
405
  </head>
369
406
  <body>
@@ -494,15 +531,33 @@ function renderHTML() {
494
531
 
495
532
  <!-- Diagrams Tab -->
496
533
  <div id="tab-diagrams" class="tab-content">
497
- <div class="grid" style="grid-template-columns: 1fr 1fr;">
498
- <div class="card">
499
- <h3>Epic Flow</h3>
534
+ <div id="diagram-epic-card" class="card diagram-card" style="display:none;">
535
+ <h3>Epic Flow <span style="color:#8b949e;font-size:11px;font-weight:normal;margin-left:8px;">PRD → Epic → Tasks workflow</span></h3>
536
+ <div class="diagram-toolbar">
537
+ <button onclick="downloadMermaid('diagram-epic')">↓ .mmd</button>
538
+ <button onclick="openDiagramModal('diagram-epic','Epic Flow')">⤢ Fullscreen</button>
539
+ </div>
540
+ <div class="mermaid-wrap" onclick="openDiagramModal('diagram-epic','Epic Flow')">
500
541
  <pre class="mermaid" id="diagram-epic"></pre>
501
542
  </div>
502
543
  </div>
503
544
  <div id="project-diagrams"></div>
504
545
  </div>
505
546
 
547
+ <!-- Diagram fullscreen modal -->
548
+ <div class="diagram-modal" id="diagram-modal" role="dialog" aria-modal="true" aria-labelledby="modal-diagram-title" onclick="if(event.target===this)closeDiagramModal()">
549
+ <div class="modal-inner">
550
+ <div class="modal-header">
551
+ <h3 id="modal-diagram-title"></h3>
552
+ <div class="modal-actions">
553
+ <button onclick="downloadMermaid(currentModalDiagram)">↓ Download .mmd</button>
554
+ <button onclick="closeDiagramModal()">✕ Close</button>
555
+ </div>
556
+ </div>
557
+ <div class="modal-body" id="modal-diagram-body"></div>
558
+ </div>
559
+ </div>
560
+
506
561
  <!-- Tests Tab -->
507
562
  <div id="tab-tests" class="tab-content">
508
563
  <div class="card">
@@ -542,26 +597,68 @@ function showTab(name, btn) {
542
597
  }
543
598
 
544
599
  let diagramsRendered = false;
600
+ const diagramSources = {};
601
+ const diagramNames = {};
602
+ let currentModalDiagram = null;
603
+
545
604
  function renderMermaid() {
546
605
  if (diagramsRendered) return;
547
606
  fetch('/api/diagrams', { headers: { 'Authorization': 'Bearer ' + TOKEN } })
548
607
  .then(r => r.json())
549
608
  .then(data => {
550
- document.getElementById('diagram-epic').textContent = data.epicFlow;
609
+ // Epic Flow — only show if there are actual epics/PRDs
610
+ const epicCard = document.getElementById('diagram-epic-card');
611
+ const epicEl = document.getElementById('diagram-epic');
612
+ if (data.epicFlowHasData) {
613
+ epicCard.style.display = '';
614
+ epicEl.className = 'mermaid';
615
+ epicEl.textContent = data.epicFlow;
616
+ diagramSources['diagram-epic'] = data.epicFlow;
617
+ diagramNames['diagram-epic'] = 'epic-flow';
618
+ } else {
619
+ epicCard.style.display = 'none';
620
+ epicEl.className = '';
621
+ epicEl.textContent = '';
622
+ }
551
623
  document.getElementById('diagram-plugins').textContent = data.pluginGraph;
552
624
  document.getElementById('diagram-agents').textContent = data.agentTree;
625
+ diagramSources['diagram-plugins'] = data.pluginGraph;
626
+ diagramSources['diagram-agents'] = data.agentTree;
627
+ diagramNames['diagram-plugins'] = 'plugins';
628
+ diagramNames['diagram-agents'] = 'agents';
553
629
  // Render project diagrams dynamically
554
630
  const container = document.getElementById('project-diagrams');
555
631
  container.innerHTML = '';
556
632
  if (data.projectDiagrams && data.projectDiagrams.length > 0) {
557
- data.projectDiagrams.forEach(d => {
633
+ data.projectDiagrams.forEach((d, i) => {
634
+ const id = 'proj-diagram-' + i;
635
+ diagramSources[id] = d.content;
636
+ diagramNames[id] = d.name;
558
637
  const card = document.createElement('div');
559
- card.className = 'card';
560
- card.innerHTML = '<h3>' + esc(d.name) + '</h3>';
638
+ card.className = 'card diagram-card';
639
+ const h3 = document.createElement('h3');
640
+ h3.textContent = d.name;
641
+ card.appendChild(h3);
642
+ const toolbar = document.createElement('div');
643
+ toolbar.className = 'diagram-toolbar';
644
+ const dlBtn = document.createElement('button');
645
+ dlBtn.textContent = '↓ .mmd';
646
+ dlBtn.onclick = function(e) { e.stopPropagation(); downloadMermaid(id); };
647
+ const fsBtn = document.createElement('button');
648
+ fsBtn.textContent = '⤢ Fullscreen';
649
+ fsBtn.onclick = function(e) { e.stopPropagation(); openDiagramModal(id, d.name); };
650
+ toolbar.appendChild(dlBtn);
651
+ toolbar.appendChild(fsBtn);
652
+ card.appendChild(toolbar);
653
+ const wrap = document.createElement('div');
654
+ wrap.className = 'mermaid-wrap';
655
+ wrap.onclick = function() { openDiagramModal(id, d.name); };
561
656
  const pre = document.createElement('pre');
562
657
  pre.className = 'mermaid';
658
+ pre.id = id;
563
659
  pre.textContent = d.content;
564
- card.appendChild(pre);
660
+ wrap.appendChild(pre);
661
+ card.appendChild(wrap);
565
662
  container.appendChild(card);
566
663
  });
567
664
  } else {
@@ -586,6 +683,59 @@ function renderMermaid() {
586
683
  });
587
684
  }
588
685
 
686
+ function openDiagramModal(diagramId, title) {
687
+ currentModalDiagram = diagramId;
688
+ document.getElementById('modal-diagram-title').textContent = title || diagramId;
689
+ const body = document.getElementById('modal-diagram-body');
690
+ body.textContent = '';
691
+ const source = diagramSources[diagramId];
692
+ if (source) {
693
+ const pre = document.createElement('pre');
694
+ pre.className = 'mermaid';
695
+ pre.textContent = source;
696
+ body.appendChild(pre);
697
+ if (typeof mermaid !== 'undefined' && mermaid.run) {
698
+ mermaid.run({ nodes: body.querySelectorAll('.mermaid') });
699
+ }
700
+ } else {
701
+ body.textContent = 'Diagram not available.';
702
+ }
703
+ const modal = document.getElementById('diagram-modal');
704
+ modal.classList.add('open');
705
+ modalPrevFocus = document.activeElement;
706
+ const closeBtn = modal.querySelector('.modal-actions button:last-child');
707
+ if (closeBtn) closeBtn.focus();
708
+ document.addEventListener('keydown', modalEscHandler);
709
+ }
710
+
711
+ let modalPrevFocus = null;
712
+ function closeDiagramModal() {
713
+ document.getElementById('diagram-modal').classList.remove('open');
714
+ document.removeEventListener('keydown', modalEscHandler);
715
+ currentModalDiagram = null;
716
+ if (modalPrevFocus) { modalPrevFocus.focus(); modalPrevFocus = null; }
717
+ }
718
+
719
+ function modalEscHandler(e) { if (e.key === 'Escape') closeDiagramModal(); }
720
+
721
+ function downloadMermaid(diagramId) {
722
+ const src = diagramSources[diagramId];
723
+ if (!src) return;
724
+ const label = diagramNames[diagramId] || diagramId.replace('proj-diagram-', 'diagram-').replace('diagram-', '');
725
+ const safeName = label.replace(/[^a-zA-Z0-9_-]/g, '_') + '.mmd';
726
+ const blob = new Blob([src], { type: 'text/plain' });
727
+ const a = document.createElement('a');
728
+ const url = URL.createObjectURL(blob);
729
+ a.href = url;
730
+ a.download = safeName;
731
+ a.style.display = 'none';
732
+ document.body.appendChild(a);
733
+ try { a.click(); } finally {
734
+ document.body.removeChild(a);
735
+ setTimeout(function() { URL.revokeObjectURL(url); }, 100);
736
+ }
737
+ }
738
+
589
739
  function renderTestPlan(md) {
590
740
  if (!md) { document.getElementById('test-plan-content').textContent = 'No test plan found. Run /pm:test-plan to generate.'; return; }
591
741
  const lines = md.split('\\n');
@@ -963,7 +1113,22 @@ function addEnvSuggestion(key, val, hint) {
963
1113
  addEnvRow(key, val);
964
1114
  }
965
1115
 
1116
+ // Pause auto-refresh while user is editing MCP or env forms
1117
+ let editingLock = false;
1118
+ document.addEventListener('focusin', function(e) {
1119
+ if (e.target.closest('#mcp-list, #env-list, .mcp-entry, .env-row')) editingLock = true;
1120
+ });
1121
+ document.addEventListener('focusout', function(e) {
1122
+ if (e.target.closest('#mcp-list, #env-list, .mcp-entry, .env-row')) {
1123
+ setTimeout(function() {
1124
+ const active = document.activeElement;
1125
+ if (!active || !active.closest('#mcp-list, #env-list, .mcp-entry, .env-row')) editingLock = false;
1126
+ }, 200);
1127
+ }
1128
+ });
1129
+
966
1130
  async function refresh() {
1131
+ if (editingLock) return;
967
1132
  try {
968
1133
  const data = await api('GET', '/api/status');
969
1134
  loadStatus(data);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-autopm",
3
- "version": "3.22.0",
3
+ "version": "3.22.1",
4
4
  "description": "Autonomous Project Management Framework for Claude Code - Advanced AI-powered development automation",
5
5
  "main": "bin/autopm.js",
6
6
  "workspaces": [