claude-code-workflow 6.3.27 → 6.3.29

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 (133) 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 +269 -176
  21. package/.claude/commands/workflow/debug.md +6 -0
  22. package/.claude/commands/workflow/execute.md +4 -0
  23. package/.claude/commands/workflow/lite-execute.md +4 -0
  24. package/.claude/commands/workflow/lite-lite-lite.md +433 -0
  25. package/.claude/commands/workflow/multi-cli-plan.md +510 -0
  26. package/.claude/commands/workflow/review-fix.md +4 -0
  27. package/.claude/commands/workflow/test-cycle-execute.md +4 -0
  28. package/.claude/skills/ccw/SKILL.md +262 -372
  29. package/.claude/skills/ccw/command.json +547 -0
  30. package/.claude/skills/ccw-help/SKILL.md +46 -107
  31. package/.claude/skills/ccw-help/command.json +511 -0
  32. package/.claude/skills/skill-tuning/SKILL.md +303 -0
  33. package/.claude/skills/skill-tuning/phases/actions/action-abort.md +164 -0
  34. package/.claude/skills/skill-tuning/phases/actions/action-analyze-requirements.md +406 -0
  35. package/.claude/skills/skill-tuning/phases/actions/action-apply-fix.md +206 -0
  36. package/.claude/skills/skill-tuning/phases/actions/action-complete.md +195 -0
  37. package/.claude/skills/skill-tuning/phases/actions/action-diagnose-agent.md +317 -0
  38. package/.claude/skills/skill-tuning/phases/actions/action-diagnose-context.md +243 -0
  39. package/.claude/skills/skill-tuning/phases/actions/action-diagnose-dataflow.md +318 -0
  40. package/.claude/skills/skill-tuning/phases/actions/action-diagnose-docs.md +299 -0
  41. package/.claude/skills/skill-tuning/phases/actions/action-diagnose-memory.md +269 -0
  42. package/.claude/skills/skill-tuning/phases/actions/action-diagnose-token-consumption.md +200 -0
  43. package/.claude/skills/skill-tuning/phases/actions/action-gemini-analysis.md +322 -0
  44. package/.claude/skills/skill-tuning/phases/actions/action-generate-report.md +228 -0
  45. package/.claude/skills/skill-tuning/phases/actions/action-init.md +149 -0
  46. package/.claude/skills/skill-tuning/phases/actions/action-propose-fixes.md +317 -0
  47. package/.claude/skills/skill-tuning/phases/actions/action-verify.md +222 -0
  48. package/.claude/skills/skill-tuning/phases/orchestrator.md +377 -0
  49. package/.claude/skills/skill-tuning/phases/state-schema.md +378 -0
  50. package/.claude/skills/skill-tuning/specs/category-mappings.json +284 -0
  51. package/.claude/skills/skill-tuning/specs/dimension-mapping.md +212 -0
  52. package/.claude/skills/skill-tuning/specs/problem-taxonomy.md +318 -0
  53. package/.claude/skills/skill-tuning/specs/quality-gates.md +263 -0
  54. package/.claude/skills/skill-tuning/specs/skill-authoring-principles.md +189 -0
  55. package/.claude/skills/skill-tuning/specs/tuning-strategies.md +1537 -0
  56. package/.claude/skills/skill-tuning/templates/diagnosis-report.md +153 -0
  57. package/.claude/skills/skill-tuning/templates/fix-proposal.md +204 -0
  58. package/.claude/workflows/cli-templates/schemas/multi-cli-discussion-schema.json +421 -0
  59. package/.claude/workflows/cli-tools-usage.md +0 -41
  60. package/.codex/prompts/issue-execute.md +72 -12
  61. package/README.md +3 -0
  62. package/ccw/dist/core/data-aggregator.d.ts +2 -0
  63. package/ccw/dist/core/data-aggregator.d.ts.map +1 -1
  64. package/ccw/dist/core/data-aggregator.js +5 -2
  65. package/ccw/dist/core/data-aggregator.js.map +1 -1
  66. package/ccw/dist/core/lite-scanner.d.ts +2 -1
  67. package/ccw/dist/core/lite-scanner.d.ts.map +1 -1
  68. package/ccw/dist/core/lite-scanner.js +348 -6
  69. package/ccw/dist/core/lite-scanner.js.map +1 -1
  70. package/ccw/dist/core/routes/session-routes.d.ts.map +1 -1
  71. package/ccw/dist/core/routes/session-routes.js +166 -48
  72. package/ccw/dist/core/routes/session-routes.js.map +1 -1
  73. package/ccw/dist/core/routes/system-routes.d.ts.map +1 -1
  74. package/ccw/dist/core/routes/system-routes.js +87 -0
  75. package/ccw/dist/core/routes/system-routes.js.map +1 -1
  76. package/ccw/dist/core/server.js +2 -2
  77. package/ccw/dist/core/server.js.map +1 -1
  78. package/ccw/dist/tools/memory-update-queue.d.ts.map +1 -1
  79. package/ccw/dist/tools/memory-update-queue.js +5 -11
  80. package/ccw/dist/tools/memory-update-queue.js.map +1 -1
  81. package/ccw/scripts/IMPLEMENTATION-SUMMARY.md +226 -0
  82. package/ccw/scripts/QUICK-REFERENCE.md +135 -0
  83. package/ccw/scripts/README-memory-embedder.md +157 -0
  84. package/ccw/scripts/__pycache__/memory_embedder.cpython-313.pyc +0 -0
  85. package/ccw/scripts/__pycache__/test_memory_embedder.cpython-313-pytest-8.4.2.pyc +0 -0
  86. package/ccw/scripts/memory-embedder-example.ts +184 -0
  87. package/ccw/scripts/memory_embedder.py +428 -0
  88. package/ccw/scripts/test_memory_embedder.py +245 -0
  89. package/ccw/src/core/data-aggregator.ts +7 -2
  90. package/ccw/src/core/lite-scanner.ts +495 -6
  91. package/ccw/src/core/routes/session-routes.ts +201 -48
  92. package/ccw/src/core/routes/system-routes.ts +102 -0
  93. package/ccw/src/core/server.ts +2 -2
  94. package/ccw/src/templates/dashboard-css/01-base.css +8 -0
  95. package/ccw/src/templates/dashboard-css/02-session.css +81 -0
  96. package/ccw/src/templates/dashboard-css/03-tasks.css +5 -0
  97. package/ccw/src/templates/dashboard-css/04-lite-tasks.css +3071 -0
  98. package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +157 -0
  99. package/ccw/src/templates/dashboard-css/32-issue-manager.css +23 -0
  100. package/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js +38 -4
  101. package/ccw/src/templates/dashboard-js/components/hook-manager.js +68 -34
  102. package/ccw/src/templates/dashboard-js/components/navigation.js +24 -4
  103. package/ccw/src/templates/dashboard-js/i18n.js +278 -4
  104. package/ccw/src/templates/dashboard-js/views/api-settings.js +32 -0
  105. package/ccw/src/templates/dashboard-js/views/claude-manager.js +44 -3
  106. package/ccw/src/templates/dashboard-js/views/cli-manager.js +303 -31
  107. package/ccw/src/templates/dashboard-js/views/history.js +44 -6
  108. package/ccw/src/templates/dashboard-js/views/home.js +1 -0
  109. package/ccw/src/templates/dashboard-js/views/issue-manager.js +54 -7
  110. package/ccw/src/templates/dashboard-js/views/lite-tasks.js +2621 -4
  111. package/ccw/src/templates/dashboard.html +5 -0
  112. package/ccw/src/tools/memory-update-queue.js +5 -11
  113. package/package.json +2 -1
  114. package/.claude/skills/ccw/index/command-capabilities.json +0 -127
  115. package/.claude/skills/ccw/index/intent-rules.json +0 -136
  116. package/.claude/skills/ccw/index/workflow-chains.json +0 -451
  117. package/.claude/skills/ccw/phases/actions/bugfix.md +0 -218
  118. package/.claude/skills/ccw/phases/actions/coupled.md +0 -194
  119. package/.claude/skills/ccw/phases/actions/docs.md +0 -93
  120. package/.claude/skills/ccw/phases/actions/full.md +0 -154
  121. package/.claude/skills/ccw/phases/actions/issue.md +0 -201
  122. package/.claude/skills/ccw/phases/actions/rapid.md +0 -104
  123. package/.claude/skills/ccw/phases/actions/review-fix.md +0 -84
  124. package/.claude/skills/ccw/phases/actions/tdd.md +0 -66
  125. package/.claude/skills/ccw/phases/actions/ui.md +0 -79
  126. package/.claude/skills/ccw/phases/orchestrator.md +0 -435
  127. package/.claude/skills/ccw/specs/intent-classification.md +0 -336
  128. package/.claude/skills/ccw-help/index/all-agents.json +0 -82
  129. package/.claude/skills/ccw-help/index/all-commands.json +0 -882
  130. package/.claude/skills/ccw-help/index/by-category.json +0 -914
  131. package/.claude/skills/ccw-help/index/by-use-case.json +0 -896
  132. package/.claude/skills/ccw-help/index/command-relationships.json +0 -160
  133. package/.claude/skills/ccw-help/index/essential-commands.json +0 -112
