claude-code-workflow 6.3.27 → 6.3.28

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 (121) hide show
  1. package/.claude/CLAUDE.md +7 -1
  2. package/.claude/agents/action-planning-agent.md +1 -0
  3. package/.claude/agents/cli-discuss-agent.md +391 -0
  4. package/.claude/agents/cli-execution-agent.md +2 -0
  5. package/.claude/agents/cli-explore-agent.md +2 -1
  6. package/.claude/agents/cli-lite-planning-agent.md +1 -0
  7. package/.claude/agents/cli-planning-agent.md +1 -0
  8. package/.claude/agents/code-developer.md +1 -0
  9. package/.claude/agents/conceptual-planning-agent.md +2 -0
  10. package/.claude/agents/context-search-agent.md +1 -0
  11. package/.claude/agents/debug-explore-agent.md +2 -0
  12. package/.claude/agents/doc-generator.md +1 -0
  13. package/.claude/agents/issue-plan-agent.md +2 -1
  14. package/.claude/agents/issue-queue-agent.md +2 -1
  15. package/.claude/agents/memory-bridge.md +2 -0
  16. package/.claude/agents/test-context-search-agent.md +2 -0
  17. package/.claude/agents/test-fix-agent.md +1 -0
  18. package/.claude/agents/ui-design-agent.md +2 -0
  19. package/.claude/agents/universal-executor.md +1 -0
  20. package/.claude/commands/issue/execute.md +141 -163
  21. package/.claude/commands/workflow/lite-lite-lite.md +798 -0
  22. package/.claude/commands/workflow/multi-cli-plan.md +510 -0
  23. package/.claude/skills/ccw/SKILL.md +262 -372
  24. package/.claude/skills/ccw/command.json +547 -0
  25. package/.claude/skills/ccw-help/SKILL.md +46 -107
  26. package/.claude/skills/ccw-help/command.json +511 -0
  27. package/.claude/skills/skill-tuning/SKILL.md +303 -0
  28. package/.claude/skills/skill-tuning/phases/actions/action-abort.md +164 -0
  29. package/.claude/skills/skill-tuning/phases/actions/action-analyze-requirements.md +406 -0
  30. package/.claude/skills/skill-tuning/phases/actions/action-apply-fix.md +206 -0
  31. package/.claude/skills/skill-tuning/phases/actions/action-complete.md +195 -0
  32. package/.claude/skills/skill-tuning/phases/actions/action-diagnose-agent.md +317 -0
  33. package/.claude/skills/skill-tuning/phases/actions/action-diagnose-context.md +243 -0
  34. package/.claude/skills/skill-tuning/phases/actions/action-diagnose-dataflow.md +318 -0
  35. package/.claude/skills/skill-tuning/phases/actions/action-diagnose-docs.md +299 -0
  36. package/.claude/skills/skill-tuning/phases/actions/action-diagnose-memory.md +269 -0
  37. package/.claude/skills/skill-tuning/phases/actions/action-diagnose-token-consumption.md +200 -0
  38. package/.claude/skills/skill-tuning/phases/actions/action-gemini-analysis.md +322 -0
  39. package/.claude/skills/skill-tuning/phases/actions/action-generate-report.md +228 -0
  40. package/.claude/skills/skill-tuning/phases/actions/action-init.md +149 -0
  41. package/.claude/skills/skill-tuning/phases/actions/action-propose-fixes.md +317 -0
  42. package/.claude/skills/skill-tuning/phases/actions/action-verify.md +222 -0
  43. package/.claude/skills/skill-tuning/phases/orchestrator.md +377 -0
  44. package/.claude/skills/skill-tuning/phases/state-schema.md +378 -0
  45. package/.claude/skills/skill-tuning/specs/category-mappings.json +284 -0
  46. package/.claude/skills/skill-tuning/specs/dimension-mapping.md +212 -0
  47. package/.claude/skills/skill-tuning/specs/problem-taxonomy.md +318 -0
  48. package/.claude/skills/skill-tuning/specs/quality-gates.md +263 -0
  49. package/.claude/skills/skill-tuning/specs/skill-authoring-principles.md +189 -0
  50. package/.claude/skills/skill-tuning/specs/tuning-strategies.md +1537 -0
  51. package/.claude/skills/skill-tuning/templates/diagnosis-report.md +153 -0
  52. package/.claude/skills/skill-tuning/templates/fix-proposal.md +204 -0
  53. package/.claude/workflows/cli-templates/schemas/multi-cli-discussion-schema.json +421 -0
  54. package/.claude/workflows/cli-tools-usage.md +0 -41
  55. package/ccw/dist/core/data-aggregator.d.ts +2 -0
  56. package/ccw/dist/core/data-aggregator.d.ts.map +1 -1
  57. package/ccw/dist/core/data-aggregator.js +5 -2
  58. package/ccw/dist/core/data-aggregator.js.map +1 -1
  59. package/ccw/dist/core/lite-scanner.d.ts +2 -1
  60. package/ccw/dist/core/lite-scanner.d.ts.map +1 -1
  61. package/ccw/dist/core/lite-scanner.js +295 -6
  62. package/ccw/dist/core/lite-scanner.js.map +1 -1
  63. package/ccw/dist/core/routes/session-routes.d.ts.map +1 -1
  64. package/ccw/dist/core/routes/session-routes.js +166 -48
  65. package/ccw/dist/core/routes/session-routes.js.map +1 -1
  66. package/ccw/dist/core/routes/system-routes.d.ts.map +1 -1
  67. package/ccw/dist/core/routes/system-routes.js +87 -0
  68. package/ccw/dist/core/routes/system-routes.js.map +1 -1
  69. package/ccw/dist/core/server.js +2 -2
  70. package/ccw/dist/core/server.js.map +1 -1
  71. package/ccw/scripts/IMPLEMENTATION-SUMMARY.md +226 -0
  72. package/ccw/scripts/QUICK-REFERENCE.md +135 -0
  73. package/ccw/scripts/README-memory-embedder.md +157 -0
  74. package/ccw/scripts/__pycache__/memory_embedder.cpython-313.pyc +0 -0
  75. package/ccw/scripts/__pycache__/test_memory_embedder.cpython-313-pytest-8.4.2.pyc +0 -0
  76. package/ccw/scripts/memory-embedder-example.ts +184 -0
  77. package/ccw/scripts/memory_embedder.py +428 -0
  78. package/ccw/scripts/test_memory_embedder.py +245 -0
  79. package/ccw/src/core/data-aggregator.ts +7 -2
  80. package/ccw/src/core/lite-scanner.ts +440 -6
  81. package/ccw/src/core/routes/session-routes.ts +201 -48
  82. package/ccw/src/core/routes/system-routes.ts +102 -0
  83. package/ccw/src/core/server.ts +2 -2
  84. package/ccw/src/templates/dashboard-css/01-base.css +8 -0
  85. package/ccw/src/templates/dashboard-css/02-session.css +81 -0
  86. package/ccw/src/templates/dashboard-css/04-lite-tasks.css +2442 -0
  87. package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +157 -0
  88. package/ccw/src/templates/dashboard-css/32-issue-manager.css +23 -0
  89. package/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js +38 -4
  90. package/ccw/src/templates/dashboard-js/components/hook-manager.js +38 -13
  91. package/ccw/src/templates/dashboard-js/components/navigation.js +24 -4
  92. package/ccw/src/templates/dashboard-js/i18n.js +178 -4
  93. package/ccw/src/templates/dashboard-js/views/api-settings.js +32 -0
  94. package/ccw/src/templates/dashboard-js/views/claude-manager.js +44 -3
  95. package/ccw/src/templates/dashboard-js/views/cli-manager.js +303 -31
  96. package/ccw/src/templates/dashboard-js/views/history.js +44 -6
  97. package/ccw/src/templates/dashboard-js/views/home.js +1 -0
  98. package/ccw/src/templates/dashboard-js/views/issue-manager.js +54 -7
  99. package/ccw/src/templates/dashboard-js/views/lite-tasks.js +1817 -4
  100. package/ccw/src/templates/dashboard.html +5 -0
  101. package/package.json +2 -1
  102. package/.claude/skills/ccw/index/command-capabilities.json +0 -127
  103. package/.claude/skills/ccw/index/intent-rules.json +0 -136
  104. package/.claude/skills/ccw/index/workflow-chains.json +0 -451
  105. package/.claude/skills/ccw/phases/actions/bugfix.md +0 -218
  106. package/.claude/skills/ccw/phases/actions/coupled.md +0 -194
  107. package/.claude/skills/ccw/phases/actions/docs.md +0 -93
  108. package/.claude/skills/ccw/phases/actions/full.md +0 -154
  109. package/.claude/skills/ccw/phases/actions/issue.md +0 -201
  110. package/.claude/skills/ccw/phases/actions/rapid.md +0 -104
  111. package/.claude/skills/ccw/phases/actions/review-fix.md +0 -84
  112. package/.claude/skills/ccw/phases/actions/tdd.md +0 -66
  113. package/.claude/skills/ccw/phases/actions/ui.md +0 -79
  114. package/.claude/skills/ccw/phases/orchestrator.md +0 -435
  115. package/.claude/skills/ccw/specs/intent-classification.md +0 -336
  116. package/.claude/skills/ccw-help/index/all-agents.json +0 -82
  117. package/.claude/skills/ccw-help/index/all-commands.json +0 -882
  118. package/.claude/skills/ccw-help/index/by-category.json +0 -914
  119. package/.claude/skills/ccw-help/index/by-use-case.json +0 -896
  120. package/.claude/skills/ccw-help/index/command-relationships.json +0 -160
  121. package/.claude/skills/ccw-help/index/essential-commands.json +0 -112
