claude-code-workflow 6.3.48 → 6.3.49

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 (211) hide show
  1. package/.claude/CLAUDE.md +6 -8
  2. package/.claude/agents/action-planning-agent.md +28 -45
  3. package/.claude/agents/cli-lite-planning-agent.md +93 -1
  4. package/.claude/agents/code-developer.md +144 -27
  5. package/.claude/commands/ccw-coordinator.md +175 -21
  6. package/.claude/commands/ccw-debug.md +832 -0
  7. package/.claude/commands/ccw.md +90 -9
  8. package/.claude/commands/cli/cli-init.md +1 -0
  9. package/.claude/commands/issue/convert-to-plan.md +718 -0
  10. package/.claude/commands/issue/from-brainstorm.md +382 -0
  11. package/.claude/commands/memory/tips.md +332 -0
  12. package/.claude/commands/workflow/analyze-with-file.md +804 -0
  13. package/.claude/commands/workflow/brainstorm/auto-parallel.md +18 -43
  14. package/.claude/commands/workflow/brainstorm/role-analysis.md +705 -0
  15. package/.claude/commands/workflow/brainstorm-with-file.md +1153 -0
  16. package/.claude/commands/workflow/debug-with-file.md +7 -5
  17. package/.claude/commands/workflow/execute.md +6 -4
  18. package/.claude/commands/workflow/lite-plan.md +2 -2
  19. package/.claude/commands/workflow/plan-verify.md +162 -327
  20. package/.claude/commands/workflow/plan.md +162 -26
  21. package/.claude/commands/workflow/replan.md +78 -2
  22. package/.claude/commands/workflow/{review-fix.md → review-cycle-fix.md} +6 -6
  23. package/.claude/commands/workflow/review-module-cycle.md +2 -2
  24. package/.claude/commands/workflow/review-session-cycle.md +2 -2
  25. package/.claude/commands/workflow/tools/conflict-resolution.md +16 -26
  26. package/.claude/commands/workflow/tools/context-gather.md +81 -118
  27. package/.claude/commands/workflow/tools/task-generate-agent.md +94 -10
  28. package/.claude/skills/ccw-help/command.json +4 -4
  29. package/.claude/skills/lite-skill-generator/SKILL.md +650 -0
  30. package/.claude/skills/lite-skill-generator/templates/simple-skill.md +68 -0
  31. package/.claude/skills/lite-skill-generator/templates/style-guide.md +64 -0
  32. package/.claude/skills/skill-generator/SKILL.md +277 -85
  33. package/.claude/skills/skill-generator/phases/01-requirements-discovery.md +4 -15
  34. package/.claude/skills/skill-generator/phases/02-structure-generation.md +72 -17
  35. package/.claude/skills/skill-generator/phases/03-phase-generation.md +218 -51
  36. package/.claude/skills/skill-generator/phases/04-specs-templates.md +111 -41
  37. package/.claude/skills/skill-generator/phases/05-validation.md +139 -56
  38. package/.claude/skills/skill-generator/templates/autonomous-action.md +78 -268
  39. package/.claude/skills/skill-generator/templates/autonomous-orchestrator.md +14 -0
  40. package/.claude/skills/skill-generator/templates/code-analysis-action.md +12 -0
  41. package/.claude/skills/skill-generator/templates/llm-action.md +12 -0
  42. package/.claude/skills/skill-generator/templates/script-template.md +368 -0
  43. package/.claude/skills/skill-generator/templates/sequential-phase.md +14 -0
  44. package/.claude/skills/skill-generator/templates/skill-md.md +14 -0
  45. package/.claude/skills/skill-tuning/SKILL.md +130 -266
  46. package/.claude/skills/skill-tuning/phases/orchestrator.md +95 -283
  47. package/.claude/skills/skill-tuning/specs/problem-taxonomy.md +90 -198
  48. package/.claude/skills/skill-tuning/specs/tuning-strategies.md +193 -1345
  49. package/.claude/workflows/cli-templates/schemas/plan-verify-agent-schema.json +47 -0
  50. package/.claude/workflows/cli-templates/schemas/verify-json-schema.json +158 -0
  51. package/.claude/workflows/cli-tools-usage.md +1 -1
  52. package/.codex/AGENTS.md +1 -3
  53. package/.codex/prompts/analyze-with-file.md +607 -0
  54. package/.codex/prompts/brainstorm-to-cycle.md +455 -0
  55. package/.codex/prompts/brainstorm-with-file.md +933 -0
  56. package/.codex/prompts/debug-with-file.md +15 -20
  57. package/.codex/skills/ccw-cli-tools/SKILL.md +559 -0
  58. package/ccw/dist/commands/cli.d.ts.map +1 -1
  59. package/ccw/dist/commands/cli.js +29 -5
  60. package/ccw/dist/commands/cli.js.map +1 -1
  61. package/ccw/dist/commands/issue.d.ts +2 -0
  62. package/ccw/dist/commands/issue.d.ts.map +1 -1
  63. package/ccw/dist/commands/issue.js +62 -20
  64. package/ccw/dist/commands/issue.js.map +1 -1
  65. package/ccw/dist/config/litellm-api-config-manager.d.ts.map +1 -1
  66. package/ccw/dist/config/litellm-api-config-manager.js +5 -3
  67. package/ccw/dist/config/litellm-api-config-manager.js.map +1 -1
  68. package/ccw/dist/config/litellm-provider-models.d.ts +73 -0
  69. package/ccw/dist/config/litellm-provider-models.d.ts.map +1 -0
  70. package/ccw/dist/config/litellm-provider-models.js +172 -0
  71. package/ccw/dist/config/litellm-provider-models.js.map +1 -0
  72. package/ccw/dist/config/provider-models.d.ts +25 -51
  73. package/ccw/dist/config/provider-models.d.ts.map +1 -1
  74. package/ccw/dist/config/provider-models.js +84 -149
  75. package/ccw/dist/config/provider-models.js.map +1 -1
  76. package/ccw/dist/config/storage-paths.d.ts.map +1 -1
  77. package/ccw/dist/config/storage-paths.js +23 -5
  78. package/ccw/dist/config/storage-paths.js.map +1 -1
  79. package/ccw/dist/core/auth/csrf-middleware.js +3 -3
  80. package/ccw/dist/core/auth/csrf-middleware.js.map +1 -1
  81. package/ccw/dist/core/dashboard-generator.d.ts.map +1 -1
  82. package/ccw/dist/core/dashboard-generator.js +3 -1
  83. package/ccw/dist/core/dashboard-generator.js.map +1 -1
  84. package/ccw/dist/core/routes/claude-routes.d.ts.map +1 -1
  85. package/ccw/dist/core/routes/claude-routes.js +206 -14
  86. package/ccw/dist/core/routes/claude-routes.js.map +1 -1
  87. package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
  88. package/ccw/dist/core/routes/cli-routes.js.map +1 -1
  89. package/ccw/dist/core/routes/commands-routes.d.ts +7 -0
  90. package/ccw/dist/core/routes/commands-routes.d.ts.map +1 -0
  91. package/ccw/dist/core/routes/commands-routes.js +480 -0
  92. package/ccw/dist/core/routes/commands-routes.js.map +1 -0
  93. package/ccw/dist/core/routes/model-routes.d.ts +11 -0
  94. package/ccw/dist/core/routes/model-routes.d.ts.map +1 -0
  95. package/ccw/dist/core/routes/model-routes.js +112 -0
  96. package/ccw/dist/core/routes/model-routes.js.map +1 -0
  97. package/ccw/dist/core/routes/nav-status-routes.d.ts.map +1 -1
  98. package/ccw/dist/core/routes/nav-status-routes.js +84 -1
  99. package/ccw/dist/core/routes/nav-status-routes.js.map +1 -1
  100. package/ccw/dist/core/routes/provider-routes.d.ts +11 -0
  101. package/ccw/dist/core/routes/provider-routes.d.ts.map +1 -0
  102. package/ccw/dist/core/routes/provider-routes.js +67 -0
  103. package/ccw/dist/core/routes/provider-routes.js.map +1 -0
  104. package/ccw/dist/core/routes/skills-routes.d.ts.map +1 -1
  105. package/ccw/dist/core/routes/skills-routes.js +219 -7
  106. package/ccw/dist/core/routes/skills-routes.js.map +1 -1
  107. package/ccw/dist/core/routes/system-routes.d.ts.map +1 -1
  108. package/ccw/dist/core/routes/system-routes.js +58 -6
  109. package/ccw/dist/core/routes/system-routes.js.map +1 -1
  110. package/ccw/dist/core/server.d.ts.map +1 -1
  111. package/ccw/dist/core/server.js +13 -0
  112. package/ccw/dist/core/server.js.map +1 -1
  113. package/ccw/dist/mcp-server/index.js +2 -2
  114. package/ccw/dist/mcp-server/index.js.map +1 -1
  115. package/ccw/dist/tools/claude-cli-tools.d.ts +48 -11
  116. package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -1
  117. package/ccw/dist/tools/claude-cli-tools.js +146 -50
  118. package/ccw/dist/tools/claude-cli-tools.js.map +1 -1
  119. package/ccw/dist/tools/cli-config-manager.d.ts +1 -13
  120. package/ccw/dist/tools/cli-config-manager.d.ts.map +1 -1
  121. package/ccw/dist/tools/cli-config-manager.js +3 -27
  122. package/ccw/dist/tools/cli-config-manager.js.map +1 -1
  123. package/ccw/dist/tools/cli-executor-core.d.ts.map +1 -1
  124. package/ccw/dist/tools/cli-executor-core.js +7 -2
  125. package/ccw/dist/tools/cli-executor-core.js.map +1 -1
  126. package/ccw/dist/tools/cli-executor-state.d.ts.map +1 -1
  127. package/ccw/dist/tools/cli-history-store.d.ts +11 -0
  128. package/ccw/dist/tools/cli-history-store.d.ts.map +1 -1
  129. package/ccw/dist/tools/cli-history-store.js +82 -2
  130. package/ccw/dist/tools/cli-history-store.js.map +1 -1
  131. package/ccw/dist/tools/command-registry.d.ts +7 -0
  132. package/ccw/dist/tools/command-registry.d.ts.map +1 -1
  133. package/ccw/dist/tools/command-registry.js +14 -1
  134. package/ccw/dist/tools/command-registry.js.map +1 -1
  135. package/ccw/dist/tools/generate-module-docs.d.ts.map +1 -1
  136. package/ccw/dist/tools/generate-module-docs.js +11 -7
  137. package/ccw/dist/tools/generate-module-docs.js.map +1 -1
  138. package/ccw/dist/tools/litellm-executor.d.ts +1 -0
  139. package/ccw/dist/tools/litellm-executor.d.ts.map +1 -1
  140. package/ccw/dist/tools/litellm-executor.js +11 -9
  141. package/ccw/dist/tools/litellm-executor.js.map +1 -1
  142. package/ccw/dist/types/skill-types.d.ts +97 -0
  143. package/ccw/dist/types/skill-types.d.ts.map +1 -0
  144. package/ccw/dist/types/skill-types.js +6 -0
  145. package/ccw/dist/types/skill-types.js.map +1 -0
  146. package/ccw/src/commands/cli.ts +36 -5
  147. package/ccw/src/commands/issue.ts +81 -26
  148. package/ccw/src/config/litellm-api-config-manager.ts +5 -3
  149. package/ccw/src/config/litellm-provider-models.ts +222 -0
  150. package/ccw/src/config/provider-models.ts +91 -190
  151. package/ccw/src/config/storage-paths.ts +20 -5
  152. package/ccw/src/core/auth/csrf-middleware.ts +3 -3
  153. package/ccw/src/core/dashboard-generator.ts +3 -1
  154. package/ccw/src/core/routes/claude-routes.ts +233 -15
  155. package/ccw/src/core/routes/cli-routes.ts +2 -3
  156. package/ccw/src/core/routes/commands-routes.ts +620 -0
  157. package/ccw/src/core/routes/nav-status-routes.ts +95 -1
  158. package/ccw/src/core/routes/provider-routes.ts +78 -0
  159. package/ccw/src/core/routes/skills-routes.ts +266 -45
  160. package/ccw/src/core/routes/system-routes.ts +102 -50
  161. package/ccw/src/core/server.ts +13 -0
  162. package/ccw/src/mcp-server/index.ts +2 -2
  163. package/ccw/src/templates/dashboard-css/18-cli-settings.css +35 -0
  164. package/ccw/src/templates/dashboard-css/37-commands.css +193 -0
  165. package/ccw/src/templates/dashboard-js/components/navigation.js +4 -0
  166. package/ccw/src/templates/dashboard-js/i18n.js +116 -0
  167. package/ccw/src/templates/dashboard-js/views/cli-manager.js +249 -4
  168. package/ccw/src/templates/dashboard-js/views/commands-manager.js +503 -0
  169. package/ccw/src/templates/dashboard-js/views/issue-manager.js +7 -7
  170. package/ccw/src/templates/dashboard-js/views/mcp-manager.js +2 -7
  171. package/ccw/src/templates/dashboard-js/views/skills-manager.js +164 -23
  172. package/ccw/src/templates/dashboard.html +7 -0
  173. package/ccw/src/tools/claude-cli-tools.ts +170 -56
  174. package/ccw/src/tools/cli-config-manager.ts +2 -33
  175. package/ccw/src/tools/cli-executor-core.ts +8 -2
  176. package/ccw/src/tools/cli-history-store.ts +92 -2
  177. package/ccw/src/tools/command-registry.ts +16 -1
  178. package/ccw/src/tools/generate-module-docs.ts +11 -7
  179. package/ccw/src/tools/litellm-executor.ts +13 -9
  180. package/ccw/src/types/skill-types.ts +99 -0
  181. package/package.json +1 -1
  182. package/.claude/commands/enhance-prompt.md +0 -93
  183. package/.claude/commands/memory/code-map-memory.md +0 -687
  184. package/.claude/commands/memory/docs.md +0 -615
  185. package/.claude/commands/memory/load-skill-memory.md +0 -182
  186. package/.claude/commands/memory/skill-memory.md +0 -525
  187. package/.claude/commands/memory/swagger-docs.md +0 -773
  188. package/.claude/commands/memory/tech-research-rules.md +0 -310
  189. package/.claude/commands/memory/workflow-skill-memory.md +0 -517
  190. package/.claude/commands/task/breakdown.md +0 -208
  191. package/.claude/commands/task/create.md +0 -152
  192. package/.claude/commands/task/execute.md +0 -270
  193. package/.claude/commands/task/replan.md +0 -441
  194. package/.claude/commands/version.md +0 -254
  195. package/.claude/commands/workflow/action-plan-verify.md +0 -485
  196. package/.claude/commands/workflow/brainstorm/api-designer.md +0 -587
  197. package/.claude/commands/workflow/brainstorm/data-architect.md +0 -220
  198. package/.claude/commands/workflow/brainstorm/product-manager.md +0 -200
  199. package/.claude/commands/workflow/brainstorm/product-owner.md +0 -200
  200. package/.claude/commands/workflow/brainstorm/scrum-master.md +0 -200
  201. package/.claude/commands/workflow/brainstorm/subject-matter-expert.md +0 -200
  202. package/.claude/commands/workflow/brainstorm/system-architect.md +0 -389
  203. package/.claude/commands/workflow/brainstorm/ui-designer.md +0 -221
  204. package/.claude/commands/workflow/brainstorm/ux-expert.md +0 -221
  205. package/.claude/commands/workflow/debug.md +0 -331
  206. package/.claude/commands/workflow/develop-with-file.md +0 -1044
  207. package/.claude/skills/ccw-loop/README.md +0 -303
  208. package/.claude/skills/skill-generator/templates/script-bash.md +0 -277
  209. package/.claude/skills/skill-generator/templates/script-python.md +0 -198
  210. package/.codex/prompts/debug.md +0 -318
  211. package/ccw/src/core/routes/mcp-routes.ts.backup +0 -549
