claude-code-workflow 6.1.4 → 6.2.2
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.
- package/.claude/CLAUDE.md +10 -0
- package/.claude/agents/action-planning-agent.md +857 -778
- package/.claude/agents/cli-execution-agent.md +266 -269
- package/.claude/agents/cli-explore-agent.md +2 -2
- package/.claude/agents/cli-lite-planning-agent.md +142 -92
- package/.claude/agents/cli-planning-agent.md +4 -4
- package/.claude/agents/code-developer.md +7 -6
- package/.claude/agents/conceptual-planning-agent.md +2 -2
- package/.claude/agents/context-search-agent.md +31 -32
- package/.claude/agents/doc-generator.md +4 -4
- package/.claude/agents/memory-bridge.md +93 -93
- package/.claude/agents/test-context-search-agent.md +8 -7
- package/.claude/agents/test-fix-agent.md +7 -6
- package/.claude/commands/clean.md +516 -0
- package/.claude/commands/memory/compact.md +383 -0
- package/.claude/commands/memory/docs-full-cli.md +471 -471
- package/.claude/commands/memory/docs-related-cli.md +386 -386
- package/.claude/commands/memory/docs.md +615 -615
- package/.claude/commands/memory/load.md +5 -5
- package/.claude/commands/memory/tech-research-rules.md +310 -0
- package/.claude/commands/memory/update-full.md +332 -332
- package/.claude/commands/memory/workflow-skill-memory.md +4 -4
- package/.claude/commands/task/create.md +151 -151
- package/.claude/commands/version.md +254 -254
- package/.claude/commands/workflow/brainstorm/api-designer.md +587 -585
- package/.claude/commands/workflow/brainstorm/artifacts.md +1 -0
- package/.claude/commands/workflow/brainstorm/auto-parallel.md +443 -443
- package/.claude/commands/workflow/brainstorm/data-architect.md +220 -220
- package/.claude/commands/workflow/brainstorm/product-manager.md +200 -200
- package/.claude/commands/workflow/brainstorm/product-owner.md +200 -200
- package/.claude/commands/workflow/brainstorm/scrum-master.md +200 -200
- package/.claude/commands/workflow/brainstorm/subject-matter-expert.md +200 -200
- package/.claude/commands/workflow/brainstorm/system-architect.md +389 -387
- package/.claude/commands/workflow/brainstorm/ui-designer.md +221 -221
- package/.claude/commands/workflow/brainstorm/ux-expert.md +221 -221
- package/.claude/commands/workflow/debug.md +321 -0
- package/.claude/commands/workflow/execute.md +13 -0
- package/.claude/commands/workflow/init.md +165 -164
- package/.claude/commands/workflow/lite-execute.md +119 -13
- package/.claude/commands/workflow/lite-fix.md +623 -621
- package/.claude/commands/workflow/lite-plan.md +610 -592
- package/.claude/commands/workflow/plan.md +5 -5
- package/.claude/commands/workflow/review-module-cycle.md +2 -0
- package/.claude/commands/workflow/review-session-cycle.md +2 -0
- package/.claude/commands/workflow/review.md +297 -291
- package/.claude/commands/workflow/session/complete.md +153 -500
- package/.claude/commands/workflow/session/list.md +95 -95
- package/.claude/commands/workflow/session/resume.md +60 -60
- package/.claude/commands/workflow/session/start.md +199 -199
- package/.claude/commands/workflow/tdd-plan.md +3 -3
- package/.claude/commands/workflow/tdd-verify.md +23 -9
- package/.claude/commands/workflow/test-cycle-execute.md +2 -0
- package/.claude/commands/workflow/test-fix-gen.md +699 -699
- package/.claude/commands/workflow/tools/conflict-resolution.md +104 -18
- package/.claude/commands/workflow/tools/context-gather.md +436 -434
- package/.claude/commands/workflow/tools/task-generate-agent.md +490 -291
- package/.claude/commands/workflow/tools/task-generate-tdd.md +18 -10
- package/.claude/commands/workflow/tools/test-concept-enhanced.md +2 -1
- package/.claude/commands/workflow/tools/test-context-gather.md +1 -0
- package/.claude/commands/workflow/tools/test-task-generate.md +1 -0
- package/.claude/commands/workflow/ui-design/import-from-code.md +9 -6
- package/.claude/skills/command-guide/SKILL.md +5 -5
- package/.claude/skills/command-guide/index/all-commands.json +1 -1
- package/.claude/skills/command-guide/index/by-category.json +1 -1
- package/.claude/skills/command-guide/index/by-use-case.json +1 -1
- package/.claude/skills/command-guide/reference/agents/action-planning-agent.md +857 -778
- package/.claude/skills/command-guide/reference/agents/cli-execution-agent.md +266 -269
- package/.claude/skills/command-guide/reference/agents/cli-explore-agent.md +2 -2
- package/.claude/skills/command-guide/reference/agents/cli-lite-planning-agent.md +142 -92
- package/.claude/skills/command-guide/reference/agents/cli-planning-agent.md +4 -4
- package/.claude/skills/command-guide/reference/agents/code-developer.md +7 -6
- package/.claude/skills/command-guide/reference/agents/conceptual-planning-agent.md +2 -2
- package/.claude/skills/command-guide/reference/agents/context-search-agent.md +31 -32
- package/.claude/skills/command-guide/reference/agents/doc-generator.md +4 -4
- package/.claude/skills/command-guide/reference/agents/memory-bridge.md +93 -93
- package/.claude/skills/command-guide/reference/agents/test-context-search-agent.md +8 -7
- package/.claude/skills/command-guide/reference/agents/test-fix-agent.md +7 -6
- package/.claude/skills/command-guide/reference/commands/memory/docs-full-cli.md +471 -471
- package/.claude/skills/command-guide/reference/commands/memory/docs-related-cli.md +386 -386
- package/.claude/skills/command-guide/reference/commands/memory/docs.md +17 -16
- package/.claude/skills/command-guide/reference/commands/memory/load.md +5 -5
- package/.claude/skills/command-guide/reference/commands/memory/tech-research.md +194 -357
- package/.claude/skills/command-guide/reference/commands/memory/update-full.md +332 -332
- package/.claude/skills/command-guide/reference/commands/memory/workflow-skill-memory.md +4 -4
- package/.claude/skills/command-guide/reference/commands/task/create.md +151 -151
- package/.claude/skills/command-guide/reference/commands/version.md +254 -254
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/api-designer.md +585 -585
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/auto-parallel.md +443 -443
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/data-architect.md +220 -220
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-manager.md +200 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/product-owner.md +200 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/scrum-master.md +200 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/subject-matter-expert.md +200 -200
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/system-architect.md +387 -387
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ui-designer.md +221 -221
- package/.claude/skills/command-guide/reference/commands/workflow/brainstorm/ux-expert.md +221 -221
- package/.claude/skills/command-guide/reference/commands/workflow/execute.md +25 -20
- package/.claude/skills/command-guide/reference/commands/workflow/init.md +164 -164
- package/.claude/skills/command-guide/reference/commands/workflow/lite-execute.md +748 -686
- package/.claude/skills/command-guide/reference/commands/workflow/lite-fix.md +664 -621
- package/.claude/skills/command-guide/reference/commands/workflow/lite-plan.md +645 -592
- package/.claude/skills/command-guide/reference/commands/workflow/plan.md +5 -5
- package/.claude/skills/command-guide/reference/commands/workflow/review.md +25 -18
- package/.claude/skills/command-guide/reference/commands/workflow/session/complete.md +547 -500
- package/.claude/skills/command-guide/reference/commands/workflow/session/list.md +45 -27
- package/.claude/skills/command-guide/reference/commands/workflow/session/resume.md +35 -19
- package/.claude/skills/command-guide/reference/commands/workflow/session/start.md +90 -33
- package/.claude/skills/command-guide/reference/commands/workflow/tdd-plan.md +3 -3
- package/.claude/skills/command-guide/reference/commands/workflow/tdd-verify.md +23 -9
- package/.claude/skills/command-guide/reference/commands/workflow/test-fix-gen.md +699 -699
- package/.claude/skills/command-guide/reference/commands/workflow/tools/conflict-resolution.md +103 -17
- package/.claude/skills/command-guide/reference/commands/workflow/tools/context-gather.md +434 -434
- package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-agent.md +487 -291
- package/.claude/skills/command-guide/reference/commands/workflow/tools/task-generate-tdd.md +17 -10
- package/.claude/skills/command-guide/reference/commands/workflow/tools/test-concept-enhanced.md +1 -1
- package/.claude/skills/command-guide/reference/commands/workflow/ui-design/import-from-code.md +6 -6
- package/.claude/workflows/chinese-response.md +38 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-api.txt +122 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-components.txt +122 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-config.txt +89 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-core.txt +60 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-patterns.txt +70 -0
- package/.claude/workflows/cli-templates/prompts/rules/rule-testing.txt +81 -0
- package/.claude/workflows/cli-templates/prompts/rules/tech-rules-agent-prompt.txt +89 -0
- package/.claude/workflows/cli-templates/prompts/workflow/gemini-solution-design.txt +131 -131
- package/.claude/workflows/cli-templates/prompts/workflow/skill-conflict-patterns.txt +5 -9
- package/.claude/workflows/cli-templates/prompts/workflow/skill-lessons-learned.txt +5 -9
- package/.claude/workflows/cli-templates/protocols/analysis-protocol.md +112 -0
- package/.claude/workflows/cli-templates/protocols/write-protocol.md +201 -0
- package/.claude/workflows/cli-templates/schemas/conflict-resolution-schema.json +137 -0
- package/.claude/workflows/cli-templates/schemas/debug-log-json-schema.json +127 -0
- package/.claude/workflows/cli-templates/schemas/fix-plan-json-schema.json +25 -0
- package/.claude/workflows/cli-templates/schemas/plan-json-schema.json +25 -0
- package/.claude/workflows/cli-tools-usage.md +526 -0
- package/{CLAUDE.md → .claude/workflows/coding-philosophy.md} +24 -45
- package/.claude/workflows/context-tools.md +84 -0
- package/.claude/workflows/file-modification.md +64 -0
- package/.claude/workflows/tool-strategy.md +216 -79
- package/.claude/workflows/windows-platform.md +16 -0
- package/.claude/workflows/workflow-architecture.md +942 -942
- package/.codex/AGENTS.md +63 -330
- package/.codex/prompts/debug.md +318 -0
- package/.codex/prompts/execute.md +273 -0
- package/.codex/prompts/lite-execute.md +164 -0
- package/.codex/prompts/lite-plan.md +469 -0
- package/.codex/prompts.zip +0 -0
- package/.gemini/GEMINI.md +25 -164
- package/.qwen/QWEN.md +0 -139
- package/README.md +29 -9
- package/ccw/README.md +30 -6
- package/ccw/bin/ccw-mcp.js +7 -0
- package/ccw/bin/ccw.js +9 -9
- package/ccw/package.json +65 -47
- package/ccw/src/.workflow/.cli-history/history.db +0 -0
- package/ccw/src/.workflow/.cli-history/history.db-shm +0 -0
- package/ccw/src/.workflow/.cli-history/history.db-wal +0 -0
- package/ccw/src/cli.ts +244 -0
- package/ccw/src/commands/cli.ts +740 -0
- package/ccw/src/commands/core-memory.ts +770 -0
- package/ccw/src/commands/hook.ts +315 -0
- package/ccw/src/commands/install.ts +519 -0
- package/ccw/src/commands/{list.js → list.ts} +1 -1
- package/ccw/src/commands/memory.ts +1090 -0
- package/ccw/src/commands/{serve.js → serve.ts} +14 -5
- package/ccw/src/commands/session-path-resolver.ts +372 -0
- package/ccw/src/commands/session.ts +1141 -0
- package/ccw/src/commands/{stop.js → stop.ts} +16 -6
- package/ccw/src/commands/tool.ts +201 -0
- package/ccw/src/commands/{uninstall.js → uninstall.ts} +89 -40
- package/ccw/src/commands/{upgrade.js → upgrade.ts} +68 -23
- package/ccw/src/commands/{view.js → view.ts} +22 -8
- package/ccw/src/config/storage-paths.ts +670 -0
- package/ccw/src/core/cache-manager.ts +294 -0
- package/ccw/src/core/claude-freshness.ts +319 -0
- package/ccw/src/core/core-memory-store.ts +1528 -0
- package/ccw/src/core/{dashboard-generator-patch.js → dashboard-generator-patch.ts} +18 -0
- package/ccw/src/core/{dashboard-generator.js → dashboard-generator.ts} +69 -12
- package/ccw/src/core/data-aggregator.ts +584 -0
- package/ccw/src/core/history-importer.ts +625 -0
- package/ccw/src/core/{lite-scanner.js → lite-scanner-complete.ts} +162 -66
- package/ccw/src/core/lite-scanner.ts +469 -0
- package/ccw/src/core/{manifest.js → manifest.ts} +104 -34
- package/ccw/src/core/memory-embedder-bridge.ts +262 -0
- package/ccw/src/core/memory-store.ts +978 -0
- package/ccw/src/core/routes/ccw-routes.ts +96 -0
- package/ccw/src/core/routes/claude-routes.ts +1183 -0
- package/ccw/src/core/routes/cli-routes.ts +561 -0
- package/ccw/src/core/routes/codexlens-routes.ts +806 -0
- package/ccw/src/core/routes/core-memory-routes.ts +605 -0
- package/ccw/src/core/routes/files-routes.ts +428 -0
- package/ccw/src/core/routes/graph-routes.md +164 -0
- package/ccw/src/core/routes/graph-routes.ts +626 -0
- package/ccw/src/core/routes/help-routes.ts +308 -0
- package/ccw/src/core/routes/hooks-routes.ts +405 -0
- package/ccw/src/core/routes/mcp-routes.ts +1271 -0
- package/ccw/src/core/routes/mcp-routes.ts.backup +550 -0
- package/ccw/src/core/routes/mcp-templates-db.ts +268 -0
- package/ccw/src/core/routes/memory-routes.ts +1206 -0
- package/ccw/src/core/routes/rules-routes.ts +526 -0
- package/ccw/src/core/routes/session-routes.ts +467 -0
- package/ccw/src/core/routes/skills-routes.ts +599 -0
- package/ccw/src/core/routes/status-routes.ts +57 -0
- package/ccw/src/core/routes/system-routes.ts +427 -0
- package/ccw/src/core/server.ts +431 -0
- package/ccw/src/core/session-clustering-service.ts +1258 -0
- package/ccw/src/core/session-scanner.ts +283 -0
- package/ccw/src/core/websocket.ts +190 -0
- package/ccw/src/{index.js → index.ts} +1 -0
- package/ccw/src/mcp-server/index.ts +186 -0
- package/ccw/src/templates/assets/css/github-dark.min.css +10 -0
- package/ccw/src/templates/assets/css/github.min.css +10 -0
- package/ccw/src/templates/assets/js/cytoscape.min.js +32 -0
- package/ccw/src/templates/assets/js/d3.min.js +2 -0
- package/ccw/src/templates/assets/js/highlight.min.js +1244 -0
- package/ccw/src/templates/assets/js/lucide.min.js +12 -0
- package/ccw/src/templates/assets/js/marked.min.js +69 -0
- package/ccw/src/templates/assets/js/tailwind.js +83 -0
- package/ccw/src/templates/dashboard-css/01-base.css +11 -0
- package/ccw/src/templates/dashboard-css/02-session.css +22 -0
- package/ccw/src/templates/dashboard-css/04-lite-tasks.css +10 -0
- package/ccw/src/templates/dashboard-css/06-cards.css +10 -4
- package/ccw/src/templates/dashboard-css/07-managers.css +1178 -7
- package/ccw/src/templates/dashboard-css/09-explorer.css +23 -12
- package/ccw/src/templates/dashboard-css/10-cli-status.css +337 -0
- package/ccw/src/templates/dashboard-css/11-cli-history.css +271 -0
- package/ccw/src/templates/dashboard-css/12-cli-legacy.css +796 -0
- package/ccw/src/templates/dashboard-css/13-cli-ccw.css +199 -0
- package/ccw/src/templates/dashboard-css/14-cli-modals.css +258 -0
- package/ccw/src/templates/dashboard-css/15-cli-endpoints.css +305 -0
- package/ccw/src/templates/dashboard-css/16-cli-session.css +241 -0
- package/ccw/src/templates/dashboard-css/17-cli-conversation.css +283 -0
- package/ccw/src/templates/dashboard-css/18-cli-settings.css +160 -0
- package/ccw/src/templates/dashboard-css/19-cli-native-session.css +496 -0
- package/ccw/src/templates/dashboard-css/20-cli-taskqueue.css +188 -0
- package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +310 -0
- package/ccw/src/templates/dashboard-css/22-cli-semantic.css +240 -0
- package/ccw/src/templates/dashboard-css/23-memory.css +2390 -0
- package/ccw/src/templates/dashboard-css/24-prompt-history.css +1089 -0
- package/ccw/src/templates/dashboard-css/25-skills-rules.css +326 -0
- package/ccw/src/templates/dashboard-css/26-claude-manager.css +908 -0
- package/ccw/src/templates/dashboard-css/27-graph-explorer.css +1678 -0
- package/ccw/src/templates/dashboard-css/28-mcp-manager.css +748 -0
- package/ccw/src/templates/dashboard-css/29-help.css +264 -0
- package/ccw/src/templates/dashboard-css/30-core-memory.css +1700 -0
- package/ccw/src/templates/dashboard-js/api.js +162 -142
- package/ccw/src/templates/dashboard-js/components/carousel.js +4 -4
- package/ccw/src/templates/dashboard-js/components/cli-history.js +876 -0
- package/ccw/src/templates/dashboard-js/components/cli-status.js +978 -0
- package/ccw/src/templates/dashboard-js/components/global-notifications.js +508 -219
- package/ccw/src/templates/dashboard-js/components/hook-manager.js +1277 -282
- package/ccw/src/templates/dashboard-js/components/index-manager.js +302 -0
- package/ccw/src/templates/dashboard-js/components/mcp-manager.js +718 -27
- package/ccw/src/templates/dashboard-js/components/modals.js +66 -0
- package/ccw/src/templates/dashboard-js/components/navigation.js +80 -12
- package/ccw/src/templates/dashboard-js/components/notifications.js +758 -194
- package/ccw/src/templates/dashboard-js/components/storage-manager.js +478 -0
- package/ccw/src/templates/dashboard-js/components/tabs-other.js +157 -6
- package/ccw/src/templates/dashboard-js/components/task-queue-sidebar.js +716 -0
- package/ccw/src/templates/dashboard-js/help-i18n.js +272 -0
- package/ccw/src/templates/dashboard-js/i18n.js +2807 -0
- package/ccw/src/templates/dashboard-js/main.js +15 -0
- package/ccw/src/templates/dashboard-js/state.js +243 -42
- package/ccw/src/templates/dashboard-js/utils.js +47 -1
- package/ccw/src/templates/dashboard-js/views/claude-manager.js +912 -0
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +2272 -0
- package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +964 -0
- package/ccw/src/templates/dashboard-js/views/core-memory-clusters.js +503 -0
- package/ccw/src/templates/dashboard-js/views/core-memory.js +782 -0
- package/ccw/src/templates/dashboard-js/views/explorer.js +888 -852
- package/ccw/src/templates/dashboard-js/views/graph-explorer.js +1157 -0
- package/ccw/src/templates/dashboard-js/views/help.js +856 -0
- package/ccw/src/templates/dashboard-js/views/history.js +337 -0
- package/ccw/src/templates/dashboard-js/views/home.js +61 -15
- package/ccw/src/templates/dashboard-js/views/hook-manager.js +311 -43
- package/ccw/src/templates/dashboard-js/views/lite-tasks.js +204 -28
- package/ccw/src/templates/dashboard-js/views/mcp-manager.js +2187 -411
- package/ccw/src/templates/dashboard-js/views/mcp-manager.js.backup +1729 -0
- package/ccw/src/templates/dashboard-js/views/mcp-manager.js.new +928 -0
- package/ccw/src/templates/dashboard-js/views/memory.js +1221 -0
- package/ccw/src/templates/dashboard-js/views/prompt-history.js +713 -0
- package/ccw/src/templates/dashboard-js/views/rules-manager.js +828 -0
- package/ccw/src/templates/dashboard-js/views/session-detail.js +54 -53
- package/ccw/src/templates/dashboard-js/views/skills-manager.js +819 -0
- package/ccw/src/templates/dashboard.html +185 -85
- package/ccw/src/templates/hooks-config-example.json +60 -0
- package/ccw/src/tools/classify-folders.ts +245 -0
- package/ccw/src/tools/cli-config-manager.ts +268 -0
- package/ccw/src/tools/cli-executor.ts +2014 -0
- package/ccw/src/tools/cli-history-store.ts +1195 -0
- package/ccw/src/tools/codex-lens.ts +1141 -0
- package/ccw/src/tools/{convert-tokens-to-css.js → convert-tokens-to-css.ts} +73 -23
- package/ccw/src/tools/core-memory.ts +444 -0
- package/ccw/src/tools/detect-changed-modules.ts +325 -0
- package/ccw/src/tools/{discover-design-files.js → discover-design-files.ts} +74 -24
- package/ccw/src/tools/edit-file.ts +568 -0
- package/ccw/src/tools/{generate-module-docs.js → generate-module-docs.ts} +207 -185
- package/ccw/src/tools/{get-modules-by-depth.js → get-modules-by-depth.ts} +120 -79
- package/ccw/src/tools/index.ts +370 -0
- package/ccw/src/tools/native-session-discovery.ts +795 -0
- package/ccw/src/tools/notifier.ts +129 -0
- package/ccw/src/tools/read-file.ts +410 -0
- package/ccw/src/tools/resume-strategy.ts +345 -0
- package/ccw/src/tools/session-content-parser.ts +619 -0
- package/ccw/src/tools/session-manager.ts +1026 -0
- package/ccw/src/tools/smart-context.ts +228 -0
- package/ccw/src/tools/smart-search.ts +2065 -0
- package/ccw/src/tools/smart-search.ts.backup +1233 -0
- package/ccw/src/tools/storage-manager.ts +455 -0
- package/ccw/src/tools/write-file.ts +222 -0
- package/ccw/src/types/config.ts +11 -0
- package/ccw/src/types/index.ts +3 -0
- package/ccw/src/types/session.ts +25 -0
- package/ccw/src/types/tool.ts +41 -0
- package/ccw/src/utils/{browser-launcher.js → browser-launcher.ts} +10 -8
- package/ccw/src/utils/file-utils.ts +48 -0
- package/ccw/src/utils/{path-resolver.js → path-resolver.ts} +114 -78
- package/ccw/src/utils/path-validator.ts +153 -0
- package/ccw/src/utils/{ui.js → ui.ts} +32 -25
- package/codex-lens/pyproject.toml +48 -0
- package/codex-lens/src/codexlens/.workflow/.cli-history/history.db +0 -0
- package/codex-lens/src/codexlens/__init__.py +28 -0
- package/codex-lens/src/codexlens/__main__.py +14 -0
- package/codex-lens/src/codexlens/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/__main__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/entities.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/__pycache__/errors.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__init__.py +27 -0
- package/codex-lens/src/codexlens/cli/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/commands.py +1931 -0
- package/codex-lens/src/codexlens/cli/embedding_manager.py +620 -0
- package/codex-lens/src/codexlens/cli/model_manager.py +289 -0
- package/codex-lens/src/codexlens/cli/output.py +124 -0
- package/codex-lens/src/codexlens/config.py +201 -0
- package/codex-lens/src/codexlens/entities.py +121 -0
- package/codex-lens/src/codexlens/errors.py +55 -0
- package/codex-lens/src/codexlens/indexing/README.md +77 -0
- package/codex-lens/src/codexlens/indexing/__init__.py +4 -0
- package/codex-lens/src/codexlens/indexing/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/indexing/__pycache__/symbol_extractor.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/indexing/symbol_extractor.py +243 -0
- package/codex-lens/src/codexlens/parsers/__init__.py +8 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/encoding.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/factory.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/tokenizer.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/__pycache__/treesitter_parser.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/parsers/encoding.py +202 -0
- package/codex-lens/src/codexlens/parsers/factory.py +256 -0
- package/codex-lens/src/codexlens/parsers/tokenizer.py +98 -0
- package/codex-lens/src/codexlens/parsers/treesitter_parser.py +335 -0
- package/codex-lens/src/codexlens/search/__init__.py +15 -0
- package/codex-lens/src/codexlens/search/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/enrichment.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/query_parser.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/chain_search.py +647 -0
- package/codex-lens/src/codexlens/search/enrichment.py +150 -0
- package/codex-lens/src/codexlens/search/hybrid_search.py +313 -0
- package/codex-lens/src/codexlens/search/query_parser.py +242 -0
- package/codex-lens/src/codexlens/search/ranking.py +274 -0
- package/codex-lens/src/codexlens/semantic/__init__.py +39 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/ann_index.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/code_extractor.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/graph_analyzer.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/llm_enhancer.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/ann_index.py +414 -0
- package/codex-lens/src/codexlens/semantic/chunker.py +448 -0
- package/codex-lens/src/codexlens/semantic/code_extractor.py +274 -0
- package/codex-lens/src/codexlens/semantic/embedder.py +185 -0
- package/codex-lens/src/codexlens/semantic/vector_store.py +955 -0
- package/codex-lens/src/codexlens/storage/__init__.py +29 -0
- package/codex-lens/src/codexlens/storage/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/dir_index.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/file_cache.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/index_tree.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/migration_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/registry.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/sqlite_store.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/__pycache__/sqlite_utils.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/dir_index.py +1850 -0
- package/codex-lens/src/codexlens/storage/file_cache.py +32 -0
- package/codex-lens/src/codexlens/storage/index_tree.py +776 -0
- package/codex-lens/src/codexlens/storage/migration_manager.py +154 -0
- package/codex-lens/src/codexlens/storage/migrations/__init__.py +1 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_001_normalize_keywords.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_002_add_token_metadata.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_003_code_relationships.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_004_dual_fts.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_005_cleanup_unused_fields.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_001_normalize_keywords.py +123 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_002_add_token_metadata.py +48 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_004_dual_fts.py +232 -0
- package/codex-lens/src/codexlens/storage/migrations/migration_005_cleanup_unused_fields.py +196 -0
- package/codex-lens/src/codexlens/storage/path_mapper.py +274 -0
- package/codex-lens/src/codexlens/storage/registry.py +670 -0
- package/codex-lens/src/codexlens/storage/sqlite_store.py +576 -0
- package/codex-lens/src/codexlens/storage/sqlite_utils.py +64 -0
- package/package.json +4 -1
- package/.claude/commands/memory/tech-research.md +0 -477
- package/.claude/scripts/classify-folders.sh +0 -39
- package/.claude/scripts/convert_tokens_to_css.sh +0 -229
- package/.claude/scripts/detect_changed_modules.sh +0 -161
- package/.claude/scripts/discover-design-files.sh +0 -87
- package/.claude/scripts/extract-animations.js +0 -243
- package/.claude/scripts/extract-computed-styles.js +0 -118
- package/.claude/scripts/extract-layout-structure.js +0 -411
- package/.claude/scripts/generate_module_docs.sh +0 -717
- package/.claude/scripts/get_modules_by_depth.sh +0 -170
- package/.claude/scripts/ui-generate-preview.sh +0 -395
- package/.claude/scripts/ui-instantiate-prototypes.sh +0 -815
- package/.claude/scripts/update_module_claude.sh +0 -337
- package/.claude/workflows/context-search-strategy.md +0 -77
- package/.claude/workflows/intelligent-tools-strategy.md +0 -662
- package/ccw/src/cli.js +0 -119
- package/ccw/src/commands/install.js +0 -324
- package/ccw/src/commands/tool.js +0 -138
- package/ccw/src/core/data-aggregator.js +0 -409
- package/ccw/src/core/server.js +0 -2063
- package/ccw/src/core/session-scanner.js +0 -235
- package/ccw/src/tools/classify-folders.js +0 -204
- package/ccw/src/tools/detect-changed-modules.js +0 -288
- package/ccw/src/tools/edit-file.js +0 -266
- package/ccw/src/tools/index.js +0 -176
- package/ccw/src/utils/file-utils.js +0 -48
|
@@ -1,283 +1,1278 @@
|
|
|
1
|
-
// Hook Manager Component
|
|
2
|
-
// Manages Claude Code hooks configuration from settings.json
|
|
3
|
-
|
|
4
|
-
// ========== Hook State ==========
|
|
5
|
-
let hookConfig = {
|
|
6
|
-
global: { hooks: {} },
|
|
7
|
-
project: { hooks: {} }
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
// ========== Hook Templates ==========
|
|
11
|
-
const HOOK_TEMPLATES = {
|
|
12
|
-
'ccw-notify': {
|
|
13
|
-
event: 'PostToolUse',
|
|
14
|
-
matcher: 'Write',
|
|
15
|
-
command: '
|
|
16
|
-
args: ['-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// ==========
|
|
255
|
-
function
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
1
|
+
// Hook Manager Component
|
|
2
|
+
// Manages Claude Code hooks configuration from settings.json
|
|
3
|
+
|
|
4
|
+
// ========== Hook State ==========
|
|
5
|
+
let hookConfig = {
|
|
6
|
+
global: { hooks: {} },
|
|
7
|
+
project: { hooks: {} }
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// ========== Hook Templates ==========
|
|
11
|
+
const HOOK_TEMPLATES = {
|
|
12
|
+
'ccw-notify': {
|
|
13
|
+
event: 'PostToolUse',
|
|
14
|
+
matcher: 'Write',
|
|
15
|
+
command: 'bash',
|
|
16
|
+
args: ['-c', 'INPUT=$(cat); FILE_PATH=$(echo "$INPUT" | jq -r ".tool_input.file_path // .tool_input.path // empty"); [ -n "$FILE_PATH" ] && curl -s -X POST -H "Content-Type: application/json" -d "{\\"type\\":\\"file_written\\",\\"filePath\\":\\"$FILE_PATH\\"}" http://localhost:3456/api/hook || true'],
|
|
17
|
+
description: 'Notify CCW dashboard when files are written',
|
|
18
|
+
category: 'notification'
|
|
19
|
+
},
|
|
20
|
+
'log-tool': {
|
|
21
|
+
event: 'PostToolUse',
|
|
22
|
+
matcher: '',
|
|
23
|
+
command: 'bash',
|
|
24
|
+
args: ['-c', 'INPUT=$(cat); TOOL=$(echo "$INPUT" | jq -r ".tool_name // empty"); FILE=$(echo "$INPUT" | jq -r ".tool_input.file_path // .tool_input.path // empty"); echo "[$(date)] Tool: $TOOL, File: $FILE" >> ~/.claude/tool-usage.log'],
|
|
25
|
+
description: 'Log all tool executions to a file',
|
|
26
|
+
category: 'logging'
|
|
27
|
+
},
|
|
28
|
+
'lint-check': {
|
|
29
|
+
event: 'PostToolUse',
|
|
30
|
+
matcher: 'Write',
|
|
31
|
+
command: 'bash',
|
|
32
|
+
args: ['-c', 'INPUT=$(cat); FILE=$(echo "$INPUT" | jq -r ".tool_input.file_path // empty"); if [[ "$FILE" =~ \\.(js|ts|jsx|tsx)$ ]]; then npx eslint "$FILE" --fix 2>/dev/null || true; fi'],
|
|
33
|
+
description: 'Run ESLint on JavaScript/TypeScript files after write',
|
|
34
|
+
category: 'quality'
|
|
35
|
+
},
|
|
36
|
+
'git-add': {
|
|
37
|
+
event: 'PostToolUse',
|
|
38
|
+
matcher: 'Write',
|
|
39
|
+
command: 'bash',
|
|
40
|
+
args: ['-c', 'INPUT=$(cat); FILE=$(echo "$INPUT" | jq -r ".tool_input.file_path // empty"); [ -n "$FILE" ] && git add "$FILE" 2>/dev/null || true'],
|
|
41
|
+
description: 'Automatically stage written files to git',
|
|
42
|
+
category: 'git'
|
|
43
|
+
},
|
|
44
|
+
'codexlens-update': {
|
|
45
|
+
event: 'PostToolUse',
|
|
46
|
+
matcher: 'Write|Edit',
|
|
47
|
+
command: 'bash',
|
|
48
|
+
args: ['-c', 'INPUT=$(cat); FILE=$(echo "$INPUT" | jq -r ".tool_input.file_path // .tool_input.path // empty"); [ -d ".codexlens" ] && [ -n "$FILE" ] && (python -m codexlens update "$FILE" --json 2>/dev/null || ~/.codexlens/venv/bin/python -m codexlens update "$FILE" --json 2>/dev/null || true)'],
|
|
49
|
+
description: 'Auto-update code index when files are written or edited',
|
|
50
|
+
category: 'indexing'
|
|
51
|
+
},
|
|
52
|
+
'memory-update-related': {
|
|
53
|
+
event: 'Stop',
|
|
54
|
+
matcher: '',
|
|
55
|
+
command: 'bash',
|
|
56
|
+
args: ['-c', 'ccw tool exec update_module_claude \'{"strategy":"related","tool":"gemini"}\''],
|
|
57
|
+
description: 'Update CLAUDE.md for changed modules when session ends',
|
|
58
|
+
category: 'memory',
|
|
59
|
+
configurable: true,
|
|
60
|
+
config: {
|
|
61
|
+
tool: { type: 'select', options: ['gemini', 'qwen', 'codex'], default: 'gemini', label: 'CLI Tool' },
|
|
62
|
+
strategy: { type: 'select', options: ['related', 'single-layer'], default: 'related', label: 'Strategy' }
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
'memory-update-periodic': {
|
|
66
|
+
event: 'PostToolUse',
|
|
67
|
+
matcher: 'Write|Edit',
|
|
68
|
+
command: 'bash',
|
|
69
|
+
args: ['-c', 'INTERVAL=300; LAST_FILE=~/.claude/.last_memory_update; NOW=$(date +%s); LAST=0; [ -f "$LAST_FILE" ] && LAST=$(cat "$LAST_FILE"); if [ $((NOW - LAST)) -ge $INTERVAL ]; then echo $NOW > "$LAST_FILE"; ccw tool exec update_module_claude \'{"strategy":"related","tool":"gemini"}\' & fi'],
|
|
70
|
+
description: 'Periodically update CLAUDE.md (default: 5 min interval)',
|
|
71
|
+
category: 'memory',
|
|
72
|
+
configurable: true,
|
|
73
|
+
config: {
|
|
74
|
+
tool: { type: 'select', options: ['gemini', 'qwen', 'codex'], default: 'gemini', label: 'CLI Tool' },
|
|
75
|
+
interval: { type: 'number', default: 300, min: 60, max: 3600, label: 'Interval (seconds)', step: 60 }
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
'memory-update-count-based': {
|
|
79
|
+
event: 'PostToolUse',
|
|
80
|
+
matcher: 'Write|Edit',
|
|
81
|
+
command: 'bash',
|
|
82
|
+
args: ['-c', 'THRESHOLD=10; COUNT_FILE=~/.claude/.memory_update_count; INPUT=$(cat); FILE_PATH=$(echo "$INPUT" | jq -r ".tool_input.file_path // .tool_input.path // empty"); [ -z "$FILE_PATH" ] && exit 0; COUNT=0; [ -f "$COUNT_FILE" ] && COUNT=$(cat "$COUNT_FILE" 2>/dev/null || echo 0); COUNT=$((COUNT + 1)); echo $COUNT > "$COUNT_FILE"; if [ $COUNT -ge $THRESHOLD ]; then echo 0 > "$COUNT_FILE"; ccw tool exec update_module_claude \'{"strategy":"related","tool":"gemini"}\' & fi'],
|
|
83
|
+
description: 'Update CLAUDE.md when file changes reach threshold (default: 10 files)',
|
|
84
|
+
category: 'memory',
|
|
85
|
+
configurable: true,
|
|
86
|
+
config: {
|
|
87
|
+
tool: { type: 'select', options: ['gemini', 'qwen', 'codex'], default: 'gemini', label: 'CLI Tool' },
|
|
88
|
+
threshold: { type: 'number', default: 10, min: 3, max: 50, label: 'File count threshold', step: 1 }
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
// SKILL Context Loader templates
|
|
92
|
+
'skill-context-keyword': {
|
|
93
|
+
event: 'UserPromptSubmit',
|
|
94
|
+
matcher: '',
|
|
95
|
+
command: 'bash',
|
|
96
|
+
args: ['-c', 'ccw tool exec skill_context_loader --stdin'],
|
|
97
|
+
description: 'Load SKILL context based on keyword matching in user prompt',
|
|
98
|
+
category: 'skill',
|
|
99
|
+
configurable: true,
|
|
100
|
+
config: {
|
|
101
|
+
keywords: { type: 'text', default: '', label: 'Keywords (comma-separated)', placeholder: 'react,workflow,api' },
|
|
102
|
+
skills: { type: 'text', default: '', label: 'SKILL Names (comma-separated)', placeholder: 'prompt-enhancer,command-guide' }
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
'skill-context-auto': {
|
|
106
|
+
event: 'UserPromptSubmit',
|
|
107
|
+
matcher: '',
|
|
108
|
+
command: 'bash',
|
|
109
|
+
args: ['-c', 'ccw tool exec skill_context_loader --stdin --mode auto'],
|
|
110
|
+
description: 'Auto-detect and load SKILL based on skill name in prompt',
|
|
111
|
+
category: 'skill',
|
|
112
|
+
configurable: false
|
|
113
|
+
},
|
|
114
|
+
'memory-file-read': {
|
|
115
|
+
event: 'PostToolUse',
|
|
116
|
+
matcher: 'Read|mcp__ccw-tools__read_file',
|
|
117
|
+
command: 'ccw',
|
|
118
|
+
args: ['memory', 'track', '--type', 'file', '--action', 'read', '--stdin'],
|
|
119
|
+
description: 'Track file reads to build context heatmap',
|
|
120
|
+
category: 'memory',
|
|
121
|
+
timeout: 5000
|
|
122
|
+
},
|
|
123
|
+
'memory-file-write': {
|
|
124
|
+
event: 'PostToolUse',
|
|
125
|
+
matcher: 'Write|Edit|mcp__ccw-tools__write_file|mcp__ccw-tools__edit_file',
|
|
126
|
+
command: 'ccw',
|
|
127
|
+
args: ['memory', 'track', '--type', 'file', '--action', 'write', '--stdin'],
|
|
128
|
+
description: 'Track file modifications to identify core modules',
|
|
129
|
+
category: 'memory',
|
|
130
|
+
timeout: 5000
|
|
131
|
+
},
|
|
132
|
+
'memory-prompt-track': {
|
|
133
|
+
event: 'UserPromptSubmit',
|
|
134
|
+
matcher: '',
|
|
135
|
+
command: 'ccw',
|
|
136
|
+
args: ['memory', 'track', '--type', 'topic', '--action', 'mention', '--stdin'],
|
|
137
|
+
description: 'Record user prompts for pattern analysis',
|
|
138
|
+
category: 'memory',
|
|
139
|
+
timeout: 5000
|
|
140
|
+
},
|
|
141
|
+
// Session Context - Progressive disclosure based on session state
|
|
142
|
+
// First prompt: returns cluster overview, subsequent: intent-matched sessions
|
|
143
|
+
'session-context': {
|
|
144
|
+
event: 'UserPromptSubmit',
|
|
145
|
+
matcher: '',
|
|
146
|
+
command: 'ccw',
|
|
147
|
+
args: ['hook', 'session-context', '--stdin'],
|
|
148
|
+
description: 'Progressive session context (cluster overview → intent matching)',
|
|
149
|
+
category: 'context',
|
|
150
|
+
timeout: 5000
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// ========== Wizard Templates (Special Category) ==========
|
|
155
|
+
const WIZARD_TEMPLATES = {
|
|
156
|
+
'memory-update': {
|
|
157
|
+
name: 'Memory Update Hook',
|
|
158
|
+
description: 'Automatically update CLAUDE.md documentation based on code changes',
|
|
159
|
+
icon: 'brain',
|
|
160
|
+
options: [
|
|
161
|
+
{
|
|
162
|
+
id: 'on-stop',
|
|
163
|
+
name: 'On Session End',
|
|
164
|
+
description: 'Update documentation when Claude session ends',
|
|
165
|
+
templateId: 'memory-update-related'
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: 'periodic',
|
|
169
|
+
name: 'Periodic Update',
|
|
170
|
+
description: 'Update documentation at regular intervals during session',
|
|
171
|
+
templateId: 'memory-update-periodic'
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: 'count-based',
|
|
175
|
+
name: 'Count-Based Update',
|
|
176
|
+
description: 'Update documentation when file changes reach threshold',
|
|
177
|
+
templateId: 'memory-update-count-based'
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
configFields: [
|
|
181
|
+
{ key: 'tool', type: 'select', label: 'CLI Tool', options: ['gemini', 'qwen', 'codex'], default: 'gemini', description: 'Tool for documentation generation' },
|
|
182
|
+
{ key: 'interval', type: 'number', label: 'Interval (seconds)', default: 300, min: 60, max: 3600, step: 60, showFor: ['periodic'], description: 'Time between updates' },
|
|
183
|
+
{ key: 'threshold', type: 'number', label: 'File Count Threshold', default: 10, min: 3, max: 50, step: 1, showFor: ['count-based'], description: 'Number of file changes to trigger update' },
|
|
184
|
+
{ key: 'strategy', type: 'select', label: 'Update Strategy', options: ['related', 'single-layer'], default: 'related', description: 'Related: changed modules, Single-layer: current directory' }
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
'skill-context': {
|
|
188
|
+
name: 'SKILL Context Loader',
|
|
189
|
+
description: 'Automatically load SKILL packages based on keywords in user prompts',
|
|
190
|
+
icon: 'sparkles',
|
|
191
|
+
options: [
|
|
192
|
+
{
|
|
193
|
+
id: 'keyword',
|
|
194
|
+
name: 'Keyword Matching',
|
|
195
|
+
description: 'Load specific SKILLs when keywords are detected in prompt',
|
|
196
|
+
templateId: 'skill-context-keyword'
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: 'auto',
|
|
200
|
+
name: 'Auto Detection',
|
|
201
|
+
description: 'Automatically detect and load SKILLs by name in prompt',
|
|
202
|
+
templateId: 'skill-context-auto'
|
|
203
|
+
}
|
|
204
|
+
],
|
|
205
|
+
configFields: [],
|
|
206
|
+
requiresSkillDiscovery: true,
|
|
207
|
+
customRenderer: 'renderSkillContextConfig'
|
|
208
|
+
},
|
|
209
|
+
'memory-setup': {
|
|
210
|
+
name: 'Memory Module Setup',
|
|
211
|
+
description: 'Configure automatic context tracking',
|
|
212
|
+
icon: 'brain',
|
|
213
|
+
options: [
|
|
214
|
+
{
|
|
215
|
+
id: 'file-read',
|
|
216
|
+
name: 'File Read Tracker',
|
|
217
|
+
description: 'Track file reads to build context heatmap',
|
|
218
|
+
templateId: 'memory-file-read'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: 'file-write',
|
|
222
|
+
name: 'File Write Tracker',
|
|
223
|
+
description: 'Track file modifications to identify core modules',
|
|
224
|
+
templateId: 'memory-file-write'
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: 'prompts',
|
|
228
|
+
name: 'Prompt Tracker',
|
|
229
|
+
description: 'Record user prompts for pattern analysis',
|
|
230
|
+
templateId: 'memory-prompt-track'
|
|
231
|
+
}
|
|
232
|
+
],
|
|
233
|
+
configFields: [],
|
|
234
|
+
multiSelect: true
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// ========== Initialization ==========
|
|
239
|
+
function initHookManager() {
|
|
240
|
+
// Initialize Hook navigation
|
|
241
|
+
document.querySelectorAll('.nav-item[data-view="hook-manager"]').forEach(item => {
|
|
242
|
+
item.addEventListener('click', () => {
|
|
243
|
+
setActiveNavItem(item);
|
|
244
|
+
currentView = 'hook-manager';
|
|
245
|
+
currentFilter = null;
|
|
246
|
+
currentLiteType = null;
|
|
247
|
+
currentSessionDetailKey = null;
|
|
248
|
+
updateContentTitle();
|
|
249
|
+
renderHookManager();
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ========== Data Loading ==========
|
|
255
|
+
async function loadHookConfig() {
|
|
256
|
+
try {
|
|
257
|
+
const response = await fetch(`/api/hooks?path=${encodeURIComponent(projectPath)}`);
|
|
258
|
+
if (!response.ok) throw new Error('Failed to load hook config');
|
|
259
|
+
const data = await response.json();
|
|
260
|
+
hookConfig = data;
|
|
261
|
+
updateHookBadge();
|
|
262
|
+
return data;
|
|
263
|
+
} catch (err) {
|
|
264
|
+
console.error('Failed to load hook config:', err);
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function loadAvailableSkills() {
|
|
270
|
+
try {
|
|
271
|
+
const response = await fetch('/api/skills?path=' + encodeURIComponent(projectPath));
|
|
272
|
+
if (!response.ok) throw new Error('Failed to load skills');
|
|
273
|
+
const data = await response.json();
|
|
274
|
+
|
|
275
|
+
// Combine project and user skills
|
|
276
|
+
const projectSkills = (data.projectSkills || []).map(s => ({
|
|
277
|
+
name: s.name,
|
|
278
|
+
path: s.path,
|
|
279
|
+
scope: 'project'
|
|
280
|
+
}));
|
|
281
|
+
const userSkills = (data.userSkills || []).map(s => ({
|
|
282
|
+
name: s.name,
|
|
283
|
+
path: s.path,
|
|
284
|
+
scope: 'user'
|
|
285
|
+
}));
|
|
286
|
+
|
|
287
|
+
// Store in window for access by wizard
|
|
288
|
+
window.availableSkills = [...projectSkills, ...userSkills];
|
|
289
|
+
|
|
290
|
+
return window.availableSkills;
|
|
291
|
+
} catch (err) {
|
|
292
|
+
console.error('Failed to load available skills:', err);
|
|
293
|
+
window.availableSkills = [];
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Convert internal hook format to Claude Code format
|
|
300
|
+
* Internal: { command, args, matcher, timeout }
|
|
301
|
+
* Claude Code: { matcher, hooks: [{ type: "command", command: "...", timeout }] }
|
|
302
|
+
*/
|
|
303
|
+
function convertToClaudeCodeFormat(hookData) {
|
|
304
|
+
// If already in correct format, return as-is
|
|
305
|
+
if (hookData.hooks && Array.isArray(hookData.hooks)) {
|
|
306
|
+
return hookData;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Build command string from command + args
|
|
310
|
+
let commandStr = hookData.command || '';
|
|
311
|
+
if (hookData.args && Array.isArray(hookData.args)) {
|
|
312
|
+
// Join args, properly quoting if needed
|
|
313
|
+
const quotedArgs = hookData.args.map(arg => {
|
|
314
|
+
if (arg.includes(' ') && !arg.startsWith('"') && !arg.startsWith("'")) {
|
|
315
|
+
return `"${arg.replace(/"/g, '\\"')}"`;
|
|
316
|
+
}
|
|
317
|
+
return arg;
|
|
318
|
+
});
|
|
319
|
+
commandStr = `${commandStr} ${quotedArgs.join(' ')}`.trim();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const converted = {
|
|
323
|
+
hooks: [{
|
|
324
|
+
type: 'command',
|
|
325
|
+
command: commandStr
|
|
326
|
+
}]
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// Add matcher if present (not needed for UserPromptSubmit, Stop, etc.)
|
|
330
|
+
if (hookData.matcher) {
|
|
331
|
+
converted.matcher = hookData.matcher;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Add timeout if present (in seconds for Claude Code)
|
|
335
|
+
if (hookData.timeout) {
|
|
336
|
+
converted.hooks[0].timeout = Math.ceil(hookData.timeout / 1000);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Preserve replaceIndex for updates
|
|
340
|
+
if (hookData.replaceIndex !== undefined) {
|
|
341
|
+
converted.replaceIndex = hookData.replaceIndex;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return converted;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async function saveHook(scope, event, hookData) {
|
|
348
|
+
try {
|
|
349
|
+
// Convert to Claude Code format before saving
|
|
350
|
+
const convertedHookData = convertToClaudeCodeFormat(hookData);
|
|
351
|
+
|
|
352
|
+
const response = await fetch('/api/hooks', {
|
|
353
|
+
method: 'POST',
|
|
354
|
+
headers: { 'Content-Type': 'application/json' },
|
|
355
|
+
body: JSON.stringify({
|
|
356
|
+
projectPath: projectPath,
|
|
357
|
+
scope: scope,
|
|
358
|
+
event: event,
|
|
359
|
+
hookData: convertedHookData
|
|
360
|
+
})
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
if (!response.ok) throw new Error('Failed to save hook');
|
|
364
|
+
|
|
365
|
+
const result = await response.json();
|
|
366
|
+
if (result.success) {
|
|
367
|
+
await loadHookConfig();
|
|
368
|
+
renderHookManager();
|
|
369
|
+
showRefreshToast(`Hook saved successfully`, 'success');
|
|
370
|
+
}
|
|
371
|
+
return result;
|
|
372
|
+
} catch (err) {
|
|
373
|
+
console.error('Failed to save hook:', err);
|
|
374
|
+
showRefreshToast(`Failed to save hook: ${err.message}`, 'error');
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async function removeHook(scope, event, hookIndex) {
|
|
380
|
+
try {
|
|
381
|
+
const response = await fetch('/api/hooks', {
|
|
382
|
+
method: 'DELETE',
|
|
383
|
+
headers: { 'Content-Type': 'application/json' },
|
|
384
|
+
body: JSON.stringify({
|
|
385
|
+
projectPath: projectPath,
|
|
386
|
+
scope: scope,
|
|
387
|
+
event: event,
|
|
388
|
+
hookIndex: hookIndex
|
|
389
|
+
})
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
if (!response.ok) throw new Error('Failed to remove hook');
|
|
393
|
+
|
|
394
|
+
const result = await response.json();
|
|
395
|
+
if (result.success) {
|
|
396
|
+
await loadHookConfig();
|
|
397
|
+
renderHookManager();
|
|
398
|
+
showRefreshToast(`Hook removed successfully`, 'success');
|
|
399
|
+
}
|
|
400
|
+
return result;
|
|
401
|
+
} catch (err) {
|
|
402
|
+
console.error('Failed to remove hook:', err);
|
|
403
|
+
showRefreshToast(`Failed to remove hook: ${err.message}`, 'error');
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// ========== Badge Update ==========
|
|
409
|
+
function updateHookBadge() {
|
|
410
|
+
const badge = document.getElementById('badgeHooks');
|
|
411
|
+
if (badge) {
|
|
412
|
+
let totalHooks = 0;
|
|
413
|
+
|
|
414
|
+
// Count global hooks
|
|
415
|
+
if (hookConfig.global?.hooks) {
|
|
416
|
+
for (const event of Object.keys(hookConfig.global.hooks)) {
|
|
417
|
+
const hooks = hookConfig.global.hooks[event];
|
|
418
|
+
totalHooks += Array.isArray(hooks) ? hooks.length : 1;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Count project hooks
|
|
423
|
+
if (hookConfig.project?.hooks) {
|
|
424
|
+
for (const event of Object.keys(hookConfig.project.hooks)) {
|
|
425
|
+
const hooks = hookConfig.project.hooks[event];
|
|
426
|
+
totalHooks += Array.isArray(hooks) ? hooks.length : 1;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
badge.textContent = totalHooks;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ========== Hook Modal Functions ==========
|
|
435
|
+
let editingHookData = null;
|
|
436
|
+
|
|
437
|
+
function openHookCreateModal(editData = null) {
|
|
438
|
+
const modal = document.getElementById('hookCreateModal');
|
|
439
|
+
const title = document.getElementById('hookModalTitle');
|
|
440
|
+
|
|
441
|
+
if (modal) {
|
|
442
|
+
modal.classList.remove('hidden');
|
|
443
|
+
editingHookData = editData;
|
|
444
|
+
|
|
445
|
+
// Set title based on mode
|
|
446
|
+
title.textContent = editData ? 'Edit Hook' : 'Create Hook';
|
|
447
|
+
|
|
448
|
+
// Clear or populate form
|
|
449
|
+
if (editData) {
|
|
450
|
+
document.getElementById('hookEvent').value = editData.event || '';
|
|
451
|
+
document.getElementById('hookMatcher').value = editData.matcher || '';
|
|
452
|
+
document.getElementById('hookCommand').value = editData.command || '';
|
|
453
|
+
document.getElementById('hookArgs').value = (editData.args || []).join('\n');
|
|
454
|
+
|
|
455
|
+
// Set scope radio
|
|
456
|
+
const scopeRadio = document.querySelector(`input[name="hookScope"][value="${editData.scope || 'project'}"]`);
|
|
457
|
+
if (scopeRadio) scopeRadio.checked = true;
|
|
458
|
+
} else {
|
|
459
|
+
document.getElementById('hookEvent').value = '';
|
|
460
|
+
document.getElementById('hookMatcher').value = '';
|
|
461
|
+
document.getElementById('hookCommand').value = '';
|
|
462
|
+
document.getElementById('hookArgs').value = '';
|
|
463
|
+
document.querySelector('input[name="hookScope"][value="project"]').checked = true;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Focus on event select
|
|
467
|
+
document.getElementById('hookEvent').focus();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function closeHookCreateModal() {
|
|
472
|
+
const modal = document.getElementById('hookCreateModal');
|
|
473
|
+
if (modal) {
|
|
474
|
+
modal.classList.add('hidden');
|
|
475
|
+
editingHookData = null;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function applyHookTemplate(templateName) {
|
|
480
|
+
const template = HOOK_TEMPLATES[templateName];
|
|
481
|
+
if (!template) return;
|
|
482
|
+
|
|
483
|
+
document.getElementById('hookEvent').value = template.event;
|
|
484
|
+
document.getElementById('hookMatcher').value = template.matcher;
|
|
485
|
+
document.getElementById('hookCommand').value = template.command;
|
|
486
|
+
document.getElementById('hookArgs').value = template.args.join('\n');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
async function submitHookCreate() {
|
|
490
|
+
const event = document.getElementById('hookEvent').value;
|
|
491
|
+
const matcher = document.getElementById('hookMatcher').value.trim();
|
|
492
|
+
const command = document.getElementById('hookCommand').value.trim();
|
|
493
|
+
const argsText = document.getElementById('hookArgs').value.trim();
|
|
494
|
+
const scope = document.querySelector('input[name="hookScope"]:checked').value;
|
|
495
|
+
|
|
496
|
+
// Validate required fields
|
|
497
|
+
if (!event) {
|
|
498
|
+
showRefreshToast('Hook event is required', 'error');
|
|
499
|
+
document.getElementById('hookEvent').focus();
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (!command) {
|
|
504
|
+
showRefreshToast('Command is required', 'error');
|
|
505
|
+
document.getElementById('hookCommand').focus();
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Parse args (one per line)
|
|
510
|
+
const args = argsText ? argsText.split('\n').map(a => a.trim()).filter(a => a) : [];
|
|
511
|
+
|
|
512
|
+
// Build hook data
|
|
513
|
+
const hookData = {
|
|
514
|
+
command: command
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
if (args.length > 0) {
|
|
518
|
+
hookData.args = args;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (matcher) {
|
|
522
|
+
hookData.matcher = matcher;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// If editing, include original index for replacement
|
|
526
|
+
if (editingHookData && editingHookData.index !== undefined) {
|
|
527
|
+
hookData.replaceIndex = editingHookData.index;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Submit to API
|
|
531
|
+
await saveHook(scope, event, hookData);
|
|
532
|
+
closeHookCreateModal();
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// ========== Helpers ==========
|
|
536
|
+
function getHookEventDescription(event) {
|
|
537
|
+
const descriptions = {
|
|
538
|
+
'PreToolUse': 'Runs before a tool is executed',
|
|
539
|
+
'PostToolUse': 'Runs after a tool completes',
|
|
540
|
+
'Notification': 'Runs when a notification is triggered',
|
|
541
|
+
'Stop': 'Runs when the agent stops',
|
|
542
|
+
'UserPromptSubmit': 'Runs when user submits a prompt'
|
|
543
|
+
};
|
|
544
|
+
return descriptions[event] || event;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function getHookEventIcon(event) {
|
|
548
|
+
const icons = {
|
|
549
|
+
'PreToolUse': '⏳',
|
|
550
|
+
'PostToolUse': '✅',
|
|
551
|
+
'Notification': '🔔',
|
|
552
|
+
'Stop': '🛑',
|
|
553
|
+
'UserPromptSubmit': '💬'
|
|
554
|
+
};
|
|
555
|
+
return icons[event] || '🪝';
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function getHookEventIconLucide(event) {
|
|
559
|
+
const icons = {
|
|
560
|
+
'PreToolUse': '<i data-lucide="clock" class="w-5 h-5"></i>',
|
|
561
|
+
'PostToolUse': '<i data-lucide="check-circle" class="w-5 h-5"></i>',
|
|
562
|
+
'Notification': '<i data-lucide="bell" class="w-5 h-5"></i>',
|
|
563
|
+
'Stop': '<i data-lucide="octagon-x" class="w-5 h-5"></i>',
|
|
564
|
+
'UserPromptSubmit': '<i data-lucide="message-square" class="w-5 h-5"></i>'
|
|
565
|
+
};
|
|
566
|
+
return icons[event] || '<i data-lucide="webhook" class="w-5 h-5"></i>';
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// ========== Wizard Modal Functions ==========
|
|
570
|
+
let currentWizardTemplate = null;
|
|
571
|
+
let wizardConfig = {};
|
|
572
|
+
|
|
573
|
+
async function openHookWizardModal(wizardId) {
|
|
574
|
+
const wizard = WIZARD_TEMPLATES[wizardId];
|
|
575
|
+
if (!wizard) {
|
|
576
|
+
showRefreshToast('Wizard template not found', 'error');
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
currentWizardTemplate = { id: wizardId, ...wizard };
|
|
581
|
+
wizardConfig = {};
|
|
582
|
+
|
|
583
|
+
// Set defaults
|
|
584
|
+
wizard.configFields.forEach(field => {
|
|
585
|
+
wizardConfig[field.key] = field.default;
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
// Initialize selectedOptions for multi-select wizards
|
|
589
|
+
if (wizard.multiSelect) {
|
|
590
|
+
wizardConfig.selectedOptions = [];
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Always refresh available skills when opening SKILL context wizard
|
|
594
|
+
if (wizardId === 'skill-context') {
|
|
595
|
+
await loadAvailableSkills();
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const modal = document.getElementById('hookWizardModal');
|
|
599
|
+
if (modal) {
|
|
600
|
+
renderWizardModalContent();
|
|
601
|
+
modal.classList.remove('hidden');
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function closeHookWizardModal() {
|
|
606
|
+
const modal = document.getElementById('hookWizardModal');
|
|
607
|
+
if (modal) {
|
|
608
|
+
modal.classList.add('hidden');
|
|
609
|
+
currentWizardTemplate = null;
|
|
610
|
+
wizardConfig = {};
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function renderWizardModalContent() {
|
|
615
|
+
const container = document.getElementById('wizardModalContent');
|
|
616
|
+
if (!container || !currentWizardTemplate) return;
|
|
617
|
+
|
|
618
|
+
const wizard = currentWizardTemplate;
|
|
619
|
+
const wizardId = wizard.id;
|
|
620
|
+
const selectedOption = wizardConfig.triggerType || wizard.options[0].id;
|
|
621
|
+
|
|
622
|
+
// Get translated wizard name and description
|
|
623
|
+
const wizardName = wizardId === 'memory-update' ? t('hook.wizard.memoryUpdate') :
|
|
624
|
+
wizardId === 'memory-setup' ? t('hook.wizard.memorySetup') :
|
|
625
|
+
wizardId === 'skill-context' ? t('hook.wizard.skillContext') : wizard.name;
|
|
626
|
+
const wizardDesc = wizardId === 'memory-update' ? t('hook.wizard.memoryUpdateDesc') :
|
|
627
|
+
wizardId === 'memory-setup' ? t('hook.wizard.memorySetupDesc') :
|
|
628
|
+
wizardId === 'skill-context' ? t('hook.wizard.skillContextDesc') : wizard.description;
|
|
629
|
+
|
|
630
|
+
// Helper to get translated option names
|
|
631
|
+
const getOptionName = (optId) => {
|
|
632
|
+
if (wizardId === 'memory-update') {
|
|
633
|
+
if (optId === 'on-stop') return t('hook.wizard.onSessionEnd');
|
|
634
|
+
if (optId === 'periodic') return t('hook.wizard.periodicUpdate');
|
|
635
|
+
if (optId === 'count-based') return t('hook.wizard.countBasedUpdate');
|
|
636
|
+
}
|
|
637
|
+
if (wizardId === 'memory-setup') {
|
|
638
|
+
if (optId === 'file-read') return t('hook.wizard.fileReadTracker');
|
|
639
|
+
if (optId === 'file-write') return t('hook.wizard.fileWriteTracker');
|
|
640
|
+
if (optId === 'prompts') return t('hook.wizard.promptTracker');
|
|
641
|
+
}
|
|
642
|
+
if (wizardId === 'skill-context') {
|
|
643
|
+
if (optId === 'keyword') return t('hook.wizard.keywordMatching');
|
|
644
|
+
if (optId === 'auto') return t('hook.wizard.autoDetection');
|
|
645
|
+
}
|
|
646
|
+
return wizard.options.find(o => o.id === optId)?.name || '';
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
const getOptionDesc = (optId) => {
|
|
650
|
+
if (wizardId === 'memory-update') {
|
|
651
|
+
if (optId === 'on-stop') return t('hook.wizard.onSessionEndDesc');
|
|
652
|
+
if (optId === 'periodic') return t('hook.wizard.periodicUpdateDesc');
|
|
653
|
+
if (optId === 'count-based') return t('hook.wizard.countBasedUpdateDesc');
|
|
654
|
+
}
|
|
655
|
+
if (wizardId === 'memory-setup') {
|
|
656
|
+
if (optId === 'file-read') return t('hook.wizard.fileReadTrackerDesc');
|
|
657
|
+
if (optId === 'file-write') return t('hook.wizard.fileWriteTrackerDesc');
|
|
658
|
+
if (optId === 'prompts') return t('hook.wizard.promptTrackerDesc');
|
|
659
|
+
}
|
|
660
|
+
if (wizardId === 'skill-context') {
|
|
661
|
+
if (optId === 'keyword') return t('hook.wizard.keywordMatchingDesc');
|
|
662
|
+
if (optId === 'auto') return t('hook.wizard.autoDetectionDesc');
|
|
663
|
+
}
|
|
664
|
+
return wizard.options.find(o => o.id === optId)?.description || '';
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
// Helper to get translated field labels
|
|
668
|
+
const getFieldLabel = (fieldKey) => {
|
|
669
|
+
const labels = {
|
|
670
|
+
'tool': t('hook.wizard.cliTool'),
|
|
671
|
+
'interval': t('hook.wizard.intervalSeconds'),
|
|
672
|
+
'threshold': t('hook.wizard.fileCountThreshold'),
|
|
673
|
+
'strategy': t('hook.wizard.updateStrategy')
|
|
674
|
+
};
|
|
675
|
+
return labels[fieldKey] || wizard.configFields.find(f => f.key === fieldKey)?.label || fieldKey;
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
const getFieldDesc = (fieldKey) => {
|
|
679
|
+
const descs = {
|
|
680
|
+
'tool': t('hook.wizard.toolForDocGen'),
|
|
681
|
+
'interval': t('hook.wizard.timeBetweenUpdates'),
|
|
682
|
+
'threshold': t('hook.wizard.fileCountThresholdDesc'),
|
|
683
|
+
'strategy': t('hook.wizard.relatedStrategy')
|
|
684
|
+
};
|
|
685
|
+
return descs[fieldKey] || wizard.configFields.find(f => f.key === fieldKey)?.description || '';
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
container.innerHTML = `
|
|
689
|
+
<div class="space-y-6">
|
|
690
|
+
<!-- Wizard Header -->
|
|
691
|
+
<div class="flex items-center gap-3 pb-4 border-b border-border">
|
|
692
|
+
<div class="p-2 bg-primary/10 rounded-lg">
|
|
693
|
+
<i data-lucide="${wizard.icon}" class="w-6 h-6 text-primary"></i>
|
|
694
|
+
</div>
|
|
695
|
+
<div>
|
|
696
|
+
<h3 class="text-lg font-semibold text-foreground">${escapeHtml(wizardName)}</h3>
|
|
697
|
+
<p class="text-sm text-muted-foreground">${escapeHtml(wizardDesc)}</p>
|
|
698
|
+
</div>
|
|
699
|
+
</div>
|
|
700
|
+
|
|
701
|
+
<!-- Trigger Type Selection -->
|
|
702
|
+
<div class="space-y-3">
|
|
703
|
+
<label class="block text-sm font-medium text-foreground">${wizard.multiSelect ? t('hook.wizard.selectTrackers') : t('hook.wizard.whenToTrigger')}</label>
|
|
704
|
+
<div class="grid grid-cols-1 gap-3">
|
|
705
|
+
${wizard.multiSelect ? wizard.options.map(opt => {
|
|
706
|
+
const isSelected = wizardConfig.selectedOptions?.includes(opt.id) || false;
|
|
707
|
+
return `
|
|
708
|
+
<label class="flex items-start gap-3 p-3 border rounded-lg cursor-pointer transition-all ${isSelected ? 'border-primary bg-primary/5' : 'border-border hover:border-muted-foreground'}">
|
|
709
|
+
<input type="checkbox" name="wizardTrigger" value="${opt.id}"
|
|
710
|
+
${isSelected ? 'checked' : ''}
|
|
711
|
+
onchange="toggleWizardOption('${opt.id}')"
|
|
712
|
+
class="mt-1">
|
|
713
|
+
<div class="flex-1">
|
|
714
|
+
<span class="font-medium text-foreground">${escapeHtml(getOptionName(opt.id))}</span>
|
|
715
|
+
<p class="text-sm text-muted-foreground">${escapeHtml(getOptionDesc(opt.id))}</p>
|
|
716
|
+
</div>
|
|
717
|
+
</label>
|
|
718
|
+
`;
|
|
719
|
+
}).join('') : wizard.options.map(opt => `
|
|
720
|
+
<label class="flex items-start gap-3 p-3 border rounded-lg cursor-pointer transition-all ${selectedOption === opt.id ? 'border-primary bg-primary/5' : 'border-border hover:border-muted-foreground'}">
|
|
721
|
+
<input type="radio" name="wizardTrigger" value="${opt.id}"
|
|
722
|
+
${selectedOption === opt.id ? 'checked' : ''}
|
|
723
|
+
onchange="updateWizardTrigger('${opt.id}')"
|
|
724
|
+
class="mt-1">
|
|
725
|
+
<div class="flex-1">
|
|
726
|
+
<span class="font-medium text-foreground">${escapeHtml(getOptionName(opt.id))}</span>
|
|
727
|
+
<p class="text-sm text-muted-foreground">${escapeHtml(getOptionDesc(opt.id))}</p>
|
|
728
|
+
</div>
|
|
729
|
+
</label>
|
|
730
|
+
`).join('')}
|
|
731
|
+
</div>
|
|
732
|
+
</div>
|
|
733
|
+
|
|
734
|
+
<!-- Configuration Fields -->
|
|
735
|
+
<div class="space-y-4">
|
|
736
|
+
<label class="block text-sm font-medium text-foreground">${t('hook.wizard.configuration')}</label>
|
|
737
|
+
${wizard.customRenderer ? window[wizard.customRenderer]() : wizard.configFields.map(field => {
|
|
738
|
+
// Check if field should be shown for current trigger type
|
|
739
|
+
const shouldShow = !field.showFor || field.showFor.includes(selectedOption);
|
|
740
|
+
if (!shouldShow) return '';
|
|
741
|
+
|
|
742
|
+
const value = wizardConfig[field.key] ?? field.default;
|
|
743
|
+
const fieldLabel = getFieldLabel(field.key);
|
|
744
|
+
const fieldDesc = getFieldDesc(field.key);
|
|
745
|
+
|
|
746
|
+
if (field.type === 'select') {
|
|
747
|
+
return `
|
|
748
|
+
<div class="space-y-1">
|
|
749
|
+
<label class="block text-sm text-muted-foreground">${escapeHtml(fieldLabel)}</label>
|
|
750
|
+
<select id="wizard_${field.key}"
|
|
751
|
+
onchange="updateWizardConfig('${field.key}', this.value)"
|
|
752
|
+
class="w-full px-3 py-2 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-primary">
|
|
753
|
+
${field.options.map(opt => `
|
|
754
|
+
<option value="${opt}" ${value === opt ? 'selected' : ''}>${opt}</option>
|
|
755
|
+
`).join('')}
|
|
756
|
+
</select>
|
|
757
|
+
${fieldDesc ? `<p class="text-xs text-muted-foreground">${escapeHtml(fieldDesc)}</p>` : ''}
|
|
758
|
+
</div>
|
|
759
|
+
`;
|
|
760
|
+
} else if (field.type === 'number') {
|
|
761
|
+
return `
|
|
762
|
+
<div class="space-y-1">
|
|
763
|
+
<label class="block text-sm text-muted-foreground">${escapeHtml(fieldLabel)}</label>
|
|
764
|
+
<div class="flex items-center gap-2">
|
|
765
|
+
<input type="number" id="wizard_${field.key}"
|
|
766
|
+
value="${value}"
|
|
767
|
+
min="${field.min || 0}"
|
|
768
|
+
max="${field.max || 9999}"
|
|
769
|
+
step="${field.step || 1}"
|
|
770
|
+
onchange="updateWizardConfig('${field.key}', parseInt(this.value))"
|
|
771
|
+
class="flex-1 px-3 py-2 bg-background border border-border rounded-lg text-foreground focus:outline-none focus:ring-2 focus:ring-primary">
|
|
772
|
+
<span class="text-sm text-muted-foreground">${formatIntervalDisplay(value)}</span>
|
|
773
|
+
</div>
|
|
774
|
+
${fieldDesc ? `<p class="text-xs text-muted-foreground">${escapeHtml(fieldDesc)}</p>` : ''}
|
|
775
|
+
</div>
|
|
776
|
+
`;
|
|
777
|
+
}
|
|
778
|
+
return '';
|
|
779
|
+
}).join('')}
|
|
780
|
+
</div>
|
|
781
|
+
|
|
782
|
+
<!-- Preview -->
|
|
783
|
+
<div class="space-y-2">
|
|
784
|
+
<label class="block text-sm font-medium text-foreground">${t('hook.wizard.commandPreview')}</label>
|
|
785
|
+
<div class="bg-muted/50 rounded-lg p-3 font-mono text-xs overflow-x-auto">
|
|
786
|
+
<pre id="wizardCommandPreview" class="whitespace-pre-wrap text-muted-foreground">${escapeHtml(generateWizardCommand())}</pre>
|
|
787
|
+
</div>
|
|
788
|
+
</div>
|
|
789
|
+
|
|
790
|
+
<!-- Scope Selection -->
|
|
791
|
+
<div class="space-y-3">
|
|
792
|
+
<label class="block text-sm font-medium text-foreground">${t('hook.wizard.installTo')}</label>
|
|
793
|
+
<div class="flex gap-4">
|
|
794
|
+
<label class="flex items-center gap-2 cursor-pointer">
|
|
795
|
+
<input type="radio" name="wizardScope" value="project" checked>
|
|
796
|
+
<span class="text-sm text-foreground">${t('hook.scopeProject').split('(')[0]}</span>
|
|
797
|
+
<span class="text-xs text-muted-foreground">(.claude/settings.json)</span>
|
|
798
|
+
</label>
|
|
799
|
+
<label class="flex items-center gap-2 cursor-pointer">
|
|
800
|
+
<input type="radio" name="wizardScope" value="global">
|
|
801
|
+
<span class="text-sm text-foreground">${t('hook.scopeGlobal').split('(')[0]}</span>
|
|
802
|
+
<span class="text-xs text-muted-foreground">(~/.claude/settings.json)</span>
|
|
803
|
+
</label>
|
|
804
|
+
</div>
|
|
805
|
+
</div>
|
|
806
|
+
</div>
|
|
807
|
+
`;
|
|
808
|
+
|
|
809
|
+
// Initialize Lucide icons
|
|
810
|
+
if (typeof lucide !== 'undefined') lucide.createIcons();
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function updateWizardTrigger(triggerId) {
|
|
814
|
+
wizardConfig.triggerType = triggerId;
|
|
815
|
+
renderWizardModalContent();
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
function toggleWizardOption(optionId) {
|
|
819
|
+
if (!wizardConfig.selectedOptions) {
|
|
820
|
+
wizardConfig.selectedOptions = [];
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
const index = wizardConfig.selectedOptions.indexOf(optionId);
|
|
824
|
+
if (index === -1) {
|
|
825
|
+
wizardConfig.selectedOptions.push(optionId);
|
|
826
|
+
} else {
|
|
827
|
+
wizardConfig.selectedOptions.splice(index, 1);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
renderWizardModalContent();
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function updateWizardConfig(key, value) {
|
|
834
|
+
wizardConfig[key] = value;
|
|
835
|
+
// Update command preview
|
|
836
|
+
const preview = document.getElementById('wizardCommandPreview');
|
|
837
|
+
if (preview) {
|
|
838
|
+
preview.textContent = generateWizardCommand();
|
|
839
|
+
}
|
|
840
|
+
// Re-render if interval changed (to update display)
|
|
841
|
+
if (key === 'interval') {
|
|
842
|
+
const displaySpan = document.querySelector(`#wizard_${key}`)?.parentElement?.querySelector('.text-muted-foreground:last-child');
|
|
843
|
+
if (displaySpan) {
|
|
844
|
+
displaySpan.textContent = formatIntervalDisplay(value);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function formatIntervalDisplay(seconds) {
|
|
850
|
+
if (seconds < 60) return `${seconds}s`;
|
|
851
|
+
const mins = Math.floor(seconds / 60);
|
|
852
|
+
const secs = seconds % 60;
|
|
853
|
+
if (secs === 0) return `${mins}min`;
|
|
854
|
+
return `${mins}min ${secs}s`;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// ========== SKILL Context Wizard Custom Functions ==========
|
|
858
|
+
function renderSkillContextConfig() {
|
|
859
|
+
const selectedOption = wizardConfig.triggerType || 'keyword';
|
|
860
|
+
const skillConfigs = wizardConfig.skillConfigs || [];
|
|
861
|
+
const availableSkills = window.availableSkills || [];
|
|
862
|
+
|
|
863
|
+
if (selectedOption === 'auto') {
|
|
864
|
+
let skillBadges = '';
|
|
865
|
+
let isLoading = typeof window.availableSkills === 'undefined' || window.skillsLoading;
|
|
866
|
+
if (isLoading) {
|
|
867
|
+
// Still loading
|
|
868
|
+
skillBadges = '<span class="px-1.5 py-0.5 bg-muted text-muted-foreground rounded text-xs">' + t('common.loading') + '...</span>';
|
|
869
|
+
} else if (availableSkills.length === 0) {
|
|
870
|
+
// No skills found
|
|
871
|
+
skillBadges = '<span class="px-1.5 py-0.5 bg-warning/10 text-warning rounded text-xs">' + t('hook.wizard.noSkillsFound') + '</span>';
|
|
872
|
+
} else {
|
|
873
|
+
// Skills found
|
|
874
|
+
skillBadges = availableSkills.map(function(s) {
|
|
875
|
+
return '<span class="px-1.5 py-0.5 bg-emerald-500/10 text-emerald-500 rounded text-xs">' + escapeHtml(s.name) + '</span>';
|
|
876
|
+
}).join(' ');
|
|
877
|
+
}
|
|
878
|
+
return '<div class="bg-muted/30 rounded-lg p-4 text-sm text-muted-foreground">' +
|
|
879
|
+
'<div class="flex items-center justify-between mb-2">' +
|
|
880
|
+
'<div class="flex items-center gap-2">' +
|
|
881
|
+
'<i data-lucide="info" class="w-4 h-4"></i>' +
|
|
882
|
+
'<span class="font-medium">' + t('hook.wizard.autoDetectionMode') + '</span>' +
|
|
883
|
+
'</div>' +
|
|
884
|
+
'<button type="button" onclick="refreshAvailableSkills()" ' +
|
|
885
|
+
'class="p-1.5 text-muted-foreground hover:text-primary hover:bg-primary/10 rounded transition-colors" ' +
|
|
886
|
+
'title="' + t('common.refresh') + '">' +
|
|
887
|
+
'<i data-lucide="refresh-cw" class="w-4 h-4' + (isLoading ? ' animate-spin' : '') + '"></i>' +
|
|
888
|
+
'</button>' +
|
|
889
|
+
'</div>' +
|
|
890
|
+
'<p>' + t('hook.wizard.autoDetectionInfo') + '</p>' +
|
|
891
|
+
'<div class="mt-2 flex items-center gap-2 flex-wrap">' +
|
|
892
|
+
'<span>' + t('hook.wizard.availableSkills') + '</span>' +
|
|
893
|
+
skillBadges +
|
|
894
|
+
'</div>' +
|
|
895
|
+
'</div>';
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
var configListHtml = '';
|
|
899
|
+
if (skillConfigs.length === 0) {
|
|
900
|
+
configListHtml = '<div class="text-center py-6 text-muted-foreground text-sm border border-dashed border-border rounded-lg">' +
|
|
901
|
+
'<i data-lucide="package" class="w-8 h-8 mx-auto mb-2 opacity-50"></i>' +
|
|
902
|
+
'<p>' + t('hook.wizard.noSkillsConfigured') + '</p>' +
|
|
903
|
+
'<p class="text-xs mt-1">' + t('hook.wizard.clickAddSkill') + '</p>' +
|
|
904
|
+
'</div>';
|
|
905
|
+
} else {
|
|
906
|
+
configListHtml = skillConfigs.map(function(config, idx) {
|
|
907
|
+
var skillOptions = '';
|
|
908
|
+
if (availableSkills.length === 0) {
|
|
909
|
+
skillOptions = '<option value="" disabled>' + t('hook.wizard.noSkillsFound') + '</option>';
|
|
910
|
+
} else {
|
|
911
|
+
skillOptions = availableSkills.map(function(s) {
|
|
912
|
+
var selected = config.skill === s.name ? 'selected' : '';
|
|
913
|
+
return '<option value="' + escapeHtml(s.name) + '" ' + selected + '>' + escapeHtml(s.name) + '</option>';
|
|
914
|
+
}).join('');
|
|
915
|
+
}
|
|
916
|
+
return '<div class="border border-border rounded-lg p-3 bg-card">' +
|
|
917
|
+
'<div class="flex items-center justify-between mb-2">' +
|
|
918
|
+
'<select onchange="updateSkillConfig(' + idx + ', \'skill\', this.value)" ' +
|
|
919
|
+
'class="px-2 py-1 text-sm bg-background border border-border rounded text-foreground">' +
|
|
920
|
+
'<option value="">' + t('hook.wizard.selectSkill') + '</option>' +
|
|
921
|
+
skillOptions +
|
|
922
|
+
'</select>' +
|
|
923
|
+
'<button onclick="removeSkillConfig(' + idx + ')" ' +
|
|
924
|
+
'class="p-1 text-muted-foreground hover:text-destructive rounded">' +
|
|
925
|
+
'<i data-lucide="trash-2" class="w-4 h-4"></i>' +
|
|
926
|
+
'</button>' +
|
|
927
|
+
'</div>' +
|
|
928
|
+
'<div class="space-y-1">' +
|
|
929
|
+
'<label class="text-xs text-muted-foreground">' + t('hook.wizard.triggerKeywords') + '</label>' +
|
|
930
|
+
'<input type="text" ' +
|
|
931
|
+
'value="' + (config.keywords || '') + '" ' +
|
|
932
|
+
'onchange="updateSkillConfig(' + idx + ', \'keywords\', this.value)" ' +
|
|
933
|
+
'placeholder="e.g., react, hooks, component" ' +
|
|
934
|
+
'class="w-full px-2 py-1.5 text-sm bg-background border border-border rounded text-foreground">' +
|
|
935
|
+
'</div>' +
|
|
936
|
+
'</div>';
|
|
937
|
+
}).join('');
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
var isLoading = typeof window.availableSkills === 'undefined' || window.skillsLoading;
|
|
941
|
+
var skillsStatusHtml = '';
|
|
942
|
+
if (isLoading) {
|
|
943
|
+
skillsStatusHtml = '<span class="text-xs text-muted-foreground flex items-center gap-1">' +
|
|
944
|
+
'<i data-lucide="loader-2" class="w-3 h-3 animate-spin"></i>' +
|
|
945
|
+
t('common.loading') +
|
|
946
|
+
'</span>';
|
|
947
|
+
} else if (availableSkills.length === 0) {
|
|
948
|
+
skillsStatusHtml = '<span class="text-xs text-amber-500 flex items-center gap-1">' +
|
|
949
|
+
'<i data-lucide="alert-triangle" class="w-3 h-3"></i>' +
|
|
950
|
+
t('hook.wizard.noSkillsFound') +
|
|
951
|
+
'</span>';
|
|
952
|
+
} else {
|
|
953
|
+
skillsStatusHtml = '<span class="text-xs text-muted-foreground">' +
|
|
954
|
+
availableSkills.length + ' ' + t('skills.skillsCount') +
|
|
955
|
+
'</span>';
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
return '<div class="space-y-4">' +
|
|
959
|
+
'<div class="flex items-center justify-between">' +
|
|
960
|
+
'<div class="flex items-center gap-2">' +
|
|
961
|
+
'<span class="text-sm font-medium text-foreground">' + t('hook.wizard.configureSkills') + '</span>' +
|
|
962
|
+
skillsStatusHtml +
|
|
963
|
+
'<button type="button" onclick="refreshAvailableSkills()" ' +
|
|
964
|
+
'class="p-1 text-muted-foreground hover:text-primary hover:bg-primary/10 rounded transition-colors" ' +
|
|
965
|
+
'title="' + t('common.refresh') + '">' +
|
|
966
|
+
'<i data-lucide="refresh-cw" class="w-3 h-3' + (isLoading ? ' animate-spin' : '') + '"></i>' +
|
|
967
|
+
'</button>' +
|
|
968
|
+
'</div>' +
|
|
969
|
+
'<button type="button" onclick="addSkillConfig()" ' +
|
|
970
|
+
'class="px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded-lg hover:opacity-90 flex items-center gap-1">' +
|
|
971
|
+
'<i data-lucide="plus" class="w-3 h-3"></i> ' + t('hook.wizard.addSkill') +
|
|
972
|
+
'</button>' +
|
|
973
|
+
'</div>' +
|
|
974
|
+
'<div id="skillConfigsList" class="space-y-3">' + configListHtml + '</div>' +
|
|
975
|
+
'</div>';
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
async function refreshAvailableSkills() {
|
|
979
|
+
// Set loading state
|
|
980
|
+
window.skillsLoading = true;
|
|
981
|
+
renderWizardModalContent();
|
|
982
|
+
|
|
983
|
+
try {
|
|
984
|
+
await loadAvailableSkills();
|
|
985
|
+
} finally {
|
|
986
|
+
window.skillsLoading = false;
|
|
987
|
+
renderWizardModalContent();
|
|
988
|
+
// Refresh Lucide icons
|
|
989
|
+
if (typeof lucide !== 'undefined') lucide.createIcons();
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
function addSkillConfig() {
|
|
994
|
+
if (!wizardConfig.skillConfigs) {
|
|
995
|
+
wizardConfig.skillConfigs = [];
|
|
996
|
+
}
|
|
997
|
+
wizardConfig.skillConfigs.push({ skill: '', keywords: '' });
|
|
998
|
+
renderWizardModalContent();
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
function removeSkillConfig(index) {
|
|
1002
|
+
if (wizardConfig.skillConfigs) {
|
|
1003
|
+
wizardConfig.skillConfigs.splice(index, 1);
|
|
1004
|
+
renderWizardModalContent();
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
function updateSkillConfig(index, key, value) {
|
|
1009
|
+
if (wizardConfig.skillConfigs && wizardConfig.skillConfigs[index]) {
|
|
1010
|
+
wizardConfig.skillConfigs[index][key] = value;
|
|
1011
|
+
const preview = document.getElementById('wizardCommandPreview');
|
|
1012
|
+
if (preview) {
|
|
1013
|
+
preview.textContent = generateWizardCommand();
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
function generateWizardCommand() {
|
|
1021
|
+
if (!currentWizardTemplate) return '';
|
|
1022
|
+
|
|
1023
|
+
const wizard = currentWizardTemplate;
|
|
1024
|
+
const wizardId = wizard.id;
|
|
1025
|
+
const triggerType = wizardConfig.triggerType || wizard.options[0].id;
|
|
1026
|
+
const selectedOption = wizard.options.find(o => o.id === triggerType);
|
|
1027
|
+
if (!selectedOption) return '';
|
|
1028
|
+
|
|
1029
|
+
const baseTemplate = HOOK_TEMPLATES[selectedOption.templateId];
|
|
1030
|
+
if (!baseTemplate) return '';
|
|
1031
|
+
|
|
1032
|
+
// Handle skill-context wizard
|
|
1033
|
+
if (wizardId === 'skill-context') {
|
|
1034
|
+
if (triggerType === 'keyword') {
|
|
1035
|
+
const skillConfigs = wizardConfig.skillConfigs || [];
|
|
1036
|
+
const validConfigs = skillConfigs.filter(c => c.skill && c.keywords);
|
|
1037
|
+
|
|
1038
|
+
if (validConfigs.length === 0) {
|
|
1039
|
+
return '# No SKILL configurations yet';
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
const configJson = validConfigs.map(c => ({
|
|
1043
|
+
skill: c.skill,
|
|
1044
|
+
keywords: c.keywords.split(',').map(k => k.trim()).filter(k => k)
|
|
1045
|
+
}));
|
|
1046
|
+
|
|
1047
|
+
const params = JSON.stringify({ configs: configJson, prompt: '$CLAUDE_PROMPT' });
|
|
1048
|
+
return `ccw tool exec skill_context_loader '${params}'`;
|
|
1049
|
+
} else {
|
|
1050
|
+
// auto mode
|
|
1051
|
+
const params = JSON.stringify({ mode: 'auto', prompt: '$CLAUDE_PROMPT' });
|
|
1052
|
+
return `ccw tool exec skill_context_loader '${params}'`;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// Handle memory-update wizard (default)
|
|
1057
|
+
const tool = wizardConfig.tool || 'gemini';
|
|
1058
|
+
const strategy = wizardConfig.strategy || 'related';
|
|
1059
|
+
const interval = wizardConfig.interval || 300;
|
|
1060
|
+
const threshold = wizardConfig.threshold || 10;
|
|
1061
|
+
|
|
1062
|
+
// Build the ccw tool command based on configuration
|
|
1063
|
+
const params = JSON.stringify({ strategy, tool });
|
|
1064
|
+
|
|
1065
|
+
if (triggerType === 'periodic') {
|
|
1066
|
+
return `INTERVAL=${interval}; LAST_FILE=~/.claude/.last_memory_update; NOW=$(date +%s); LAST=0; [ -f "$LAST_FILE" ] && LAST=$(cat "$LAST_FILE"); if [ $((NOW - LAST)) -ge $INTERVAL ]; then echo $NOW > "$LAST_FILE"; ccw tool exec update_module_claude '${params}' & fi`;
|
|
1067
|
+
} else if (triggerType === 'count-based') {
|
|
1068
|
+
return `THRESHOLD=${threshold}; COUNT_FILE=~/.claude/.memory_update_count; INPUT=$(cat); FILE_PATH=$(echo "$INPUT" | jq -r ".tool_input.file_path // .tool_input.path // empty"); [ -z "$FILE_PATH" ] && exit 0; COUNT=0; [ -f "$COUNT_FILE" ] && COUNT=$(cat "$COUNT_FILE" 2>/dev/null || echo 0); COUNT=$((COUNT + 1)); echo $COUNT > "$COUNT_FILE"; if [ $COUNT -ge $THRESHOLD ]; then echo 0 > "$COUNT_FILE"; ccw tool exec update_module_claude '${params}' & fi`;
|
|
1069
|
+
} else {
|
|
1070
|
+
return `ccw tool exec update_module_claude '${params}'`;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
async function submitHookWizard() {
|
|
1075
|
+
if (!currentWizardTemplate) return;
|
|
1076
|
+
|
|
1077
|
+
const wizard = currentWizardTemplate;
|
|
1078
|
+
const scope = document.querySelector('input[name="wizardScope"]:checked')?.value || 'project';
|
|
1079
|
+
|
|
1080
|
+
// Handle multi-select wizards
|
|
1081
|
+
if (wizard.multiSelect) {
|
|
1082
|
+
const selectedOptions = wizardConfig.selectedOptions || [];
|
|
1083
|
+
if (selectedOptions.length === 0) {
|
|
1084
|
+
showRefreshToast('Please select at least one option', 'error');
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// Install each selected hook (skip if already exists)
|
|
1089
|
+
let installedCount = 0;
|
|
1090
|
+
let skippedCount = 0;
|
|
1091
|
+
|
|
1092
|
+
for (const optionId of selectedOptions) {
|
|
1093
|
+
const selectedOption = wizard.options.find(o => o.id === optionId);
|
|
1094
|
+
if (!selectedOption) continue;
|
|
1095
|
+
|
|
1096
|
+
const baseTemplate = HOOK_TEMPLATES[selectedOption.templateId];
|
|
1097
|
+
if (!baseTemplate) continue;
|
|
1098
|
+
|
|
1099
|
+
// Check if hook already exists
|
|
1100
|
+
const existingHooks = scope === 'global'
|
|
1101
|
+
? hookConfig.global?.hooks?.[baseTemplate.event] || []
|
|
1102
|
+
: hookConfig.project?.hooks?.[baseTemplate.event] || [];
|
|
1103
|
+
|
|
1104
|
+
const hookList = Array.isArray(existingHooks) ? existingHooks : [existingHooks];
|
|
1105
|
+
const alreadyExists = hookList.some(h => {
|
|
1106
|
+
// Check by matcher and command
|
|
1107
|
+
const existingMatcher = h.matcher || '';
|
|
1108
|
+
const templateMatcher = baseTemplate.matcher || '';
|
|
1109
|
+
const existingCmd = h.hooks?.[0]?.command || h.command || '';
|
|
1110
|
+
const templateCmd = baseTemplate.command + ' ' + (baseTemplate.args || []).join(' ');
|
|
1111
|
+
return existingMatcher === templateMatcher && existingCmd.includes(baseTemplate.command);
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
if (alreadyExists) {
|
|
1115
|
+
skippedCount++;
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
const hookData = {
|
|
1120
|
+
command: baseTemplate.command,
|
|
1121
|
+
args: baseTemplate.args
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
if (baseTemplate.matcher) {
|
|
1125
|
+
hookData.matcher = baseTemplate.matcher;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
if (baseTemplate.timeout) {
|
|
1129
|
+
hookData.timeout = baseTemplate.timeout;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
await saveHook(scope, baseTemplate.event, hookData);
|
|
1133
|
+
installedCount++;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
closeHookWizardModal();
|
|
1137
|
+
|
|
1138
|
+
if (skippedCount > 0 && installedCount === 0) {
|
|
1139
|
+
showRefreshToast(`All ${skippedCount} hook(s) already installed`, 'info');
|
|
1140
|
+
} else if (skippedCount > 0) {
|
|
1141
|
+
showRefreshToast(`Installed ${installedCount}, skipped ${skippedCount} (already exists)`, 'success');
|
|
1142
|
+
}
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// Handle single-select wizards
|
|
1147
|
+
const triggerType = wizardConfig.triggerType || wizard.options[0].id;
|
|
1148
|
+
const selectedOption = wizard.options.find(o => o.id === triggerType);
|
|
1149
|
+
if (!selectedOption) return;
|
|
1150
|
+
|
|
1151
|
+
const baseTemplate = HOOK_TEMPLATES[selectedOption.templateId];
|
|
1152
|
+
if (!baseTemplate) return;
|
|
1153
|
+
|
|
1154
|
+
const command = generateWizardCommand();
|
|
1155
|
+
|
|
1156
|
+
const hookData = {
|
|
1157
|
+
command: 'bash',
|
|
1158
|
+
args: ['-c', command]
|
|
1159
|
+
};
|
|
1160
|
+
|
|
1161
|
+
if (baseTemplate.matcher) {
|
|
1162
|
+
hookData.matcher = baseTemplate.matcher;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
await saveHook(scope, baseTemplate.event, hookData);
|
|
1166
|
+
closeHookWizardModal();
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
// ========== Template View/Copy Functions ==========
|
|
1170
|
+
function viewTemplateDetails(templateId) {
|
|
1171
|
+
const template = HOOK_TEMPLATES[templateId];
|
|
1172
|
+
if (!template) return;
|
|
1173
|
+
|
|
1174
|
+
const modal = document.getElementById('templateViewModal');
|
|
1175
|
+
const content = document.getElementById('templateViewContent');
|
|
1176
|
+
|
|
1177
|
+
if (modal && content) {
|
|
1178
|
+
const args = template.args || [];
|
|
1179
|
+
content.innerHTML = `
|
|
1180
|
+
<div class="space-y-4">
|
|
1181
|
+
<div class="flex items-center gap-3 pb-3 border-b border-border">
|
|
1182
|
+
<i data-lucide="webhook" class="w-5 h-5 text-primary"></i>
|
|
1183
|
+
<div>
|
|
1184
|
+
<h4 class="font-semibold text-foreground">${escapeHtml(templateId)}</h4>
|
|
1185
|
+
<p class="text-sm text-muted-foreground">${escapeHtml(template.description || 'No description')}</p>
|
|
1186
|
+
</div>
|
|
1187
|
+
</div>
|
|
1188
|
+
|
|
1189
|
+
<div class="space-y-3 text-sm">
|
|
1190
|
+
<div class="flex items-start gap-2">
|
|
1191
|
+
<span class="font-mono text-xs bg-muted px-1.5 py-0.5 rounded shrink-0 w-16">Event</span>
|
|
1192
|
+
<span class="font-medium text-foreground">${escapeHtml(template.event)}</span>
|
|
1193
|
+
</div>
|
|
1194
|
+
<div class="flex items-start gap-2">
|
|
1195
|
+
<span class="font-mono text-xs bg-muted px-1.5 py-0.5 rounded shrink-0 w-16">Matcher</span>
|
|
1196
|
+
<span class="text-muted-foreground">${escapeHtml(template.matcher || 'All tools')}</span>
|
|
1197
|
+
</div>
|
|
1198
|
+
<div class="flex items-start gap-2">
|
|
1199
|
+
<span class="font-mono text-xs bg-muted px-1.5 py-0.5 rounded shrink-0 w-16">Command</span>
|
|
1200
|
+
<code class="font-mono text-xs text-foreground">${escapeHtml(template.command)}</code>
|
|
1201
|
+
</div>
|
|
1202
|
+
${args.length > 0 ? `
|
|
1203
|
+
<div class="flex items-start gap-2">
|
|
1204
|
+
<span class="font-mono text-xs bg-muted px-1.5 py-0.5 rounded shrink-0 w-16">Args</span>
|
|
1205
|
+
<div class="flex-1">
|
|
1206
|
+
<pre class="font-mono text-xs text-muted-foreground bg-muted/50 rounded p-2 overflow-x-auto whitespace-pre-wrap">${escapeHtml(args.join('\n'))}</pre>
|
|
1207
|
+
</div>
|
|
1208
|
+
</div>
|
|
1209
|
+
` : ''}
|
|
1210
|
+
${template.category ? `
|
|
1211
|
+
<div class="flex items-start gap-2">
|
|
1212
|
+
<span class="font-mono text-xs bg-muted px-1.5 py-0.5 rounded shrink-0 w-16">Category</span>
|
|
1213
|
+
<span class="px-2 py-0.5 text-xs rounded-full bg-primary/10 text-primary">${escapeHtml(template.category)}</span>
|
|
1214
|
+
</div>
|
|
1215
|
+
` : ''}
|
|
1216
|
+
</div>
|
|
1217
|
+
|
|
1218
|
+
<div class="flex gap-2 pt-3 border-t border-border">
|
|
1219
|
+
<button class="flex-1 px-3 py-2 text-sm bg-primary text-primary-foreground rounded-lg hover:opacity-90 transition-opacity"
|
|
1220
|
+
onclick="copyTemplateToClipboard('${templateId}')">
|
|
1221
|
+
<i data-lucide="copy" class="w-4 h-4 inline mr-1"></i> Copy JSON
|
|
1222
|
+
</button>
|
|
1223
|
+
<button class="flex-1 px-3 py-2 text-sm bg-muted text-foreground rounded-lg hover:bg-hover transition-colors"
|
|
1224
|
+
onclick="editTemplateAsNew('${templateId}')">
|
|
1225
|
+
<i data-lucide="pencil" class="w-4 h-4 inline mr-1"></i> Edit as New
|
|
1226
|
+
</button>
|
|
1227
|
+
</div>
|
|
1228
|
+
</div>
|
|
1229
|
+
`;
|
|
1230
|
+
|
|
1231
|
+
modal.classList.remove('hidden');
|
|
1232
|
+
if (typeof lucide !== 'undefined') lucide.createIcons();
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
function closeTemplateViewModal() {
|
|
1237
|
+
const modal = document.getElementById('templateViewModal');
|
|
1238
|
+
if (modal) {
|
|
1239
|
+
modal.classList.add('hidden');
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
function copyTemplateToClipboard(templateId) {
|
|
1244
|
+
const template = HOOK_TEMPLATES[templateId];
|
|
1245
|
+
if (!template) return;
|
|
1246
|
+
|
|
1247
|
+
const hookJson = {
|
|
1248
|
+
matcher: template.matcher || undefined,
|
|
1249
|
+
command: template.command,
|
|
1250
|
+
args: template.args
|
|
1251
|
+
};
|
|
1252
|
+
|
|
1253
|
+
// Clean up undefined values
|
|
1254
|
+
Object.keys(hookJson).forEach(key => {
|
|
1255
|
+
if (hookJson[key] === undefined || hookJson[key] === '') {
|
|
1256
|
+
delete hookJson[key];
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
navigator.clipboard.writeText(JSON.stringify(hookJson, null, 2))
|
|
1261
|
+
.then(() => showRefreshToast('Template copied to clipboard', 'success'))
|
|
1262
|
+
.catch(() => showRefreshToast('Failed to copy', 'error'));
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
function editTemplateAsNew(templateId) {
|
|
1266
|
+
const template = HOOK_TEMPLATES[templateId];
|
|
1267
|
+
if (!template) return;
|
|
1268
|
+
|
|
1269
|
+
closeTemplateViewModal();
|
|
1270
|
+
|
|
1271
|
+
// Open create modal with template data
|
|
1272
|
+
openHookCreateModal({
|
|
1273
|
+
event: template.event,
|
|
1274
|
+
matcher: template.matcher || '',
|
|
1275
|
+
command: template.command,
|
|
1276
|
+
args: template.args || []
|
|
1277
|
+
});
|
|
283
1278
|
}
|