claude-autopm 3.22.2 → 3.23.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.
@@ -429,6 +429,23 @@ function renderHTML() {
429
429
  .panzoom-hint {
430
430
  position: absolute; bottom: 8px; right: 12px; color: #484f58; font-size: 10px; pointer-events: none; z-index: 1;
431
431
  }
432
+ /* Diagram card actions */
433
+ .diagram-grid-item .card-actions {
434
+ display: flex; gap: 6px; margin-top: 8px; border-top: 1px solid #21262d; padding-top: 8px;
435
+ }
436
+ .diagram-grid-item .card-actions button {
437
+ background: #21262d; color: #8b949e; border: 1px solid #30363d;
438
+ padding: 3px 10px; border-radius: 4px; cursor: pointer; font-size: 11px;
439
+ }
440
+ .diagram-grid-item .card-actions button:hover { background: #30363d; color: #c9d1d9; }
441
+ .diagram-grid-item .card-actions .btn-remove { color: #f85149; }
442
+ .diagram-grid-item .card-actions .btn-remove:hover { background: #3d1116; border-color: #f85149; }
443
+ /* Fullscreen editor */
444
+ .diagram-editor.fullscreen {
445
+ position: fixed; inset: 0; z-index: 998; background: #0d1117;
446
+ padding: 12px; display: flex; flex-direction: column;
447
+ }
448
+ .diagram-editor.fullscreen .diagram-split { min-height: 0; flex: 1; }
432
449
  </style>
433
450
  </head>
434
451
  <body>
@@ -562,7 +579,27 @@ function renderHTML() {
562
579
  <!-- Diagram list view -->
563
580
  <div id="diagram-list-view">
564
581
  <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
565
- <p class="desc" style="margin:0;">Click a diagram to view and edit. Create new: <code>/pm:diagram-new &lt;name&gt;</code></p>
582
+ <p class="desc" style="margin:0;">Project diagrams stored in <code>.claude/pm/diagrams/</code></p>
583
+ <button class="btn" onclick="showNewDiagramForm()">+ New Diagram</button>
584
+ </div>
585
+ <!-- New diagram form (hidden by default) -->
586
+ <div id="new-diagram-form" class="card" style="display:none;margin-bottom:16px;">
587
+ <div class="row">
588
+ <div><label>Name (slug)</label><input type="text" id="new-diagram-name" placeholder="e.g. frontend, backend, database"></div>
589
+ <div><label>Type</label><select id="new-diagram-type">
590
+ <option value="architecture">architecture</option>
591
+ <option value="modules">modules</option>
592
+ <option value="data-flow">data-flow</option>
593
+ <option value="dependencies">dependencies</option>
594
+ <option value="custom">custom</option>
595
+ </select></div>
596
+ </div>
597
+ <label>Description</label>
598
+ <input type="text" id="new-diagram-desc" placeholder="e.g. Frontend React component tree">
599
+ <div style="margin-top:12px;">
600
+ <button class="btn" onclick="createNewDiagram()">Create</button>
601
+ <button class="btn" style="background:#21262d;margin-left:6px;" onclick="hideNewDiagramForm()">Cancel</button>
602
+ </div>
566
603
  </div>
567
604
  <div id="diagram-grid" class="diagram-grid"></div>
568
605
  </div>
@@ -572,15 +609,15 @@ function renderHTML() {
572
609
  <div class="left">
573
610
  <button class="btn-back" onclick="closeDiagramEditor()">← Back</button>
574
611
  <strong id="editor-diagram-name" style="color:#c9d1d9;font-size:14px;"></strong>
575
- <span id="editor-diagram-meta" style="color:#8b949e;font-size:11px;"></span>
576
612
  <span id="editor-unsaved" style="color:#d29922;font-size:11px;display:none;">● unsaved</span>
577
613
  </div>
578
614
  <div class="right">
579
615
  <button onclick="downloadCurrentDiagram()">↓ .mmd</button>
616
+ <button onclick="toggleEditorFullscreen()">⤢ Fullscreen</button>
580
617
  <button class="btn-save" onclick="saveDiagram()">Save</button>
581
618
  </div>
582
619
  </div>
583
- <div class="diagram-split">
620
+ <div class="diagram-split" id="diagram-split">
584
621
  <div class="code-pane">
585
622
  <div class="pane-header"><span>Mermaid Source</span></div>
586
623
  <textarea id="diagram-code" spellcheck="false"></textarea>
@@ -654,16 +691,10 @@ function renderDiagramsTab() {
654
691
  fetch('/api/diagrams', { headers: { 'Authorization': 'Bearer ' + TOKEN } })
655
692
  .then(r => r.json())
656
693
  .then(data => {
657
- // Merge system-generated + project diagrams into one list
658
694
  allDiagrams = [];
659
- if (data.epicFlowHasData) {
660
- allDiagrams.push({ name: 'epic-flow', content: data.epicFlow, type: 'system', meta: 'PRD → Epic → Tasks' });
661
- }
662
- allDiagrams.push({ name: 'plugins', content: data.pluginGraph, type: 'system', meta: 'Plugin dependencies' });
663
- allDiagrams.push({ name: 'agents', content: data.agentTree, type: 'system', meta: 'Agent selection tree' });
664
695
  if (data.projectDiagrams) {
665
696
  data.projectDiagrams.forEach(d => {
666
- allDiagrams.push({ name: d.name, content: d.content, type: 'project', meta: d.type || 'custom' });
697
+ allDiagrams.push({ name: d.name, content: d.content, type: d.type || 'custom' });
667
698
  });
668
699
  }
669
700
  renderDiagramGrid();
@@ -675,49 +706,112 @@ function renderDiagramGrid() {
675
706
  const grid = document.getElementById('diagram-grid');
676
707
  grid.textContent = '';
677
708
  if (allDiagrams.length === 0) {
678
- grid.innerHTML = '<p class="desc">No diagrams yet. Run <code>/pm:diagram-new &lt;name&gt;</code> to create one.</p>';
709
+ const msg = document.createElement('p');
710
+ msg.className = 'desc';
711
+ msg.textContent = 'No diagrams yet. Click "+ New Diagram" or run /pm:diagram-new <name>.';
712
+ grid.appendChild(msg);
679
713
  return;
680
714
  }
681
715
  allDiagrams.forEach(d => {
682
716
  const item = document.createElement('div');
683
717
  item.className = 'diagram-grid-item';
684
- item.setAttribute('role', 'button');
685
718
  item.setAttribute('tabindex', '0');
686
- item.onclick = function() { openDiagramEditor(d.name); };
687
- item.onkeydown = function(e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openDiagramEditor(d.name); } };
688
719
  const h4 = document.createElement('h4');
689
720
  h4.textContent = d.name;
690
721
  const meta = document.createElement('div');
691
722
  meta.className = 'diagram-meta';
692
- meta.textContent = d.type === 'system' ? '⚙ ' + d.meta : '📄 ' + d.meta;
723
+ meta.textContent = d.type;
693
724
  const preview = document.createElement('div');
694
725
  preview.className = 'diagram-preview';
695
726
  const pre = document.createElement('pre');
696
727
  pre.className = 'mermaid';
697
728
  pre.textContent = d.content;
698
729
  preview.appendChild(pre);
730
+ // Action buttons
731
+ const actions = document.createElement('div');
732
+ actions.className = 'card-actions';
733
+ const editBtn = document.createElement('button');
734
+ editBtn.textContent = 'Edit';
735
+ editBtn.onclick = function(e) { e.stopPropagation(); openDiagramEditor(d.name); };
736
+ const dlBtn = document.createElement('button');
737
+ dlBtn.textContent = '↓ .mmd';
738
+ dlBtn.onclick = function(e) { e.stopPropagation(); downloadDiagram(d.name, d.content); };
739
+ const rmBtn = document.createElement('button');
740
+ rmBtn.textContent = 'Remove';
741
+ rmBtn.className = 'btn-remove';
742
+ rmBtn.onclick = function(e) { e.stopPropagation(); removeDiagram(d.name); };
743
+ actions.appendChild(editBtn);
744
+ actions.appendChild(dlBtn);
745
+ actions.appendChild(rmBtn);
699
746
  item.appendChild(h4);
700
747
  item.appendChild(meta);
701
748
  item.appendChild(preview);
749
+ item.appendChild(actions);
750
+ // Click card body (not actions) opens editor
751
+ item.onclick = function(e) { if (!e.target.closest('.card-actions')) openDiagramEditor(d.name); };
752
+ item.onkeydown = function(e) {
753
+ if (e.target && e.target.closest('.card-actions')) return;
754
+ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openDiagramEditor(d.name); }
755
+ };
702
756
  grid.appendChild(item);
703
757
  });
704
- // Render mini previews
705
758
  if (typeof mermaid !== 'undefined' && mermaid.run) {
706
759
  mermaid.run({ nodes: grid.querySelectorAll('.mermaid') });
707
760
  }
708
761
  }
709
762
 
763
+ // New diagram form
764
+ function showNewDiagramForm() { document.getElementById('new-diagram-form').style.display = ''; }
765
+ function hideNewDiagramForm() { document.getElementById('new-diagram-form').style.display = 'none'; }
766
+
767
+ async function createNewDiagram() {
768
+ const name = document.getElementById('new-diagram-name').value.trim().replace(/[^a-zA-Z0-9_-]/g, '-');
769
+ if (!name) { toast('Name is required', false); return; }
770
+ if (allDiagrams.find(d => d.name === name)) { toast('Diagram "' + name + '" already exists', false); return; }
771
+ const type = document.getElementById('new-diagram-type').value;
772
+ const desc = document.getElementById('new-diagram-desc').value.trim();
773
+ const template = 'graph TD\\n A[' + esc(desc || name) + ']';
774
+ try {
775
+ await api('POST', '/api/diagrams/' + encodeURIComponent(name), { content: template, type: type, description: desc });
776
+ hideNewDiagramForm();
777
+ document.getElementById('new-diagram-name').value = '';
778
+ document.getElementById('new-diagram-desc').value = '';
779
+ diagramsLoaded = false;
780
+ renderDiagramsTab();
781
+ toast('Diagram "' + name + '" created', true);
782
+ } catch (e) { /* api() already shows toast */ }
783
+ }
784
+
785
+ async function removeDiagram(name) {
786
+ if (!confirm('Remove diagram "' + name + '"?')) return;
787
+ try {
788
+ await api('DELETE', '/api/diagrams/' + encodeURIComponent(name));
789
+ allDiagrams = allDiagrams.filter(d => d.name !== name);
790
+ renderDiagramGrid();
791
+ toast('Diagram removed', true);
792
+ } catch (e) { /* api() already shows toast */ }
793
+ }
794
+
795
+ function downloadDiagram(name, content) {
796
+ const safeName = name.replace(/[^a-zA-Z0-9_-]/g, '_') + '.mmd';
797
+ const blob = new Blob([content], { type: 'text/plain' });
798
+ const a = document.createElement('a');
799
+ const url = URL.createObjectURL(blob);
800
+ a.href = url; a.download = safeName; a.style.display = 'none';
801
+ document.body.appendChild(a);
802
+ try { a.click(); } finally { document.body.removeChild(a); setTimeout(function() { URL.revokeObjectURL(url); }, 100); }
803
+ }
804
+
710
805
  function openDiagramEditor(name) {
711
806
  const d = allDiagrams.find(x => x.name === name);
712
807
  if (!d) return;
713
808
  currentDiagramName = name;
714
809
  currentDiagramSaved = d.content;
715
810
  document.getElementById('editor-diagram-name').textContent = name;
716
- document.getElementById('editor-diagram-meta').textContent = d.type === 'system' ? '(system — read-only)' : '';
717
811
  document.getElementById('editor-unsaved').style.display = 'none';
718
812
  const codeEl = document.getElementById('diagram-code');
719
813
  codeEl.value = d.content;
720
- codeEl.readOnly = d.type === 'system';
814
+ codeEl.readOnly = false;
721
815
  document.getElementById('diagram-list-view').style.display = 'none';
722
816
  document.getElementById('diagram-editor-view').classList.add('active');
723
817
  renderPreview(d.content);
@@ -726,11 +820,19 @@ function openDiagramEditor(name) {
726
820
  function closeDiagramEditor() {
727
821
  clearTimeout(previewDebounce); previewDebounce = null;
728
822
  if (panzoomInstance) { panzoomInstance.dispose(); panzoomInstance = null; }
729
- document.getElementById('diagram-editor-view').classList.remove('active');
823
+ const editor = document.getElementById('diagram-editor-view');
824
+ editor.classList.remove('active', 'fullscreen');
730
825
  document.getElementById('diagram-list-view').style.display = '';
731
826
  currentDiagramName = null;
732
827
  }
733
828
 
829
+ function toggleEditorFullscreen() {
830
+ document.getElementById('diagram-editor-view').classList.toggle('fullscreen');
831
+ // Re-render preview to fit new size
832
+ if (panzoomInstance) { panzoomInstance.dispose(); panzoomInstance = null; }
833
+ setTimeout(function() { renderPreview(document.getElementById('diagram-code').value); }, 100);
834
+ }
835
+
734
836
  function renderPreview(source) {
735
837
  const container = document.getElementById('diagram-preview-render');
736
838
  container.textContent = '';
@@ -776,7 +878,6 @@ document.addEventListener('DOMContentLoaded', function() {
776
878
  async function saveDiagram() {
777
879
  if (!currentDiagramName) return;
778
880
  const d = allDiagrams.find(x => x.name === currentDiagramName);
779
- if (d && d.type === 'system') { toast('System diagrams are read-only', false); return; }
780
881
  const content = document.getElementById('diagram-code').value;
781
882
  try {
782
883
  await api('POST', '/api/diagrams/' + encodeURIComponent(currentDiagramName), { content: content });
@@ -787,7 +888,7 @@ async function saveDiagram() {
787
888
  diagramsLoaded = false;
788
889
  toast('Diagram saved', true);
789
890
  } catch (e) {
790
- toast('Save failed: ' + e.message, false);
891
+ /* api() already shows toast */
791
892
  }
792
893
  }
793
894
 
@@ -1273,6 +1374,8 @@ const server = http.createServer(async (req, res) => {
1273
1374
  let meta = {};
1274
1375
  try { meta = JSON.parse(fs.readFileSync(metaPath, 'utf8')); } catch {}
1275
1376
  meta.name = meta.name || name;
1377
+ if (typeof body.type === 'string') meta.type = body.type.slice(0, 50);
1378
+ if (typeof body.description === 'string') meta.description = body.description.slice(0, 500);
1276
1379
  meta.updated = new Date().toISOString().replace(/\\.\\d{3}Z$/, 'Z');
1277
1380
  if (!meta.created) meta.created = meta.updated;
1278
1381
  fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2), 'utf8');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-autopm",
3
- "version": "3.22.2",
3
+ "version": "3.23.0",
4
4
  "description": "Autonomous Project Management Framework for Claude Code - Advanced AI-powered development automation",
5
5
  "main": "bin/autopm.js",
6
6
  "workspaces": [