moai-adk 0.8.0__py3-none-any.whl → 1.1.0__py3-none-any.whl
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.
- moai_adk/__init__.py +2 -6
- moai_adk/__main__.py +267 -21
- moai_adk/astgrep/__init__.py +37 -0
- moai_adk/astgrep/analyzer.py +522 -0
- moai_adk/astgrep/models.py +124 -0
- moai_adk/astgrep/rules.py +179 -0
- moai_adk/cli/__init__.py +6 -2
- moai_adk/cli/commands/__init__.py +1 -4
- moai_adk/cli/commands/analyze.py +125 -0
- moai_adk/cli/commands/doctor.py +24 -6
- moai_adk/cli/commands/init.py +437 -57
- moai_adk/cli/commands/language.py +254 -0
- moai_adk/cli/commands/rank.py +449 -0
- moai_adk/cli/commands/status.py +15 -14
- moai_adk/cli/commands/switch.py +325 -0
- moai_adk/cli/commands/update.py +2195 -93
- moai_adk/cli/main.py +3 -2
- moai_adk/cli/prompts/init_prompts.py +457 -108
- moai_adk/cli/prompts/translations/__init__.py +573 -0
- moai_adk/cli/spec_status.py +263 -0
- moai_adk/cli/ui/__init__.py +44 -0
- moai_adk/cli/ui/progress.py +422 -0
- moai_adk/cli/ui/prompts.py +448 -0
- moai_adk/cli/ui/theme.py +129 -0
- moai_adk/cli/worktree/__init__.py +27 -0
- moai_adk/cli/worktree/__main__.py +31 -0
- moai_adk/cli/worktree/cli.py +788 -0
- moai_adk/cli/worktree/exceptions.py +89 -0
- moai_adk/cli/worktree/manager.py +648 -0
- moai_adk/cli/worktree/models.py +65 -0
- moai_adk/cli/worktree/registry.py +422 -0
- moai_adk/core/PHASE2_OPTIMIZATIONS.md +467 -0
- moai_adk/core/__init__.py +0 -1
- moai_adk/core/analysis/__init__.py +9 -0
- moai_adk/core/analysis/session_analyzer.py +400 -0
- moai_adk/core/claude_integration.py +393 -0
- moai_adk/core/command_helpers.py +270 -0
- moai_adk/core/comprehensive_monitoring_system.py +1183 -0
- moai_adk/core/config/__init__.py +6 -0
- moai_adk/core/config/migration.py +148 -17
- moai_adk/core/config/unified.py +617 -0
- moai_adk/core/context_manager.py +273 -0
- moai_adk/core/credentials.py +264 -0
- moai_adk/core/diagnostics/slash_commands.py +0 -1
- moai_adk/core/enterprise_features.py +1404 -0
- moai_adk/core/error_recovery_system.py +1920 -0
- moai_adk/core/event_driven_hook_system.py +1371 -0
- moai_adk/core/git/__init__.py +8 -1
- moai_adk/core/git/branch.py +0 -1
- moai_adk/core/git/branch_manager.py +2 -10
- moai_adk/core/git/checkpoint.py +1 -7
- moai_adk/core/git/commit.py +0 -1
- moai_adk/core/git/conflict_detector.py +422 -0
- moai_adk/core/git/event_detector.py +16 -7
- moai_adk/core/git/manager.py +91 -2
- moai_adk/core/input_validation_middleware.py +1006 -0
- moai_adk/core/integration/__init__.py +22 -0
- moai_adk/core/integration/engine.py +157 -0
- moai_adk/core/integration/integration_tester.py +226 -0
- moai_adk/core/integration/models.py +88 -0
- moai_adk/core/integration/utils.py +211 -0
- moai_adk/core/issue_creator.py +305 -0
- moai_adk/core/jit_context_loader.py +956 -0
- moai_adk/core/jit_enhanced_hook_manager.py +1987 -0
- moai_adk/core/language_config.py +202 -0
- moai_adk/core/language_config_resolver.py +578 -0
- moai_adk/core/language_validator.py +543 -0
- moai_adk/core/mcp/setup.py +116 -0
- moai_adk/core/merge/__init__.py +9 -0
- moai_adk/core/merge/analyzer.py +666 -0
- moai_adk/core/migration/__init__.py +18 -0
- moai_adk/core/migration/alfred_to_moai_migrator.py +389 -0
- moai_adk/core/migration/backup_manager.py +327 -0
- moai_adk/core/migration/custom_element_scanner.py +358 -0
- moai_adk/core/migration/file_migrator.py +381 -0
- moai_adk/core/migration/interactive_checkbox_ui.py +499 -0
- moai_adk/core/migration/selective_restorer.py +470 -0
- moai_adk/core/migration/template_utils.py +74 -0
- moai_adk/core/migration/user_selection_ui.py +338 -0
- moai_adk/core/migration/version_detector.py +243 -0
- moai_adk/core/migration/version_migrator.py +263 -0
- moai_adk/core/model_allocator.py +241 -0
- moai_adk/core/performance/__init__.py +6 -0
- moai_adk/core/performance/cache_system.py +316 -0
- moai_adk/core/performance/parallel_processor.py +116 -0
- moai_adk/core/phase_optimized_hook_scheduler.py +879 -0
- moai_adk/core/project/__init__.py +0 -1
- moai_adk/core/project/backup_utils.py +13 -8
- moai_adk/core/project/checker.py +2 -4
- moai_adk/core/project/detector.py +189 -22
- moai_adk/core/project/initializer.py +177 -29
- moai_adk/core/project/phase_executor.py +482 -48
- moai_adk/core/project/validator.py +22 -32
- moai_adk/core/quality/__init__.py +1 -1
- moai_adk/core/quality/trust_checker.py +66 -110
- moai_adk/core/quality/validators/__init__.py +1 -1
- moai_adk/core/quality/validators/base_validator.py +1 -1
- moai_adk/core/realtime_monitoring_dashboard.py +1724 -0
- moai_adk/core/robust_json_parser.py +611 -0
- moai_adk/core/rollback_manager.py +953 -0
- moai_adk/core/session_manager.py +651 -0
- moai_adk/core/skill_loading_system.py +579 -0
- moai_adk/core/spec_status_manager.py +478 -0
- moai_adk/core/template/__init__.py +0 -1
- moai_adk/core/template/backup.py +168 -21
- moai_adk/core/template/config.py +141 -45
- moai_adk/core/template/languages.py +0 -1
- moai_adk/core/template/merger.py +107 -32
- moai_adk/core/template/processor.py +1075 -74
- moai_adk/core/template_engine.py +319 -0
- moai_adk/core/template_variable_synchronizer.py +431 -0
- moai_adk/core/unified_permission_manager.py +745 -0
- moai_adk/core/user_behavior_analytics.py +851 -0
- moai_adk/core/version_sync.py +477 -0
- moai_adk/foundation/__init__.py +37 -0
- moai_adk/foundation/backend.py +1027 -0
- moai_adk/foundation/database.py +1115 -0
- moai_adk/foundation/devops.py +1585 -0
- moai_adk/foundation/ears.py +431 -0
- moai_adk/foundation/frontend.py +870 -0
- moai_adk/foundation/git/__init__.py +376 -0
- moai_adk/foundation/git/commit_templates.py +557 -0
- moai_adk/foundation/langs.py +484 -0
- moai_adk/foundation/ml_ops.py +1162 -0
- moai_adk/foundation/testing.py +1524 -0
- moai_adk/foundation/trust/trust_principles.py +676 -0
- moai_adk/foundation/trust/validation_checklist.py +1573 -0
- moai_adk/loop/__init__.py +54 -0
- moai_adk/loop/controller.py +305 -0
- moai_adk/loop/feedback.py +230 -0
- moai_adk/loop/state.py +209 -0
- moai_adk/loop/storage.py +220 -0
- moai_adk/lsp/__init__.py +70 -0
- moai_adk/lsp/client.py +320 -0
- moai_adk/lsp/models.py +261 -0
- moai_adk/lsp/protocol.py +404 -0
- moai_adk/lsp/server_manager.py +248 -0
- moai_adk/project/__init__.py +0 -0
- moai_adk/project/configuration.py +1091 -0
- moai_adk/project/documentation.py +566 -0
- moai_adk/project/schema.py +447 -0
- moai_adk/py.typed +0 -0
- moai_adk/ralph/__init__.py +37 -0
- moai_adk/ralph/engine.py +307 -0
- moai_adk/rank/__init__.py +21 -0
- moai_adk/rank/auth.py +425 -0
- moai_adk/rank/client.py +557 -0
- moai_adk/rank/config.py +147 -0
- moai_adk/rank/hook.py +1503 -0
- moai_adk/rank/py.typed +0 -0
- moai_adk/statusline/__init__.py +41 -0
- moai_adk/statusline/alfred_detector.py +105 -0
- moai_adk/statusline/config.py +376 -0
- moai_adk/statusline/enhanced_output_style_detector.py +372 -0
- moai_adk/statusline/git_collector.py +190 -0
- moai_adk/statusline/main.py +341 -0
- moai_adk/statusline/memory_collector.py +268 -0
- moai_adk/statusline/metrics_tracker.py +78 -0
- moai_adk/statusline/renderer.py +359 -0
- moai_adk/statusline/update_checker.py +129 -0
- moai_adk/statusline/version_reader.py +741 -0
- moai_adk/tag_system/__init__.py +48 -0
- moai_adk/tag_system/atomic_ops.py +117 -0
- moai_adk/tag_system/linkage.py +335 -0
- moai_adk/tag_system/parser.py +176 -0
- moai_adk/tag_system/validator.py +200 -0
- moai_adk/templates/.claude/agents/moai/builder-agent.md +490 -0
- moai_adk/templates/.claude/agents/moai/builder-command.md +1218 -0
- moai_adk/templates/.claude/agents/moai/builder-plugin.md +763 -0
- moai_adk/templates/.claude/agents/moai/builder-skill.md +682 -0
- moai_adk/templates/.claude/agents/moai/expert-backend.md +963 -0
- moai_adk/templates/.claude/agents/moai/expert-debug.md +407 -0
- moai_adk/templates/.claude/agents/moai/expert-devops.md +722 -0
- moai_adk/templates/.claude/agents/moai/expert-frontend.md +748 -0
- moai_adk/templates/.claude/agents/moai/expert-performance.md +661 -0
- moai_adk/templates/.claude/agents/moai/expert-refactoring.md +228 -0
- moai_adk/templates/.claude/agents/moai/expert-security.md +525 -0
- moai_adk/templates/.claude/agents/moai/expert-testing.md +737 -0
- moai_adk/templates/.claude/agents/moai/manager-claude-code.md +438 -0
- moai_adk/templates/.claude/agents/moai/manager-docs.md +578 -0
- moai_adk/templates/.claude/agents/moai/manager-git.md +1092 -0
- moai_adk/templates/.claude/agents/moai/manager-project.md +971 -0
- moai_adk/templates/.claude/agents/moai/manager-quality.md +641 -0
- moai_adk/templates/.claude/agents/moai/manager-spec.md +815 -0
- moai_adk/templates/.claude/agents/moai/manager-strategy.md +811 -0
- moai_adk/templates/.claude/agents/moai/manager-tdd.md +797 -0
- moai_adk/templates/.claude/commands/moai/0-project.md +438 -0
- moai_adk/templates/.claude/commands/moai/1-plan.md +1447 -0
- moai_adk/templates/.claude/commands/moai/2-run.md +850 -0
- moai_adk/templates/.claude/commands/moai/3-sync.md +1398 -0
- moai_adk/templates/.claude/commands/moai/9-feedback.md +330 -0
- moai_adk/templates/.claude/commands/moai/alfred.md +339 -0
- moai_adk/templates/.claude/commands/moai/cancel-loop.md +163 -0
- moai_adk/templates/.claude/commands/moai/fix.md +264 -0
- moai_adk/templates/.claude/commands/moai/loop.md +363 -0
- moai_adk/templates/.claude/hooks/__init__.py +8 -0
- moai_adk/templates/.claude/hooks/moai/__init__.py +8 -0
- moai_adk/templates/.claude/hooks/moai/lib/README.md +143 -0
- moai_adk/templates/.claude/hooks/moai/lib/__init__.py +41 -0
- moai_adk/templates/.claude/hooks/moai/lib/alfred_detector.py +105 -0
- moai_adk/templates/.claude/hooks/moai/lib/atomic_write.py +122 -0
- moai_adk/templates/.claude/hooks/{alfred/core → moai/lib}/checkpoint.py +13 -37
- moai_adk/templates/.claude/hooks/moai/lib/common.py +161 -0
- moai_adk/templates/.claude/hooks/moai/lib/config.py +376 -0
- moai_adk/templates/.claude/hooks/moai/lib/config_manager.py +442 -0
- moai_adk/templates/.claude/hooks/moai/lib/config_validator.py +639 -0
- moai_adk/templates/.claude/hooks/moai/lib/enhanced_output_style_detector.py +372 -0
- moai_adk/templates/.claude/hooks/moai/lib/example_config.json +104 -0
- moai_adk/templates/.claude/hooks/moai/lib/exceptions.py +171 -0
- moai_adk/templates/.claude/hooks/moai/lib/file_utils.py +95 -0
- moai_adk/templates/.claude/hooks/moai/lib/git_collector.py +190 -0
- moai_adk/templates/.claude/hooks/moai/lib/git_operations_manager.py +592 -0
- moai_adk/templates/.claude/hooks/moai/lib/language_detector.py +298 -0
- moai_adk/templates/.claude/hooks/moai/lib/language_validator.py +417 -0
- moai_adk/templates/.claude/hooks/moai/lib/main.py +341 -0
- moai_adk/templates/.claude/hooks/moai/lib/memory_collector.py +268 -0
- moai_adk/templates/.claude/hooks/moai/lib/metrics_tracker.py +78 -0
- moai_adk/templates/.claude/hooks/moai/lib/models.py +104 -0
- moai_adk/templates/.claude/hooks/moai/lib/path_utils.py +219 -0
- moai_adk/templates/.claude/hooks/moai/lib/project.py +777 -0
- moai_adk/templates/.claude/hooks/moai/lib/renderer.py +359 -0
- moai_adk/templates/.claude/hooks/moai/lib/tag_linkage.py +333 -0
- moai_adk/templates/.claude/hooks/moai/lib/tag_parser.py +176 -0
- moai_adk/templates/.claude/hooks/moai/lib/tag_validator.py +200 -0
- moai_adk/templates/.claude/hooks/moai/lib/test_hooks_improvements.py +443 -0
- moai_adk/templates/.claude/hooks/moai/lib/timeout.py +160 -0
- moai_adk/templates/.claude/hooks/moai/lib/tool_registry.py +896 -0
- moai_adk/templates/.claude/hooks/moai/lib/unified_timeout_manager.py +542 -0
- moai_adk/templates/.claude/hooks/moai/lib/update_checker.py +129 -0
- moai_adk/templates/.claude/hooks/moai/lib/version_reader.py +741 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__ast_grep_scan.py +276 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__code_formatter.py +255 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__coverage_guard.py +325 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__linter.py +315 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__lsp_diagnostic.py +508 -0
- moai_adk/templates/.claude/hooks/moai/pre_commit__tag_validator.py +287 -0
- moai_adk/templates/.claude/hooks/moai/pre_tool__security_guard.py +268 -0
- moai_adk/templates/.claude/hooks/moai/pre_tool__tdd_enforcer.py +208 -0
- moai_adk/templates/.claude/hooks/moai/session_end__auto_cleanup.py +894 -0
- moai_adk/templates/.claude/hooks/moai/session_end__rank_submit.py +69 -0
- moai_adk/templates/.claude/hooks/moai/session_start__show_project_info.py +1170 -0
- moai_adk/templates/.claude/hooks/moai/shared/utils/announcement_translator.py +206 -0
- moai_adk/templates/.claude/hooks/moai/stop__loop_controller.py +621 -0
- moai_adk/templates/.claude/output-styles/moai/alfred.md +758 -0
- moai_adk/templates/.claude/output-styles/moai/r2d2.md +643 -0
- moai_adk/templates/.claude/output-styles/moai/yoda.md +359 -0
- moai_adk/templates/.claude/settings.json +177 -72
- moai_adk/templates/.claude/skills/moai-docs-generation/SKILL.md +303 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/examples.md +252 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/README.md +56 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/api-documentation.md +120 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/code-documentation.md +152 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/multi-format-output.md +185 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/user-guides.md +207 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/reference.md +234 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +132 -281
- moai_adk/templates/.claude/skills/moai-domain-backend/examples.md +610 -1525
- moai_adk/templates/.claude/skills/moai-domain-backend/reference.md +423 -619
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +133 -77
- moai_adk/templates/.claude/skills/moai-domain-database/examples.md +817 -16
- moai_adk/templates/.claude/skills/moai-domain-database/modules/README.md +53 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/mongodb.md +231 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/postgresql.md +169 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/redis.md +262 -0
- moai_adk/templates/.claude/skills/moai-domain-database/reference.md +532 -17
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +91 -78
- moai_adk/templates/.claude/skills/moai-domain-frontend/examples.md +955 -16
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/component-architecture.md +723 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/nextjs16-patterns.md +713 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/performance-optimization.md +694 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/react19-patterns.md +591 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/state-management.md +680 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/vue35-patterns.md +802 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/reference.md +651 -18
- moai_adk/templates/.claude/skills/moai-domain-uiux/SKILL.md +234 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/examples.md +560 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/accessibility-wcag.md +260 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/component-architecture.md +228 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/icon-libraries.md +401 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/theming-system.md +373 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/reference.md +243 -0
- moai_adk/templates/.claude/skills/moai-formats-data/SKILL.md +189 -0
- moai_adk/templates/.claude/skills/moai-formats-data/examples.md +804 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/README.md +327 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/SKILL-MODULARIZATION-TEMPLATE.md +278 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/caching-performance.md +459 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/data-validation.md +485 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/json-optimization.md +374 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/toon-encoding.md +308 -0
- moai_adk/templates/.claude/skills/moai-formats-data/reference.md +585 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/SKILL.md +225 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/examples.md +732 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/advanced-agent-patterns.md +370 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/best-practices-checklist.md +616 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-cli-reference-official.md +420 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-custom-slash-commands-official.md +739 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-devcontainers-official.md +381 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-discover-plugins-official.md +379 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-headless-official.md +378 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-hooks-official.md +670 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-iam-official.md +635 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-memory-official.md +543 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-plugin-marketplaces-official.md +308 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-plugins-official.md +640 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sandboxing-official.md +282 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-settings-official.md +663 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-skills-official.md +467 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-statusline-official.md +293 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sub-agents-official.md +420 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/complete-configuration-guide.md +175 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-examples.md +1674 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-formatting-guide.md +729 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-examples.md +1513 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-formatting-guide.md +1086 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-integration-patterns.md +1100 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference.md +209 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/SKILL.md +221 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/examples.md +1048 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/reference.md +246 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/SKILL.md +242 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/examples.md +358 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/README.md +296 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/agents-reference.md +359 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/commands-reference.md +432 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-advanced.md +279 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-implementation.md +267 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-patterns.md +228 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/execution-rules.md +687 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/modular-system.md +665 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/patterns.md +22 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/progressive-disclosure.md +649 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-ears-format.md +200 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-first-tdd.md +171 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-tdd-implementation.md +275 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/token-optimization.md +708 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-framework.md +239 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-implementation.md +244 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-validation.md +219 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/reference.md +478 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/SKILL.md +311 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/examples.md +228 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/assumption-matrix.md +80 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/cognitive-bias.md +199 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/first-principles.md +140 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/trade-off-analysis.md +154 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/reference.md +157 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/SKILL.md +180 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/examples.md +1232 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/best-practices.md +261 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/integration-patterns.md +194 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/proactive-analysis.md +229 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/trust5-validation.md +169 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/reference.md +1266 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/scripts/quality-gate.sh +668 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/templates/github-actions-quality.yml +481 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/templates/quality-config.yaml +519 -0
- moai_adk/templates/.claude/skills/moai-framework-electron/SKILL.md +288 -0
- moai_adk/templates/.claude/skills/moai-framework-electron/examples.md +2082 -0
- moai_adk/templates/.claude/skills/moai-framework-electron/reference.md +1649 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +96 -77
- moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +1226 -16
- moai_adk/templates/.claude/skills/moai-lang-cpp/modules/advanced-patterns.md +401 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +1119 -14
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +80 -79
- moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +572 -16
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/aspnet-core.md +627 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/blazor-components.md +767 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/cqrs-validation.md +626 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/csharp12-features.md +580 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/efcore-patterns.md +622 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +388 -15
- moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +135 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/examples.md +1171 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/modules/advanced-patterns.md +531 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/reference.md +889 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/SKILL.md +104 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/examples.md +1090 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/reference.md +686 -0
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +153 -80
- moai_adk/templates/.claude/skills/moai-lang-go/examples.md +906 -16
- moai_adk/templates/.claude/skills/moai-lang-go/reference.md +721 -15
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +117 -80
- moai_adk/templates/.claude/skills/moai-lang-java/examples.md +851 -16
- moai_adk/templates/.claude/skills/moai-lang-java/reference.md +278 -18
- moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +133 -79
- moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +960 -16
- moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +1528 -17
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +100 -79
- moai_adk/templates/.claude/skills/moai-lang-kotlin/examples.md +993 -16
- moai_adk/templates/.claude/skills/moai-lang-kotlin/reference.md +549 -18
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +135 -76
- moai_adk/templates/.claude/skills/moai-lang-php/examples.md +1595 -16
- moai_adk/templates/.claude/skills/moai-lang-php/modules/advanced-patterns.md +538 -0
- moai_adk/templates/.claude/skills/moai-lang-php/reference.md +1309 -16
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +141 -341
- moai_adk/templates/.claude/skills/moai-lang-python/examples.md +849 -496
- moai_adk/templates/.claude/skills/moai-lang-python/reference.md +731 -243
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +134 -76
- moai_adk/templates/.claude/skills/moai-lang-r/examples.md +1141 -16
- moai_adk/templates/.claude/skills/moai-lang-r/modules/advanced-patterns.md +489 -0
- moai_adk/templates/.claude/skills/moai-lang-r/reference.md +1074 -17
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +136 -77
- moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +1093 -16
- moai_adk/templates/.claude/skills/moai-lang-ruby/modules/advanced-patterns.md +309 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/modules/testing-patterns.md +306 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +1010 -17
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +112 -78
- moai_adk/templates/.claude/skills/moai-lang-rust/examples.md +646 -16
- moai_adk/templates/.claude/skills/moai-lang-rust/reference.md +491 -18
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +113 -75
- moai_adk/templates/.claude/skills/moai-lang-scala/examples.md +620 -16
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/akka-actors.md +479 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/cats-effect.md +489 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/functional-programming.md +460 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/spark-data.md +498 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/zio-patterns.md +541 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/reference.md +410 -17
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +88 -83
- moai_adk/templates/.claude/skills/moai-lang-swift/examples.md +905 -16
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/combine-reactive.md +256 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/concurrency.md +270 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/swift6-features.md +265 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/swiftui-patterns.md +314 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/reference.md +659 -17
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +115 -82
- moai_adk/templates/.claude/skills/moai-lang-typescript/examples.md +1076 -16
- moai_adk/templates/.claude/skills/moai-lang-typescript/reference.md +718 -21
- moai_adk/templates/.claude/skills/moai-library-mermaid/SKILL.md +145 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/examples.md +270 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/modules/advanced-patterns.md +465 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/modules/optimization.md +440 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/reference.md +228 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/SKILL.md +143 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/examples.md +592 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-deployment-patterns.md +182 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-patterns.md +336 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/configuration.md +350 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/content-architecture-optimization.md +162 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/deployment.md +52 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/framework-core-configuration.md +186 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/i18n-setup.md +55 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/mdx-components.md +52 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/optimization.md +303 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/reference.md +379 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/SKILL.md +175 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/examples.md +575 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/advanced-patterns.md +394 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/optimization.md +278 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-components.md +457 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-theming.md +373 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/reference.md +74 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/SKILL.md +284 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/examples.md +2446 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/adaptive-mfa.md +233 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/akamai-integration.md +214 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/application-credentials.md +280 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/attack-protection-log-events.md +224 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/attack-protection-overview.md +140 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/bot-detection.md +144 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/breached-password-detection.md +187 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/brute-force-protection.md +189 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/certifications.md +282 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/compliance-overview.md +263 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/continuous-session-protection.md +307 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/customize-mfa.md +177 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/dpop-implementation.md +283 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/fapi-implementation.md +259 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/gdpr-compliance.md +313 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/guardian-configuration.md +269 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/highly-regulated-identity.md +272 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/jwt-fundamentals.md +248 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mdl-verification.md +210 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-api-management.md +278 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-factors.md +226 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-overview.md +174 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mtls-sender-constraining.md +316 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/ropg-flow-mfa.md +216 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/security-center.md +325 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/security-guidance.md +277 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/state-parameters.md +177 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/step-up-authentication.md +251 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/suspicious-ip-throttling.md +240 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/tenant-access-control.md +179 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/webauthn-fido.md +235 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/reference.md +224 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/SKILL.md +135 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/examples.md +1426 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/modules/advanced-patterns.md +417 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/reference.md +273 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/SKILL.md +158 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/examples.md +506 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/auth-integration.md +421 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/file-storage.md +474 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/reactive-queries.md +302 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/server-functions.md +452 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/reference.md +385 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/SKILL.md +166 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/examples.md +514 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/custom-claims.md +374 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/phone-auth.md +372 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/social-auth.md +339 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/reference.md +382 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/SKILL.md +127 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/examples.md +445 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/offline-cache.md +392 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/realtime-listeners.md +441 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/security-rules.md +352 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/transactions.md +452 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/reference.md +322 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/SKILL.md +156 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/examples.md +470 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/auto-scaling.md +349 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/branching-workflows.md +354 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/connection-pooling.md +412 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/pitr-backups.md +458 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/reference.md +272 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/SKILL.md +146 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/examples.md +539 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/docker-deployment.md +261 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/multi-service.md +291 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/networking-domains.md +338 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/volumes-storage.md +353 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/reference.md +374 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/SKILL.md +141 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/examples.md +502 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/auth-integration.md +384 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/edge-functions.md +371 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/postgresql-pgvector.md +231 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/realtime-presence.md +354 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/row-level-security.md +286 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/storage-cdn.md +319 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/typescript-patterns.md +453 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/reference.md +284 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/SKILL.md +132 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/examples.md +502 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/analytics-speed.md +348 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/deployment-config.md +344 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/edge-functions.md +222 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/isr-caching.md +306 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/kv-storage.md +399 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/reference.md +360 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/SKILL.md +193 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/examples.md +1099 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/language-specific.md +307 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/pattern-syntax.md +237 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/refactoring-patterns.md +260 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/security-rules.md +239 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/reference.md +288 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/go.yml +90 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/python.yml +101 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/typescript.yml +83 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/quality/complexity-check.yml +94 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/quality/deprecated-apis.yml +84 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/secrets-detection.yml +89 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/sql-injection.yml +45 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/xss-prevention.yml +50 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/sgconfig.yml +54 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/SKILL.md +251 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/examples.md +544 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/modules/advanced-patterns.md +379 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/modules/optimization.md +286 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/reference.md +307 -0
- moai_adk/templates/.claude/skills/moai-workflow-loop/SKILL.md +197 -0
- moai_adk/templates/.claude/skills/moai-workflow-loop/examples.md +1063 -0
- moai_adk/templates/.claude/skills/moai-workflow-loop/reference.md +1414 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/README.md +190 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/SKILL.md +287 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples.md +547 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/reference.md +275 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/schemas/config-schema.json +316 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/schemas/tab_schema.json +1434 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/config-template.json +71 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/product-template.md +44 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/structure-template.md +48 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/tech-template.md +92 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/config-manager-setup.json +109 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/language-initializer.json +228 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/menu-project-config.json +130 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/project-batch-questions.json +97 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/spec-workflow-setup.json +150 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/SKILL.md +337 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/examples.md +900 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/modules/advanced-patterns.md +237 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/reference.md +704 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/SKILL.md +270 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/examples.md +552 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/code-templates.md +124 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/feedback-templates.md +100 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/template-optimizer.md +138 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/reference.md +346 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/LICENSE.txt +202 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/SKILL.md +269 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/ai-powered-testing.py +294 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/console_logging.py +35 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/element_discovery.py +40 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/static_html_automation.py +34 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples.md +672 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/README.md +269 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/advanced-patterns.md +576 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/ai-debugging.md +302 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/context7-integration.md +286 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/review-workflows.md +500 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/relevance-analysis.md +154 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/safety-analysis.md +148 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/scoring-algorithms.md +196 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/timeliness-analysis.md +168 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/truthfulness-analysis.md +136 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/usability-analysis.md +153 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework.md +257 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review.md +263 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/analysis-patterns.md +340 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/core-classes.md +299 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/tool-integration.md +380 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/debugging/debugging-workflows.md +451 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/debugging/error-analysis.md +442 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/optimization.md +505 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance/optimization-patterns.md +473 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance/profiling-techniques.md +481 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/ai-optimization.md +241 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/bottleneck-detection.md +397 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/optimization-plan.md +315 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/profiler-core.md +277 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/real-time-monitoring.md +187 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization.md +327 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/quality-metrics.md +415 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/refactoring/ai-workflows.md +620 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/refactoring/patterns.md +692 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/security-analysis.md +429 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/smart-refactoring.md +313 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/static-analysis.md +438 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd/core-classes.md +397 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/advanced-features.md +494 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/red-green-refactor.md +316 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/test-generation.md +471 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/test-patterns.md +371 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7.md +265 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/trust5-validation.md +428 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/reference/playwright-best-practices.md +57 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/reference.md +440 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/scripts/with_server.py +218 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/templates/alfred-integration.md +376 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/workflows/enterprise-testing-workflow.py +571 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/SKILL.md +228 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/examples.md +606 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/integration-patterns.md +149 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/moai-adk-integration.md +245 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-advanced.md +310 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-development.md +202 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-workflows.md +302 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/registry-architecture.md +271 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/resource-optimization.md +300 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/tools-integration.md +280 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/troubleshooting.md +397 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/worktree-commands.md +296 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/worktree-management.md +217 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/reference.md +357 -0
- moai_adk/templates/.git-hooks/pre-commit +128 -0
- moai_adk/templates/.git-hooks/pre-push +468 -0
- moai_adk/templates/.github/workflows/ci-universal.yml +1314 -0
- moai_adk/templates/.github/workflows/security-secrets-check.yml +179 -0
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +206 -36
- moai_adk/templates/.gitignore +152 -13
- moai_adk/templates/.lsp.json +152 -0
- moai_adk/templates/.mcp.json +13 -0
- moai_adk/templates/.moai/announcements/en.json +18 -0
- moai_adk/templates/.moai/announcements/ja.json +18 -0
- moai_adk/templates/.moai/announcements/ko.json +18 -0
- moai_adk/templates/.moai/announcements/zh.json +18 -0
- moai_adk/templates/.moai/config/config.yaml +64 -0
- moai_adk/templates/.moai/config/multilingual-triggers.yaml +213 -0
- moai_adk/templates/.moai/config/sections/git-strategy.yaml +116 -0
- moai_adk/templates/.moai/config/sections/language.yaml +11 -0
- moai_adk/templates/.moai/config/sections/llm.yaml +41 -0
- moai_adk/templates/.moai/config/sections/pricing.yaml +30 -0
- moai_adk/templates/.moai/config/sections/project.yaml +13 -0
- moai_adk/templates/.moai/config/sections/quality.yaml +55 -0
- moai_adk/templates/.moai/config/sections/ralph.yaml +55 -0
- moai_adk/templates/.moai/config/sections/system.yaml +59 -0
- moai_adk/templates/.moai/config/sections/user.yaml +5 -0
- moai_adk/templates/.moai/config/statusline-config.yaml +92 -0
- moai_adk/templates/.moai/llm-configs/glm.json +22 -0
- moai_adk/templates/CLAUDE.ja.md +343 -0
- moai_adk/templates/CLAUDE.ko.md +343 -0
- moai_adk/templates/CLAUDE.md +274 -246
- moai_adk/templates/CLAUDE.zh.md +343 -0
- moai_adk/utils/__init__.py +24 -2
- moai_adk/utils/banner.py +9 -13
- moai_adk/utils/common.py +331 -0
- moai_adk/utils/link_validator.py +241 -0
- moai_adk/utils/logger.py +4 -9
- moai_adk/utils/safe_file_reader.py +206 -0
- moai_adk/utils/timeout.py +160 -0
- moai_adk/utils/toon_utils.py +256 -0
- moai_adk/version.py +22 -0
- moai_adk-1.1.0.dist-info/METADATA +2443 -0
- moai_adk-1.1.0.dist-info/RECORD +701 -0
- {moai_adk-0.8.0.dist-info → moai_adk-1.1.0.dist-info}/WHEEL +1 -1
- moai_adk-1.1.0.dist-info/entry_points.txt +5 -0
- moai_adk-1.1.0.dist-info/licenses/LICENSE +99 -0
- moai_adk/cli/commands/backup.py +0 -80
- moai_adk/templates/.claude/agents/alfred/cc-manager.md +0 -293
- moai_adk/templates/.claude/agents/alfred/debug-helper.md +0 -196
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +0 -207
- moai_adk/templates/.claude/agents/alfred/git-manager.md +0 -375
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +0 -343
- moai_adk/templates/.claude/agents/alfred/project-manager.md +0 -246
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +0 -320
- moai_adk/templates/.claude/agents/alfred/skill-factory.md +0 -837
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +0 -272
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +0 -265
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +0 -311
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +0 -352
- moai_adk/templates/.claude/commands/alfred/0-project.md +0 -1184
- moai_adk/templates/.claude/commands/alfred/1-plan.md +0 -665
- moai_adk/templates/.claude/commands/alfred/2-run.md +0 -488
- moai_adk/templates/.claude/commands/alfred/3-sync.md +0 -623
- moai_adk/templates/.claude/hooks/alfred/HOOK_SCHEMA_VALIDATION.md +0 -313
- moai_adk/templates/.claude/hooks/alfred/README.md +0 -230
- moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -174
- moai_adk/templates/.claude/hooks/alfred/core/__init__.py +0 -170
- moai_adk/templates/.claude/hooks/alfred/core/context.py +0 -67
- moai_adk/templates/.claude/hooks/alfred/core/project.py +0 -416
- moai_adk/templates/.claude/hooks/alfred/core/tags.py +0 -198
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +0 -21
- moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +0 -25
- moai_adk/templates/.claude/hooks/alfred/handlers/session.py +0 -161
- moai_adk/templates/.claude/hooks/alfred/handlers/tool.py +0 -90
- moai_adk/templates/.claude/hooks/alfred/handlers/user.py +0 -42
- moai_adk/templates/.claude/hooks/alfred/test_hook_output.py +0 -175
- moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -640
- moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -696
- moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -474
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/SKILL.md +0 -122
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/reference.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-interactive-questions/SKILL.md +0 -237
- moai_adk/templates/.claude/skills/moai-alfred-interactive-questions/examples.md +0 -615
- moai_adk/templates/.claude/skills/moai-alfred-interactive-questions/reference.md +0 -653
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-cc-agents/SKILL.md +0 -269
- moai_adk/templates/.claude/skills/moai-cc-agents/templates/agent-template.md +0 -32
- moai_adk/templates/.claude/skills/moai-cc-claude-md/SKILL.md +0 -298
- moai_adk/templates/.claude/skills/moai-cc-claude-md/templates/CLAUDE-template.md +0 -26
- moai_adk/templates/.claude/skills/moai-cc-commands/SKILL.md +0 -307
- moai_adk/templates/.claude/skills/moai-cc-commands/templates/command-template.md +0 -21
- moai_adk/templates/.claude/skills/moai-cc-hooks/SKILL.md +0 -252
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/pre-bash-check.sh +0 -19
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/preserve-permissions.sh +0 -19
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/validate-bash-command.py +0 -24
- moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/SKILL.md +0 -199
- moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/templates/settings-mcp-template.json +0 -39
- moai_adk/templates/.claude/skills/moai-cc-memory/SKILL.md +0 -316
- moai_adk/templates/.claude/skills/moai-cc-memory/templates/session-summary-template.md +0 -18
- moai_adk/templates/.claude/skills/moai-cc-settings/SKILL.md +0 -263
- moai_adk/templates/.claude/skills/moai-cc-settings/templates/settings-complete-template.json +0 -30
- moai_adk/templates/.claude/skills/moai-cc-skills/SKILL.md +0 -291
- moai_adk/templates/.claude/skills/moai-cc-skills/templates/SKILL-template.md +0 -15
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-data-science/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-data-science/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-domain-devops/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-devops/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-ml/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-ml/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-security/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-security/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-web-api/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-web-api/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +0 -303
- moai_adk/templates/.claude/skills/moai-essentials-debug/examples.md +0 -1064
- moai_adk/templates/.claude/skills/moai-essentials-debug/reference.md +0 -1047
- moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-essentials-perf/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-essentials-perf/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-essentials-refactor/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-essentials-refactor/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-essentials-review/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-essentials-review/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-foundation-ears/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-ears/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +0 -122
- moai_adk/templates/.claude/skills/moai-foundation-git/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-git/reference.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-foundation-langs/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-langs/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-foundation-specs/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-specs/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-foundation-tags/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-tags/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +0 -307
- moai_adk/templates/.claude/skills/moai-foundation-trust/examples.md +0 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/reference.md +0 -1099
- moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-lang-c/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-c/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-lang-dart/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-dart/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-lang-shell/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-shell/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-lang-sql/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-sql/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-skill-factory/CHECKLIST.md +0 -482
- moai_adk/templates/.claude/skills/moai-skill-factory/EXAMPLES.md +0 -278
- moai_adk/templates/.claude/skills/moai-skill-factory/INTERACTIVE-DISCOVERY.md +0 -524
- moai_adk/templates/.claude/skills/moai-skill-factory/METADATA.md +0 -477
- moai_adk/templates/.claude/skills/moai-skill-factory/PARALLEL-ANALYSIS-REPORT.md +0 -429
- moai_adk/templates/.claude/skills/moai-skill-factory/PYTHON-VERSION-MATRIX.md +0 -391
- moai_adk/templates/.claude/skills/moai-skill-factory/SKILL-FACTORY-WORKFLOW.md +0 -431
- moai_adk/templates/.claude/skills/moai-skill-factory/SKILL-UPDATE-ADVISOR.md +0 -577
- moai_adk/templates/.claude/skills/moai-skill-factory/SKILL.md +0 -271
- moai_adk/templates/.claude/skills/moai-skill-factory/STEP-BY-STEP-GUIDE.md +0 -466
- moai_adk/templates/.claude/skills/moai-skill-factory/STRUCTURE.md +0 -583
- moai_adk/templates/.claude/skills/moai-skill-factory/WEB-RESEARCH.md +0 -526
- moai_adk/templates/.claude/skills/moai-skill-factory/reference.md +0 -465
- moai_adk/templates/.claude/skills/moai-skill-factory/scripts/generate-structure.sh +0 -328
- moai_adk/templates/.claude/skills/moai-skill-factory/scripts/validate-skill.sh +0 -312
- moai_adk/templates/.claude/skills/moai-skill-factory/templates/SKILL_TEMPLATE.md +0 -245
- moai_adk/templates/.claude/skills/moai-skill-factory/templates/examples-template.md +0 -285
- moai_adk/templates/.claude/skills/moai-skill-factory/templates/reference-template.md +0 -278
- moai_adk/templates/.claude/skills/moai-skill-factory/templates/scripts-template.sh +0 -303
- moai_adk/templates/.claude/skills/moai-spec-authoring/README.md +0 -137
- moai_adk/templates/.claude/skills/moai-spec-authoring/SKILL.md +0 -218
- moai_adk/templates/.claude/skills/moai-spec-authoring/examples/validate-spec.sh +0 -161
- moai_adk/templates/.claude/skills/moai-spec-authoring/examples.md +0 -541
- moai_adk/templates/.claude/skills/moai-spec-authoring/reference.md +0 -622
- moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +0 -176
- moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
- moai_adk/templates/.github/workflows/moai-gitflow.yml +0 -256
- moai_adk/templates/.moai/config.json +0 -96
- moai_adk/templates/.moai/memory/CLAUDE-AGENTS-GUIDE.md +0 -208
- moai_adk/templates/.moai/memory/CLAUDE-PRACTICES.md +0 -369
- moai_adk/templates/.moai/memory/CLAUDE-RULES.md +0 -539
- moai_adk/templates/.moai/memory/CONFIG-SCHEMA.md +0 -444
- moai_adk/templates/.moai/memory/DEVELOPMENT-GUIDE.md +0 -344
- moai_adk/templates/.moai/memory/GITFLOW-PROTECTION-POLICY.md +0 -220
- moai_adk/templates/.moai/memory/SKILLS-DESCRIPTION-POLICY.md +0 -218
- moai_adk/templates/.moai/memory/SPEC-METADATA.md +0 -356
- moai_adk/templates/.moai/memory/config-schema.md +0 -444
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -220
- moai_adk/templates/.moai/memory/spec-metadata.md +0 -356
- moai_adk/templates/.moai/project/product.md +0 -161
- moai_adk/templates/.moai/project/structure.md +0 -156
- moai_adk/templates/.moai/project/tech.md +0 -227
- moai_adk/templates/__init__.py +0 -2
- moai_adk-0.8.0.dist-info/METADATA +0 -1722
- moai_adk-0.8.0.dist-info/RECORD +0 -282
- moai_adk-0.8.0.dist-info/entry_points.txt +0 -2
- moai_adk-0.8.0.dist-info/licenses/LICENSE +0 -21
moai_adk/rank/hook.py
ADDED
|
@@ -0,0 +1,1503 @@
|
|
|
1
|
+
"""Session end hook for automatic token usage submission.
|
|
2
|
+
|
|
3
|
+
This module provides a hook that is called when a Claude Code session ends,
|
|
4
|
+
automatically submitting the session's token usage to the MoAI Rank service.
|
|
5
|
+
|
|
6
|
+
The hook is installed globally at ~/.claude/hooks/moai/ by default to collect
|
|
7
|
+
session data from all projects. Users can opt-out specific projects via
|
|
8
|
+
~/.moai/rank/config.yaml configuration.
|
|
9
|
+
|
|
10
|
+
Token Usage Collection:
|
|
11
|
+
Claude Code's SessionEnd hook only provides metadata (session_id, transcript_path,
|
|
12
|
+
cwd, etc.) - NOT token counts. Token usage must be extracted from the transcript
|
|
13
|
+
JSONL file by parsing message.usage fields.
|
|
14
|
+
|
|
15
|
+
Cost Calculation:
|
|
16
|
+
Based on Anthropic's pricing (as of 2024):
|
|
17
|
+
- Claude 3.5 Sonnet: Input $3/MTok, Output $15/MTok
|
|
18
|
+
- Claude 3 Opus: Input $15/MTok, Output $75/MTok
|
|
19
|
+
- Cache Creation: $3.75/MTok (Sonnet), $18.75/MTok (Opus)
|
|
20
|
+
- Cache Read: $0.30/MTok (Sonnet), $1.50/MTok (Opus)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import hashlib
|
|
24
|
+
import os
|
|
25
|
+
from dataclasses import dataclass
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Any, Optional
|
|
28
|
+
|
|
29
|
+
from moai_adk.rank.client import RankClient, RankClientError, SessionSubmission
|
|
30
|
+
from moai_adk.rank.config import RankConfig
|
|
31
|
+
|
|
32
|
+
# Token pricing per million tokens (USD)
|
|
33
|
+
# Source: https://platform.claude.com/docs/en/about-claude/pricing
|
|
34
|
+
# Last updated: 2025-01
|
|
35
|
+
MODEL_PRICING: dict[str, dict[str, float]] = {
|
|
36
|
+
# Claude Opus 4.5 - Latest flagship model
|
|
37
|
+
"claude-opus-4-5-20251101": {
|
|
38
|
+
"input": 5.00,
|
|
39
|
+
"output": 25.00,
|
|
40
|
+
"cache_creation": 6.25, # 5m cache writes
|
|
41
|
+
"cache_read": 0.50,
|
|
42
|
+
},
|
|
43
|
+
# Claude Opus 4.1
|
|
44
|
+
"claude-opus-4-1-20250414": {
|
|
45
|
+
"input": 15.00,
|
|
46
|
+
"output": 75.00,
|
|
47
|
+
"cache_creation": 18.75,
|
|
48
|
+
"cache_read": 1.50,
|
|
49
|
+
},
|
|
50
|
+
# Claude Opus 4
|
|
51
|
+
"claude-opus-4-20250514": {
|
|
52
|
+
"input": 15.00,
|
|
53
|
+
"output": 75.00,
|
|
54
|
+
"cache_creation": 18.75,
|
|
55
|
+
"cache_read": 1.50,
|
|
56
|
+
},
|
|
57
|
+
# Claude Sonnet 4.5
|
|
58
|
+
"claude-sonnet-4-5-20251022": {
|
|
59
|
+
"input": 3.00,
|
|
60
|
+
"output": 15.00,
|
|
61
|
+
"cache_creation": 3.75,
|
|
62
|
+
"cache_read": 0.30,
|
|
63
|
+
},
|
|
64
|
+
# Claude Sonnet 4
|
|
65
|
+
"claude-sonnet-4-20250514": {
|
|
66
|
+
"input": 3.00,
|
|
67
|
+
"output": 15.00,
|
|
68
|
+
"cache_creation": 3.75,
|
|
69
|
+
"cache_read": 0.30,
|
|
70
|
+
},
|
|
71
|
+
# Claude Sonnet 3.7 (deprecated but still supported)
|
|
72
|
+
"claude-3-7-sonnet-20250219": {
|
|
73
|
+
"input": 3.00,
|
|
74
|
+
"output": 15.00,
|
|
75
|
+
"cache_creation": 3.75,
|
|
76
|
+
"cache_read": 0.30,
|
|
77
|
+
},
|
|
78
|
+
# Claude Haiku 4.5
|
|
79
|
+
"claude-haiku-4-5-20251022": {
|
|
80
|
+
"input": 1.00,
|
|
81
|
+
"output": 5.00,
|
|
82
|
+
"cache_creation": 1.25,
|
|
83
|
+
"cache_read": 0.10,
|
|
84
|
+
},
|
|
85
|
+
# Claude Haiku 3.5
|
|
86
|
+
"claude-3-5-haiku-20241022": {
|
|
87
|
+
"input": 0.80,
|
|
88
|
+
"output": 4.00,
|
|
89
|
+
"cache_creation": 1.00,
|
|
90
|
+
"cache_read": 0.08,
|
|
91
|
+
},
|
|
92
|
+
# Claude Opus 3 (deprecated)
|
|
93
|
+
"claude-3-opus-20240229": {
|
|
94
|
+
"input": 15.00,
|
|
95
|
+
"output": 75.00,
|
|
96
|
+
"cache_creation": 18.75,
|
|
97
|
+
"cache_read": 1.50,
|
|
98
|
+
},
|
|
99
|
+
# Claude Haiku 3
|
|
100
|
+
"claude-3-haiku-20240307": {
|
|
101
|
+
"input": 0.25,
|
|
102
|
+
"output": 1.25,
|
|
103
|
+
"cache_creation": 0.30,
|
|
104
|
+
"cache_read": 0.03,
|
|
105
|
+
},
|
|
106
|
+
# Legacy Claude 3.5 Sonnet versions
|
|
107
|
+
"claude-3-5-sonnet-20241022": {
|
|
108
|
+
"input": 3.00,
|
|
109
|
+
"output": 15.00,
|
|
110
|
+
"cache_creation": 3.75,
|
|
111
|
+
"cache_read": 0.30,
|
|
112
|
+
},
|
|
113
|
+
"claude-3-5-sonnet-20240620": {
|
|
114
|
+
"input": 3.00,
|
|
115
|
+
"output": 15.00,
|
|
116
|
+
"cache_creation": 3.75,
|
|
117
|
+
"cache_read": 0.30,
|
|
118
|
+
},
|
|
119
|
+
# Default fallback (Sonnet 4 pricing)
|
|
120
|
+
"default": {
|
|
121
|
+
"input": 3.00,
|
|
122
|
+
"output": 15.00,
|
|
123
|
+
"cache_creation": 3.75,
|
|
124
|
+
"cache_read": 0.30,
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@dataclass
|
|
130
|
+
class TokenUsage:
|
|
131
|
+
"""Token usage extracted from a session transcript.
|
|
132
|
+
|
|
133
|
+
Attributes:
|
|
134
|
+
input_tokens: Total input tokens consumed
|
|
135
|
+
output_tokens: Total output tokens generated
|
|
136
|
+
cache_creation_tokens: Tokens used for cache creation
|
|
137
|
+
cache_read_tokens: Tokens read from cache
|
|
138
|
+
model_name: Primary model used in the session
|
|
139
|
+
cost_usd: Calculated cost in USD
|
|
140
|
+
|
|
141
|
+
# Dashboard fields (for activity visualization)
|
|
142
|
+
started_at: Session start timestamp (UTC ISO format)
|
|
143
|
+
duration_seconds: Total session duration in seconds
|
|
144
|
+
turn_count: Number of user turns (messages)
|
|
145
|
+
tool_usage: Tool usage counts (e.g., {"Read": 5, "Write": 3})
|
|
146
|
+
model_usage: Per-model token usage (e.g., {"claude-opus-4-5": {"input": 5000, "output": 2000}})
|
|
147
|
+
code_metrics: Code change metrics (linesAdded, linesDeleted, filesModified, filesCreated)
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
input_tokens: int = 0
|
|
151
|
+
output_tokens: int = 0
|
|
152
|
+
cache_creation_tokens: int = 0
|
|
153
|
+
cache_read_tokens: int = 0
|
|
154
|
+
model_name: Optional[str] = None
|
|
155
|
+
cost_usd: float = 0.0
|
|
156
|
+
|
|
157
|
+
# Dashboard fields for activity visualization
|
|
158
|
+
started_at: Optional[str] = None
|
|
159
|
+
duration_seconds: int = 0
|
|
160
|
+
turn_count: int = 0
|
|
161
|
+
tool_usage: Optional[dict[str, int]] = None
|
|
162
|
+
model_usage: Optional[dict[str, dict[str, int]]] = None
|
|
163
|
+
code_metrics: Optional[dict[str, int]] = None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def compute_anonymous_project_id(project_path: str) -> str:
|
|
167
|
+
"""Compute an anonymized project identifier.
|
|
168
|
+
|
|
169
|
+
Uses a hash of the project path to track sessions by project
|
|
170
|
+
without revealing the actual project name or path.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
project_path: Full path to the project directory
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
First 16 characters of the SHA-256 hash
|
|
177
|
+
"""
|
|
178
|
+
# Normalize the path
|
|
179
|
+
normalized = os.path.normpath(os.path.expanduser(project_path))
|
|
180
|
+
# Hash it
|
|
181
|
+
hash_value = hashlib.sha256(normalized.encode()).hexdigest()
|
|
182
|
+
return hash_value[:16]
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def get_model_pricing(model_name: Optional[str]) -> dict[str, float]:
|
|
186
|
+
"""Get pricing for a specific model.
|
|
187
|
+
|
|
188
|
+
Uses exact match first, then falls back to pattern matching
|
|
189
|
+
for model families (opus, sonnet, haiku).
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
model_name: Model identifier string (e.g., 'claude-sonnet-4-20250514')
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Dictionary with pricing per million tokens:
|
|
196
|
+
- input: Base input token price
|
|
197
|
+
- output: Output token price
|
|
198
|
+
- cache_creation: 5-minute cache write price
|
|
199
|
+
- cache_read: Cache hit/refresh price
|
|
200
|
+
"""
|
|
201
|
+
if not model_name:
|
|
202
|
+
return MODEL_PRICING["default"]
|
|
203
|
+
|
|
204
|
+
# Try exact match first
|
|
205
|
+
if model_name in MODEL_PRICING:
|
|
206
|
+
return MODEL_PRICING[model_name]
|
|
207
|
+
|
|
208
|
+
# Try pattern matching for model families
|
|
209
|
+
model_lower = model_name.lower()
|
|
210
|
+
|
|
211
|
+
# Opus family - check version specifics
|
|
212
|
+
if "opus" in model_lower:
|
|
213
|
+
if "4-5" in model_lower or "4.5" in model_lower:
|
|
214
|
+
return MODEL_PRICING["claude-opus-4-5-20251101"]
|
|
215
|
+
elif "4-1" in model_lower or "4.1" in model_lower:
|
|
216
|
+
return MODEL_PRICING["claude-opus-4-1-20250414"]
|
|
217
|
+
elif "opus-4" in model_lower or "opus4" in model_lower:
|
|
218
|
+
return MODEL_PRICING["claude-opus-4-20250514"]
|
|
219
|
+
elif "opus-3" in model_lower or "opus3" in model_lower:
|
|
220
|
+
return MODEL_PRICING["claude-3-opus-20240229"]
|
|
221
|
+
# Default to Opus 4 pricing for unknown opus versions
|
|
222
|
+
return MODEL_PRICING["claude-opus-4-20250514"]
|
|
223
|
+
|
|
224
|
+
# Haiku family
|
|
225
|
+
elif "haiku" in model_lower:
|
|
226
|
+
if "4-5" in model_lower or "4.5" in model_lower:
|
|
227
|
+
return MODEL_PRICING["claude-haiku-4-5-20251022"]
|
|
228
|
+
elif "3-5" in model_lower or "3.5" in model_lower:
|
|
229
|
+
return MODEL_PRICING["claude-3-5-haiku-20241022"]
|
|
230
|
+
elif "haiku-3" in model_lower or "haiku3" in model_lower:
|
|
231
|
+
return MODEL_PRICING["claude-3-haiku-20240307"]
|
|
232
|
+
# Default to Haiku 3.5 pricing
|
|
233
|
+
return MODEL_PRICING["claude-3-5-haiku-20241022"]
|
|
234
|
+
|
|
235
|
+
# Sonnet family
|
|
236
|
+
elif "sonnet" in model_lower:
|
|
237
|
+
if "4-5" in model_lower or "4.5" in model_lower:
|
|
238
|
+
return MODEL_PRICING["claude-sonnet-4-5-20251022"]
|
|
239
|
+
elif "sonnet-4" in model_lower or "sonnet4" in model_lower:
|
|
240
|
+
return MODEL_PRICING["claude-sonnet-4-20250514"]
|
|
241
|
+
elif "3-7" in model_lower or "3.7" in model_lower:
|
|
242
|
+
return MODEL_PRICING["claude-3-7-sonnet-20250219"]
|
|
243
|
+
elif "3-5" in model_lower or "3.5" in model_lower:
|
|
244
|
+
return MODEL_PRICING["claude-3-5-sonnet-20241022"]
|
|
245
|
+
# Default to Sonnet 4 pricing
|
|
246
|
+
return MODEL_PRICING["claude-sonnet-4-20250514"]
|
|
247
|
+
|
|
248
|
+
return MODEL_PRICING["default"]
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def calculate_cost(usage: TokenUsage) -> float:
|
|
252
|
+
"""Calculate the cost in USD for token usage.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
usage: TokenUsage dataclass with token counts and model
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Cost in USD
|
|
259
|
+
"""
|
|
260
|
+
pricing = get_model_pricing(usage.model_name)
|
|
261
|
+
|
|
262
|
+
# Cost per token (pricing is per million tokens)
|
|
263
|
+
input_cost = (usage.input_tokens / 1_000_000) * pricing["input"]
|
|
264
|
+
output_cost = (usage.output_tokens / 1_000_000) * pricing["output"]
|
|
265
|
+
cache_creation_cost = (usage.cache_creation_tokens / 1_000_000) * pricing["cache_creation"]
|
|
266
|
+
cache_read_cost = (usage.cache_read_tokens / 1_000_000) * pricing["cache_read"]
|
|
267
|
+
|
|
268
|
+
return input_cost + output_cost + cache_creation_cost + cache_read_cost
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def parse_transcript_for_usage(transcript_path: str) -> Optional[TokenUsage]:
|
|
272
|
+
"""Parse a JSONL transcript file to extract total token usage and session metadata.
|
|
273
|
+
|
|
274
|
+
Claude Code stores conversation transcripts as JSONL files where each line
|
|
275
|
+
contains a message object. Token usage is in the message.usage field.
|
|
276
|
+
|
|
277
|
+
Extracts:
|
|
278
|
+
- Token counts: input, output, cache creation, cache read
|
|
279
|
+
- Model name: Primary model used
|
|
280
|
+
- Session timing: started_at, duration_seconds
|
|
281
|
+
- User engagement: turn_count (number of user messages)
|
|
282
|
+
- Tool usage: Count of each tool used (e.g., {"Read": 5, "Write": 3})
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
transcript_path: Path to the transcript JSONL file
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
TokenUsage dataclass with aggregated data, or None if parsing fails
|
|
289
|
+
"""
|
|
290
|
+
import json
|
|
291
|
+
from datetime import datetime as dt
|
|
292
|
+
|
|
293
|
+
usage = TokenUsage()
|
|
294
|
+
transcript_file = Path(transcript_path)
|
|
295
|
+
|
|
296
|
+
if not transcript_file.exists():
|
|
297
|
+
return None
|
|
298
|
+
|
|
299
|
+
first_timestamp: Optional[str] = None
|
|
300
|
+
last_timestamp: Optional[str] = None
|
|
301
|
+
turn_count = 0
|
|
302
|
+
tool_counts: dict[str, int] = {}
|
|
303
|
+
model_counts: dict[str, dict[str, int]] = {}
|
|
304
|
+
current_model: Optional[str] = None
|
|
305
|
+
|
|
306
|
+
# Code metrics tracking
|
|
307
|
+
lines_added = 0
|
|
308
|
+
lines_deleted = 0
|
|
309
|
+
files_modified: set[str] = set()
|
|
310
|
+
files_created: set[str] = set()
|
|
311
|
+
|
|
312
|
+
def count_lines(text: str) -> int:
|
|
313
|
+
"""Count non-empty lines in text."""
|
|
314
|
+
if not text:
|
|
315
|
+
return 0
|
|
316
|
+
return len([line for line in text.split("\n") if line.strip()])
|
|
317
|
+
|
|
318
|
+
try:
|
|
319
|
+
with open(transcript_file, encoding="utf-8") as f:
|
|
320
|
+
for line in f:
|
|
321
|
+
line = line.strip()
|
|
322
|
+
if not line:
|
|
323
|
+
continue
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
data = json.loads(line)
|
|
327
|
+
except json.JSONDecodeError:
|
|
328
|
+
continue
|
|
329
|
+
|
|
330
|
+
# Extract timestamp for session timing
|
|
331
|
+
timestamp = data.get("timestamp")
|
|
332
|
+
if timestamp:
|
|
333
|
+
if first_timestamp is None:
|
|
334
|
+
first_timestamp = timestamp
|
|
335
|
+
last_timestamp = timestamp
|
|
336
|
+
|
|
337
|
+
# Count user turns
|
|
338
|
+
msg_type = data.get("type")
|
|
339
|
+
if msg_type == "user":
|
|
340
|
+
turn_count += 1
|
|
341
|
+
|
|
342
|
+
# Extract usage from message.usage field
|
|
343
|
+
message = data.get("message", {})
|
|
344
|
+
msg_usage = message.get("usage", {})
|
|
345
|
+
|
|
346
|
+
# Extract model name first (needed for per-model tracking)
|
|
347
|
+
model = message.get("model") or data.get("model")
|
|
348
|
+
if model:
|
|
349
|
+
current_model = model
|
|
350
|
+
usage.model_name = model
|
|
351
|
+
|
|
352
|
+
if msg_usage:
|
|
353
|
+
input_toks = msg_usage.get("input_tokens", 0)
|
|
354
|
+
output_toks = msg_usage.get("output_tokens", 0)
|
|
355
|
+
|
|
356
|
+
# Aggregate total tokens
|
|
357
|
+
usage.input_tokens += input_toks
|
|
358
|
+
usage.output_tokens += output_toks
|
|
359
|
+
usage.cache_creation_tokens += msg_usage.get("cache_creation_input_tokens", 0)
|
|
360
|
+
usage.cache_read_tokens += msg_usage.get("cache_read_input_tokens", 0)
|
|
361
|
+
|
|
362
|
+
# Track per-model token usage
|
|
363
|
+
if current_model:
|
|
364
|
+
if current_model not in model_counts:
|
|
365
|
+
model_counts[current_model] = {"input": 0, "output": 0}
|
|
366
|
+
model_counts[current_model]["input"] += input_toks
|
|
367
|
+
model_counts[current_model]["output"] += output_toks
|
|
368
|
+
|
|
369
|
+
# Count tool usage and extract code metrics from message.content
|
|
370
|
+
content = message.get("content", [])
|
|
371
|
+
if isinstance(content, list):
|
|
372
|
+
for block in content:
|
|
373
|
+
if isinstance(block, dict) and block.get("type") == "tool_use":
|
|
374
|
+
tool_name = block.get("name", "unknown")
|
|
375
|
+
tool_counts[tool_name] = tool_counts.get(tool_name, 0) + 1
|
|
376
|
+
|
|
377
|
+
# Extract code metrics from Edit/Write tools
|
|
378
|
+
tool_input = block.get("input", {})
|
|
379
|
+
if isinstance(tool_input, dict):
|
|
380
|
+
file_path = tool_input.get("file_path", "")
|
|
381
|
+
|
|
382
|
+
if tool_name == "Edit":
|
|
383
|
+
# Edit: count old_string (deleted) and new_string (added)
|
|
384
|
+
old_str = tool_input.get("old_string", "")
|
|
385
|
+
new_str = tool_input.get("new_string", "")
|
|
386
|
+
lines_deleted += count_lines(old_str)
|
|
387
|
+
lines_added += count_lines(new_str)
|
|
388
|
+
if file_path:
|
|
389
|
+
files_modified.add(file_path)
|
|
390
|
+
|
|
391
|
+
elif tool_name == "Write":
|
|
392
|
+
# Write: count content lines as added
|
|
393
|
+
content_str = tool_input.get("content", "")
|
|
394
|
+
lines_added += count_lines(content_str)
|
|
395
|
+
if file_path:
|
|
396
|
+
files_created.add(file_path)
|
|
397
|
+
|
|
398
|
+
elif tool_name == "MultiEdit":
|
|
399
|
+
# MultiEdit: process multiple edits
|
|
400
|
+
edits = tool_input.get("edits", [])
|
|
401
|
+
if isinstance(edits, list):
|
|
402
|
+
for edit in edits:
|
|
403
|
+
if isinstance(edit, dict):
|
|
404
|
+
old_str = edit.get("old_string", "")
|
|
405
|
+
new_str = edit.get("new_string", "")
|
|
406
|
+
lines_deleted += count_lines(old_str)
|
|
407
|
+
lines_added += count_lines(new_str)
|
|
408
|
+
if file_path:
|
|
409
|
+
files_modified.add(file_path)
|
|
410
|
+
|
|
411
|
+
# Set dashboard fields
|
|
412
|
+
usage.started_at = first_timestamp
|
|
413
|
+
usage.turn_count = turn_count
|
|
414
|
+
usage.tool_usage = tool_counts if tool_counts else None
|
|
415
|
+
usage.model_usage = model_counts if model_counts else None
|
|
416
|
+
|
|
417
|
+
# Set code metrics (only if there was any code activity)
|
|
418
|
+
if lines_added > 0 or lines_deleted > 0 or files_modified or files_created:
|
|
419
|
+
usage.code_metrics = {
|
|
420
|
+
"linesAdded": lines_added,
|
|
421
|
+
"linesDeleted": lines_deleted,
|
|
422
|
+
"filesModified": len(files_modified),
|
|
423
|
+
"filesCreated": len(files_created),
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
# Calculate duration in seconds
|
|
427
|
+
if first_timestamp and last_timestamp:
|
|
428
|
+
try:
|
|
429
|
+
# Parse ISO format timestamps
|
|
430
|
+
start_dt = dt.fromisoformat(first_timestamp.replace("Z", "+00:00"))
|
|
431
|
+
end_dt = dt.fromisoformat(last_timestamp.replace("Z", "+00:00"))
|
|
432
|
+
usage.duration_seconds = int((end_dt - start_dt).total_seconds())
|
|
433
|
+
except (ValueError, TypeError):
|
|
434
|
+
usage.duration_seconds = 0
|
|
435
|
+
|
|
436
|
+
# Calculate cost
|
|
437
|
+
if usage.input_tokens > 0 or usage.output_tokens > 0:
|
|
438
|
+
usage.cost_usd = calculate_cost(usage)
|
|
439
|
+
return usage
|
|
440
|
+
|
|
441
|
+
return None
|
|
442
|
+
|
|
443
|
+
except (OSError, IOError):
|
|
444
|
+
return None
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def parse_session_data(session_data: dict[str, Any]) -> Optional[dict[str, Any]]:
|
|
448
|
+
"""Parse and validate session data from Claude Code SessionEnd hook.
|
|
449
|
+
|
|
450
|
+
IMPORTANT: Claude Code's SessionEnd hook does NOT provide token counts directly.
|
|
451
|
+
Token usage must be extracted from the transcript_path JSONL file.
|
|
452
|
+
|
|
453
|
+
Args:
|
|
454
|
+
session_data: Raw session data from Claude Code containing:
|
|
455
|
+
- session_id: Unique session identifier
|
|
456
|
+
- transcript_path: Path to the JSONL transcript file
|
|
457
|
+
- cwd: Current working directory (project path)
|
|
458
|
+
- permission_mode: Permission mode used
|
|
459
|
+
- hook_event_name: Name of the hook event
|
|
460
|
+
- reason: Reason for session end
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
Parsed session data dictionary with token usage, or None if invalid
|
|
464
|
+
"""
|
|
465
|
+
try:
|
|
466
|
+
# Get transcript path - this is where token data lives
|
|
467
|
+
transcript_path = session_data.get("transcript_path")
|
|
468
|
+
if not transcript_path:
|
|
469
|
+
return None
|
|
470
|
+
|
|
471
|
+
# Parse the transcript file for token usage
|
|
472
|
+
usage = parse_transcript_for_usage(transcript_path)
|
|
473
|
+
if not usage:
|
|
474
|
+
return None
|
|
475
|
+
|
|
476
|
+
# Skip sessions with no token usage
|
|
477
|
+
if usage.input_tokens == 0 and usage.output_tokens == 0:
|
|
478
|
+
return None
|
|
479
|
+
|
|
480
|
+
# Get project path from cwd
|
|
481
|
+
project_path = session_data.get("cwd")
|
|
482
|
+
|
|
483
|
+
# Generate ended_at timestamp
|
|
484
|
+
from datetime import datetime as dt
|
|
485
|
+
from datetime import timezone
|
|
486
|
+
|
|
487
|
+
ended_at = dt.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
488
|
+
|
|
489
|
+
return {
|
|
490
|
+
"input_tokens": usage.input_tokens,
|
|
491
|
+
"output_tokens": usage.output_tokens,
|
|
492
|
+
"cache_creation_tokens": usage.cache_creation_tokens,
|
|
493
|
+
"cache_read_tokens": usage.cache_read_tokens,
|
|
494
|
+
"model_name": usage.model_name,
|
|
495
|
+
"project_path": project_path,
|
|
496
|
+
"ended_at": ended_at,
|
|
497
|
+
"cost_usd": usage.cost_usd,
|
|
498
|
+
"session_id": session_data.get("session_id"),
|
|
499
|
+
# Dashboard fields
|
|
500
|
+
"started_at": usage.started_at,
|
|
501
|
+
"duration_seconds": usage.duration_seconds,
|
|
502
|
+
"turn_count": usage.turn_count,
|
|
503
|
+
"tool_usage": usage.tool_usage,
|
|
504
|
+
"model_usage": usage.model_usage,
|
|
505
|
+
"code_metrics": usage.code_metrics,
|
|
506
|
+
}
|
|
507
|
+
except (KeyError, ValueError, TypeError):
|
|
508
|
+
return None
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def parse_transcript_to_submission(
|
|
512
|
+
transcript_path: Path,
|
|
513
|
+
) -> Optional[SessionSubmission]:
|
|
514
|
+
"""Parse a transcript file and return a SessionSubmission object.
|
|
515
|
+
|
|
516
|
+
This function is used for batch processing - it parses the transcript
|
|
517
|
+
without submitting, allowing multiple sessions to be batched together.
|
|
518
|
+
|
|
519
|
+
Args:
|
|
520
|
+
transcript_path: Path to the JSONL transcript file
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
SessionSubmission object if valid, None if no token usage or error
|
|
524
|
+
"""
|
|
525
|
+
try:
|
|
526
|
+
session_data = {
|
|
527
|
+
"session_id": transcript_path.stem,
|
|
528
|
+
"transcript_path": str(transcript_path),
|
|
529
|
+
"cwd": str(transcript_path.parent.parent),
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
parsed = parse_session_data(session_data)
|
|
533
|
+
if not parsed:
|
|
534
|
+
return None
|
|
535
|
+
|
|
536
|
+
client = RankClient()
|
|
537
|
+
|
|
538
|
+
# Compute session hash
|
|
539
|
+
session_hash = client.compute_session_hash(
|
|
540
|
+
input_tokens=parsed["input_tokens"],
|
|
541
|
+
output_tokens=parsed["output_tokens"],
|
|
542
|
+
cache_creation_tokens=parsed["cache_creation_tokens"],
|
|
543
|
+
cache_read_tokens=parsed["cache_read_tokens"],
|
|
544
|
+
model_name=parsed["model_name"],
|
|
545
|
+
ended_at=parsed["ended_at"],
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
return SessionSubmission(
|
|
549
|
+
session_hash=session_hash,
|
|
550
|
+
ended_at=parsed["ended_at"],
|
|
551
|
+
input_tokens=parsed["input_tokens"],
|
|
552
|
+
output_tokens=parsed["output_tokens"],
|
|
553
|
+
cache_creation_tokens=parsed["cache_creation_tokens"],
|
|
554
|
+
cache_read_tokens=parsed["cache_read_tokens"],
|
|
555
|
+
model_name=parsed["model_name"],
|
|
556
|
+
started_at=parsed.get("started_at"),
|
|
557
|
+
duration_seconds=parsed.get("duration_seconds", 0),
|
|
558
|
+
turn_count=parsed.get("turn_count", 0),
|
|
559
|
+
tool_usage=parsed.get("tool_usage"),
|
|
560
|
+
model_usage=parsed.get("model_usage"),
|
|
561
|
+
code_metrics=parsed.get("code_metrics"),
|
|
562
|
+
)
|
|
563
|
+
except Exception:
|
|
564
|
+
return None
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def submit_session_hook(session_data: dict[str, Any]) -> dict[str, Any]:
|
|
568
|
+
"""Hook function to submit session data to MoAI Rank.
|
|
569
|
+
|
|
570
|
+
This function is called by the Claude Code hook system when a session ends.
|
|
571
|
+
It parses the transcript JSONL file to extract token usage and submits it
|
|
572
|
+
to the rank service.
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
session_data: Session data dictionary from Claude Code SessionEnd hook:
|
|
576
|
+
- session_id: Unique session identifier
|
|
577
|
+
- transcript_path: Path to the JSONL transcript file (contains token data)
|
|
578
|
+
- cwd: Current working directory (project path)
|
|
579
|
+
- permission_mode: Permission mode used
|
|
580
|
+
- hook_event_name: Name of the hook event
|
|
581
|
+
- reason: Reason for session end
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
Result dictionary with:
|
|
585
|
+
- success: Whether submission succeeded
|
|
586
|
+
- message: Status message
|
|
587
|
+
- session_id: Session ID from server (if successful)
|
|
588
|
+
- cost_usd: Calculated cost in USD (if successful)
|
|
589
|
+
"""
|
|
590
|
+
result = {
|
|
591
|
+
"success": False,
|
|
592
|
+
"message": "",
|
|
593
|
+
"session_id": None,
|
|
594
|
+
"cost_usd": 0.0,
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
# Check if user has registered
|
|
598
|
+
if not RankConfig.has_credentials():
|
|
599
|
+
result["message"] = "Not registered with MoAI Rank"
|
|
600
|
+
return result
|
|
601
|
+
|
|
602
|
+
# Parse session data
|
|
603
|
+
parsed = parse_session_data(session_data)
|
|
604
|
+
if not parsed:
|
|
605
|
+
result["message"] = "No token usage to submit"
|
|
606
|
+
return result
|
|
607
|
+
|
|
608
|
+
try:
|
|
609
|
+
client = RankClient()
|
|
610
|
+
|
|
611
|
+
# Compute session hash
|
|
612
|
+
session_hash = client.compute_session_hash(
|
|
613
|
+
input_tokens=parsed["input_tokens"],
|
|
614
|
+
output_tokens=parsed["output_tokens"],
|
|
615
|
+
cache_creation_tokens=parsed["cache_creation_tokens"],
|
|
616
|
+
cache_read_tokens=parsed["cache_read_tokens"],
|
|
617
|
+
model_name=parsed["model_name"],
|
|
618
|
+
ended_at=parsed["ended_at"],
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
# Create submission
|
|
622
|
+
submission = SessionSubmission(
|
|
623
|
+
session_hash=session_hash,
|
|
624
|
+
ended_at=parsed["ended_at"],
|
|
625
|
+
input_tokens=parsed["input_tokens"],
|
|
626
|
+
output_tokens=parsed["output_tokens"],
|
|
627
|
+
cache_creation_tokens=parsed["cache_creation_tokens"],
|
|
628
|
+
cache_read_tokens=parsed["cache_read_tokens"],
|
|
629
|
+
model_name=parsed["model_name"],
|
|
630
|
+
# Dashboard fields
|
|
631
|
+
started_at=parsed.get("started_at"),
|
|
632
|
+
duration_seconds=parsed.get("duration_seconds", 0),
|
|
633
|
+
turn_count=parsed.get("turn_count", 0),
|
|
634
|
+
tool_usage=parsed.get("tool_usage"),
|
|
635
|
+
model_usage=parsed.get("model_usage"),
|
|
636
|
+
code_metrics=parsed.get("code_metrics"),
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
# Submit to API
|
|
640
|
+
response = client.submit_session(submission)
|
|
641
|
+
|
|
642
|
+
result["success"] = True
|
|
643
|
+
result["message"] = response.get("message", "Session submitted")
|
|
644
|
+
result["session_id"] = response.get("sessionId")
|
|
645
|
+
result["cost_usd"] = parsed.get("cost_usd", 0.0)
|
|
646
|
+
|
|
647
|
+
except RankClientError as e:
|
|
648
|
+
result["message"] = f"Submission failed: {e}"
|
|
649
|
+
|
|
650
|
+
return result
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def create_hook_script() -> str:
|
|
654
|
+
"""Generate a hook script for Claude Code session end.
|
|
655
|
+
|
|
656
|
+
Returns:
|
|
657
|
+
Python script content for the hook
|
|
658
|
+
"""
|
|
659
|
+
return '''#!/usr/bin/env python3
|
|
660
|
+
"""MoAI Rank Session Hook
|
|
661
|
+
|
|
662
|
+
This hook submits Claude Code session token usage to the MoAI Rank service.
|
|
663
|
+
It is triggered automatically when a session ends.
|
|
664
|
+
"""
|
|
665
|
+
|
|
666
|
+
import json
|
|
667
|
+
import sys
|
|
668
|
+
|
|
669
|
+
def main():
|
|
670
|
+
# Read session data from stdin
|
|
671
|
+
try:
|
|
672
|
+
input_data = sys.stdin.read()
|
|
673
|
+
if not input_data:
|
|
674
|
+
return
|
|
675
|
+
|
|
676
|
+
session_data = json.loads(input_data)
|
|
677
|
+
|
|
678
|
+
# Lazy import to avoid startup delay
|
|
679
|
+
from moai_adk.rank.hook import submit_session_hook
|
|
680
|
+
|
|
681
|
+
result = submit_session_hook(session_data)
|
|
682
|
+
|
|
683
|
+
if result["success"]:
|
|
684
|
+
print(f"Session submitted to MoAI Rank", file=sys.stderr)
|
|
685
|
+
elif result["message"] != "Not registered with MoAI Rank":
|
|
686
|
+
print(f"MoAI Rank: {result['message']}", file=sys.stderr)
|
|
687
|
+
|
|
688
|
+
except json.JSONDecodeError:
|
|
689
|
+
pass
|
|
690
|
+
except ImportError:
|
|
691
|
+
# moai-adk not installed, silently skip
|
|
692
|
+
pass
|
|
693
|
+
except Exception as e:
|
|
694
|
+
print(f"MoAI Rank hook error: {e}", file=sys.stderr)
|
|
695
|
+
|
|
696
|
+
if __name__ == "__main__":
|
|
697
|
+
main()
|
|
698
|
+
'''
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def load_rank_config() -> dict[str, Any]:
|
|
702
|
+
"""Load rank configuration from ~/.moai/rank/config.yaml.
|
|
703
|
+
|
|
704
|
+
Returns:
|
|
705
|
+
Configuration dictionary with rank settings
|
|
706
|
+
"""
|
|
707
|
+
import yaml
|
|
708
|
+
|
|
709
|
+
config_file = Path.home() / ".moai" / "rank" / "config.yaml"
|
|
710
|
+
if not config_file.exists():
|
|
711
|
+
return {"enabled": True, "exclude_projects": []}
|
|
712
|
+
|
|
713
|
+
try:
|
|
714
|
+
with open(config_file) as f:
|
|
715
|
+
data = yaml.safe_load(f) or {}
|
|
716
|
+
rank_config = data.get("rank", {})
|
|
717
|
+
return {
|
|
718
|
+
"enabled": rank_config.get("enabled", True),
|
|
719
|
+
"exclude_projects": rank_config.get("exclude_projects", []),
|
|
720
|
+
}
|
|
721
|
+
except (OSError, yaml.YAMLError):
|
|
722
|
+
return {"enabled": True, "exclude_projects": []}
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
def is_project_excluded(project_path: Optional[str]) -> bool:
|
|
726
|
+
"""Check if a project is excluded from rank submission.
|
|
727
|
+
|
|
728
|
+
Args:
|
|
729
|
+
project_path: Path to the project directory
|
|
730
|
+
|
|
731
|
+
Returns:
|
|
732
|
+
True if project should be excluded from submission
|
|
733
|
+
"""
|
|
734
|
+
import fnmatch
|
|
735
|
+
|
|
736
|
+
if not project_path:
|
|
737
|
+
return False
|
|
738
|
+
|
|
739
|
+
config = load_rank_config()
|
|
740
|
+
|
|
741
|
+
# Check if rank is globally disabled
|
|
742
|
+
if not config.get("enabled", True):
|
|
743
|
+
return True
|
|
744
|
+
|
|
745
|
+
# Normalize the project path
|
|
746
|
+
normalized_path = os.path.normpath(os.path.expanduser(project_path))
|
|
747
|
+
|
|
748
|
+
# Check against exclusion patterns
|
|
749
|
+
for pattern in config.get("exclude_projects", []):
|
|
750
|
+
pattern = os.path.expanduser(pattern)
|
|
751
|
+
# Support both exact paths and wildcard patterns
|
|
752
|
+
if fnmatch.fnmatch(normalized_path, pattern):
|
|
753
|
+
return True
|
|
754
|
+
# Also check if pattern is a prefix (directory match)
|
|
755
|
+
if normalized_path.startswith(os.path.normpath(pattern)):
|
|
756
|
+
return True
|
|
757
|
+
|
|
758
|
+
return False
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def save_rank_config(config: dict[str, Any]) -> bool:
|
|
762
|
+
"""Save rank configuration to ~/.moai/rank/config.yaml.
|
|
763
|
+
|
|
764
|
+
Args:
|
|
765
|
+
config: Configuration dictionary with rank settings
|
|
766
|
+
|
|
767
|
+
Returns:
|
|
768
|
+
True if saved successfully
|
|
769
|
+
"""
|
|
770
|
+
import yaml
|
|
771
|
+
|
|
772
|
+
config_dir = Path.home() / ".moai" / "rank"
|
|
773
|
+
config_file = config_dir / "config.yaml"
|
|
774
|
+
|
|
775
|
+
try:
|
|
776
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
777
|
+
|
|
778
|
+
# Load existing config to preserve other settings
|
|
779
|
+
existing: dict[str, Any] = {}
|
|
780
|
+
if config_file.exists():
|
|
781
|
+
with open(config_file) as f:
|
|
782
|
+
existing = yaml.safe_load(f) or {}
|
|
783
|
+
|
|
784
|
+
existing["rank"] = config
|
|
785
|
+
with open(config_file, "w") as f:
|
|
786
|
+
yaml.safe_dump(existing, f, default_flow_style=False)
|
|
787
|
+
return True
|
|
788
|
+
except (OSError, yaml.YAMLError):
|
|
789
|
+
return False
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
def add_project_exclusion(project_path: str) -> bool:
|
|
793
|
+
"""Add a project to the exclusion list.
|
|
794
|
+
|
|
795
|
+
Args:
|
|
796
|
+
project_path: Path or pattern to exclude
|
|
797
|
+
|
|
798
|
+
Returns:
|
|
799
|
+
True if added successfully
|
|
800
|
+
"""
|
|
801
|
+
config = load_rank_config()
|
|
802
|
+
exclude_list = config.get("exclude_projects", [])
|
|
803
|
+
|
|
804
|
+
# Normalize the path
|
|
805
|
+
normalized = os.path.normpath(os.path.expanduser(project_path))
|
|
806
|
+
|
|
807
|
+
if normalized not in exclude_list:
|
|
808
|
+
exclude_list.append(normalized)
|
|
809
|
+
config["exclude_projects"] = exclude_list
|
|
810
|
+
return save_rank_config(config)
|
|
811
|
+
|
|
812
|
+
return True # Already excluded
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
def remove_project_exclusion(project_path: str) -> bool:
|
|
816
|
+
"""Remove a project from the exclusion list.
|
|
817
|
+
|
|
818
|
+
Args:
|
|
819
|
+
project_path: Path or pattern to remove from exclusion
|
|
820
|
+
|
|
821
|
+
Returns:
|
|
822
|
+
True if removed successfully
|
|
823
|
+
"""
|
|
824
|
+
config = load_rank_config()
|
|
825
|
+
exclude_list = config.get("exclude_projects", [])
|
|
826
|
+
|
|
827
|
+
normalized = os.path.normpath(os.path.expanduser(project_path))
|
|
828
|
+
|
|
829
|
+
if normalized in exclude_list:
|
|
830
|
+
exclude_list.remove(normalized)
|
|
831
|
+
config["exclude_projects"] = exclude_list
|
|
832
|
+
return save_rank_config(config)
|
|
833
|
+
|
|
834
|
+
return True # Not in list anyway
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def create_global_hook_script() -> str:
|
|
838
|
+
"""Generate a global hook script with opt-out support.
|
|
839
|
+
|
|
840
|
+
Returns:
|
|
841
|
+
Python script content for the global hook
|
|
842
|
+
"""
|
|
843
|
+
return '''#!/usr/bin/env python3
|
|
844
|
+
"""MoAI Rank Session Hook (Global)
|
|
845
|
+
|
|
846
|
+
This hook submits Claude Code session token usage to the MoAI Rank service.
|
|
847
|
+
It is installed globally at ~/.claude/hooks/moai/ and runs for all projects.
|
|
848
|
+
|
|
849
|
+
Opt-out: Configure ~/.moai/rank/config.yaml to exclude specific projects:
|
|
850
|
+
rank:
|
|
851
|
+
enabled: true
|
|
852
|
+
exclude_projects:
|
|
853
|
+
- "/path/to/private-project"
|
|
854
|
+
- "*/confidential/*"
|
|
855
|
+
"""
|
|
856
|
+
|
|
857
|
+
import json
|
|
858
|
+
import sys
|
|
859
|
+
|
|
860
|
+
def main():
|
|
861
|
+
try:
|
|
862
|
+
input_data = sys.stdin.read()
|
|
863
|
+
if not input_data:
|
|
864
|
+
return
|
|
865
|
+
|
|
866
|
+
session_data = json.loads(input_data)
|
|
867
|
+
|
|
868
|
+
# Lazy import to avoid startup delay
|
|
869
|
+
from moai_adk.rank.hook import is_project_excluded, submit_session_hook
|
|
870
|
+
|
|
871
|
+
# Check if this project is excluded
|
|
872
|
+
project_path = session_data.get("projectPath") or session_data.get("cwd")
|
|
873
|
+
if is_project_excluded(project_path):
|
|
874
|
+
return # Silently skip excluded projects
|
|
875
|
+
|
|
876
|
+
result = submit_session_hook(session_data)
|
|
877
|
+
|
|
878
|
+
if result["success"]:
|
|
879
|
+
print("Session submitted to MoAI Rank", file=sys.stderr)
|
|
880
|
+
elif result["message"] != "Not registered with MoAI Rank":
|
|
881
|
+
print(f"MoAI Rank: {result['message']}", file=sys.stderr)
|
|
882
|
+
|
|
883
|
+
except json.JSONDecodeError:
|
|
884
|
+
pass
|
|
885
|
+
except ImportError:
|
|
886
|
+
# moai-adk not installed, silently skip
|
|
887
|
+
pass
|
|
888
|
+
except Exception as e:
|
|
889
|
+
print(f"MoAI Rank hook error: {e}", file=sys.stderr)
|
|
890
|
+
|
|
891
|
+
if __name__ == "__main__":
|
|
892
|
+
main()
|
|
893
|
+
'''
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
def _register_hook_in_settings() -> bool:
|
|
897
|
+
"""Register SessionEnd hook in ~/.claude/settings.json.
|
|
898
|
+
|
|
899
|
+
Claude Code requires hooks to be explicitly registered in settings.json
|
|
900
|
+
to be executed. This function adds the SessionEnd hook configuration.
|
|
901
|
+
|
|
902
|
+
Returns:
|
|
903
|
+
True if successfully registered, False otherwise
|
|
904
|
+
"""
|
|
905
|
+
import json
|
|
906
|
+
|
|
907
|
+
settings_file = Path.home() / ".claude" / "settings.json"
|
|
908
|
+
hook_command = f"python3 {Path.home()}/.claude/hooks/moai/session_end__rank_submit.py"
|
|
909
|
+
|
|
910
|
+
try:
|
|
911
|
+
# Load existing settings or create new
|
|
912
|
+
if settings_file.exists():
|
|
913
|
+
with open(settings_file, encoding="utf-8") as f:
|
|
914
|
+
settings = json.load(f)
|
|
915
|
+
else:
|
|
916
|
+
settings = {}
|
|
917
|
+
|
|
918
|
+
# Ensure hooks section exists
|
|
919
|
+
if "hooks" not in settings:
|
|
920
|
+
settings["hooks"] = {}
|
|
921
|
+
|
|
922
|
+
# Check if SessionEnd hook already exists
|
|
923
|
+
if "SessionEnd" in settings["hooks"]:
|
|
924
|
+
# Check if our hook is already registered
|
|
925
|
+
for hook_config in settings["hooks"]["SessionEnd"]:
|
|
926
|
+
for hook in hook_config.get("hooks", []):
|
|
927
|
+
if "session_end__rank_submit.py" in hook.get("command", ""):
|
|
928
|
+
return True # Already registered
|
|
929
|
+
|
|
930
|
+
# Add SessionEnd hook
|
|
931
|
+
session_end_config = {
|
|
932
|
+
"matcher": ".*",
|
|
933
|
+
"hooks": [{"type": "command", "command": hook_command}],
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
if "SessionEnd" not in settings["hooks"]:
|
|
937
|
+
settings["hooks"]["SessionEnd"] = []
|
|
938
|
+
|
|
939
|
+
settings["hooks"]["SessionEnd"].append(session_end_config)
|
|
940
|
+
|
|
941
|
+
# Write back
|
|
942
|
+
with open(settings_file, "w", encoding="utf-8") as f:
|
|
943
|
+
json.dump(settings, f, indent=2)
|
|
944
|
+
|
|
945
|
+
return True
|
|
946
|
+
|
|
947
|
+
except (OSError, json.JSONDecodeError):
|
|
948
|
+
return False
|
|
949
|
+
|
|
950
|
+
|
|
951
|
+
def install_hook(project_path: Optional[Path] = None) -> bool:
|
|
952
|
+
"""Install the session end hook globally to ~/.claude/hooks/moai/.
|
|
953
|
+
|
|
954
|
+
This installs the hook at the user level so it runs for all projects.
|
|
955
|
+
Users can opt-out specific projects via ~/.moai/rank/config.yaml.
|
|
956
|
+
|
|
957
|
+
Also registers the hook in ~/.claude/settings.json so Claude Code
|
|
958
|
+
knows to execute it on session end.
|
|
959
|
+
|
|
960
|
+
Args:
|
|
961
|
+
project_path: Deprecated, ignored. Hook is always installed globally.
|
|
962
|
+
|
|
963
|
+
Returns:
|
|
964
|
+
True if hook was installed successfully
|
|
965
|
+
"""
|
|
966
|
+
_ = project_path # Deprecated parameter, kept for backwards compatibility
|
|
967
|
+
|
|
968
|
+
# Install to global user hooks directory
|
|
969
|
+
hooks_dir = Path.home() / ".claude" / "hooks" / "moai"
|
|
970
|
+
hooks_dir.mkdir(parents=True, exist_ok=True)
|
|
971
|
+
|
|
972
|
+
hook_file = hooks_dir / "session_end__rank_submit.py"
|
|
973
|
+
|
|
974
|
+
try:
|
|
975
|
+
hook_file.write_text(create_global_hook_script())
|
|
976
|
+
hook_file.chmod(0o755) # Make executable
|
|
977
|
+
|
|
978
|
+
# Register hook in settings.json
|
|
979
|
+
if not _register_hook_in_settings():
|
|
980
|
+
return False
|
|
981
|
+
|
|
982
|
+
return True
|
|
983
|
+
except OSError:
|
|
984
|
+
return False
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
def _unregister_hook_from_settings() -> bool:
|
|
988
|
+
"""Remove SessionEnd hook from ~/.claude/settings.json.
|
|
989
|
+
|
|
990
|
+
Returns:
|
|
991
|
+
True if successfully unregistered, False otherwise
|
|
992
|
+
"""
|
|
993
|
+
import json
|
|
994
|
+
|
|
995
|
+
settings_file = Path.home() / ".claude" / "settings.json"
|
|
996
|
+
|
|
997
|
+
try:
|
|
998
|
+
if not settings_file.exists():
|
|
999
|
+
return True # Nothing to remove
|
|
1000
|
+
|
|
1001
|
+
with open(settings_file, encoding="utf-8") as f:
|
|
1002
|
+
settings = json.load(f)
|
|
1003
|
+
|
|
1004
|
+
if "hooks" not in settings or "SessionEnd" not in settings["hooks"]:
|
|
1005
|
+
return True # Nothing to remove
|
|
1006
|
+
|
|
1007
|
+
# Filter out our hook
|
|
1008
|
+
settings["hooks"]["SessionEnd"] = [
|
|
1009
|
+
hook_config
|
|
1010
|
+
for hook_config in settings["hooks"]["SessionEnd"]
|
|
1011
|
+
if not any(
|
|
1012
|
+
"session_end__rank_submit.py" in hook.get("command", "") for hook in hook_config.get("hooks", [])
|
|
1013
|
+
)
|
|
1014
|
+
]
|
|
1015
|
+
|
|
1016
|
+
# Remove empty SessionEnd array
|
|
1017
|
+
if not settings["hooks"]["SessionEnd"]:
|
|
1018
|
+
del settings["hooks"]["SessionEnd"]
|
|
1019
|
+
|
|
1020
|
+
# Write back
|
|
1021
|
+
with open(settings_file, "w", encoding="utf-8") as f:
|
|
1022
|
+
json.dump(settings, f, indent=2)
|
|
1023
|
+
|
|
1024
|
+
return True
|
|
1025
|
+
|
|
1026
|
+
except (OSError, json.JSONDecodeError):
|
|
1027
|
+
return False
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
def uninstall_hook() -> bool:
|
|
1031
|
+
"""Uninstall the global session end hook.
|
|
1032
|
+
|
|
1033
|
+
Removes both the hook file and the registration in settings.json.
|
|
1034
|
+
|
|
1035
|
+
Returns:
|
|
1036
|
+
True if hook was uninstalled successfully
|
|
1037
|
+
"""
|
|
1038
|
+
hook_file = Path.home() / ".claude" / "hooks" / "moai" / "session_end__rank_submit.py"
|
|
1039
|
+
|
|
1040
|
+
try:
|
|
1041
|
+
# Remove hook file
|
|
1042
|
+
if hook_file.exists():
|
|
1043
|
+
hook_file.unlink()
|
|
1044
|
+
|
|
1045
|
+
# Unregister from settings.json
|
|
1046
|
+
_unregister_hook_from_settings()
|
|
1047
|
+
|
|
1048
|
+
return True
|
|
1049
|
+
except OSError:
|
|
1050
|
+
return False
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
def is_hook_installed() -> bool:
|
|
1054
|
+
"""Check if the global hook is installed.
|
|
1055
|
+
|
|
1056
|
+
Checks both the hook file existence and registration in settings.json.
|
|
1057
|
+
|
|
1058
|
+
Returns:
|
|
1059
|
+
True if the hook file exists and is registered
|
|
1060
|
+
"""
|
|
1061
|
+
import json
|
|
1062
|
+
|
|
1063
|
+
hook_file = Path.home() / ".claude" / "hooks" / "moai" / "session_end__rank_submit.py"
|
|
1064
|
+
if not hook_file.exists():
|
|
1065
|
+
return False
|
|
1066
|
+
|
|
1067
|
+
# Also check if registered in settings.json
|
|
1068
|
+
settings_file = Path.home() / ".claude" / "settings.json"
|
|
1069
|
+
try:
|
|
1070
|
+
if not settings_file.exists():
|
|
1071
|
+
return False
|
|
1072
|
+
|
|
1073
|
+
with open(settings_file, encoding="utf-8") as f:
|
|
1074
|
+
settings = json.load(f)
|
|
1075
|
+
|
|
1076
|
+
if "hooks" not in settings or "SessionEnd" not in settings["hooks"]:
|
|
1077
|
+
return False
|
|
1078
|
+
|
|
1079
|
+
# Check if our hook is registered
|
|
1080
|
+
for hook_config in settings["hooks"]["SessionEnd"]:
|
|
1081
|
+
for hook in hook_config.get("hooks", []):
|
|
1082
|
+
if "session_end__rank_submit.py" in hook.get("command", ""):
|
|
1083
|
+
return True
|
|
1084
|
+
|
|
1085
|
+
return False
|
|
1086
|
+
|
|
1087
|
+
except (OSError, json.JSONDecodeError):
|
|
1088
|
+
return False
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
def prompt_hook_installation(console: Any = None, confirm_func: Any = None) -> bool:
|
|
1092
|
+
"""Prompt user to install MoAI Rank hook if eligible.
|
|
1093
|
+
|
|
1094
|
+
This function checks if the user is registered with MoAI Rank
|
|
1095
|
+
and if the global hook is not yet installed. If both conditions
|
|
1096
|
+
are met, it prompts the user to install the hook.
|
|
1097
|
+
|
|
1098
|
+
Args:
|
|
1099
|
+
console: Rich console instance for output (optional)
|
|
1100
|
+
confirm_func: Confirmation function like click.confirm (optional)
|
|
1101
|
+
|
|
1102
|
+
Returns:
|
|
1103
|
+
True if hook was installed, False otherwise
|
|
1104
|
+
"""
|
|
1105
|
+
# Check if user is registered with MoAI Rank
|
|
1106
|
+
if not RankConfig.has_credentials():
|
|
1107
|
+
return False
|
|
1108
|
+
|
|
1109
|
+
# Check if hook is already installed
|
|
1110
|
+
if is_hook_installed():
|
|
1111
|
+
return False
|
|
1112
|
+
|
|
1113
|
+
# Lazy import to avoid circular dependencies
|
|
1114
|
+
if console is None:
|
|
1115
|
+
from rich.console import Console
|
|
1116
|
+
|
|
1117
|
+
console = Console()
|
|
1118
|
+
|
|
1119
|
+
if confirm_func is None:
|
|
1120
|
+
import click
|
|
1121
|
+
|
|
1122
|
+
confirm_func = click.confirm
|
|
1123
|
+
|
|
1124
|
+
# Prompt user for hook installation
|
|
1125
|
+
console.print()
|
|
1126
|
+
console.print("[cyan]🏆 MoAI Rank Hook Installation[/cyan]")
|
|
1127
|
+
console.print("[dim]You are registered with MoAI Rank but the session tracking hook is not installed.[/dim]")
|
|
1128
|
+
console.print("[dim]The hook automatically submits your Claude Code session stats to the leaderboard.[/dim]")
|
|
1129
|
+
console.print()
|
|
1130
|
+
|
|
1131
|
+
if confirm_func("Would you like to install the MoAI Rank session hook?", default=True):
|
|
1132
|
+
if install_hook():
|
|
1133
|
+
console.print()
|
|
1134
|
+
console.print("[green]✅ MoAI Rank hook installed successfully![/green]")
|
|
1135
|
+
console.print("[dim]Location: ~/.claude/hooks/moai/session_end__rank_submit.py[/dim]")
|
|
1136
|
+
console.print("[dim]To exclude specific projects: moai rank exclude /path/to/project[/dim]")
|
|
1137
|
+
console.print()
|
|
1138
|
+
return True
|
|
1139
|
+
else:
|
|
1140
|
+
console.print("[yellow]⚠ Failed to install hook. You can try later with: moai rank register[/yellow]")
|
|
1141
|
+
return False
|
|
1142
|
+
else:
|
|
1143
|
+
console.print("[dim]Skipped. You can install later with: moai rank register[/dim]")
|
|
1144
|
+
return False
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
def find_all_transcripts() -> list[Path]:
|
|
1148
|
+
"""Find all Claude Code transcript files.
|
|
1149
|
+
|
|
1150
|
+
Scans ~/.claude/projects/ for all session transcript JSONL files.
|
|
1151
|
+
Excludes subagent transcripts.
|
|
1152
|
+
|
|
1153
|
+
Returns:
|
|
1154
|
+
List of paths to transcript files
|
|
1155
|
+
"""
|
|
1156
|
+
claude_dir = Path.home() / ".claude" / "projects"
|
|
1157
|
+
if not claude_dir.exists():
|
|
1158
|
+
return []
|
|
1159
|
+
|
|
1160
|
+
transcripts = []
|
|
1161
|
+
for jsonl_file in claude_dir.rglob("*.jsonl"):
|
|
1162
|
+
# Skip subagent transcripts
|
|
1163
|
+
if "subagents" in str(jsonl_file):
|
|
1164
|
+
continue
|
|
1165
|
+
transcripts.append(jsonl_file)
|
|
1166
|
+
|
|
1167
|
+
return transcripts
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
def sync_all_sessions(
|
|
1171
|
+
console: Any = None,
|
|
1172
|
+
max_workers: int = 20,
|
|
1173
|
+
batch_size: int = 100,
|
|
1174
|
+
) -> dict[str, int]:
|
|
1175
|
+
"""Sync all existing Claude Code sessions to MoAI Rank.
|
|
1176
|
+
|
|
1177
|
+
Uses a 2-phase approach:
|
|
1178
|
+
1. Parse all transcripts in parallel (local I/O)
|
|
1179
|
+
2. Submit in batches to minimize network requests (falls back to individual if batch not available)
|
|
1180
|
+
|
|
1181
|
+
Args:
|
|
1182
|
+
console: Rich console instance for output (optional)
|
|
1183
|
+
max_workers: Maximum number of parallel workers for parsing (default: 20)
|
|
1184
|
+
batch_size: Number of sessions per batch request (default: 100, max: 100)
|
|
1185
|
+
|
|
1186
|
+
Returns:
|
|
1187
|
+
Dictionary with sync statistics:
|
|
1188
|
+
- total: Total transcripts found
|
|
1189
|
+
- submitted: Successfully submitted
|
|
1190
|
+
- skipped: Already submitted or no token usage
|
|
1191
|
+
- failed: Failed to submit
|
|
1192
|
+
"""
|
|
1193
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
1194
|
+
from threading import Lock
|
|
1195
|
+
|
|
1196
|
+
from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
|
|
1197
|
+
|
|
1198
|
+
if console is None:
|
|
1199
|
+
from rich.console import Console
|
|
1200
|
+
|
|
1201
|
+
console = Console()
|
|
1202
|
+
|
|
1203
|
+
# Check credentials
|
|
1204
|
+
if not RankConfig.has_credentials():
|
|
1205
|
+
console.print("[yellow]Not registered with MoAI Rank.[/yellow]")
|
|
1206
|
+
return {"total": 0, "submitted": 0, "skipped": 0, "failed": 0}
|
|
1207
|
+
|
|
1208
|
+
# Find all transcripts
|
|
1209
|
+
transcripts = find_all_transcripts()
|
|
1210
|
+
if not transcripts:
|
|
1211
|
+
console.print("[dim]No session transcripts found.[/dim]")
|
|
1212
|
+
return {"total": 0, "submitted": 0, "skipped": 0, "failed": 0}
|
|
1213
|
+
|
|
1214
|
+
stats = {"total": len(transcripts), "submitted": 0, "skipped": 0, "failed": 0}
|
|
1215
|
+
stats_lock = Lock()
|
|
1216
|
+
|
|
1217
|
+
console.print()
|
|
1218
|
+
console.print(f"[cyan]Syncing {len(transcripts)} session(s) to MoAI Rank[/cyan]")
|
|
1219
|
+
console.print(f"[dim]Phase 1: Parsing transcripts (parallel: {max_workers} workers)[/dim]")
|
|
1220
|
+
console.print()
|
|
1221
|
+
|
|
1222
|
+
# Phase 1: Parse all transcripts in parallel
|
|
1223
|
+
submissions: list[SessionSubmission] = []
|
|
1224
|
+
skipped_count = 0
|
|
1225
|
+
|
|
1226
|
+
with Progress(
|
|
1227
|
+
SpinnerColumn(),
|
|
1228
|
+
TextColumn("[progress.description]{task.description}"),
|
|
1229
|
+
BarColumn(),
|
|
1230
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
1231
|
+
TextColumn("[dim]({task.completed}/{task.total})[/dim]"),
|
|
1232
|
+
console=console,
|
|
1233
|
+
) as progress:
|
|
1234
|
+
task = progress.add_task("Parsing transcripts", total=len(transcripts))
|
|
1235
|
+
|
|
1236
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
1237
|
+
futures = {executor.submit(parse_transcript_to_submission, path): path for path in transcripts}
|
|
1238
|
+
|
|
1239
|
+
for future in as_completed(futures):
|
|
1240
|
+
submission = future.result()
|
|
1241
|
+
if submission is not None:
|
|
1242
|
+
submissions.append(submission)
|
|
1243
|
+
with stats_lock:
|
|
1244
|
+
stats["submitted"] += 1
|
|
1245
|
+
else:
|
|
1246
|
+
with stats_lock:
|
|
1247
|
+
stats["skipped"] += 1
|
|
1248
|
+
skipped_count += 1
|
|
1249
|
+
|
|
1250
|
+
progress.update(task, advance=1)
|
|
1251
|
+
|
|
1252
|
+
# Phase 2: Submit
|
|
1253
|
+
if not submissions:
|
|
1254
|
+
console.print()
|
|
1255
|
+
console.print("[dim]No valid sessions to submit.[/dim]")
|
|
1256
|
+
return {
|
|
1257
|
+
"total": len(transcripts),
|
|
1258
|
+
"submitted": 0,
|
|
1259
|
+
"skipped": len(transcripts),
|
|
1260
|
+
"failed": 0,
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
# Reset submitted count for submission tracking
|
|
1264
|
+
stats["submitted"] = 0
|
|
1265
|
+
stats["skipped"] = skipped_count
|
|
1266
|
+
|
|
1267
|
+
client = RankClient()
|
|
1268
|
+
|
|
1269
|
+
# First, try to detect if batch API is available
|
|
1270
|
+
batch_available = False
|
|
1271
|
+
if submissions:
|
|
1272
|
+
try:
|
|
1273
|
+
# Try a small batch first
|
|
1274
|
+
test_batch = submissions[:1]
|
|
1275
|
+
client.submit_sessions_batch(test_batch)
|
|
1276
|
+
batch_available = True
|
|
1277
|
+
except Exception:
|
|
1278
|
+
batch_available = False
|
|
1279
|
+
|
|
1280
|
+
if batch_available:
|
|
1281
|
+
# Use batch submission
|
|
1282
|
+
batch_count = (len(submissions) + batch_size - 1) // batch_size
|
|
1283
|
+
console.print()
|
|
1284
|
+
console.print(f"[cyan]Phase 2: Submitting {len(submissions)} session(s) [dim](batch mode)[/dim][/cyan]")
|
|
1285
|
+
console.print(f"[dim]Batch size: {batch_size} | Batches: {batch_count}[/dim]")
|
|
1286
|
+
console.print()
|
|
1287
|
+
|
|
1288
|
+
batch_submitted = 0
|
|
1289
|
+
batch_skipped = 0
|
|
1290
|
+
batch_failed = 0
|
|
1291
|
+
|
|
1292
|
+
with Progress(
|
|
1293
|
+
SpinnerColumn(),
|
|
1294
|
+
TextColumn("[progress.description]{task.description}"),
|
|
1295
|
+
BarColumn(),
|
|
1296
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
1297
|
+
TextColumn("[dim]({task.completed}/{task.total})[/dim]"),
|
|
1298
|
+
console=console,
|
|
1299
|
+
) as progress:
|
|
1300
|
+
task = progress.add_task("Submitting batches", total=batch_count)
|
|
1301
|
+
|
|
1302
|
+
for i in range(0, len(submissions), batch_size):
|
|
1303
|
+
batch = submissions[i : i + batch_size]
|
|
1304
|
+
|
|
1305
|
+
try:
|
|
1306
|
+
response = client.submit_sessions_batch(batch)
|
|
1307
|
+
|
|
1308
|
+
if response.get("success"):
|
|
1309
|
+
batch_submitted += response.get("succeeded", 0)
|
|
1310
|
+
batch_failed += response.get("failed", 0)
|
|
1311
|
+
|
|
1312
|
+
# Check individual results for duplicates/skipped
|
|
1313
|
+
for result in response.get("results", []):
|
|
1314
|
+
if not result.get("success"):
|
|
1315
|
+
error_msg = result.get("error", "").lower()
|
|
1316
|
+
if "duplicate" in error_msg or "already" in error_msg:
|
|
1317
|
+
batch_skipped += 1
|
|
1318
|
+
batch_failed -= 1
|
|
1319
|
+
else:
|
|
1320
|
+
batch_failed += len(batch)
|
|
1321
|
+
|
|
1322
|
+
except Exception:
|
|
1323
|
+
batch_failed += len(batch)
|
|
1324
|
+
|
|
1325
|
+
progress.update(task, advance=1)
|
|
1326
|
+
|
|
1327
|
+
stats["submitted"] = batch_submitted
|
|
1328
|
+
stats["skipped"] += batch_skipped
|
|
1329
|
+
stats["failed"] = batch_failed
|
|
1330
|
+
else:
|
|
1331
|
+
# Fall back to individual submissions (sequential)
|
|
1332
|
+
console.print()
|
|
1333
|
+
console.print(f"[cyan]Phase 2: Submitting {len(submissions)} session(s) [dim](individual mode)[/dim][/cyan]")
|
|
1334
|
+
console.print()
|
|
1335
|
+
|
|
1336
|
+
from time import sleep
|
|
1337
|
+
|
|
1338
|
+
from moai_adk.rank.client import ApiError, RankClientError
|
|
1339
|
+
|
|
1340
|
+
# Track errors for debugging
|
|
1341
|
+
error_samples: list[str] = []
|
|
1342
|
+
error_type_counts: dict[str, int] = {}
|
|
1343
|
+
error_lock = Lock()
|
|
1344
|
+
|
|
1345
|
+
# Ensure log directory exists and set up logging
|
|
1346
|
+
RankConfig.ensure_config_dir()
|
|
1347
|
+
log_file = RankConfig.CONFIG_DIR / "sync_errors.log"
|
|
1348
|
+
|
|
1349
|
+
# Clear previous log
|
|
1350
|
+
if log_file.exists():
|
|
1351
|
+
log_file.unlink()
|
|
1352
|
+
|
|
1353
|
+
import logging
|
|
1354
|
+
|
|
1355
|
+
logging.basicConfig(
|
|
1356
|
+
filename=str(log_file),
|
|
1357
|
+
level=logging.ERROR,
|
|
1358
|
+
format="%(asctime)s - %(message)s",
|
|
1359
|
+
force=True,
|
|
1360
|
+
)
|
|
1361
|
+
|
|
1362
|
+
# Use sequential submission with rate limiting to avoid server limits
|
|
1363
|
+
submission_delay = 0.1 # 100ms between submissions
|
|
1364
|
+
|
|
1365
|
+
console.print(f"[dim]Rate limit: {submission_delay}s between submissions[/dim]")
|
|
1366
|
+
console.print()
|
|
1367
|
+
|
|
1368
|
+
# Estimate time
|
|
1369
|
+
estimated_seconds = len(submissions) * submission_delay
|
|
1370
|
+
console.print(f"[dim]Estimated time: ~{int(estimated_seconds / 60)}m {int(estimated_seconds % 60)}s[/dim]")
|
|
1371
|
+
console.print()
|
|
1372
|
+
|
|
1373
|
+
with Progress(
|
|
1374
|
+
SpinnerColumn(),
|
|
1375
|
+
TextColumn("[progress.description]{task.description}"),
|
|
1376
|
+
BarColumn(),
|
|
1377
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
1378
|
+
TextColumn("[dim]({task.completed}/{task.total})[/dim]"),
|
|
1379
|
+
TextColumn("[dim]|[/dim]"),
|
|
1380
|
+
TextColumn("[green]✓ {task.fields[submitted]}[/green]"),
|
|
1381
|
+
TextColumn("[dim]○ {task.fields[skipped]}[/dim]"),
|
|
1382
|
+
TextColumn("[red]✗ {task.fields[failed]}[/red]"),
|
|
1383
|
+
console=console,
|
|
1384
|
+
) as progress:
|
|
1385
|
+
task = progress.add_task(
|
|
1386
|
+
"Submitting sessions",
|
|
1387
|
+
total=len(submissions),
|
|
1388
|
+
submitted=0,
|
|
1389
|
+
skipped=0,
|
|
1390
|
+
failed=0,
|
|
1391
|
+
)
|
|
1392
|
+
|
|
1393
|
+
for idx, submission in enumerate(submissions):
|
|
1394
|
+
try:
|
|
1395
|
+
response = client.submit_session(submission)
|
|
1396
|
+
|
|
1397
|
+
# Handle nested response structure
|
|
1398
|
+
session_id: str | None = None
|
|
1399
|
+
if isinstance(response, dict):
|
|
1400
|
+
# Try direct access
|
|
1401
|
+
session_id = response.get("sessionId")
|
|
1402
|
+
# If not found, check nested data structure
|
|
1403
|
+
if not session_id and isinstance(response.get("data"), dict):
|
|
1404
|
+
session_id = response["data"].get("sessionId")
|
|
1405
|
+
|
|
1406
|
+
if session_id:
|
|
1407
|
+
stats["submitted"] += 1
|
|
1408
|
+
else:
|
|
1409
|
+
# Check for duplicate/already recorded
|
|
1410
|
+
msg = str(response.get("message", "")).lower()
|
|
1411
|
+
if "duplicate" in msg or "already" in msg:
|
|
1412
|
+
stats["skipped"] += 1
|
|
1413
|
+
else:
|
|
1414
|
+
# Log unexpected failure
|
|
1415
|
+
with error_lock:
|
|
1416
|
+
if len(error_samples) < 5:
|
|
1417
|
+
error_samples.append(f"No sessionId: {response}")
|
|
1418
|
+
stats["failed"] += 1
|
|
1419
|
+
|
|
1420
|
+
except (ApiError, RankClientError) as e:
|
|
1421
|
+
error_msg = str(e).lower()
|
|
1422
|
+
if "duplicate" in error_msg or "already" in error_msg:
|
|
1423
|
+
stats["skipped"] += 1
|
|
1424
|
+
elif "too soon" in error_msg or "rate limit" in error_msg:
|
|
1425
|
+
# Rate limit hit - wait and retry
|
|
1426
|
+
with error_lock:
|
|
1427
|
+
if len(error_samples) < 5:
|
|
1428
|
+
error_samples.append(f"Rate limited: {e}")
|
|
1429
|
+
sleep(2) # Wait before retry
|
|
1430
|
+
try:
|
|
1431
|
+
response = client.submit_session(submission)
|
|
1432
|
+
session_id = None
|
|
1433
|
+
if isinstance(response, dict):
|
|
1434
|
+
session_id = response.get("sessionId")
|
|
1435
|
+
if not session_id and isinstance(response.get("data"), dict):
|
|
1436
|
+
session_id = response["data"].get("sessionId")
|
|
1437
|
+
if session_id:
|
|
1438
|
+
stats["submitted"] += 1
|
|
1439
|
+
else:
|
|
1440
|
+
stats["failed"] += 1
|
|
1441
|
+
except Exception:
|
|
1442
|
+
stats["failed"] += 1
|
|
1443
|
+
else:
|
|
1444
|
+
# Log API errors
|
|
1445
|
+
with error_lock:
|
|
1446
|
+
if len(error_samples) < 20:
|
|
1447
|
+
error_samples.append(f"API Error: {e}")
|
|
1448
|
+
# Categorize error type
|
|
1449
|
+
err_str = str(e)
|
|
1450
|
+
if "too soon" in err_str.lower():
|
|
1451
|
+
error_type_counts["rate_limited"] = error_type_counts.get("rate_limited", 0) + 1
|
|
1452
|
+
elif "validation" in err_str.lower():
|
|
1453
|
+
error_type_counts["validation"] = error_type_counts.get("validation", 0) + 1
|
|
1454
|
+
else:
|
|
1455
|
+
error_type_counts["other_api"] = error_type_counts.get("other_api", 0) + 1
|
|
1456
|
+
logging.error(f"API Error: {e}")
|
|
1457
|
+
stats["failed"] += 1
|
|
1458
|
+
except Exception as e:
|
|
1459
|
+
# Log unexpected errors
|
|
1460
|
+
with error_lock:
|
|
1461
|
+
if len(error_samples) < 20:
|
|
1462
|
+
error_samples.append(f"Unexpected: {e}")
|
|
1463
|
+
error_type_counts["unexpected"] = error_type_counts.get("unexpected", 0) + 1
|
|
1464
|
+
logging.error(f"Unexpected error: {e}")
|
|
1465
|
+
stats["failed"] += 1
|
|
1466
|
+
|
|
1467
|
+
# Update progress bar
|
|
1468
|
+
progress.update(
|
|
1469
|
+
task,
|
|
1470
|
+
advance=1,
|
|
1471
|
+
submitted=stats["submitted"],
|
|
1472
|
+
skipped=stats["skipped"],
|
|
1473
|
+
failed=stats["failed"],
|
|
1474
|
+
)
|
|
1475
|
+
|
|
1476
|
+
# Small delay to avoid rate limiting
|
|
1477
|
+
if idx < len(submissions) - 1:
|
|
1478
|
+
sleep(submission_delay)
|
|
1479
|
+
|
|
1480
|
+
# Show error samples if there were failures
|
|
1481
|
+
if error_samples:
|
|
1482
|
+
console.print()
|
|
1483
|
+
console.print("[yellow]Sample errors:[/yellow]")
|
|
1484
|
+
for i, error in enumerate(error_samples, 1):
|
|
1485
|
+
console.print(f" [dim]{i}. {error}[/dim]")
|
|
1486
|
+
|
|
1487
|
+
# Show error type breakdown
|
|
1488
|
+
if error_type_counts:
|
|
1489
|
+
console.print()
|
|
1490
|
+
console.print("[yellow]Error breakdown:[/yellow]")
|
|
1491
|
+
for error_type, count in sorted(error_type_counts.items(), key=lambda x: -x[1]):
|
|
1492
|
+
console.print(f" [dim]{error_type}: {count}[/dim]")
|
|
1493
|
+
|
|
1494
|
+
# Print summary
|
|
1495
|
+
console.print()
|
|
1496
|
+
console.print("[bold]Sync Complete[/bold]")
|
|
1497
|
+
console.print(f" [green]✓ Submitted:[/green] {stats['submitted']}")
|
|
1498
|
+
console.print(f" [dim]○ Skipped:[/dim] {stats['skipped']} [dim](no usage or duplicate)[/dim]")
|
|
1499
|
+
if stats["failed"] > 0:
|
|
1500
|
+
console.print(f" [red]✗ Failed:[/red] {stats['failed']}")
|
|
1501
|
+
console.print()
|
|
1502
|
+
|
|
1503
|
+
return stats
|