@@ -4,10 +4,13 @@
4
4
  // ========== Skills State ==========
5
5
  var skillsData = {
6
6
  projectSkills: [],
7
- userSkills: []
7
+ userSkills: [],
8
+ disabledProjectSkills: [],
9
+ disabledUserSkills: []
8
10
  };
9
11
  var selectedSkill = null;
10
12
  var skillsLoading = false;
13
+ var showDisabledSkills = false;
11
14
 
12
15
  // ========== Main Render Function ==========
13
16
  async function renderSkillsManager() {
@@ -36,18 +39,20 @@ async function renderSkillsManager() {
36
39
  async function loadSkillsData() {
37
40
  skillsLoading = true;
38
41
  try {
39
- const response = await fetch('/api/skills?path=' + encodeURIComponent(projectPath));
42
+ const response = await fetch('/api/skills?path=' + encodeURIComponent(projectPath) + '&includeDisabled=true');
40
43
  if (!response.ok) throw new Error('Failed to load skills');
41
44
  const data = await response.json();
42
45
  skillsData = {
43
46
  projectSkills: data.projectSkills || [],
44
- userSkills: data.userSkills || []
47
+ userSkills: data.userSkills || [],
48
+ disabledProjectSkills: data.disabledProjectSkills || [],
49
+ disabledUserSkills: data.disabledUserSkills || []
45
50
  };
46
51
  // Update badge
47
52
  updateSkillsBadge();
48
53
  } catch (err) {
49
54
  console.error('Failed to load skills:', err);
50
- skillsData = { projectSkills: [], userSkills: [] };
55
+ skillsData = { projectSkills: [], userSkills: [], disabledProjectSkills: [], disabledUserSkills: [] };
51
56
  } finally {
52
57
  skillsLoading = false;
53
58
  }
@@ -67,6 +72,9 @@ function renderSkillsView() {
67
72
 
68
73
  const projectSkills = skillsData.projectSkills || [];
69
74
  const userSkills = skillsData.userSkills || [];
75
+ const disabledProjectSkills = skillsData.disabledProjectSkills || [];
76
+ const disabledUserSkills = skillsData.disabledUserSkills || [];
77
+ const totalDisabled = disabledProjectSkills.length + disabledUserSkills.length;
70
78
 
71
79
  container.innerHTML = `
72
80
  <div class="skills-manager">
@@ -109,7 +117,7 @@ function renderSkillsView() {
109
117
  </div>
110
118
  ` : `
111
119
  <div class="skills-grid grid gap-3">
112
- ${projectSkills.map(skill => renderSkillCard(skill, 'project')).join('')}
120
+ ${projectSkills.map(skill => renderSkillCard(skill, 'project', false)).join('')}
113
121
  </div>
114
122
  `}
115
123
  </div>
@@ -133,11 +141,48 @@ function renderSkillsView() {
133
141
  </div>
134
142
  ` : `
135
143
  <div class="skills-grid grid gap-3">
136
- ${userSkills.map(skill => renderSkillCard(skill, 'user')).join('')}
144
+ ${userSkills.map(skill => renderSkillCard(skill, 'user', false)).join('')}
137
145
  </div>
138
146
  `}
139
147
  </div>
140
148
 
149
+ <!-- Disabled Skills Section -->
150
+ ${totalDisabled > 0 ? `
151
+ <div class="skills-section mb-6">
152
+ <div class="flex items-center justify-between mb-4 cursor-pointer" onclick="toggleDisabledSkillsSection()">
153
+ <div class="flex items-center gap-2">
154
+ <i data-lucide="${showDisabledSkills ? 'chevron-down' : 'chevron-right'}" class="w-5 h-5 text-muted-foreground transition-transform"></i>
155
+ <i data-lucide="eye-off" class="w-5 h-5 text-muted-foreground"></i>
156
+ <h3 class="text-lg font-semibold text-muted-foreground">${t('skills.disabledSkills')}</h3>
157
+ </div>
158
+ <span class="text-sm text-muted-foreground">${totalDisabled} ${t('skills.skillsCount')}</span>
159
+ </div>
160
+
161
+ ${showDisabledSkills ? `
162
+ ${disabledProjectSkills.length > 0 ? `
163
+ <div class="mb-4">
164
+ <div class="flex items-center gap-2 mb-2">
165
+ <span class="text-xs px-2 py-0.5 bg-muted text-muted-foreground rounded-full">${t('skills.projectSkills')}</span>
166
+ </div>
167
+ <div class="skills-grid grid gap-3">
168
+ ${disabledProjectSkills.map(skill => renderSkillCard(skill, 'project', true)).join('')}
169
+ </div>
170
+ </div>
171
+ ` : ''}
172
+ ${disabledUserSkills.length > 0 ? `
173
+ <div>
174
+ <div class="flex items-center gap-2 mb-2">
175
+ <span class="text-xs px-2 py-0.5 bg-muted text-muted-foreground rounded-full">${t('skills.userSkills')}</span>
176
+ </div>
177
+ <div class="skills-grid grid gap-3">
178
+ ${disabledUserSkills.map(skill => renderSkillCard(skill, 'user', true)).join('')}
179
+ </div>
180
+ </div>
181
+ ` : ''}
182
+ ` : ''}
183
+ </div>
184
+ ` : ''}
185
+
141
186
  <!-- Skill Detail Panel -->
142
187
  ${selectedSkill ? renderSkillDetailPanel(selectedSkill) : ''}
143
188
  </div>
@@ -147,19 +192,19 @@ function renderSkillsView() {
147
192
  if (typeof lucide !== 'undefined') lucide.createIcons();
148
193
  }
149
194
 
150
- function renderSkillCard(skill, location) {
195
+ function renderSkillCard(skill, location, isDisabled = false) {
151
196
  const hasAllowedTools = skill.allowedTools && skill.allowedTools.length > 0;
152
197
  const hasSupportingFiles = skill.supportingFiles && skill.supportingFiles.length > 0;
153
198
  const locationIcon = location === 'project' ? 'folder' : 'user';
154
199
  const locationClass = location === 'project' ? 'text-primary' : 'text-indigo';
155
200
  const locationBg = location === 'project' ? 'bg-primary/10' : 'bg-indigo/10';
156
201
  const folderName = skill.folderName || skill.name;
202
+ const cardOpacity = isDisabled ? 'opacity-60' : '';
157
203
 
158
204
  return `
159
- <div class="skill-card bg-card border border-border rounded-lg p-4 hover:shadow-md transition-all cursor-pointer"
160
- onclick="showSkillDetail('${escapeHtml(folderName)}', '${location}')">
205
+ <div class="skill-card bg-card border border-border rounded-lg p-4 hover:shadow-md transition-all ${cardOpacity}">
161
206
  <div class="flex items-start justify-between mb-3">
162
- <div class="flex items-center gap-3">
207
+ <div class="flex items-center gap-3 cursor-pointer" onclick="showSkillDetail('${escapeHtml(folderName)}', '${location}')">
163
208
  <div class="w-10 h-10 ${locationBg} rounded-lg flex items-center justify-center">
164
209
  <i data-lucide="sparkles" class="w-5 h-5 ${locationClass}"></i>
165
210
  </div>
@@ -168,27 +213,40 @@ function renderSkillCard(skill, location) {
168
213
  ${skill.version ? `<span class="text-xs text-muted-foreground">v${escapeHtml(skill.version)}</span>` : ''}
169
214
  </div>
170
215
  </div>
171
- <div class="flex items-center gap-1">
216
+ <div class="flex items-center gap-2">
172
217
  <span class="inline-flex items-center px-2 py-0.5 text-xs font-medium rounded-full ${locationBg} ${locationClass}">
173
218
  <i data-lucide="${locationIcon}" class="w-3 h-3 mr-1"></i>
174
219
  ${location}
175
220
  </span>
221
+ <button class="p-1.5 rounded-lg transition-colors ${isDisabled ? 'text-green-600 hover:bg-green-100' : 'text-amber-600 hover:bg-amber-100'}"
222
+ data-skill-toggle="${escapeHtml(folderName)}"
223
+ onclick="event.stopPropagation(); toggleSkillEnabled('${escapeHtml(folderName)}', '${location}', ${!isDisabled})"
224
+ title="${isDisabled ? t('skills.enable') : t('skills.disable')}">
225
+ <i data-lucide="${isDisabled ? 'toggle-left' : 'toggle-right'}" class="w-4 h-4"></i>
226
+ </button>
176
227
  </div>
177
228
  </div>
178
229
 
179
- <p class="text-sm text-muted-foreground mb-3 line-clamp-2">${escapeHtml(skill.description || t('skills.noDescription'))}</p>
230
+ <p class="text-sm text-muted-foreground mb-3 line-clamp-2 cursor-pointer" onclick="showSkillDetail('${escapeHtml(folderName)}', '${location}')">${escapeHtml(skill.description || t('skills.noDescription'))}</p>
180
231
 
181
- <div class="flex items-center gap-3 text-xs text-muted-foreground">
182
- ${hasAllowedTools ? `
183
- <span class="flex items-center gap-1">
184
- <i data-lucide="lock" class="w-3 h-3"></i>
185
- ${skill.allowedTools.length} ${t('skills.tools')}
186
- </span>
187
- ` : ''}
188
- ${hasSupportingFiles ? `
189
- <span class="flex items-center gap-1">
190
- <i data-lucide="file-text" class="w-3 h-3"></i>
191
- ${skill.supportingFiles.length} ${t('skills.files')}
232
+ <div class="flex items-center justify-between text-xs text-muted-foreground">
233
+ <div class="flex items-center gap-3">
234
+ ${hasAllowedTools ? `
235
+ <span class="flex items-center gap-1">
236
+ <i data-lucide="lock" class="w-3 h-3"></i>
237
+ ${skill.allowedTools.length} ${t('skills.tools')}
238
+ </span>
239
+ ` : ''}
240
+ ${hasSupportingFiles ? `
241
+ <span class="flex items-center gap-1">
242
+ <i data-lucide="file-text" class="w-3 h-3"></i>
243
+ ${skill.supportingFiles.length} ${t('skills.files')}
244
+ </span>
245
+ ` : ''}
246
+ </div>
247
+ ${isDisabled && skill.disabledAt ? `
248
+ <span class="text-xs text-muted-foreground/70">
249
+ ${t('skills.disabledAt')}: ${formatDisabledDate(skill.disabledAt)}
192
250
  </span>
193
251
  ` : ''}
194
252
  </div>
@@ -373,6 +431,89 @@ function editSkill(skillName, location) {
373
431
  }
374
432
  }
375
433
 
434
+ // ========== Enable/Disable Skills Functions ==========
435
+
436
+ // Track loading state for skill toggle operations
437
+ var toggleLoadingSkills = {};
438
+
439
+ async function toggleSkillEnabled(skillName, location, currentlyEnabled) {
440
+ // Prevent double-click
441
+ var loadingKey = skillName + '-' + location;
442
+ if (toggleLoadingSkills[loadingKey]) return;
443
+
444
+ var action = currentlyEnabled ? 'disable' : 'enable';
445
+ var confirmMessage = currentlyEnabled
446
+ ? t('skills.disableConfirm', { name: skillName })
447
+ : t('skills.enableConfirm', { name: skillName });
448
+
449
+ if (!confirm(confirmMessage)) return;
450
+
451
+ // Set loading state
452
+ toggleLoadingSkills[loadingKey] = true;
453
+ var toggleBtn = document.querySelector('[data-skill-toggle="' + skillName + '"]');
454
+ if (toggleBtn) {
455
+ toggleBtn.disabled = true;
456
+ toggleBtn.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i>';
457
+ if (window.lucide) lucide.createIcons();
458
+ }
459
+
460
+ try {
461
+ var response = await fetch('/api/skills/' + encodeURIComponent(skillName) + '/' + action, {
462
+ method: 'POST',
463
+ headers: { 'Content-Type': 'application/json' },
464
+ body: JSON.stringify({ location: location, projectPath: projectPath })
465
+ });
466
+
467
+ if (!response.ok) {
468
+ // Robust JSON parsing with fallback
469
+ var errorMessage = 'Operation failed';
470
+ try {
471
+ var error = await response.json();
472
+ errorMessage = error.message || errorMessage;
473
+ } catch (jsonErr) {
474
+ errorMessage = response.statusText || errorMessage;
475
+ }
476
+ throw new Error(errorMessage);
477
+ }
478
+
479
+ // Close detail panel if open
480
+ selectedSkill = null;
481
+
482
+ // Reload skills data
483
+ await loadSkillsData();
484
+ renderSkillsView();
485
+
486
+ if (window.showToast) {
487
+ var message = currentlyEnabled
488
+ ? t('skills.disableSuccess', { name: skillName })
489
+ : t('skills.enableSuccess', { name: skillName });
490
+ showToast(message, 'success');
491
+ }
492
+ } catch (err) {
493
+ console.error('Failed to toggle skill:', err);
494
+ if (window.showToast) {
495
+ showToast(err.message || t('skills.toggleError'), 'error');
496
+ }
497
+ } finally {
498
+ // Clear loading state
499
+ delete toggleLoadingSkills[loadingKey];
500
+ }
501
+ }
502
+
503
+ function toggleDisabledSkillsSection() {
504
+ showDisabledSkills = !showDisabledSkills;
505
+ renderSkillsView();
506
+ }
507
+
508
+ function formatDisabledDate(isoString) {
509
+ try {
510
+ const date = new Date(isoString);
511
+ return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
512
+ } catch {
513
+ return isoString;
514
+ }
515
+ }
516
+
376
517
  // ========== Create Skill Modal ==========
377
518
  var skillCreateState = {
378
519
  mode: 'import', // 'import' or 'cli-generate'
@@ -503,11 +503,13 @@
503
503
  <i data-lucide="history" class="nav-icon"></i>
504
504
  <span class="nav-text flex-1" data-i18n="nav.history">History</span>
505
505
  </li>
506
+ <!-- Hidden: CodexLens Manager (feature disabled)
506
507
  <li class="nav-item flex items-center gap-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="codexlens-manager" data-tooltip="CodexLens Manager">
507
508
  <i data-lucide="search-code" class="nav-icon"></i>
508
509
  <span class="nav-text flex-1" data-i18n="nav.codexLensManager">CodexLens</span>
509
510
  <span class="badge px-2 py-0.5 text-xs font-semibold rounded-full bg-hover text-muted-foreground" id="badgeCodexLens">-</span>
510
511
  </li>
512
+ -->
511
513
  <li class="nav-item flex items-center gap-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="api-settings" data-tooltip="API Settings">
512
514
  <i data-lucide="settings" class="nav-icon"></i>
513
515
  <span class="nav-text flex-1" data-i18n="nav.apiSettings">API Settings</span>
@@ -670,6 +672,11 @@
670
672
  <span class="nav-text flex-1" data-i18n="nav.rules">Rules</span>
671
673
  <span class="badge px-2 py-0.5 text-xs font-semibold rounded-full bg-hover text-muted-foreground" id="badgeRules">0</span>
672
674
  </li>
675
+ <li class="nav-item flex items-center gap-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="commands-manager" data-tooltip="Commands Management">
676
+ <i data-lucide="terminal" class="nav-icon"></i>
677
+ <span class="nav-text flex-1" data-i18n="nav.commands">Commands</span>
678
+ <span class="badge px-2 py-0.5 text-xs font-semibold rounded-full bg-hover text-muted-foreground" id="badgeCommands">0</span>
679
+ </li>
673
680
  <li class="nav-item flex items-center gap-2 px-3 py-2.5 text-sm text-muted-foreground hover:bg-hover hover:text-foreground rounded cursor-pointer transition-colors" data-view="claude-manager" data-tooltip="CLAUDE.md Manager">
674
681
  <i data-lucide="file-code" class="nav-icon"></i>
675
682
  <span class="nav-text flex-1" data-i18n="nav.claudeManager">CLAUDE.md</span>
@@ -33,6 +33,11 @@ export interface ClaudeCliTool {
33
33
  enabled: boolean;
34
34
  primaryModel?: string;
35
35
  secondaryModel?: string;
36
+ /**
37
+ * Available models for this tool (shown in UI dropdown)
38
+ * If not provided, defaults will be used based on tool type
39
+ */
40
+ availableModels?: string[];
36
41
  tags: string[];
37
42
  /**
38
43
  * Tool type determines routing:
@@ -80,7 +85,6 @@ export interface ClaudeCacheSettings {
80
85
  export interface ClaudeCliToolsConfig {
81
86
  $schema?: string;
82
87
  version: string;
83
- models?: Record<string, string[]>; // PREDEFINED_MODELS
84
88
  tools: Record<string, ClaudeCliTool>; // All tools: builtin, cli-wrapper, api-endpoint
85
89
  apiEndpoints?: ClaudeApiEndpoint[]; // @deprecated Use tools with type: 'api-endpoint' instead
86
90
  customEndpoints?: ClaudeCustomEndpoint[]; // @deprecated Use tools with type: 'cli-wrapper' or 'api-endpoint' instead
@@ -120,34 +124,14 @@ export interface ClaudeCliCombinedConfig extends ClaudeCliToolsConfig {
120
124
 
121
125
  // ========== Default Config ==========
122
126
 
123
- // Predefined models for each tool
124
- const PREDEFINED_MODELS: Record<CliToolName, string[]> = {
125
- gemini: ['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.0-flash', 'gemini-1.5-pro', 'gemini-1.5-flash'],
126
- qwen: ['coder-model', 'vision-model', 'qwen2.5-coder-32b'],
127
- codex: ['gpt-5.2', 'gpt-4.1', 'o4-mini', 'o3'],
128
- claude: ['sonnet', 'opus', 'haiku', 'claude-sonnet-4-5-20250929', 'claude-opus-4-5-20251101'],
129
- opencode: [
130
- 'opencode/glm-4.7-free',
131
- 'opencode/gpt-5-nano',
132
- 'opencode/grok-code',
133
- 'opencode/minimax-m2.1-free',
134
- 'anthropic/claude-sonnet-4-20250514',
135
- 'anthropic/claude-opus-4-20250514',
136
- 'openai/gpt-4.1',
137
- 'openai/o3',
138
- 'google/gemini-2.5-pro',
139
- 'google/gemini-2.5-flash'
140
- ]
141
- };
142
-
143
127
  const DEFAULT_TOOLS_CONFIG: ClaudeCliToolsConfig = {
144
- version: '3.2.0',
145
- models: { ...PREDEFINED_MODELS },
128
+ version: '3.4.0',
146
129
  tools: {
147
130
  gemini: {
148
131
  enabled: true,
149
132
  primaryModel: 'gemini-2.5-pro',
150
133
  secondaryModel: 'gemini-2.5-flash',
134
+ availableModels: ['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.0-flash', 'gemini-2.0-flash-thinking', 'gemini-1.5-pro'],
151
135
  tags: [],
152
136
  type: 'builtin'
153
137
  },
@@ -155,6 +139,7 @@ const DEFAULT_TOOLS_CONFIG: ClaudeCliToolsConfig = {
155
139
  enabled: true,
156
140
  primaryModel: 'coder-model',
157
141
  secondaryModel: 'coder-model',
142
+ availableModels: ['coder-model', 'vision-model', 'qwen-2.5-coder', 'qwen-2.5-72b'],
158
143
  tags: [],
159
144
  type: 'builtin'
160
145
  },
@@ -162,6 +147,7 @@ const DEFAULT_TOOLS_CONFIG: ClaudeCliToolsConfig = {
162
147
  enabled: true,
163
148
  primaryModel: 'gpt-5.2',
164
149
  secondaryModel: 'gpt-5.2',
150
+ availableModels: ['gpt-5.2', 'gpt-5', 'gpt5-codex', 'o3', 'o1'],
165
151
  tags: [],
166
152
  type: 'builtin'
167
153
  },
@@ -169,6 +155,7 @@ const DEFAULT_TOOLS_CONFIG: ClaudeCliToolsConfig = {
169
155
  enabled: true,
170
156
  primaryModel: 'sonnet',
171
157
  secondaryModel: 'haiku',
158
+ availableModels: ['opus', 'sonnet', 'haiku'],
172
159
  tags: [],
173
160
  type: 'builtin'
174
161
  },
@@ -176,6 +163,7 @@ const DEFAULT_TOOLS_CONFIG: ClaudeCliToolsConfig = {
176
163
  enabled: true,
177
164
  primaryModel: 'opencode/glm-4.7-free',
178
165
  secondaryModel: 'opencode/glm-4.7-free',
166
+ availableModels: ['opencode/glm-4.7-free', 'opencode/deepseek-v3-free'],
179
167
  tags: [],
180
168
  type: 'builtin'
181
169
  }
@@ -260,6 +248,28 @@ function resolveSettingsPath(projectDir: string): { path: string; source: 'proje
260
248
 
261
249
  // ========== Main Functions ==========
262
250
 
251
+ /**
252
+ * Create a timestamped backup of the config file
253
+ * @param filePath - Path to the config file to backup
254
+ * @returns Path to the backup file
255
+ */
256
+ function backupConfigFile(filePath: string): string {
257
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0] + '-' +
258
+ new Date().toISOString().replace(/[:.]/g, '-').split('T')[1].substring(0, 8);
259
+ const backupPath = `${filePath}.${timestamp}.bak`;
260
+
261
+ try {
262
+ if (fs.existsSync(filePath)) {
263
+ fs.copyFileSync(filePath, backupPath);
264
+ debugLog(`[claude-cli-tools] Created backup: ${backupPath}`);
265
+ }
266
+ return backupPath;
267
+ } catch (err) {
268
+ console.warn('[claude-cli-tools] Failed to create backup:', err);
269
+ return '';
270
+ }
271
+ }
272
+
263
273
  /**
264
274
  * Ensure tool has required fields (for backward compatibility)
265
275
  */
@@ -274,18 +284,31 @@ function ensureToolTags(tool: Partial<ClaudeCliTool>): ClaudeCliTool {
274
284
  }
275
285
 
276
286
  /**
277
- * Migrate config from older versions to v3.2.0
287
+ * Migrate config from older versions to v3.3.0
278
288
  * v3.2.0: All endpoints (cli-wrapper, api-endpoint) are in tools with type field
289
+ * v3.3.0: Remove models field (moved to system reference)
279
290
  */
280
- function migrateConfig(config: any, projectDir: string): ClaudeCliToolsConfig {
291
+ function migrateConfig(config: any, projectDir: string, configPath?: string): ClaudeCliToolsConfig {
281
292
  const version = parseFloat(config.version || '1.0');
293
+ let needsMigration = false;
282
294
 
283
- // Already v3.2+, no migration needed
284
- if (version >= 3.2) {
295
+ // Check if models field exists (v3.3.0 migration)
296
+ if (config.models) {
297
+ needsMigration = true;
298
+ debugLog('[claude-cli-tools] Detected models field, will remove (moved to system reference)');
299
+ }
300
+
301
+ // Already v3.3+, no migration needed
302
+ if (version >= 3.3 && !needsMigration) {
285
303
  return config as ClaudeCliToolsConfig;
286
304
  }
287
305
 
288
- debugLog(`[claude-cli-tools] Migrating config from v${config.version || '1.0'} to v3.2.0`);
306
+ // Create backup before migration if config path is provided
307
+ if (configPath && (version < 3.3 || needsMigration)) {
308
+ backupConfigFile(configPath);
309
+ }
310
+
311
+ debugLog(`[claude-cli-tools] Migrating config from v${config.version || '1.0'} to v3.3.0`);
289
312
 
290
313
  // Try to load legacy cli-config.json for model data
291
314
  let legacyCliConfig: any = null;
@@ -372,9 +395,13 @@ function migrateConfig(config: any, projectDir: string): ClaudeCliToolsConfig {
372
395
  }
373
396
  }
374
397
 
398
+ // Remove models field if it exists (v3.3.0 migration)
399
+ if (config.models) {
400
+ debugLog('[claude-cli-tools] Removed models field (moved to system reference)');
401
+ }
402
+
375
403
  return {
376
- version: '3.2.0',
377
- models: { ...PREDEFINED_MODELS },
404
+ version: '3.3.0',
378
405
  tools: migratedTools,
379
406
  $schema: config.$schema
380
407
  };
@@ -485,9 +512,8 @@ export function loadClaudeCliTools(projectDir: string): ClaudeCliToolsConfig & {
485
512
  const content = fs.readFileSync(resolved.path, 'utf-8');
486
513
  const parsed = JSON.parse(content) as Partial<ClaudeCliCombinedConfig>;
487
514
 
488
- // Migrate older versions to v3.2.0
489
- const migrated = migrateConfig(parsed, projectDir);
490
- const needsSave = migrated.version !== parsed.version;
515
+ // Migrate older versions to v3.3.0 (pass config path for backup)
516
+ const migrated = migrateConfig(parsed, projectDir, resolved.path);
491
517
 
492
518
  // Load user-configured tools only (defaults NOT merged)
493
519
  const mergedTools: Record<string, ClaudeCliTool> = {};
@@ -501,14 +527,15 @@ export function loadClaudeCliTools(projectDir: string): ClaudeCliToolsConfig & {
501
527
 
502
528
  const config: ClaudeCliToolsConfig & { _source?: string } = {
503
529
  version: migrated.version || DEFAULT_TOOLS_CONFIG.version,
504
- models: migrated.models || DEFAULT_TOOLS_CONFIG.models,
505
530
  tools: mergedTools,
506
531
  $schema: migrated.$schema,
507
532
  _source: resolved.source
508
533
  };
509
534
 
510
- // Save migrated config if version changed
511
- if (needsSave) {
535
+ // Save migrated config if version changed or models field exists
536
+ const needsVersionUpdate = migrated.version !== (parsed as any).version;
537
+ const hasModelsField = (parsed as any).models !== undefined;
538
+ if (needsVersionUpdate || hasModelsField) {
512
539
  try {
513
540
  saveClaudeCliTools(projectDir, config);
514
541
  debugLog(`[claude-cli-tools] Saved migrated config to: ${resolved.path}`);
@@ -674,19 +701,116 @@ export function getDefaultTool(projectDir: string): string {
674
701
  }
675
702
  }
676
703
 
704
+ // ========== Settings Persistence Functions ==========
705
+
706
+ /**
707
+ * Update prompt format setting
708
+ * @param projectDir - Project directory path
709
+ * @param format - Prompt format: 'plain' | 'yaml' | 'json'
710
+ * @returns Updated settings config
711
+ */
712
+ export function setPromptFormat(
713
+ projectDir: string,
714
+ format: 'plain' | 'yaml' | 'json'
715
+ ): ClaudeCliSettingsConfig {
716
+ const settings = loadClaudeCliSettings(projectDir);
717
+ settings.promptFormat = format;
718
+ saveClaudeCliSettings(projectDir, settings);
719
+ return settings;
720
+ }
721
+
722
+ /**
723
+ * Get prompt format setting
724
+ * @param projectDir - Project directory path
725
+ * @returns Current prompt format or 'plain' as fallback
726
+ */
727
+ export function getPromptFormat(projectDir: string): 'plain' | 'yaml' | 'json' {
728
+ try {
729
+ const settings = loadClaudeCliSettings(projectDir);
730
+ return settings.promptFormat || 'plain';
731
+ } catch {
732
+ return 'plain';
733
+ }
734
+ }
735
+
736
+ /**
737
+ * Update smart context enabled setting
738
+ * @param projectDir - Project directory path
739
+ * @param enabled - Whether smart context is enabled
740
+ * @returns Updated settings config
741
+ */
742
+ export function setSmartContextEnabled(
743
+ projectDir: string,
744
+ enabled: boolean
745
+ ): ClaudeCliSettingsConfig {
746
+ const settings = loadClaudeCliSettings(projectDir);
747
+ settings.smartContext = {
748
+ ...settings.smartContext,
749
+ enabled
750
+ };
751
+ saveClaudeCliSettings(projectDir, settings);
752
+ return settings;
753
+ }
754
+
755
+ /**
756
+ * Get smart context enabled setting
757
+ * @param projectDir - Project directory path
758
+ * @returns Current smart context status or false as fallback
759
+ */
760
+ export function getSmartContextEnabled(projectDir: string): boolean {
761
+ try {
762
+ const settings = loadClaudeCliSettings(projectDir);
763
+ return settings.smartContext?.enabled ?? false;
764
+ } catch {
765
+ return false;
766
+ }
767
+ }
768
+
769
+ /**
770
+ * Update native resume setting
771
+ * @param projectDir - Project directory path
772
+ * @param enabled - Whether native resume is enabled
773
+ * @returns Updated settings config
774
+ */
775
+ export function setNativeResume(
776
+ projectDir: string,
777
+ enabled: boolean
778
+ ): ClaudeCliSettingsConfig {
779
+ const settings = loadClaudeCliSettings(projectDir);
780
+ settings.nativeResume = enabled;
781
+ saveClaudeCliSettings(projectDir, settings);
782
+ return settings;
783
+ }
784
+
785
+ /**
786
+ * Get native resume setting
787
+ * @param projectDir - Project directory path
788
+ * @returns Current native resume status or true as fallback
789
+ */
790
+ export function getNativeResume(projectDir: string): boolean {
791
+ try {
792
+ const settings = loadClaudeCliSettings(projectDir);
793
+ return settings.nativeResume ?? true;
794
+ } catch {
795
+ return true;
796
+ }
797
+ }
798
+
677
799
  /**
678
800
  * Add API endpoint as a tool with type: 'api-endpoint'
679
801
  * Usage: --tool <name> or --tool custom --model <id>
680
802
  */
681
803
  export function addClaudeApiEndpoint(
682
804
  projectDir: string,
683
- endpoint: { id: string; name: string; enabled: boolean }
805
+ endpoint: { id: string; name: string; enabled: boolean; model?: string }
684
806
  ): ClaudeCliToolsConfig {
685
807
  const config = loadClaudeCliTools(projectDir);
686
808
 
687
809
  // Add as a tool with type: 'api-endpoint'
688
810
  config.tools[endpoint.name] = {
689
811
  enabled: endpoint.enabled,
812
+ primaryModel: endpoint.model, // Use endpoint.model as primaryModel (can be overridden via --model)
813
+ secondaryModel: endpoint.model, // Same as primary for fallback
690
814
  tags: [],
691
815
  type: 'api-endpoint',
692
816
  id: endpoint.id // Store endpoint ID for settings lookup
@@ -879,21 +1003,8 @@ export function getContextToolsPath(provider: 'codexlens' | 'ace' | 'none'): str
879
1003
  }
880
1004
 
881
1005
  // ========== Model Configuration Functions ==========
882
-
883
- /**
884
- * Get predefined models for a specific tool
885
- */
886
- export function getPredefinedModels(tool: string): string[] {
887
- const toolName = tool as CliToolName;
888
- return PREDEFINED_MODELS[toolName] ? [...PREDEFINED_MODELS[toolName]] : [];
889
- }
890
-
891
- /**
892
- * Get all predefined models
893
- */
894
- export function getAllPredefinedModels(): Record<string, string[]> {
895
- return { ...PREDEFINED_MODELS };
896
- }
1006
+ // NOTE: Model reference data has been moved to system reference (src/config/provider-models.ts)
1007
+ // User configuration only manages primaryModel/secondaryModel per tool via tools.{tool}
897
1008
 
898
1009
  /**
899
1010
  * Get tool configuration (compatible with cli-config-manager interface)
@@ -937,6 +1048,7 @@ export function updateToolConfig(
937
1048
  enabled: boolean;
938
1049
  primaryModel: string;
939
1050
  secondaryModel: string;
1051
+ availableModels: string[];
940
1052
  tags: string[];
941
1053
  envFile: string | null;
942
1054
  }>
@@ -953,6 +1065,9 @@ export function updateToolConfig(
953
1065
  if (updates.secondaryModel !== undefined) {
954
1066
  config.tools[tool].secondaryModel = updates.secondaryModel;
955
1067
  }
1068
+ if (updates.availableModels !== undefined) {
1069
+ config.tools[tool].availableModels = updates.availableModels;
1070
+ }
956
1071
  if (updates.tags !== undefined) {
957
1072
  config.tools[tool].tags = updates.tags;
958
1073
  }
@@ -995,16 +1110,15 @@ export function isToolEnabled(projectDir: string, tool: string): boolean {
995
1110
  }
996
1111
 
997
1112
  /**
998
- * Get full config response for API (includes predefined models)
1113
+ * Get full config response for API
1114
+ * Note: Provider model reference has been moved to system reference (see provider-routes.ts)
999
1115
  */
1000
1116
  export function getFullConfigResponse(projectDir: string): {
1001
1117
  config: ClaudeCliToolsConfig;
1002
- predefinedModels: Record<string, string[]>;
1003
1118
  } {
1004
1119
  const config = loadClaudeCliTools(projectDir);
1005
1120
  return {
1006
- config,
1007
- predefinedModels: { ...PREDEFINED_MODELS }
1121
+ config
1008
1122
  };
1009
1123
  }
1010
1124