@@ -661,3 +661,160 @@
661
661
  color: hsl(var(--success));
662
662
  }
663
663
 
664
+ /* ========================================
665
+ * File Browser Modal
666
+ * ======================================== */
667
+
668
+ .file-browser-modal {
669
+ width: 600px;
670
+ max-width: 90vw;
671
+ max-height: 80vh;
672
+ display: flex;
673
+ flex-direction: column;
674
+ }
675
+
676
+ .file-browser-toolbar {
677
+ display: flex;
678
+ align-items: center;
679
+ gap: 0.5rem;
680
+ padding: 0.5rem;
681
+ background: hsl(var(--muted) / 0.3);
682
+ border-radius: 0.375rem;
683
+ margin-bottom: 0.75rem;
684
+ }
685
+
686
+ .file-browser-toolbar .btn-sm {
687
+ flex-shrink: 0;
688
+ padding: 0.375rem;
689
+ }
690
+
691
+ .file-browser-path {
692
+ flex: 1;
693
+ padding: 0.375rem 0.5rem;
694
+ font-family: monospace;
695
+ font-size: 0.75rem;
696
+ background: hsl(var(--background));
697
+ border: 1px solid hsl(var(--border));
698
+ border-radius: 0.25rem;
699
+ color: hsl(var(--foreground));
700
+ }
701
+
702
+ .file-browser-path:focus {
703
+ outline: none;
704
+ border-color: hsl(var(--primary));
705
+ box-shadow: 0 0 0 2px hsl(var(--primary) / 0.2);
706
+ }
707
+
708
+ .file-browser-drives {
709
+ display: flex;
710
+ gap: 0.25rem;
711
+ }
712
+
713
+ .btn-xs {
714
+ padding: 0.25rem 0.5rem;
715
+ font-size: 0.6875rem;
716
+ border-radius: 0.25rem;
717
+ }
718
+
719
+ .drive-btn {
720
+ font-family: monospace;
721
+ font-weight: 600;
722
+ }
723
+
724
+ .file-browser-hidden-toggle {
725
+ display: flex;
726
+ align-items: center;
727
+ gap: 0.375rem;
728
+ font-size: 0.75rem;
729
+ color: hsl(var(--muted-foreground));
730
+ cursor: pointer;
731
+ white-space: nowrap;
732
+ }
733
+
734
+ .file-browser-hidden-toggle input {
735
+ cursor: pointer;
736
+ }
737
+
738
+ .file-browser-list {
739
+ flex: 1;
740
+ min-height: 300px;
741
+ max-height: 400px;
742
+ overflow-y: auto;
743
+ border: 1px solid hsl(var(--border));
744
+ border-radius: 0.375rem;
745
+ background: hsl(var(--background));
746
+ }
747
+
748
+ .file-browser-loading,
749
+ .file-browser-empty {
750
+ display: flex;
751
+ align-items: center;
752
+ justify-content: center;
753
+ height: 100%;
754
+ min-height: 200px;
755
+ color: hsl(var(--muted-foreground));
756
+ font-size: 0.875rem;
757
+ }
758
+
759
+ .file-browser-error {
760
+ display: flex;
761
+ flex-direction: column;
762
+ align-items: center;
763
+ justify-content: center;
764
+ height: 100%;
765
+ min-height: 200px;
766
+ font-size: 0.875rem;
767
+ text-align: center;
768
+ padding: 1rem;
769
+ gap: 0.5rem;
770
+ }
771
+
772
+ .file-browser-error p {
773
+ margin: 0;
774
+ color: hsl(var(--destructive));
775
+ }
776
+
777
+ .file-browser-hint {
778
+ color: hsl(var(--muted-foreground));
779
+ font-size: 0.8rem;
780
+ }
781
+
782
+ .file-browser-item {
783
+ display: flex;
784
+ align-items: center;
785
+ gap: 0.5rem;
786
+ padding: 0.5rem 0.75rem;
787
+ cursor: pointer;
788
+ border-bottom: 1px solid hsl(var(--border) / 0.5);
789
+ transition: background-color 0.15s;
790
+ }
791
+
792
+ .file-browser-item:last-child {
793
+ border-bottom: none;
794
+ }
795
+
796
+ .file-browser-item:hover {
797
+ background: hsl(var(--muted) / 0.5);
798
+ }
799
+
800
+ .file-browser-item.selected {
801
+ background: hsl(var(--primary) / 0.15);
802
+ border-color: hsl(var(--primary) / 0.3);
803
+ }
804
+
805
+ .file-browser-item.is-directory {
806
+ color: hsl(var(--primary));
807
+ }
808
+
809
+ .file-browser-item.is-file {
810
+ color: hsl(var(--foreground));
811
+ }
812
+
813
+ .file-browser-item-name {
814
+ flex: 1;
815
+ font-size: 0.8125rem;
816
+ overflow: hidden;
817
+ text-overflow: ellipsis;
818
+ white-space: nowrap;
819
+ }
820
+
@@ -128,6 +128,29 @@
128
128
  box-shadow: 0 4px 12px hsl(var(--foreground) / 0.08);