@@ -216,6 +216,11 @@ async function saveCliSettingsEndpoint(data) {
216
216
  const result = await response.json();
217
217
  showRefreshToast(t('apiSettings.settingsSaved'), 'success');
218
218
 
219
+ // Invalidate cache to ensure fresh data on page refresh
220
+ if (window.cacheManager) {
221
+ window.cacheManager.invalidate('cli-wrapper-endpoints');
222
+ }
223
+
219
224
  // Refresh data and re-render
220
225
  await loadCliSettings(true);
221
226
  renderCliSettingsList();
@@ -250,6 +255,11 @@ async function deleteCliSettingsEndpoint(endpointId) {
250
255
 
251
256
  showRefreshToast(t('apiSettings.settingsDeleted'), 'success');
252
257
 
258
+ // Invalidate cache to ensure fresh data on page refresh
259
+ if (window.cacheManager) {
260
+ window.cacheManager.invalidate('cli-wrapper-endpoints');
261
+ }
262
+
253
263
  // Refresh data and re-render
254
264
  await loadCliSettings(true);
255
265
  selectedCliSettingsId = null;
@@ -635,6 +645,11 @@ async function saveProvider() {
635
645
  const result = await response.json();
636
646
  showRefreshToast(t('apiSettings.providerSaved'), 'success');
637
647
 
648
+ // Invalidate cache to ensure fresh data on page refresh
649
+ if (window.cacheManager) {
650
+ window.cacheManager.invalidate('cli-litellm-endpoints');
651
+ }
652
+
638
653
  closeProviderModal();
639
654
  // Force refresh data after saving
640
655
  apiSettingsData = null;
@@ -660,6 +675,12 @@ async function deleteProvider(providerId) {
660
675
  if (!response.ok) throw new Error('Failed to delete provider');
661
676
 
662
677
  showRefreshToast(t('apiSettings.providerDeleted'), 'success');
678
+
679
+ // Invalidate cache to ensure fresh data on page refresh
680
+ if (window.cacheManager) {
681
+ window.cacheManager.invalidate('cli-litellm-endpoints');
682
+ }
683
+
663
684
  // Force refresh data after deleting
664
685
  apiSettingsData = null;
665
686
  await renderApiSettings();
@@ -998,6 +1019,11 @@ async function saveEndpoint() {
998
1019
  const result = await response.json();
999
1020
  showRefreshToast(t('apiSettings.endpointSaved'), 'success');
1000
1021
 
1022
+ // Invalidate cache to ensure fresh data on page refresh
1023
+ if (window.cacheManager) {
1024
+ window.cacheManager.invalidate('cli-litellm-endpoints');
1025
+ }
1026
+
1001
1027
  closeEndpointModal();
1002
1028
  // Force refresh data after saving
1003
1029
  apiSettingsData = null;
@@ -1023,6 +1049,12 @@ async function deleteEndpoint(endpointId) {
1023
1049
  if (!response.ok) throw new Error('Failed to delete endpoint');
1024
1050
 
1025
1051
  showRefreshToast(t('apiSettings.endpointDeleted'), 'success');
1052
+
1053
+ // Invalidate cache to ensure fresh data on page refresh
1054
+ if (window.cacheManager) {
1055
+ window.cacheManager.invalidate('cli-litellm-endpoints');
1056
+ }
1057
+
1026
1058
  // Force refresh data after deleting
1027
1059
  apiSettingsData = null;
1028
1060
  await renderApiSettings();
@@ -1,6 +1,10 @@
1
1
  // CLAUDE.md Manager View
2
2
  // Three-column layout: File Tree | Viewer/Editor | Metadata & Actions
3
3
 
4
+ // ========== Lifecycle Management ==========
5
+ var claudeManagerDestroy = null;
6
+ var createDialogOverlay = null; // Track create dialog overlay for cleanup
7
+
4
8
  // ========== State Management ==========
5
9
  var claudeFilesData = {
6
10
  user: { main: null },
@@ -19,9 +23,15 @@ var fileTreeExpanded = {
19
23
  var searchQuery = '';
20
24
  var freshnessData = {}; // { [filePath]: FreshnessResult }
21
25
  var freshnessSummary = null;
26
+ var searchKeyboardHandlerAdded = false;
22
27
 
23
28
  // ========== Main Render Function ==========
24
29
  async function renderClaudeManager() {
30
+ // Initialize lifecycle
31
+ if (typeof initClaudeManager === 'function') {
32
+ initClaudeManager();
33
+ }
34
+
25
35
  var container = document.getElementById('mainContent');
26
36
  if (!container) return;
27
37
 
@@ -749,9 +759,11 @@ function filterFileTree(query) {
749
759
  renderFileTree();
750
760
 
751
761
  // Add keyboard shortcut handler
752
- if (query && !window.claudeSearchKeyboardHandlerAdded) {
753
- document.addEventListener('keydown', handleSearchKeyboard);
754
- window.claudeSearchKeyboardHandlerAdded = true;
762
+ if (query && !searchKeyboardHandlerAdded) {
763
+ (function() {
764
+ document.addEventListener('keydown', handleSearchKeyboard);
765
+ searchKeyboardHandlerAdded = true;
766
+ })();
755
767
  }
756
768
  }
757
769
 
@@ -796,6 +808,7 @@ function showCreateFileDialog() {
796
808
  '</div>';
797
809
 
798
810
  document.body.insertAdjacentHTML('beforeend', dialog);
811
+ createDialogOverlay = document.querySelector('.modal-overlay');
799
812
  if (window.lucide) lucide.createIcons();
800
813
  }
801
814
 
@@ -918,3 +931,31 @@ function updateClaudeBadge() {
918
931
  badge.textContent = total;
919
932
  }
920
933
  }
934
+
935
+ // ========== Lifecycle Functions ==========
936
+ function destroyClaudeManager() {
937
+ // Remove search keyboard event listener if added
938
+ if (searchKeyboardHandlerAdded) {
939
+ document.removeEventListener('keydown', handleSearchKeyboard);
940
+ searchKeyboardHandlerAdded = false;
941
+ }
942
+
943
+ // Remove create dialog overlay if exists
944
+ if (createDialogOverlay) {
945
+ createDialogOverlay.remove();
946
+ createDialogOverlay = null;
947
+ }
948
+
949
+ // Reset view-specific state
950
+ selectedFile = null;
951
+ isEditMode = false;
952
+ isDirty = false;
953
+ }
954
+
955
+ // Expose init/destroy functions for lifecycle management
956
+ window.initClaudeManager = function() {
957
+ claudeManagerDestroy = destroyClaudeManager;
958
+ };
959
+
960
+ // Make destroyClaudeManager accessible globally as well
961
+ window.destroyClaudeManager = destroyClaudeManager;
@@ -395,6 +395,10 @@ async function updateCliToolConfig(tool, updates) {
395
395
  }
396
396
  if (data.success && cliToolConfig && cliToolConfig.tools) {
397
397
  cliToolConfig.tools[tool] = data.config;
398
+ // Invalidate cache to ensure fresh data on page refresh
399
+ if (window.cacheManager) {
400
+ window.cacheManager.invalidate('cli-config');
401
+ }
398
402
  }
399
403
  return data;
400
404
  } catch (err) {
@@ -554,6 +558,298 @@ function buildToolConfigModalContent(tool, config, models, status) {
554
558
  '</div>';
555
559
  }
556
560
 
561
+ // ========== File Browser Modal ==========
562
+
563
+ var fileBrowserState = {
564
+ currentPath: '',
565
+ showHidden: false,
566
+ onSelect: null
567
+ };
568
+
569
+ function showFileBrowserModal(onSelect) {
570
+ fileBrowserState.onSelect = onSelect;
571
+ fileBrowserState.showHidden = false;
572
+
573
+ // Create modal overlay
574
+ var overlay = document.createElement('div');
575
+ overlay.id = 'fileBrowserOverlay';
576
+ overlay.className = 'modal-overlay';
577
+ overlay.innerHTML = buildFileBrowserModalContent();
578
+ document.body.appendChild(overlay);
579
+
580
+ // Load initial directory (home)
581
+ loadFileBrowserDirectory('');
582
+
583
+ // Initialize events
584
+ initFileBrowserEvents();
585
+
586
+ // Initialize icons
587
+ if (window.lucide) lucide.createIcons();
588
+ }
589
+
590
+ function buildFileBrowserModalContent() {
591
+ // Detect if Windows
592
+ var isWindows = navigator.platform.indexOf('Win') > -1;
593
+ var driveButtons = '';
594
+ if (isWindows) {
595
+ driveButtons = '<div class="file-browser-drives">' +
596
+ '<button class="btn-xs btn-outline drive-btn" data-drive="C:/">C:</button>' +
597
+ '<button class="btn-xs btn-outline drive-btn" data-drive="D:/">D:</button>' +
598
+ '<button class="btn-xs btn-outline drive-btn" data-drive="E:/">E:</button>' +
599
+ '</div>';
600
+ }
601
+
602
+ return '<div class="modal-content file-browser-modal">' +
603
+ '<div class="modal-header">' +
604
+ '<h3><i data-lucide="folder-open" class="w-4 h-4"></i> ' + t('cli.fileBrowser') + '</h3>' +
605
+ '<button class="modal-close" id="fileBrowserCloseBtn">&times;</button>' +
606
+ '</div>' +
607
+ '<div class="modal-body">' +
608
+ '<div class="file-browser-toolbar">' +
609
+ '<button class="btn-sm btn-outline" id="fileBrowserUpBtn" title="' + t('cli.fileBrowserUp') + '">' +
610
+ '<i data-lucide="arrow-up" class="w-3.5 h-3.5"></i>' +
611
+ '</button>' +
612
+ '<button class="btn-sm btn-outline" id="fileBrowserHomeBtn" title="' + t('cli.fileBrowserHome') + '">' +
613
+ '<i data-lucide="home" class="w-3.5 h-3.5"></i>' +
614
+ '</button>' +
615
+ driveButtons +
616
+ '<input type="text" id="fileBrowserPathInput" class="file-browser-path" placeholder="Enter path and press Enter" />' +
617
+ '<label class="file-browser-hidden-toggle">' +
618
+ '<input type="checkbox" id="fileBrowserShowHidden" checked />' +
619
+ '<span>' + t('cli.fileBrowserShowHidden') + '</span>' +
620
+ '</label>' +
621
+ '</div>' +
622
+ '<div class="file-browser-list" id="fileBrowserList">' +
623
+ '<div class="file-browser-loading"><i data-lucide="loader-2" class="w-5 h-5 animate-spin"></i></div>' +
624
+ '</div>' +
625
+ '</div>' +
626
+ '<div class="modal-footer">' +
627
+ '<button class="btn btn-outline" id="fileBrowserCancelBtn">' + t('cli.fileBrowserCancel') + '</button>' +
628
+ '<button class="btn btn-primary" id="fileBrowserSelectBtn" disabled>' +
629
+ '<i data-lucide="check" class="w-3.5 h-3.5"></i> ' + t('cli.fileBrowserSelect') +
630
+ '</button>' +
631
+ '</div>' +
632
+ '</div>';
633
+ }
634
+
635
+ async function loadFileBrowserDirectory(path) {
636
+ var listContainer = document.getElementById('fileBrowserList');
637
+ var pathInput = document.getElementById('fileBrowserPathInput');
638
+
639
+ if (listContainer) {
640
+ listContainer.innerHTML = '<div class="file-browser-loading"><i data-lucide="loader-2" class="w-5 h-5 animate-spin"></i></div>';
641
+ if (window.lucide) lucide.createIcons();
642
+ }
643
+
644
+ try {
645
+ var response = await fetch('/api/dialog/browse', {
646
+ method: 'POST',
647
+ headers: { 'Content-Type': 'application/json' },
648
+ body: JSON.stringify({ path: path, showHidden: fileBrowserState.showHidden })
649
+ });
650
+
651
+ if (!response.ok) {
652
+ throw new Error('Failed to load directory');
653
+ }
654
+
655
+ var data = await response.json();
656
+ fileBrowserState.currentPath = data.currentPath;
657
+
658
+ if (pathInput) {
659
+ pathInput.value = data.currentPath;
660
+ }
661
+
662
+ renderFileBrowserItems(data.items);
663
+ } catch (err) {
664
+ console.error('Failed to load directory:', err);
665
+ if (listContainer) {
666
+ listContainer.innerHTML = '<div class="file-browser-error">' +
667
+ '<p>' + t('cli.fileBrowserApiError') + '</p>' +
668
+ '<p class="file-browser-hint">' + t('cli.fileBrowserManualHint') + '</p>' +
669
+ '</div>';
670
+ }
671
+ // Enable manual path entry mode - enable select button when path is typed
672
+ var selectBtn = document.getElementById('fileBrowserSelectBtn');
673
+ var pathInput = document.getElementById('fileBrowserPathInput');
674
+ if (selectBtn && pathInput) {
675
+ selectBtn.disabled = false;
676
+ pathInput.focus();
677
+ }
678
+ }
679
+ }
680
+
681
+ function renderFileBrowserItems(items) {
682
+ var listContainer = document.getElementById('fileBrowserList');
683
+ if (!listContainer) return;
684
+
685
+ if (!items || items.length === 0) {
686
+ listContainer.innerHTML = '<div class="file-browser-empty">Empty directory</div>';
687
+ return;
688
+ }
689
+
690
+ var html = items.map(function(item) {
691
+ var icon = item.isDirectory ? 'folder' : 'file';
692
+ var itemClass = 'file-browser-item' + (item.isDirectory ? ' is-directory' : ' is-file');
693
+ return '<div class="' + itemClass + '" data-path="' + escapeHtml(item.path) + '" data-is-dir="' + item.isDirectory + '">' +
694
+ '<i data-lucide="' + icon + '" class="w-4 h-4"></i>' +
695
+ '<span class="file-browser-item-name">' + escapeHtml(item.name) + '</span>' +
696
+ '</div>';
697
+ }).join('');
698
+
699
+ listContainer.innerHTML = html;
700
+
701
+ // Initialize icons
702
+ if (window.lucide) lucide.createIcons();
703
+
704
+ // Add click handlers
705
+ listContainer.querySelectorAll('.file-browser-item').forEach(function(el) {
706
+ el.onclick = function() {
707
+ var isDir = el.getAttribute('data-is-dir') === 'true';
708
+ var path = el.getAttribute('data-path');
709
+
710
+ if (isDir) {
711
+ // Navigate into directory
712
+ loadFileBrowserDirectory(path);
713
+ } else {
714
+ // Select file
715
+ listContainer.querySelectorAll('.file-browser-item').forEach(function(item) {
716
+ item.classList.remove('selected');
717
+ });
718
+ el.classList.add('selected');
719
+
720
+ // Enable select button
721
+ var selectBtn = document.getElementById('fileBrowserSelectBtn');
722
+ if (selectBtn) {
723
+ selectBtn.disabled = false;
724
+ selectBtn.setAttribute('data-selected-path', path);
725
+ }
726
+ }
727
+ };
728
+
729
+ // Double-click to select file or enter directory
730
+ el.ondblclick = function() {
731
+ var isDir = el.getAttribute('data-is-dir') === 'true';
732
+ var path = el.getAttribute('data-path');
733
+
734
+ if (isDir) {
735
+ loadFileBrowserDirectory(path);
736
+ } else {
737
+ // Select and close
738
+ closeFileBrowserModal(path);
739
+ }
740
+ };
741
+ });
742
+ }
743
+
744
+ function initFileBrowserEvents() {
745
+ // Close button
746
+ var closeBtn = document.getElementById('fileBrowserCloseBtn');
747
+ if (closeBtn) {
748
+ closeBtn.onclick = function() { closeFileBrowserModal(null); };
749
+ }
750
+
751
+ // Cancel button
752
+ var cancelBtn = document.getElementById('fileBrowserCancelBtn');
753
+ if (cancelBtn) {
754
+ cancelBtn.onclick = function() { closeFileBrowserModal(null); };
755
+ }
756
+
757
+ // Select button
758
+ var selectBtn = document.getElementById('fileBrowserSelectBtn');
759
+ if (selectBtn) {
760
+ selectBtn.onclick = function() {
761
+ // First try selected path from list, then fall back to path input
762
+ var path = selectBtn.getAttribute('data-selected-path');
763
+ if (!path) {
764
+ var pathInput = document.getElementById('fileBrowserPathInput');
765
+ if (pathInput && pathInput.value.trim()) {
766
+ path = pathInput.value.trim();
767
+ }
768
+ }
769
+ if (path) {
770
+ closeFileBrowserModal(path);
771
+ }
772
+ };
773
+ }
774
+
775
+ // Up button
776
+ var upBtn = document.getElementById('fileBrowserUpBtn');
777
+ if (upBtn) {
778
+ upBtn.onclick = function() {
779
+ // Get parent path
780
+ var currentPath = fileBrowserState.currentPath;
781
+ var parentPath = currentPath.replace(/[/\\][^/\\]+$/, '') || '/';
782
+ loadFileBrowserDirectory(parentPath);
783
+ };
784
+ }
785
+
786
+ // Home button
787
+ var homeBtn = document.getElementById('fileBrowserHomeBtn');
788
+ if (homeBtn) {
789
+ homeBtn.onclick = function() {
790
+ loadFileBrowserDirectory('');
791
+ };
792
+ }
793
+
794
+ // Drive buttons (Windows)
795
+ document.querySelectorAll('.drive-btn').forEach(function(btn) {
796
+ btn.onclick = function() {
797
+ var drive = btn.getAttribute('data-drive');
798
+ if (drive) {
799
+ loadFileBrowserDirectory(drive);
800
+ }
801
+ };
802
+ });
803
+
804
+ // Path input - allow manual entry
805
+ var pathInput = document.getElementById('fileBrowserPathInput');
806
+ if (pathInput) {
807
+ pathInput.onkeydown = function(e) {
808
+ if (e.key === 'Enter') {
809
+ e.preventDefault();
810
+ var path = pathInput.value.trim();
811
+ if (path) {
812
+ loadFileBrowserDirectory(path);
813
+ }
814
+ }
815
+ };
816
+ }
817
+
818
+ // Show hidden checkbox
819
+ var showHiddenCheckbox = document.getElementById('fileBrowserShowHidden');
820
+ if (showHiddenCheckbox) {
821
+ showHiddenCheckbox.checked = true; // Default to show hidden
822
+ fileBrowserState.showHidden = true;
823
+ showHiddenCheckbox.onchange = function() {
824
+ fileBrowserState.showHidden = showHiddenCheckbox.checked;
825
+ loadFileBrowserDirectory(fileBrowserState.currentPath);
826
+ };
827
+ }
828
+
829
+ // Click outside to close
830
+ var overlay = document.getElementById('fileBrowserOverlay');
831
+ if (overlay) {
832
+ overlay.onclick = function(e) {
833
+ if (e.target === overlay) {
834
+ closeFileBrowserModal(null);
835
+ }
836
+ };
837
+ }
838
+ }
839
+
840
+ function closeFileBrowserModal(selectedPath) {
841
+ var overlay = document.getElementById('fileBrowserOverlay');
842
+ if (overlay) {
843
+ overlay.remove();
844
+ }
845
+
846
+ if (fileBrowserState.onSelect && selectedPath) {
847
+ fileBrowserState.onSelect(selectedPath);
848
+ }
849
+
850
+ fileBrowserState.onSelect = null;
851
+ }
852
+
557
853
  function initToolConfigModalEvents(tool, currentConfig, models) {
558
854
  // Local tags state (copy from config)
559
855
  var currentTags = (currentConfig.tags || []).slice();
@@ -754,38 +1050,14 @@ function initToolConfigModalEvents(tool, currentConfig, models) {
754
1050
  // Environment file browse button (only for gemini/qwen)
755
1051
  var envFileBrowseBtn = document.getElementById('envFileBrowseBtn');
756
1052
  if (envFileBrowseBtn) {
757
- envFileBrowseBtn.onclick = async function() {
758
- try {
759
- // Use file dialog API if available
760
- var response = await fetch('/api/dialog/open-file', {
761
- method: 'POST',
762
- headers: { 'Content-Type': 'application/json' },
763
- body: JSON.stringify({
764
- title: t('cli.envFile'),
765
- filters: [
766
- { name: 'Environment Files', extensions: ['env'] },
767
- { name: 'All Files', extensions: ['*'] }
768
- ],
769
- defaultPath: ''
770
- })
771
- });
772
-
773
- if (response.ok) {
774
- var data = await response.json();
775
- if (data.filePath) {
776
- var envFileInput = document.getElementById('envFileInput');
777
- if (envFileInput) {
778
- envFileInput.value = data.filePath;
779
- }
780
- }
781
- } else {
782
- // Fallback: prompt user to enter path manually
783
- showRefreshToast('File dialog not available. Please enter path manually.', 'info');
1053
+ envFileBrowseBtn.onclick = function() {
1054
+ showFileBrowserModal(function(selectedPath) {
1055
+ var envFileInput = document.getElementById('envFileInput');
1056
+ if (envFileInput && selectedPath) {
1057
+ envFileInput.value = selectedPath;
1058
+ envFileInput.focus();
784
1059
  }
785
- } catch (err) {
786
- console.error('Failed to open file dialog:', err);
787
- showRefreshToast('File dialog not available. Please enter path manually.', 'info');
788
- }
1060
+ });
789
1061
  };
790
1062
  }
791
1063
 
@@ -7,6 +7,9 @@ var isMultiSelectMode = false;
7
7
 
8
8
  // ========== Rendering ==========
9
9
  async function renderCliHistoryView() {
10
+ // Reset view state to prevent stale data persistence
11
+ resetHistoryViewState();
12
+
10
13
  var container = document.getElementById('mainContent');
11
14
  if (!container) return;
12
15
 
@@ -183,6 +186,9 @@ async function renderCliHistoryView() {
183
186
  if (window.lucide) lucide.createIcons();
184
187
  }
185
188
 
189
+ // Export destroy function for lifecycle management
190
+ window.destroyCliHistoryView = destroyCliHistoryView;
191
+
186
192
  // ========== Actions ==========
187
193
  async function copyExecutionId(executionId) {
188
194
  try {
@@ -218,16 +224,28 @@ async function refreshCliHistoryView() {
218
224
  }
219
225
 
220
226
  // ========== Multi-Select Functions ==========
227
+ var deleteDropdownListenerActive = false;
228
+
221
229
  function toggleDeleteDropdown(event) {
222
230
  event.stopPropagation();
223
231
  var menu = document.getElementById('deleteDropdownMenu');
224
232
  if (menu) {
225
- menu.classList.toggle('show');
226
- // Close on outside click
227
233
  if (menu.classList.contains('show')) {
228
- setTimeout(function() {
229
- document.addEventListener('click', closeDeleteDropdown);
230
- }, 0);
234
+ // Closing: remove listener if active
235
+ menu.classList.remove('show');
236
+ if (deleteDropdownListenerActive) {
237
+ document.removeEventListener('click', closeDeleteDropdown);
238
+ deleteDropdownListenerActive = false;
239
+ }
240
+ } else {
241
+ // Opening: add listener if not already active
242
+ menu.classList.add('show');
243
+ if (!deleteDropdownListenerActive) {
244
+ setTimeout(function() {
245
+ document.addEventListener('click', closeDeleteDropdown);
246
+ deleteDropdownListenerActive = true;
247
+ }, 0);
248
+ }
231
249
  }
232
250
  }
233
251
  }
@@ -235,7 +253,10 @@ function toggleDeleteDropdown(event) {
235
253
  function closeDeleteDropdown() {
236
254
  var menu = document.getElementById('deleteDropdownMenu');
237
255
  if (menu) menu.classList.remove('show');
238
- document.removeEventListener('click', closeDeleteDropdown);
256
+ if (deleteDropdownListenerActive) {
257
+ document.removeEventListener('click', closeDeleteDropdown);
258
+ deleteDropdownListenerActive = false;
259
+ }
239
260
  }
240
261
 
241
262
  function enterMultiSelectMode() {
@@ -279,6 +300,23 @@ function clearExecutionSelection() {
279
300
  renderCliHistoryView();
280
301
  }
281
302
 
303
+ function resetHistoryViewState() {
304
+ selectedExecutions.clear();
305
+ isMultiSelectMode = false;
306
+ }
307
+
308
+ // ========== View Lifecycle ==========
309
+ function destroyCliHistoryView() {
310
+ // Clean up dropdown listener if active
311
+ if (deleteDropdownListenerActive) {
312
+ document.removeEventListener('click', closeDeleteDropdown);
313
+ deleteDropdownListenerActive = false;
314
+ }
315
+ // Ensure dropdown menu is closed
316
+ var menu = document.getElementById('deleteDropdownMenu');
317
+ if (menu) menu.classList.remove('show');
318
+ }
319
+
282
320
  // ========== Batch Delete Functions ==========
283
321
  function confirmBatchDelete() {
284
322
  var count = selectedExecutions.size;
@@ -51,6 +51,7 @@ function updateBadges() {
51
51
  const liteTasks = workflowData.liteTasks || {};
52
52
  document.getElementById('badgeLitePlan').textContent = liteTasks.litePlan?.length || 0;
53
53
  document.getElementById('badgeLiteFix').textContent = liteTasks.liteFix?.length || 0;
54
+ document.getElementById('badgeMultiCliPlan').textContent = liteTasks.multiCliPlan?.length || 0;
54
55
 
55
56
  // MCP badge - load async if needed
56
57
  if (typeof loadMcpConfig === 'function') {