129
129
  }
130
130
 
131
+ /* Archived Issue Card */
132
+ .issue-card.archived {
133
+ opacity: 0.85;
134
+ background: hsl(var(--muted) / 0.3);
135
+ }
136
+
137
+ .issue-card.archived:hover {
138
+ opacity: 1;
139
+ }
140
+
141
+ .issue-archived-badge {
142
+ display: inline-flex;
143
+ align-items: center;
144
+ padding: 0.125rem 0.375rem;
145
+ background: hsl(var(--muted));
146
+ color: hsl(var(--muted-foreground));
147
+ font-size: 0.625rem;
148
+ font-weight: 500;
149
+ border-radius: 0.25rem;
150
+ text-transform: uppercase;
151
+ letter-spacing: 0.025em;
152
+ }
153
+
131
154
  .issue-card-header {
132
155
  display: flex;
133
156
  align-items: flex-start;
@@ -3,6 +3,12 @@
3
3
  * Real-time streaming output viewer for CLI executions
4
4
  */
5
5
 
6
+ // ===== Lifecycle Management =====
7
+ let cliStreamViewerDestroy = null;
8
+ let streamKeyboardHandler = null;
9
+ let streamScrollHandler = null; // Track scroll listener
10
+ let streamStatusTimers = []; // Track status update timers
11
+
6
12
  // ===== State Management =====
7
13
  let cliStreamExecutions = {}; // { executionId: { tool, mode, output, status, startTime, endTime } }
8
14
  let activeStreamTab = null;
@@ -91,7 +97,7 @@ async function syncActiveExecutions() {
91
97
  // ===== Initialization =====
92
98
  function initCliStreamViewer() {
93
99
  // Initialize keyboard shortcuts
94
- document.addEventListener('keydown', function(e) {
100
+ streamKeyboardHandler = function(e) {
95
101
  if (e.key === 'Escape' && isCliStreamViewerOpen) {
96
102
  if (searchFilter) {
97
103
  clearSearch();
@@ -108,12 +114,14 @@ function initCliStreamViewer() {
108
114
  searchInput.select();
109
115
  }
110
116
  }
111
- });
117
+ };
118
+ document.addEventListener('keydown', streamKeyboardHandler);
112
119
 
113
120
  // Initialize scroll detection for auto-scroll
114
121
  const content = document.getElementById('cliStreamContent');
115
122
  if (content) {
116
- content.addEventListener('scroll', handleStreamContentScroll);
123
+ streamScrollHandler = handleStreamContentScroll;
124
+ content.addEventListener('scroll', streamScrollHandler);
117
125
  }
118
126
 
119
127
  // Sync active executions from server (recover state for mid-execution joins)
@@ -592,11 +600,12 @@ function renderStreamStatus(executionId) {
592
600
 
593
601
  // Update duration periodically for running executions
594
602
  if (exec.status === 'running') {
595
- setTimeout(() => {
603
+ const timerId = setTimeout(() => {
596
604
  if (activeStreamTab === executionId && cliStreamExecutions[executionId]?.status === 'running') {
597
605
  renderStreamStatus(executionId);
598
606
  }
599
607
  }, 1000);
608
+ streamStatusTimers.push(timerId);
600
609
  }
601
610
  }
602
611
 
@@ -760,6 +769,31 @@ if (document.readyState === 'loading') {
760
769
  initCliStreamViewer();
761
770
  }
762
771
 
772
+ // ===== Lifecycle Functions =====
773
+ function destroyCliStreamViewer() {
774
+ // Remove keyboard event listener if exists
775
+ if (streamKeyboardHandler) {
776
+ document.removeEventListener('keydown', streamKeyboardHandler);
777
+ streamKeyboardHandler = null;
778
+ }
779
+
780
+ // Remove scroll event listener if exists
781
+ if (streamScrollHandler) {
782
+ const content = document.getElementById('cliStreamContent');
783
+ if (content) {
784
+ content.removeEventListener('scroll', streamScrollHandler);
785
+ }
786
+ streamScrollHandler = null;
787
+ }
788
+
789
+ // Clear all pending status update timers
790
+ streamStatusTimers.forEach(timerId => clearTimeout(timerId));
791
+ streamStatusTimers = [];
792
+ }
793
+
794
+ // Export lifecycle functions
795
+ window.destroyCliStreamViewer = destroyCliStreamViewer;
796
+
763
797
  // ===== Global Exposure =====
764
798
  window.toggleCliStreamViewer = toggleCliStreamViewer;
765
799
  window.handleCliStreamStarted = handleCliStreamStarted;
@@ -52,12 +52,13 @@ const HOOK_TEMPLATES = {
52
52
  'memory-update-queue': {
53
53
  event: 'Stop',
54
54
  matcher: '',
55
- command: 'bash',
56
- args: ['-c', 'ccw tool exec memory_queue "{\\"action\\":\\"add\\",\\"path\\":\\"$CLAUDE_PROJECT_DIR\\"}"'],
55
+ command: 'node',
56
+ args: ['-e', "require('child_process').spawnSync(process.platform==='win32'?'cmd':'ccw',process.platform==='win32'?['/c','ccw','tool','exec','memory_queue',JSON.stringify({action:'add',path:process.env.CLAUDE_PROJECT_DIR,tool:'gemini'})]:['tool','exec','memory_queue',JSON.stringify({action:'add',path:process.env.CLAUDE_PROJECT_DIR,tool:'gemini'})],{stdio:'inherit'})"],
57
57
  description: 'Queue CLAUDE.md update when session ends (batched by threshold/timeout)',
58
58
  category: 'memory',
59
59
  configurable: true,
60
60
  config: {
61
+ tool: { type: 'select', default: 'gemini', options: ['gemini', 'qwen', 'codex', 'opencode'], label: 'CLI Tool' },
61
62
  threshold: { type: 'number', default: 5, min: 1, max: 20, label: 'Threshold (paths)', step: 1 },
62
63
  timeout: { type: 'number', default: 300, min: 60, max: 1800, label: 'Timeout (seconds)', step: 60 }
63
64
  }
@@ -66,8 +67,8 @@ const HOOK_TEMPLATES = {
66
67
  'skill-context-keyword': {
67
68
  event: 'UserPromptSubmit',
68
69
  matcher: '',
69
- command: 'bash',
70
- args: ['-c', 'ccw tool exec skill_context_loader --stdin'],
70
+ command: 'node',
71
+ args: ['-e', "const p=JSON.parse(process.env.HOOK_INPUT||'{}');require('child_process').spawnSync('ccw',['tool','exec','skill_context_loader',JSON.stringify({prompt:p.user_prompt||''})],{stdio:'inherit'})"],
71
72
  description: 'Load SKILL context based on keyword matching in user prompt',
72
73
  category: 'skill',
73
74
  configurable: true,
@@ -79,8 +80,8 @@ const HOOK_TEMPLATES = {
79
80
  'skill-context-auto': {
80
81
  event: 'UserPromptSubmit',
81
82
  matcher: '',
82
- command: 'bash',
83
- args: ['-c', 'ccw tool exec skill_context_loader --stdin --mode auto'],
83
+ command: 'node',
84
+ args: ['-e', "const p=JSON.parse(process.env.HOOK_INPUT||'{}');require('child_process').spawnSync('ccw',['tool','exec','skill_context_loader',JSON.stringify({mode:'auto',prompt:p.user_prompt||''})],{stdio:'inherit'})"],
84
85
  description: 'Auto-detect and load SKILL based on skill name in prompt',
85
86
  category: 'skill',
86
87
  configurable: false
@@ -195,6 +196,7 @@ const WIZARD_TEMPLATES = {
195
196
  }
196
197
  ],
197
198
  configFields: [
199
+ { key: 'tool', type: 'select', label: 'CLI Tool', default: 'gemini', options: ['gemini', 'qwen', 'codex', 'opencode'], description: 'CLI tool for CLAUDE.md generation' },
198
200
  { key: 'threshold', type: 'number', label: 'Threshold (paths)', default: 5, min: 1, max: 20, step: 1, description: 'Number of paths to trigger batch update' },
199
201
  { key: 'timeout', type: 'number', label: 'Timeout (seconds)', default: 300, min: 60, max: 1800, step: 60, description: 'Auto-flush queue after this time' }
200
202
  ]
@@ -359,48 +361,73 @@ async function loadAvailableSkills() {
359
361
  * Convert internal hook format to Claude Code format
360
362
  * Internal: { command, args, matcher, timeout }
361
363
  * Claude Code: { matcher, hooks: [{ type: "command", command: "...", timeout }] }
364
+ *
365
+ * IMPORTANT: For bash -c commands, use single quotes to wrap the script argument
366
+ * to avoid complex escaping issues with jq commands inside.
367
+ * See: https://github.com/catlog22/Claude-Code-Workflow/issues/73
362
368
  */
363
369
  function convertToClaudeCodeFormat(hookData) {
364
370
  // If already in correct format, return as-is
365
371
  if (hookData.hooks && Array.isArray(hookData.hooks)) {
366
372
  return hookData;
367
373
  }
368
-
374
+
369
375
  // Build command string from command + args
370
376
  let commandStr = hookData.command || '';
371
377
  if (hookData.args && Array.isArray(hookData.args)) {
372
- // Join args, properly quoting if needed
373
- const quotedArgs = hookData.args.map(arg => {
374
- if (arg.includes(' ') && !arg.startsWith('"') && !arg.startsWith("'")) {
375
- return `"${arg.replace(/"/g, '\\"')}"`;
378
+ // Special handling for bash -c commands: use single quotes for the script
379
+ // This avoids complex escaping issues with jq and other shell commands
380
+ if (commandStr === 'bash' && hookData.args.length >= 2 && hookData.args[0] === '-c') {
381
+ // Use single quotes for bash -c script argument
382
+ // Single quotes prevent shell expansion, so internal double quotes work naturally
383
+ const script = hookData.args[1];
384
+ // Escape single quotes within the script: ' -> '\''
385
+ const escapedScript = script.replace(/'/g, "'\\''");
386
+ commandStr = `bash -c '${escapedScript}'`;
387
+ // Handle any additional args after the script
388
+ if (hookData.args.length > 2) {
389
+ const additionalArgs = hookData.args.slice(2).map(arg => {
390
+ if (arg.includes(' ') && !arg.startsWith('"') && !arg.startsWith("'")) {
391
+ return `"${arg.replace(/"/g, '\\"')}"`;
392
+ }
393
+ return arg;
394
+ });
395
+ commandStr += ' ' + additionalArgs.join(' ');
376
396
  }
377
- return arg;
378
- });
379
- commandStr = `${commandStr} ${quotedArgs.join(' ')}`.trim();
397
+ } else {
398
+ // Default handling for other commands
399
+ const quotedArgs = hookData.args.map(arg => {
400
+ if (arg.includes(' ') && !arg.startsWith('"') && !arg.startsWith("'")) {
401
+ return `"${arg.replace(/"/g, '\\"')}"`;
402
+ }
403
+ return arg;
404
+ });
405
+ commandStr = `${commandStr} ${quotedArgs.join(' ')}`.trim();
406
+ }
380
407
  }
381
-
408
+
382
409
  const converted = {
383
410
  hooks: [{
384
411
  type: 'command',
385
412
  command: commandStr
386
413
  }]
387
414
  };
388
-
415
+
389
416
  // Add matcher if present (not needed for UserPromptSubmit, Stop, etc.)
390
417
  if (hookData.matcher) {
391
418
  converted.matcher = hookData.matcher;
392
419
  }
393
-
420
+
394
421
  // Add timeout if present (in seconds for Claude Code)
395
422
  if (hookData.timeout) {
396
423
  converted.hooks[0].timeout = Math.ceil(hookData.timeout / 1000);
397
424
  }
398
-
425
+
399
426
  // Preserve replaceIndex for updates
400
427
  if (hookData.replaceIndex !== undefined) {
401
428
  converted.replaceIndex = hookData.replaceIndex;
402
429
  }
403
-
430
+
404
431
  return converted;
405
432
  }
406
433
 
@@ -723,6 +750,7 @@ function renderWizardModalContent() {
723
750
  // Helper to get translated field labels
724
751
  const getFieldLabel = (fieldKey) => {
725
752
  const labels = {
753
+ 'tool': t('hook.wizard.cliTool') || 'CLI Tool',
726
754
  'threshold': t('hook.wizard.thresholdPaths') || 'Threshold (paths)',
727
755
  'timeout': t('hook.wizard.timeoutSeconds') || 'Timeout (seconds)'
728
756
  };
@@ -731,6 +759,7 @@ function renderWizardModalContent() {
731
759
 
732
760
  const getFieldDesc = (fieldKey) => {
733
761
  const descs = {
762
+ 'tool': t('hook.wizard.cliToolDesc') || 'CLI tool for CLAUDE.md generation',
734
763
  'threshold': t('hook.wizard.thresholdPathsDesc') || 'Number of paths to trigger batch update',
735
764
  'timeout': t('hook.wizard.timeoutSecondsDesc') || 'Auto-flush queue after this time'
736
765
  };
@@ -1096,20 +1125,19 @@ function generateWizardCommand() {
1096
1125
  keywords: c.keywords.split(',').map(k => k.trim()).filter(k => k)
1097
1126
  }));
1098
1127
 
1099
- const params = JSON.stringify({ configs: configJson, prompt: '$CLAUDE_PROMPT' });
1100
- return `ccw tool exec skill_context_loader '${params}'`;
1128
+ // Use node + spawnSync for cross-platform JSON handling
1129
+ const paramsObj = { configs: configJson, prompt: '${p.user_prompt}' };
1130
+ return `node -e "const p=JSON.parse(process.env.HOOK_INPUT||'{}');require('child_process').spawnSync('ccw',['tool','exec','skill_context_loader',JSON.stringify(${JSON.stringify(paramsObj).replace('${p.user_prompt}', "'+p.user_prompt+'")})],{stdio:'inherit'})"`;
1101
1131
  } else {
1102
- // auto mode
1103
- const params = JSON.stringify({ mode: 'auto', prompt: '$CLAUDE_PROMPT' });
1104
- return `ccw tool exec skill_context_loader '${params}'`;
1132
+ // auto mode - use node + spawnSync
1133
+ return `node -e "const p=JSON.parse(process.env.HOOK_INPUT||'{}');require('child_process').spawnSync('ccw',['tool','exec','skill_context_loader',JSON.stringify({mode:'auto',prompt:p.user_prompt||''})],{stdio:'inherit'})"`;
1105
1134
  }
1106
1135
  }
1107
1136
 
1108
1137
  // Handle memory-update wizard (default)
1109
- // Now uses memory_queue for batched updates with configurable threshold/timeout
1110
- // The command adds to queue, configuration is applied separately via submitHookWizard
1111
- const params = `"{\\"action\\":\\"add\\",\\"path\\":\\"$CLAUDE_PROJECT_DIR\\"}"`;
1112
- return `ccw tool exec memory_queue ${params}`;
1138
+ // Use node + spawnSync for cross-platform JSON handling
1139
+ const selectedTool = wizardConfig.tool || 'gemini';
1140
+ return `node -e "require('child_process').spawnSync(process.platform==='win32'?'cmd':'ccw',process.platform==='win32'?['/c','ccw','tool','exec','memory_queue',JSON.stringify({action:'add',path:process.env.CLAUDE_PROJECT_DIR,tool:'${selectedTool}'})]:['tool','exec','memory_queue',JSON.stringify({action:'add',path:process.env.CLAUDE_PROJECT_DIR,tool:'${selectedTool}'})],{stdio:'inherit'})"`;
1113
1141
  }
1114
1142
 
1115
1143
  async function submitHookWizard() {
@@ -1192,13 +1220,18 @@ async function submitHookWizard() {
1192
1220
  const baseTemplate = HOOK_TEMPLATES[selectedOption.templateId];
1193
1221
  if (!baseTemplate) return;
1194
1222
 
1195
- const command = generateWizardCommand();
1196
-
1197
- const hookData = {
1198
- command: 'bash',
1199
- args: ['-c', command]
1223
+ // Build hook data with configured values
1224
+ let hookData = {
1225
+ command: baseTemplate.command,
1226
+ args: [...baseTemplate.args]
1200
1227
  };
1201
1228
 
1229
+ // For memory-update wizard, use configured tool in args (cross-platform)
1230
+ if (wizard.id === 'memory-update') {
1231
+ const selectedTool = wizardConfig.tool || 'gemini';
1232
+ hookData.args = ['-e', `require('child_process').spawnSync(process.platform==='win32'?'cmd':'ccw',process.platform==='win32'?['/c','ccw','tool','exec','memory_queue',JSON.stringify({action:'add',path:process.env.CLAUDE_PROJECT_DIR,tool:'${selectedTool}'})]:['tool','exec','memory_queue',JSON.stringify({action:'add',path:process.env.CLAUDE_PROJECT_DIR,tool:'${selectedTool}'})],{stdio:'inherit'})`];
1233
+ }
1234
+
1202
1235
  if (baseTemplate.matcher) {
1203
1236
  hookData.matcher = baseTemplate.matcher;
1204
1237
  }
@@ -1207,6 +1240,7 @@ async function submitHookWizard() {
1207
1240
 
1208
1241
  // For memory-update wizard, also configure queue settings
1209
1242
  if (wizard.id === 'memory-update') {
1243
+ const selectedTool = wizardConfig.tool || 'gemini';
1210
1244
  const threshold = wizardConfig.threshold || 5;
1211
1245
  const timeout = wizardConfig.timeout || 300;
1212
1246
  try {
@@ -1217,7 +1251,7 @@ async function submitHookWizard() {
1217
1251
  body: JSON.stringify({ tool: 'memory_queue', params: configParams })
1218
1252
  });
1219
1253
  if (response.ok) {
1220
- showRefreshToast(`Queue configured: threshold=${threshold}, timeout=${timeout}s`, 'success');
1254
+ showRefreshToast(`Queue configured: tool=${selectedTool}, threshold=${threshold}, timeout=${timeout}s`, 'success');
1221
1255
  }
1222
1256
  } catch (e) {
1223
1257
  console.warn('Failed to configure memory queue:', e);
@@ -1,6 +1,9 @@
1
1
  // Navigation and Routing
2
2
  // Manages navigation events, active state, content title updates, search, and path selector
3
3
 
4
+ // View lifecycle management
5
+ var currentViewDestroy = null;
6
+
4
7
  // Path Selector
5
8
  function initPathSelector() {
6
9
  const btn = document.getElementById('pathButton');
@@ -54,6 +57,11 @@ function initPathSelector() {
54
57
 
55
58
  // Cleanup function for view transitions
56
59
  function cleanupPreviousView() {
60
+ // Call current view's destroy function if exists
61
+ if (currentViewDestroy) {
62
+ currentViewDestroy();
63
+ currentViewDestroy = null;
64
+ }
57
65
  // Cleanup graph explorer
58
66
  if (currentView === 'graph-explorer' && typeof window.cleanupGraphExplorer === 'function') {
59
67
  window.cleanupGraphExplorer();
@@ -121,6 +129,10 @@ function initNavigation() {
121
129
  renderCliManager();
122
130
  } else if (currentView === 'cli-history') {
123
131
  renderCliHistoryView();
132
+ // Register destroy function for cli-history view
133
+ if (typeof window.destroyCliHistoryView === 'function') {
134
+ currentViewDestroy = window.destroyCliHistoryView;
135
+ }
124
136
  } else if (currentView === 'hook-manager') {
125
137
  renderHookManager();
126
138
  } else if (currentView === 'memory') {
@@ -133,6 +145,10 @@ function initNavigation() {
133
145
  renderRulesManager();
134
146
  } else if (currentView === 'claude-manager') {
135
147
  renderClaudeManager();
148
+ // Register destroy function for claude-manager view
149
+ if (typeof window.initClaudeManager === 'function') {
150
+ window.initClaudeManager();
151
+ }
136
152
  } else if (currentView === 'graph-explorer') {
137
153
  renderGraphExplorer();
138
154
  } else if (currentView === 'help') {
@@ -216,8 +232,10 @@ function updateContentTitle() {
216
232
  } else if (currentView === 'issue-discovery') {
217
233
  titleEl.textContent = t('title.issueDiscovery');
218
234
  } else if (currentView === 'liteTasks') {
219
- const names = { 'lite-plan': t('title.litePlanSessions'), 'lite-fix': t('title.liteFixSessions') };
235
+ const names = { 'lite-plan': t('title.litePlanSessions'), 'lite-fix': t('title.liteFixSessions'), 'multi-cli-plan': t('title.multiCliPlanSessions') || 'Multi-CLI Plan Sessions' };
220
236
  titleEl.textContent = names[currentLiteType] || t('title.liteTasks');
237
+ } else if (currentView === 'multiCliDetail') {
238
+ titleEl.textContent = t('title.multiCliDetail') || 'Multi-CLI Discussion Detail';
221
239
  } else if (currentView === 'sessionDetail') {
222
240
  titleEl.textContent = t('title.sessionDetail');
223
241
  } else if (currentView === 'liteTaskDetail') {
@@ -319,12 +337,14 @@ function updateSidebarCounts(data) {
319
337
  if (archivedCount) archivedCount.textContent = data.archivedSessions?.length || 0;
320
338
  if (allCount) allCount.textContent = (data.activeSessions?.length || 0) + (data.archivedSessions?.length || 0);
321
339
 
322
- // Update lite task counts
323
- const litePlanCount = document.querySelector('.nav-item[data-lite="lite-plan"] .nav-count');
324
- const liteFixCount = document.querySelector('.nav-item[data-lite="lite-fix"] .nav-count');
340
+ // Update lite task counts (using ID selectors to match dashboard.html structure)
341
+ const litePlanCount = document.getElementById('badgeLitePlan');
342
+ const liteFixCount = document.getElementById('badgeLiteFix');
343
+ const multiCliPlanCount = document.getElementById('badgeMultiCliPlan');
325
344
 
326
345
  if (litePlanCount) litePlanCount.textContent = data.liteTasks?.litePlan?.length || 0;
327
346
  if (liteFixCount) liteFixCount.textContent = data.liteTasks?.liteFix?.length || 0;
347
+ if (multiCliPlanCount) multiCliPlanCount.textContent = data.liteTasks?.multiCliPlan?.length || 0;
328
348
  }
329
349
 
330
350
  // ========== Navigation Badge Aggregation ==========